diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 788e4e6..fa70e0c 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -249,6 +249,11 @@ void CommandDistributor::broadcastLoco(byte slot) { } void CommandDistributor::broadcastPower() { + char pstr[] = "? x"; + for(byte t=0; t\n"),pstr); + bool main=TrackManager::getMainPower()==POWERMODE::ON; bool prog=TrackManager::getProgPower()==POWERMODE::ON; bool join=TrackManager::isJoined(); @@ -271,7 +276,7 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) { } void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr) { - broadcastReply(COMMAND_TYPE, format,trackLetter, dcAddr); + broadcastReply(COMMAND_TYPE, format, trackLetter, dcAddr); } void CommandDistributor::broadcastRouteState(uint16_t routeId, byte state ) { diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 369d941..2e2a3c1 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -553,131 +553,66 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case '1': // POWERON <1 [MAIN|PROG|JOIN]> { - bool main=false; - bool prog=false; - bool join=false; - bool singletrack=false; - //byte t=0; - if (params > 1) break; - if (params==0) { // All - main=true; - prog=true; - } - if (params==1) { - if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN> - main=true; + if (params > 1) break; + if (params==0) { // All + TrackManager::setTrackPower(TRACK_MODE_ALL, POWERMODE::ON); + } + if (params==1) { + if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN> + TrackManager::setTrackPower(TRACK_MODE_MAIN, POWERMODE::ON); } #ifndef DISABLE_PROG else if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN> - main=true; - prog=true; - join=true; + TrackManager::setJoin(true); + TrackManager::setTrackPower(TRACK_MODE_MAIN|TRACK_MODE_PROG, POWERMODE::ON); } else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG> - prog=true; + TrackManager::setJoin(false); + TrackManager::setTrackPower(TRACK_MODE_PROG, POWERMODE::ON); } #endif - //else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H> - byte t = (p[0] - 'A'); - //DIAG(F("Processing track - %d "), t); - if (TrackManager::isProg(t)) { - main = false; - prog = true; - } - else - { - main=true; - prog=false; - } - singletrack=true; - if (main) TrackManager::setTrackPower(false, false, POWERMODE::ON, t); - if (prog) TrackManager::setTrackPower(true, false, POWERMODE::ON, t); - - StringFormatter::send(stream, F("<1 %c>\n"), t+'A'); - //CommandDistributor::broadcastPower(); - //TrackManager::streamTrackState(NULL,t); - return; + byte t = (p[0] - 'A'); + TrackManager::setTrackPower(POWERMODE::ON, t); + //StringFormatter::send(stream, F("\n"), t+'A'); } - else break; // will reply - } - - if (!singletrack) { - TrackManager::setJoin(join); - if (join) TrackManager::setJoinPower(POWERMODE::ON); - else { - if (main) TrackManager::setMainPower(POWERMODE::ON); - if (prog) TrackManager::setProgPower(POWERMODE::ON); - } - CommandDistributor::broadcastPower(); + } + CommandDistributor::broadcastPower(); + //TrackManager::streamTrackState(NULL,t); - return; - } + return; + } - } - case '0': // POWEROFF <0 [MAIN | PROG] > { - bool main=false; - bool prog=false; - bool singletrack=false; - //byte t=0; - if (params > 1) break; - if (params==0) { // All - main=true; - prog=true; - } - if (params==1) { - if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN> - main=true; - } + if (params > 1) break; + if (params==0) { // All + TrackManager::setJoin(false); + TrackManager::setTrackPower(TRACK_MODE_ALL, POWERMODE::OFF); + } + if (params==1) { + if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN> + TrackManager::setJoin(false); + TrackManager::setTrackPower(TRACK_MODE_MAIN, POWERMODE::OFF); + } #ifndef DISABLE_PROG else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG> - prog=true; + TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off + TrackManager::setTrackPower(TRACK_MODE_PROG, POWERMODE::OFF); } #endif - //else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> - else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H> - byte t = (p[0] - 'A'); - //DIAG(F("Processing track - %d "), t); - if (TrackManager::isProg(t)) { - main = false; - prog = true; - } - else - { - main=true; - prog=false; - } - singletrack=true; - TrackManager::setJoin(false); - if (main) TrackManager::setTrackPower(false, false, POWERMODE::OFF, t); - if (prog) { - TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off - TrackManager::setTrackPower(true, false, POWERMODE::OFF, t); - } - StringFormatter::send(stream, F("<0 %c>\n"), t+'A'); - //CommandDistributor::broadcastPower(); - //TrackManager::streamTrackState(NULL, t); - return; - } - - else break; // will reply + else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H> + byte t = (p[0] - 'A'); + TrackManager::setJoin(false); + TrackManager::setTrackPower(POWERMODE::OFF, t); + //StringFormatter::send(stream, F("\n"), t+'A'); } - - if (!singletrack) { - TrackManager::setJoin(false); - - if (main) TrackManager::setMainPower(POWERMODE::OFF); - if (prog) { - TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off - TrackManager::setProgPower(POWERMODE::OFF); - } - CommandDistributor::broadcastPower(); - return; - } - } + else break; // will reply + } + CommandDistributor::broadcastPower(); + return; + } case '!': // ESTOP ALL DCC::setThrottle(0,1,1); // this broadcasts speed 1(estop) and sets all reminders to speed 1. diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 305be22..ccd4d9e 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -636,10 +636,10 @@ void RMFT2::loop2() { //byte thistrack=getOperand(1); switch (operand) { case TRACK_POWER_0: - TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), false, POWERMODE::OFF, getOperand(1)); + TrackManager::setTrackPower(POWERMODE::OFF, getOperand(1)); break; case TRACK_POWER_1: - TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), false, POWERMODE::ON, getOperand(1)); + TrackManager::setTrackPower(POWERMODE::ON, getOperand(1)); break; } @@ -650,7 +650,7 @@ void RMFT2::loop2() { // If DC/DCX use my loco for DC address { TRACK_MODE mode = (TRACK_MODE)(operand>>8); - int16_t cab=(mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX) ? loco : 0; + int16_t cab=(mode & TRACK_MODE_DC) ? loco : 0; TrackManager::setTrackMode(operand & 0x0F, mode, cab); } break; diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index d2a7fd1..3f9b1e1 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202310230944Z" +#define GITHUB_SHA "devel-202311141013Z" diff --git a/MotorDriver.cpp b/MotorDriver.cpp index af322b8..bd25be4 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -605,6 +605,10 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { DIAG(F("TRACK %c ALERT FAULT"), trackno + 'A'); } setPower(POWERMODE::ALERT); + if ((trackMode & TRACK_MODE_AUTOINV) && (trackMode & (TRACK_MODE_MAIN|TRACK_MODE_EXT|TRACK_MODE_BOOST))){ + DIAG(F("TRACK %c INVERT"), trackno + 'A'); + invertOutput(); + } break; } // all well diff --git a/MotorDriver.h b/MotorDriver.h index 20a91d3..19bfd13 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -3,7 +3,7 @@ * © 2021 Mike S * © 2021 Fred Decker * © 2020 Chris Harlow - * © 2022 Harald Barth + * © 2022,2023 Harald Barth * All rights reserved. * * This file is part of CommandStation-EX @@ -28,8 +28,15 @@ #include "DCCTimer.h" // use powers of two so we can do logical and/or on the track modes in if clauses. +// RACK_MODE_DCX is (TRACK_MODE_DC|TRACK_MODE_INV) +template inline T operator~ (T a) { return (T)~(int)a; } +template inline T operator| (T a, T b) { return (T)((int)a | (int)b); } +template inline T operator& (T a, T b) { return (T)((int)a & (int)b); } +template inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); } enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4, - TRACK_MODE_DC = 8, TRACK_MODE_DCX = 16, TRACK_MODE_EXT = 32}; + TRACK_MODE_DC = 8, TRACK_MODE_EXT = 16, TRACK_MODE_BOOST = 32, + TRACK_MODE_ALL = 62, // only to operate all tracks + TRACK_MODE_INV = 64, TRACK_MODE_DCX = 72 /*DC + INV*/, TRACK_MODE_AUTOINV = 128}; #define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH #define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW @@ -148,7 +155,9 @@ class MotorDriver { // otherwise the call from interrupt context can undo whatever we do // from outside interrupt void setBrake( bool on, bool interruptContext=false); - __attribute__((always_inline)) inline void setSignal( bool high) { + __attribute__((always_inline)) inline void setSignal( bool high) { + if (invertPhase) + high = !high; if (trackPWM) { DCCTimer::setPWM(signalPin,high); } @@ -168,6 +177,12 @@ class MotorDriver { pinMode(signalPin, OUTPUT); else pinMode(signalPin, INPUT); + if (signalPin2 != UNUSED_PIN) { + if (on) + pinMode(signalPin2, OUTPUT); + else + pinMode(signalPin2, INPUT); + } }; inline pinpair getSignalPin() { return pinpair(signalPin,signalPin2); }; void setDCSignal(byte speedByte); @@ -232,6 +247,32 @@ class MotorDriver { #endif inline void setMode(TRACK_MODE m) { trackMode = m; + invertOutput(trackMode & TRACK_MODE_INV); + }; + inline void invertOutput() { // toggles output inversion + invertPhase = !invertPhase; + invertOutput(invertPhase); + }; + inline void invertOutput(bool b) { // sets output inverted or not + if (b) + invertPhase = 1; + else + invertPhase = 0; +#if defined(ARDUINO_ARCH_ESP32) + pinpair p = getSignalPin(); + uint32_t *outreg = (uint32_t *)(GPIO_FUNC0_OUT_SEL_CFG_REG + 4*p.pin); + if (invertPhase) // set or clear the invert bit in the gpio out register + *outreg |= ((uint32_t)0x1 << GPIO_FUNC0_OUT_INV_SEL_S); + else + *outreg &= ~((uint32_t)0x1 << GPIO_FUNC0_OUT_INV_SEL_S); + if (p.invpin != UNUSED_PIN) { + outreg = (uint32_t *)(GPIO_FUNC0_OUT_SEL_CFG_REG + 4*p.invpin); + if (invertPhase) // clear or set the invert bit in the gpio out register + *outreg &= ~((uint32_t)0x1 << GPIO_FUNC0_OUT_INV_SEL_S); + else + *outreg |= ((uint32_t)0x1 << GPIO_FUNC0_OUT_INV_SEL_S); + } +#endif }; inline TRACK_MODE getMode() { return trackMode; @@ -263,7 +304,7 @@ class MotorDriver { bool invertBrake; // brake pin passed as negative means pin is inverted bool invertPower; // power pin passed as negative means pin is inverted bool invertFault; // fault pin passed as negative means pin is inverted - + bool invertPhase = 0; // phase of out pin is inverted // Raw to milliamp conversion factors avoiding float data types. // Milliamps=rawADCreading * sensefactorInternal / senseScale // diff --git a/TrackManager.cpp b/TrackManager.cpp index dedf45e..7c1e651 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -1,6 +1,6 @@ /* * © 2022 Chris Harlow - * © 2022 Harald Barth + * © 2022,2023 Harald Barth * © 2023 Colin Murdoch * All rights reserved. * @@ -45,6 +45,11 @@ const int16_t HASH_KEYWORD_DC = 2183; const int16_t HASH_KEYWORD_DCX = 6463; // DC reversed polarity const int16_t HASH_KEYWORD_EXT = 8201; // External DCC signal const int16_t HASH_KEYWORD_A = 65; // parser makes single chars the ascii. +const int16_t HASH_KEYWORD_AUTO = -5457; +#ifdef BOOSTER_INPUT +const int16_t HASH_KEYWORD_BOOST = 11269; +#endif +const int16_t HASH_KEYWORD_INV = 11857; MotorDriver * TrackManager::track[MAX_TRACKS]; int16_t TrackManager::trackDCAddr[MAX_TRACKS]; @@ -87,7 +92,7 @@ void TrackManager::sampleCurrent() { if (!waiting) { // look for a valid track to sample or until we are around while (true) { - if (track[tr]->getMode() & ( TRACK_MODE_MAIN|TRACK_MODE_PROG|TRACK_MODE_DC|TRACK_MODE_DCX|TRACK_MODE_EXT )) { + if (track[tr]->getMode() & ( TRACK_MODE_MAIN|TRACK_MODE_PROG|TRACK_MODE_DC|TRACK_MODE_BOOST|TRACK_MODE_EXT )) { track[tr]->startCurrentFromHW(); // for scope debug track[1]->setBrake(1); waiting = true; @@ -197,8 +202,8 @@ void TrackManager::setPROGSignal( bool on) { void TrackManager::setDCSignal(int16_t cab, byte speedbyte) { FOR_EACH_TRACK(t) { if (trackDCAddr[t]!=cab && cab != 0) continue; - if (track[t]->getMode()==TRACK_MODE_DC) track[t]->setDCSignal(speedbyte); - else if (track[t]->getMode()==TRACK_MODE_DCX) track[t]->setDCSignal(speedbyte ^ 128); + if (track[t]->getMode() & TRACK_MODE_DC) + track[t]->setDCSignal(speedbyte); } } @@ -207,7 +212,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr //DIAG(F("Track=%c Mode=%d"),trackToSet+'A', mode); // DC tracks require a motorDriver that can set brake! - if (mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX) { + if (mode & TRACK_MODE_DC) { #if defined(ARDUINO_AVR_UNO) DIAG(F("Uno has no PWM timers available for DC")); return false; @@ -223,21 +228,37 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr pinpair p = track[trackToSet]->getSignalPin(); //DIAG(F("Track=%c remove pin %d"),trackToSet+'A', p.pin); gpio_reset_pin((gpio_num_t)p.pin); - pinMode(p.pin, OUTPUT); // gpio_reset_pin may reset to input if (p.invpin != UNUSED_PIN) { //DIAG(F("Track=%c remove ^pin %d"),trackToSet+'A', p.invpin); gpio_reset_pin((gpio_num_t)p.invpin); - pinMode(p.invpin, OUTPUT); // gpio_reset_pin may reset to input } +#ifdef BOOSTER_INPUT + if (mode & TRACK_MODE_BOOST) { + //DIAG(F("Track=%c mode boost pin %d"),trackToSet+'A', p.pin); + pinMode(BOOSTER_INPUT, INPUT); + gpio_matrix_in(26, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback + gpio_matrix_out(p.pin, SIG_IN_FUNC228_IDX, false, false); + if (p.invpin != UNUSED_PIN) { + gpio_matrix_out(p.invpin, SIG_IN_FUNC228_IDX, true /*inverted*/, false); + } + } else // elseif clause continues +#endif + if (mode & (TRACK_MODE_MAIN | TRACK_MODE_PROG | TRACK_MODE_DC)) { + // gpio_reset_pin may reset to input + pinMode(p.pin, OUTPUT); + if (p.invpin != UNUSED_PIN) + pinMode(p.invpin, OUTPUT); + } + #endif #ifndef DISABLE_PROG - if (mode==TRACK_MODE_PROG) { + if (mode & TRACK_MODE_PROG) { #else if (false) { #endif // only allow 1 track to be prog FOR_EACH_TRACK(t) - if (track[t]->getMode()==TRACK_MODE_PROG && t != trackToSet) { + if ( (track[t]->getMode() & TRACK_MODE_PROG) && t != trackToSet) { track[t]->setPower(POWERMODE::OFF); track[t]->setMode(TRACK_MODE_NONE); track[t]->makeProgTrack(false); // revoke prog track special handling @@ -255,16 +276,20 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr // state, otherwise trains run away or just dont move. // This can be done BEFORE the PWM-Timer evaluation (methinks) - if (!(mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX)) { + if (!(mode & TRACK_MODE_DC)) { // DCC tracks need to have set the PWM to zero or they will not work. track[trackToSet]->detachDCSignal(); track[trackToSet]->setBrake(false); } - // EXT is a special case where the signal pin is - // turned off. So unless that is set, the signal - // pin should be turned on - track[trackToSet]->enableSignal(mode != TRACK_MODE_EXT); + // BOOST: + // Leave it as is + // otherwise: + // EXT is a special case where the signal pin is + // turned off. So unless that is set, the signal + // pin should be turned on + if (!(mode & TRACK_MODE_BOOST)) + track[trackToSet]->enableSignal(!(mode & TRACK_MODE_EXT)); #ifndef ARDUINO_ARCH_ESP32 // re-evaluate HighAccuracy mode @@ -274,7 +299,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr // DC tracks must not have the DCC PWM switched on // so we globally turn it off if one of the PWM // capable tracks is now DC or DCX. - if (track[t]->getMode()==TRACK_MODE_DC || track[t]->getMode()==TRACK_MODE_DCX) { + if (track[t]->getMode() & TRACK_MODE_DC) { if (track[t]->isPWMCapable()) { canDo=false; // this track is capable but can not run PWM break; // in this mode, so abort and prevent globally below @@ -282,7 +307,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr track[t]->trackPWM=false; // this track sure can not run with PWM //DIAG(F("Track %c trackPWM 0 (not capable)"), t+'A'); } - } else if (track[t]->getMode()==TRACK_MODE_MAIN || track[t]->getMode()==TRACK_MODE_PROG) { + } else if (track[t]->getMode() & (TRACK_MODE_MAIN |TRACK_MODE_PROG)) { track[t]->trackPWM = track[t]->isPWMCapable(); // trackPWM is still a guess here //DIAG(F("Track %c trackPWM %d"), t+'A', track[t]->trackPWM); canDo &= track[t]->trackPWM; @@ -300,10 +325,12 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr #else // For ESP32 we just reinitialize the DCC Waveform DCCWaveform::begin(); + // setMode() again AFTER Waveform::begin() of ESP32 fixes INVERTED signal + track[trackToSet]->setMode(mode); #endif // This block must be AFTER the PWM-Timer modifications - if (mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX) { + if (mode & TRACK_MODE_DC) { // DC tracks need to be given speed of the throttle for that cab address // otherwise will not match other tracks on same cab. // This also needs to allow for inverted DCX @@ -312,7 +339,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr // Normal running tracks are set to the global power state track[trackToSet]->setPower( - (mode==TRACK_MODE_MAIN || mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX || mode==TRACK_MODE_EXT) ? + (mode & (TRACK_MODE_MAIN | TRACK_MODE_DC | TRACK_MODE_EXT | TRACK_MODE_BOOST)) ? mainPowerGuess : POWERMODE::OFF); //DIAG(F("TrackMode=%d"),mode); return true; @@ -320,8 +347,6 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr void TrackManager::applyDCSpeed(byte t) { uint8_t speedByte=DCC::getThrottleSpeedByte(trackDCAddr[t]); - if (track[t]->getMode()==TRACK_MODE_DCX) - speedByte = speedByte ^ 128; // reverse direction bit track[t]->setDCSignal(speedByte); } @@ -353,12 +378,21 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) if (params==2 && p[1]==HASH_KEYWORD_EXT) // <= id EXT> return setTrackMode(p[0],TRACK_MODE_EXT); +#ifdef BOOSTER_INPUT + if (params==2 && p[1]==HASH_KEYWORD_BOOST) // <= id BOOST> + return setTrackMode(p[0],TRACK_MODE_BOOST); +#endif + if (params==2 && p[1]==HASH_KEYWORD_AUTO) // <= id AUTO> + return setTrackMode(p[0], track[p[0]]->getMode() | TRACK_MODE_AUTOINV); + + if (params==2 && p[1]==HASH_KEYWORD_INV) // <= id AUTO> + return setTrackMode(p[0], track[p[0]]->getMode() | TRACK_MODE_INV); if (params==3 && p[1]==HASH_KEYWORD_DC && p[2]>0) // <= id DC cab> return setTrackMode(p[0],TRACK_MODE_DC,p[2]); if (params==3 && p[1]==HASH_KEYWORD_DCX && p[2]>0) // <= id DCX cab> - return setTrackMode(p[0],TRACK_MODE_DCX,p[2]); + return setTrackMode(p[0],TRACK_MODE_DC|TRACK_MODE_INV,p[2]); return false; } @@ -366,36 +400,43 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) void TrackManager::streamTrackState(Print* stream, byte t) { // null stream means send to commandDistributor for broadcast if (track[t]==NULL) return; - auto format=F(""); - bool pstate = TrackManager::isPowerOn(t); - - switch(track[t]->getMode()) { - case TRACK_MODE_MAIN: - if (pstate) {format=F("<= %c MAIN ON>\n");} else {format = F("<= %c MAIN OFF>\n");} - break; + auto format=F("<= %d XXX>\n"); + TRACK_MODE tm = track[t]->getMode(); + if (tm & TRACK_MODE_MAIN) { + if(tm & TRACK_MODE_AUTOINV) + format=F("<= %c MAIN A>\n"); + else if (tm & TRACK_MODE_INV) + format=F("<= %c MAIN I>\n"); + else + format=F("<= %c MAIN>\n"); + } #ifndef DISABLE_PROG - case TRACK_MODE_PROG: - if (pstate) {format=F("<= %c PROG ON>\n");} else {format=F("<= %c PROG OFF>\n");} - break; + else if (tm & TRACK_MODE_PROG) + format=F("<= %c PROG>\n"); #endif - case TRACK_MODE_NONE: - if (pstate) {format=F("<= %c NONE ON>\n");} else {format=F("<= %c NONE OFF>\n");} - break; - case TRACK_MODE_EXT: - if (pstate) {format=F("<= %c EXT ON>\n");} else {format=F("<= %c EXT OFF>\n");} - break; - case TRACK_MODE_DC: - if (pstate) {format=F("<= %c DC %d ON>\n");} else {format=F("<= %c DC %d OFF>\n");} - break; - case TRACK_MODE_DCX: - if (pstate) {format=F("<= %c DCX %d ON>\n");} else {format=F("<= %c DCX %d OFF>\n");} - break; - default: - break; // unknown, dont care + else if (tm & TRACK_MODE_NONE) + format=F("<= %c NONE>\n"); + else if(tm & TRACK_MODE_EXT) + format=F("<= %c EXT>\n"); + else if(tm & TRACK_MODE_BOOST) { + if(tm & TRACK_MODE_AUTOINV) + format=F("<= %c B A>\n"); + else if (tm & TRACK_MODE_INV) + format=F("<= %c B I>\n"); + else + format=F("<= %c B>\n"); + } + else if (tm & TRACK_MODE_DC) { + if (tm & TRACK_MODE_INV) + format=F("<= %c DCX %d>\n"); + else + format=F("<= %c DC %d>\n"); } - if (stream) StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); - else CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); + if (stream) + StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); + else + CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); } @@ -411,13 +452,13 @@ void TrackManager::loop() { if (nextCycleTrack>lastTrack) nextCycleTrack=0; if (track[nextCycleTrack]==NULL) return; MotorDriver * motorDriver=track[nextCycleTrack]; - bool useProgLimit=dontLimitProg? false: track[nextCycleTrack]->getMode()==TRACK_MODE_PROG; + bool useProgLimit=dontLimitProg ? false : (bool)(track[nextCycleTrack]->getMode() & TRACK_MODE_PROG); motorDriver->checkPowerOverload(useProgLimit, nextCycleTrack); } MotorDriver * TrackManager::getProgDriver() { FOR_EACH_TRACK(t) - if (track[t]->getMode()==TRACK_MODE_PROG) return track[t]; + if (track[t]->getMode() & TRACK_MODE_PROG) return track[t]; return NULL; } @@ -425,63 +466,53 @@ MotorDriver * TrackManager::getProgDriver() { std::vectorTrackManager::getMainDrivers() { std::vector v; FOR_EACH_TRACK(t) - if (track[t]->getMode()==TRACK_MODE_MAIN) v.push_back(track[t]); + if (track[t]->getMode() & TRACK_MODE_MAIN) v.push_back(track[t]); return v; } #endif -void TrackManager::setPower2(bool setProg,bool setJoin, POWERMODE mode) { - if (!setProg) mainPowerGuess=mode; - FOR_EACH_TRACK(t) { - - TrackManager::setTrackPower(setProg, setJoin, mode, t); - +// Set track power for all tracks with this mode +void TrackManager::setTrackPower(TRACK_MODE trackmode, POWERMODE powermode) { + FOR_EACH_TRACK(t) { + MotorDriver *driver=track[t]; + if (trackmode & driver->getMode()) { + if (powermode == POWERMODE::ON) { + if (trackmode & TRACK_MODE_DC) { + driver->setBrake(true); // DC starts with brake on + applyDCSpeed(t); // speed match DCC throttles + } else { + // toggle brake before turning power on - resets overcurrent error + // on the Pololu board if brake is wired to ^D2. + driver->setBrake(true); + driver->setBrake(false); // DCC runs with brake off + } + } + driver->setPower(powermode); } - return; + } } -void TrackManager::setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byte thistrack) { +// Set track power for this track, inependent of mode +void TrackManager::setTrackPower(POWERMODE powermode, byte t) { + MotorDriver *driver=track[t]; + TRACK_MODE trackmode = driver->getMode(); + if (trackmode & TRACK_MODE_DC) { + if (powermode == POWERMODE::ON) { + driver->setBrake(true); // DC starts with brake on + applyDCSpeed(t); // speed match DCC throttles + } + } else { + if (powermode == POWERMODE::ON) { + // toggle brake before turning power on - resets overcurrent error + // on the Pololu board if brake is wired to ^D2. + driver->setBrake(true); + driver->setBrake(false); // DCC runs with brake off + } + } + driver->setPower(powermode); +} - //DIAG(F("SetTrackPower Processing Track %d"), thistrack); - MotorDriver * driver=track[thistrack]; - if (!driver) return; - - switch (track[thistrack]->getMode()) { - case TRACK_MODE_MAIN: - if (setProg) break; - // toggle brake before turning power on - resets overcurrent error - // on the Pololu board if brake is wired to ^D2. - // XXX see if we can make this conditional - driver->setBrake(true); - driver->setBrake(false); // DCC runs with brake off - driver->setPower(mode); - break; - case TRACK_MODE_DC: - case TRACK_MODE_DCX: - //DIAG(F("Processing track - %d setProg %d"), thistrack, setProg); - if (setProg || setJoin) break; - driver->setBrake(true); // DC starts with brake on - applyDCSpeed(thistrack); // speed match DCC throttles - driver->setPower(mode); - break; - case TRACK_MODE_PROG: - if (!setProg && !setJoin) break; - driver->setBrake(true); - driver->setBrake(false); - driver->setPower(mode); - break; - case TRACK_MODE_EXT: - driver->setBrake(true); - driver->setBrake(false); - driver->setPower(mode); - break; - case TRACK_MODE_NONE: - break; - } - - } - - void TrackManager::reportPowerChange(Print* stream, byte thistrack) { +void TrackManager::reportPowerChange(Print* stream, byte thistrack) { // This function is for backward JMRI compatibility only // It reports the first track only, as main, regardless of track settings. // @@ -490,12 +521,40 @@ void TrackManager::setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byt track[0]->raw2mA(track[0]->getCurrentRaw(false)), maxCurrent, maxCurrent); } +// returns state of the one and only prog track POWERMODE TrackManager::getProgPower() { - FOR_EACH_TRACK(t) - if (track[t]->getMode()==TRACK_MODE_PROG) - return track[t]->getPower(); - return POWERMODE::OFF; + FOR_EACH_TRACK(t) + if (track[t]->getMode() & TRACK_MODE_PROG) + return track[t]->getPower(); // optimize: there is max one prog track + return POWERMODE::OFF; +} + +// returns on if all are on. returns off otherwise +POWERMODE TrackManager::getMainPower() { + POWERMODE result = POWERMODE::OFF; + FOR_EACH_TRACK(t) { + if (track[t]->getMode() & TRACK_MODE_MAIN) { + POWERMODE p = track[t]->getPower(); + if (p == POWERMODE::OFF) + return POWERMODE::OFF; // done and out + if (p == POWERMODE::ON) + result = POWERMODE::ON; + } } + return result; +} + +bool TrackManager::getPower(byte t, char s[]) { + if (t > lastTrack) + return false; + if (track[t]) { + s[0] = track[t]->getPower() == POWERMODE::ON ? '1' : '0'; + s[2] = t + 'A'; + return true; + } + return false; +} + void TrackManager::reportObsoleteCurrent(Print* stream) { // This function is for backward JMRI compatibility only @@ -537,7 +596,7 @@ void TrackManager::setJoin(bool joined) { #ifdef ARDUINO_ARCH_ESP32 if (joined) { FOR_EACH_TRACK(t) { - if (track[t]->getMode()==TRACK_MODE_PROG) { + if (track[t]->getMode() & TRACK_MODE_PROG) { tempProgTrack = t; setTrackMode(t, TRACK_MODE_MAIN); break; @@ -566,7 +625,7 @@ bool TrackManager::isPowerOn(byte t) { } bool TrackManager::isProg(byte t) { - if (track[t]->getMode()==TRACK_MODE_PROG) + if (track[t]->getMode() & TRACK_MODE_PROG) return true; return false; } diff --git a/TrackManager.h b/TrackManager.h index d197751..644c45a 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -62,23 +62,22 @@ class TrackManager { static void setDCSignal(int16_t cab, byte speedbyte); static MotorDriver * getProgDriver(); #ifdef ARDUINO_ARCH_ESP32 - static std::vectorgetMainDrivers(); + static std::vectorgetMainDrivers(); #endif - static void setPower2(bool progTrack,bool joinTrack,POWERMODE mode); static void setPower(POWERMODE mode) {setMainPower(mode); setProgPower(mode);} - static void setMainPower(POWERMODE mode) {setPower2(false,false,mode);} - static void setProgPower(POWERMODE mode) {setPower2(true,false,mode);} - static void setJoinPower(POWERMODE mode) {setPower2(false,true,mode);} - static void setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byte thistrack); - + static void setTrackPower(POWERMODE mode, byte t); + static void setTrackPower(TRACK_MODE trackmode, POWERMODE powermode); + static void setMainPower(POWERMODE mode) {setTrackPower(TRACK_MODE_MAIN, mode);} + static void setProgPower(POWERMODE mode) {setTrackPower(TRACK_MODE_PROG, mode);} static const int16_t MAX_TRACKS=8; static bool setTrackMode(byte track, TRACK_MODE mode, int16_t DCaddr=0); static bool parseJ(Print * stream, int16_t params, int16_t p[]); static void loop(); - static POWERMODE getMainPower() {return mainPowerGuess;} + static POWERMODE getMainPower(); static POWERMODE getProgPower(); + static bool getPower(byte t, char s[]); static void setJoin(bool join); static bool isJoined() { return progTrackSyncMain;} static void setJoinRelayPin(byte joinRelayPin); @@ -112,7 +111,7 @@ class TrackManager { static POWERMODE mainPowerGuess; static void applyDCSpeed(byte t); - static int16_t trackDCAddr[MAX_TRACKS]; // dc address if TRACK_MODE_DC or TRACK_MODE_DCX + static int16_t trackDCAddr[MAX_TRACKS]; // dc address if TRACK_MODE_DC #ifdef ARDUINO_ARCH_ESP32 static byte tempProgTrack; // holds the prog track number during join #endif diff --git a/config.example.h b/config.example.h index 0f136f9..de31743 100644 --- a/config.example.h +++ b/config.example.h @@ -266,6 +266,12 @@ The configuration file for DCC-EX Command Station // //#define SERIAL_BT_COMMANDS +// BOOSTER PIN INPUT ON ESP32 +// On ESP32 you have the possibility to define a pin as booster input +// Arduio pin D2 is GPIO 26 on ESPDuino32 +// +//#define BOOSTER_INPUT 26 + // SABERTOOTH // // This is a very special option and only useful if you happen to have a diff --git a/version.h b/version.h index 242d730..5640feb 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.1.21" +#define VERSION "5.2.1" +// 5.2.1 - Trackmanager rework for simpler structure +// 5.2.0 - ESP32: Autoreverse and booster mode support // 5.1.21 - EXRAIL invoke multiple ON handlers for same event // 5.1.20 - EXRAIL Tidy and ROUTE_STATE, ROUTE_CAPTION // 5.1.19 - Only flag 2.2.0.0-dev as broken, not 2.2.0.0