diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index ab6b52f..ee760b0 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -161,6 +161,10 @@ void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) { #endif } +void CommandDistributor::broadcastTurntable(int16_t id, uint8_t position, bool moving) { + broadcastReply(COMMAND_TYPE, F("\n"), id, position, moving); +} + void CommandDistributor::broadcastClockTime(int16_t time, int8_t rate) { // The JMRI clock command is of the form : PFT65871<;>4 // The CS broadcast is of the form "\n")); return; +// No turntables without HAL support +#ifndef IO_NO_HAL + case HASH_KEYWORD_O: // + for (Turntable * tto=Turntable::first(); tto; tto=tto->next()) { + if (tto->isHidden()) continue; + StringFormatter::send(stream, F(" %d"),tto->getId()); + } + StringFormatter::send(stream, F(">\n")); + } else { // + Turntable *tto=Turntable::get(id); + if (!tto || tto->isHidden()) { + StringFormatter::send(stream, F(" %d X>\n"), id); + } else { + uint8_t pos = tto->getPosition(); + uint8_t type = tto->isEXTT(); + uint8_t posCount = tto->getPositionCount(); + const FSH *todesc = NULL; +#ifdef EXRAIL_ACTIVE + todesc = RMFT2::getTurntableDescription(id); +#endif + if (todesc == NULL) todesc = F(""); + StringFormatter::send(stream, F(" %d %d %d %d \"%S\">\n"), id, type, pos, posCount, todesc); + } + } + return; + case HASH_KEYWORD_P: // returns turntable position list for the turntable id + if (params==2) { // + Turntable *tto=Turntable::get(id); + if (!tto || tto->isHidden()) { + StringFormatter::send(stream, F(" %d X>\n"), id); + } else { + uint8_t posCount = tto->getPositionCount(); + const FSH *tpdesc = NULL; + for (uint8_t p = 0; p < posCount; p++) { + StringFormatter::send(stream, F("getPositionValue(p); + int16_t angle = tto->getPositionAngle(p); +#ifdef EXRAIL_ACTIVE + tpdesc = RMFT2::getTurntablePositionDescription(id, p); +#endif + if (tpdesc == NULL) tpdesc = F(""); + StringFormatter::send(stream, F(" %d %d %d %d \"%S\""), id, p, value, angle, tpdesc); + StringFormatter::send(stream, F(">\n")); + } + } + } else { + StringFormatter::send(stream, F("\n")); + } + return; +#endif default: break; } // switch(p[1]) break; // case J } +// No turntables without HAL support +#ifndef IO_NO_HAL + case 'I': // TURNTABLE + if (parseI(stream, params, p)) + return; + break; +#endif + default: //anything else will diagnose and drop out to DIAG(F("Opcode=%c params=%d"), opcode, params); for (int i = 0; i < params; i++) @@ -1094,6 +1159,99 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) return false; } +// ========================== +// Turntable - no support if no HAL +// - list all +// - broadcast type and current position +// - create DCC - This is TBA +// - operate (DCC) +// - operate (EXTT) +// - add position +// - create EXTT +#ifndef IO_NO_HAL +bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) +{ + switch (params) + { + case 0: // list turntable objects + return Turntable::printAll(stream); + + case 1: // broadcast type and current position + { + Turntable *tto = Turntable::get(p[0]); + if (tto) { + bool type = tto->isEXTT(); + uint8_t position = tto->getPosition(); + StringFormatter::send(stream, F("\n"), type, position); + } else { + return false; + } + } + return true; + + case 2: // - rotate a DCC turntable + { + Turntable *tto = Turntable::get(p[0]); + if (tto && !tto->isEXTT()) { + if (!tto->setPosition(p[0], p[1])) return false; + } else { + return false; + } + } + return true; + + case 3: // | - rotate to position for EX-Turntable or create DCC turntable + { + Turntable *tto = Turntable::get(p[0]); + if (p[1] == HASH_KEYWORD_DCC) { + if (tto || p[2] < 0 || p[2] > 3600) return false; + if (!DCCTurntable::create(p[0])) return false; + Turntable *tto = Turntable::get(p[0]); + tto->addPosition(0, 0, p[2]); + StringFormatter::send(stream, F("\n")); + } else { + if (!tto) return false; + if (!tto->isEXTT()) return false; + if (!tto->setPosition(p[0], p[1], p[2])) return false; + } + } + return true; + + case 4: // create an EXTT turntable + { + Turntable *tto = Turntable::get(p[0]); + if (p[1] == HASH_KEYWORD_EXTT) { + if (tto || p[3] < 0 || p[3] > 3600) return false; + if (!EXTTTurntable::create(p[0], (VPIN)p[2])) return false; + Turntable *tto = Turntable::get(p[0]); + tto->addPosition(0, 0, p[3]); + StringFormatter::send(stream, F("\n")); + } else { + return false; + } + } + return true; + + case 5: // add a position + { + Turntable *tto = Turntable::get(p[0]); + if (p[1] == HASH_KEYWORD_ADD) { + // tto must exist, no more than 48 positions, angle 0 - 3600 + if (!tto || p[2] > 48 || p[4] < 0 || p[4] > 3600) return false; + tto->addPosition(p[2], p[3], p[4]); + StringFormatter::send(stream, F("\n")); + } else { + return false; + } + } + return true; + + default: // Anything else is invalid + return false; + } +} +#endif + // CALLBACKS must be static bool DCCEXParser::stashCallback(Print *stream, int16_t p[MAX_COMMAND_PARAMS], RingStream * ringStream) { diff --git a/DCCEXParser.h b/DCCEXParser.h index bb05ebf..8a7367a 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -24,6 +24,7 @@ #include #include "FSH.h" #include "RingStream.h" +#include "defines.h" typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); typedef void (*AT_COMMAND_CALLBACK)(HardwareSerial * stream,const byte * command); @@ -45,13 +46,16 @@ struct DCCEXParser static int16_t splitValues( int16_t result[MAX_COMMAND_PARAMS], const byte * command, bool usehex); static bool parseT(Print * stream, int16_t params, int16_t p[]); - static bool parseZ(Print * stream, int16_t params, int16_t p[]); - static bool parseS(Print * stream, int16_t params, int16_t p[]); - static bool parsef(Print * stream, int16_t params, int16_t p[]); - static bool parseD(Print * stream, int16_t params, int16_t p[]); + static bool parseZ(Print * stream, int16_t params, int16_t p[]); + static bool parseS(Print * stream, int16_t params, int16_t p[]); + static bool parsef(Print * stream, int16_t params, int16_t p[]); + static bool parseD(Print * stream, int16_t params, int16_t p[]); +#ifndef IO_NO_HAL + static bool parseI(Print * stream, int16_t params, int16_t p[]); +#endif - static Print * getAsyncReplyStream(); - static void commitAsyncReplyStream(); + static Print * getAsyncReplyStream(); + static void commitAsyncReplyStream(); static bool stashBusy; static byte stashTarget; diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index f9935d7..d6d2597 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -52,6 +52,8 @@ #include "Turnouts.h" #include "CommandDistributor.h" #include "TrackManager.h" +#include "Turntables.h" +#include "IODevice.h" // Command parsing keywords const int16_t HASH_KEYWORD_EXRAIL=15435; @@ -94,6 +96,9 @@ LookList * RMFT2::onAmberLookup=NULL; LookList * RMFT2::onGreenLookup=NULL; LookList * RMFT2::onChangeLookup=NULL; LookList * RMFT2::onClockLookup=NULL; +#ifndef IO_NO_HAL +LookList * RMFT2::onRotateLookup=NULL; +#endif LookList * RMFT2::onOverloadLookup=NULL; #define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter) @@ -176,9 +181,11 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { onGreenLookup=LookListLoader(OPCODE_ONGREEN); onChangeLookup=LookListLoader(OPCODE_ONCHANGE); onClockLookup=LookListLoader(OPCODE_ONTIME); +#ifndef IO_NO_HAL + onRotateLookup=LookListLoader(OPCODE_ONROTATE); +#endif onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD); - // Second pass startup, define any turnouts or servos, set signals red // add sequences onRoutines to the lookups for (int sigslot=0;;sigslot++) { @@ -241,7 +248,38 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { setTurnoutHiddenState(VpinTurnout::create(id,pin)); break; } - + +#ifndef IO_NO_HAL + case OPCODE_DCCTURNTABLE: { + VPIN id=operand; + int home=getOperand(progCounter,1); + setTurntableHiddenState(DCCTurntable::create(id)); + Turntable *tto=Turntable::get(id); + tto->addPosition(0,0,home); + break; + } + + case OPCODE_EXTTTURNTABLE: { + VPIN id=operand; + VPIN pin=getOperand(progCounter,1); + int home=getOperand(progCounter,3); + setTurntableHiddenState(EXTTTurntable::create(id,pin)); + Turntable *tto=Turntable::get(id); + tto->addPosition(0,0,home); + break; + } + + case OPCODE_TTADDPOSITION: { + VPIN id=operand; + int position=getOperand(progCounter,1); + int value=getOperand(progCounter,2); + int angle=getOperand(progCounter,3); + Turntable *tto=Turntable::get(id); + tto->addPosition(position,value,angle); + break; + } +#endif + case OPCODE_AUTOSTART: // automatically create a task from here at startup. // Removed if (progCounter>0) check 4.2.31 because @@ -266,6 +304,12 @@ void RMFT2::setTurnoutHiddenState(Turnout * t) { t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01); } +#ifndef IO_NO_HAL +void RMFT2::setTurntableHiddenState(Turntable * tto) { + tto->setHidden(GETFLASH(getTurntableDescription(tto->getId()))==0x01); +} +#endif + char RMFT2::getRouteType(int16_t id) { for (int16_t i=0;;i+=2) { int16_t rid= GETHIGHFLASHW(routeIdList,i); @@ -602,6 +646,14 @@ void RMFT2::loop2() { Turnout::setClosed(operand, true); break; +#ifndef IO_NO_HAL + case OPCODE_ROTATE: + uint8_t activity; + activity=getOperand(2); + Turntable::setPosition(operand,getOperand(1),activity); + break; +#endif + case OPCODE_REV: forward = false; driveLoco(operand); @@ -801,7 +853,13 @@ void RMFT2::loop2() { case OPCODE_IFCLOSED: skipIf=Turnout::isThrown(operand); break; - + +#ifndef IO_NO_HAL + case OPCODE_IFTTPOSITION: // do block if turntable at this position + skipIf=Turntable::getPosition(operand)!=(int)getOperand(1); + break; +#endif + case OPCODE_ENDIF: break; @@ -974,7 +1032,16 @@ void RMFT2::loop2() { return; } break; - + +#ifndef IO_NO_HAL + case OPCODE_WAITFORTT: // OPCODE_WAITFOR,V(turntable_id) + if (Turntable::ttMoving(operand)) { + delayMe(100); + return; + } + break; +#endif + case OPCODE_PRINT: printMessage(operand); break; @@ -999,6 +1066,12 @@ void RMFT2::loop2() { case OPCODE_ONGREEN: case OPCODE_ONCHANGE: case OPCODE_ONTIME: +#ifndef IO_NO_HAL + case OPCODE_DCCTURNTABLE: // Turntable definition ignored at runtime + case OPCODE_EXTTTURNTABLE: // Turntable definition ignored at runtime + case OPCODE_TTADDPOSITION: // Turntable position definition ignored at runtime + case OPCODE_ONROTATE: +#endif case OPCODE_ONOVERLOAD: break; @@ -1144,6 +1217,13 @@ void RMFT2::changeEvent(int16_t vpin, bool change) { if (change) handleEvent(F("CHANGE"),onChangeLookup,vpin); } +#ifndef IO_NO_HAL +void RMFT2::rotateEvent(int16_t turntableId, bool change) { + // Hunt or an ONROTATE for this turntable + if (change) handleEvent(F("ROTATE"),onRotateLookup,turntableId); +} +#endif + void RMFT2::clockEvent(int16_t clocktime, bool change) { // Hunt for an ONTIME for this time if (Diag::CMD) diff --git a/EXRAIL2.h b/EXRAIL2.h index 6e00561..8af36d2 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -25,6 +25,7 @@ #include "FSH.h" #include "IODevice.h" #include "Turnouts.h" +#include "Turntables.h" // The following are the operation codes (or instructions) for a kind of virtual machine. // Each instruction is normally 3 bytes long with an operation code followed by a parameter. @@ -63,6 +64,10 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONCHANGE, OPCODE_ONCLOCKTIME, OPCODE_ONTIME, +#ifndef IO_NO_HAL + OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE, + OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_IFTTPOSITION,OPCODE_WAITFORTT, +#endif OPCODE_ONOVERLOAD, // OPcodes below this point are skip-nesting IF operations @@ -132,6 +137,7 @@ class LookList { static void activateEvent(int16_t addr, bool active); static void changeEvent(int16_t id, bool change); static void clockEvent(int16_t clocktime, bool change); + static void rotateEvent(int16_t id, bool change); static void powerEvent(int16_t track, bool overload); static const int16_t SERVO_SIGNAL_FLAG=0x4000; static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000; @@ -147,6 +153,8 @@ class LookList { static const FSH * getTurnoutDescription(int16_t id); static const FSH * getRosterName(int16_t id); static const FSH * getRosterFunctions(int16_t id); + static const FSH * getTurntableDescription(int16_t id); + static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId); private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); @@ -159,6 +167,9 @@ private: static bool isSignal(int16_t id,char rag); static int16_t getSignalSlot(int16_t id); static void setTurnoutHiddenState(Turnout * t); + #ifndef IO_NO_HAL + static void setTurntableHiddenState(Turntable * tto); + #endif static LookList* LookListLoader(OPCODE op1, OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); static void handleEvent(const FSH* reason,LookList* handlers, int16_t id); @@ -191,6 +202,9 @@ private: static LookList * onGreenLookup; static LookList * onChangeLookup; static LookList * onClockLookup; +#ifndef IO_NO_HAL + static LookList * onRotateLookup; +#endif static LookList * onOverloadLookup; // Local variables - exist for each instance/task diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 22cf562..11c598b 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -41,6 +41,7 @@ #undef CALL #undef CLOSE #undef DCC_SIGNAL +#undef DCC_TURNTABLE #undef DEACTIVATE #undef DEACTIVATEL #undef DELAY @@ -52,8 +53,9 @@ #undef ENDEXRAIL #undef ENDIF #undef ENDTASK -#undef ESTOP -#undef EXRAIL +#undef ESTOP +#undef EXRAIL +#undef EXTT_TURNTABLE #undef FADE #undef FOFF #undef FOLLOW @@ -76,6 +78,7 @@ #undef IFRESERVE #undef IFTHROWN #undef IFTIMEOUT +#undef IFTTPOSITION #undef IFRE #undef INVERT_DIRECTION #undef JOIN @@ -97,6 +100,7 @@ #undef ONOVERLOAD #undef ONGREEN #undef ONRED +#undef ONROTATE #undef ONTHROW #undef ONCHANGE #undef PARSE @@ -115,7 +119,9 @@ #undef RESUME #undef RETURN #undef REV -#undef ROSTER +#undef ROSTER +#undef ROTATE +#undef ROTATE_DCC #undef ROUTE #undef SENDLOCO #undef SEQUENCE @@ -138,7 +144,8 @@ #undef SPEED #undef START #undef STOP -#undef THROW +#undef THROW +#undef TT_ADDPOSITION #undef TURNOUT #undef TURNOUTL #undef UNJOIN @@ -146,6 +153,9 @@ #undef VIRTUAL_SIGNAL #undef VIRTUAL_TURNOUT #undef WAITFOR +#ifndef IO_NO_HAL +#undef WAITFORTT +#endif #undef WITHROTTLE #undef XFOFF #undef XFON @@ -168,6 +178,7 @@ #define CALL(route) #define CLOSE(id) #define DCC_SIGNAL(id,add,subaddr) +#define DCC_TURNTABLE(id,home,description) #define DEACTIVATE(addr,subaddr) #define DEACTIVATEL(addr) #define DELAY(mindelay) @@ -180,7 +191,8 @@ #define ENDIF #define ENDTASK #define ESTOP -#define EXRAIL +#define EXRAIL +#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description) #define FADE(pin,value,ms) #define FOFF(func) #define FOLLOW(route) @@ -203,6 +215,7 @@ #define IFTHROWN(turnout_id) #define IFRESERVE(block) #define IFTIMEOUT +#define IFTTPOSITION(turntable_id,position) #define IFRE(sensor_id,value) #define INVERT_DIRECTION #define JOIN @@ -223,7 +236,8 @@ #define ONDEACTIVATEL(linear) #define ONCLOSE(turnout_id) #define ONGREEN(signal_id) -#define ONRED(signal_id) +#define ONRED(signal_id) +#define ONROTATE(turntable_id) #define ONTHROW(turnout_id) #define ONCHANGE(sensor_id) #define PAUSE @@ -242,8 +256,10 @@ #define RESUME #define RETURN #define REV(speed) -#define ROUTE(id,description) +#define ROTATE(turntable_id,position,activity) +#define ROTATE_DCC(turntable_id,position) #define ROSTER(cab,name,funcmap...) +#define ROUTE(id,description) #define SENDLOCO(cab,route) #define SEQUENCE(id) #define SERIAL(msg) @@ -265,7 +281,8 @@ #define SPEED(speed) #define START(route) #define STOP -#define THROW(id) +#define THROW(id) +#define TT_ADDPOSITION(turntable_id,position,value,angle,description...) #define TURNOUT(id,addr,subaddr,description...) #define TURNOUTL(id,addr,description...) #define UNJOIN @@ -273,6 +290,9 @@ #define VIRTUAL_SIGNAL(id) #define VIRTUAL_TURNOUT(id,description...) #define WAITFOR(pin) +#ifndef IO_NO_HAL +#define WAITFORTT(turntable_id) +#endif #define WITHROTTLE(msg) #define XFOFF(cab,func) #define XFON(cab,func) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index d1cad0c..0784242 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -54,6 +54,8 @@ // helper macro for turnout descriptions, creates NULL for missing description #define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL; +// helper macro for turntable descriptions, creates NULL for missing description +#define T_DESC(tid,pid,desc) if(turntableId==tid && positionId==pid) return ("" desc)[0]?F("" desc):NULL; // helper macro for turnout description as HIDDEN #define HIDDEN "\x01" @@ -67,10 +69,13 @@ #define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10; #include "myAutomation.h" -// Pass 1h Implements HAL macro by creating exrailHalSetup function +// Pass 1h Implements HAL macro by creating exrailHalSetup function +// Also allows creating EXTurntable object #include "EXRAIL2MacroReset.h" #undef HAL #define HAL(haltype,params...) haltype::create(params); +#undef EXTT_TURNTABLE +#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) EXTurntable::create(vpin,1,i2c_address); void exrailHalSetup() { #include "myAutomation.h" } @@ -187,6 +192,31 @@ const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) { return NULL; } +// Pass to get turntable descriptions (optional) +#include "EXRAIL2MacroReset.h" +#undef DCC_TURNTABLE +#define DCC_TURNTABLE(id,home,description...) O_DESC(id,description) +#undef EXTT_TURNTABLE +#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) O_DESC(id,description) + +const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { + switch (turntableId) { + #include "myAutomation.h" + default:break; + } + return NULL; +} + +// Pass to get turntable position descriptions (optional) +#include "EXRAIL2MacroReset.h" +#undef TT_ADDPOSITION +#define TT_ADDPOSITION(turntable_id,position,value,home,description...) T_DESC(turntable_id,position,description) + +const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) { + #include "myAutomation.h" + return NULL; +} + // Pass 6: Roster IDs (count) #include "EXRAIL2MacroReset.h" #undef ROSTER @@ -269,6 +299,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define BROADCAST(msg) PRINT(msg) #define CALL(route) OPCODE_CALL,V(route), #define CLOSE(id) OPCODE_CLOSE,V(id), +#ifndef IO_NO_HAL +#define DCC_TURNTABLE(id,home,description...) OPCODE_DCCTURNTABLE,V(id),OPCODE_PAD,V(home), +#endif #define DEACTIVATE(addr,subaddr) OPCODE_DCCACTIVATE,V(addr<<3 | subaddr<<1), #define DEACTIVATEL(addr) OPCODE_DCCACTIVATE,V((addr+3)<<1), #define DELAY(ms) ms<30000?OPCODE_DELAYMS:OPCODE_DELAY,V(ms/(ms<30000?1L:100L)), @@ -282,7 +315,10 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ENDIF OPCODE_ENDIF,0,0, #define ENDTASK OPCODE_ENDTASK,0,0, #define ESTOP OPCODE_SPEED,V(1), -#define EXRAIL +#define EXRAIL +#ifndef IO_NO_HAL +#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(i2c_address),OPCODE_PAD,V(home), +#endif #define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L), #define FOFF(func) OPCODE_FOFF,V(func), #define FOLLOW(route) OPCODE_FOLLOW,V(route), @@ -305,6 +341,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define IFRESERVE(block) OPCODE_IFRESERVE,V(block), #define IFTHROWN(turnout_id) OPCODE_IFTHROWN,V(turnout_id), #define IFTIMEOUT OPCODE_IFTIMEOUT,0,0, +#ifndef IO_NO_HAL +#define IFTTPOSITION(id,position) OPCODE_IFTTPOSITION,V(id),OPCODE_PAD,V(position), +#endif #define IFRE(sensor_id,value) OPCODE_IFRE,V(sensor_id),OPCODE_PAD,V(value), #define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,0,0, #define JOIN OPCODE_JOIN,0,0, @@ -326,6 +365,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3), #define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id), #define ONRED(signal_id) OPCODE_ONRED,V(signal_id), +#ifndef IO_NO_HAL +#define ONROTATE(id) OPCODE_ONROTATE,V(id), +#endif #define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id), #define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id), #define PAUSE OPCODE_PAUSE,0,0, @@ -345,6 +387,10 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define RETURN OPCODE_RETURN,0,0, #define REV(speed) OPCODE_REV,V(speed), #define ROSTER(cabid,name,funcmap...) +#ifndef IO_NO_HAL +#define ROTATE(id,position,activity) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(EXTurntable::activity), +#define ROTATE_DCC(id,position) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(0), +#endif #define ROUTE(id, description) OPCODE_ROUTE, V(id), #define SENDLOCO(cab,route) OPCODE_SENDLOCO,V(cab),OPCODE_PAD,V(route), #define SEQUENCE(id) OPCODE_SEQUENCE, V(id), @@ -368,6 +414,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define START(route) OPCODE_START,V(route), #define STOP OPCODE_SPEED,V(0), #define THROW(id) OPCODE_THROW,V(id), +#ifndef IO_NO_HAL +#define TT_ADDPOSITION(id,position,value,angle,description...) OPCODE_TTADDPOSITION,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(value),OPCODE_PAD,V(angle), +#endif #define TURNOUT(id,addr,subaddr,description...) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr), #define TURNOUTL(id,addr,description...) TURNOUT(id,(addr-1)/4+1,(addr-1)%4, description) #define UNJOIN OPCODE_UNJOIN,0,0, @@ -376,6 +425,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define VIRTUAL_TURNOUT(id,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(0), #define WITHROTTLE(msg) PRINT(msg) #define WAITFOR(pin) OPCODE_WAITFOR,V(pin), +#ifndef IO_NO_HAL +#define WAITFORTT(turntable_id) OPCODE_WAITFORTT,V(turntable_id), +#endif #define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func), #define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func), diff --git a/IODevice.cpp b/IODevice.cpp index 2ed21b6..e811fff 100644 --- a/IODevice.cpp +++ b/IODevice.cpp @@ -176,6 +176,13 @@ bool IODevice::exists(VPIN vpin) { return findDevice(vpin) != NULL; } +// Return the status of the device att vpin. +uint8_t IODevice::getStatus(VPIN vpin) { + IODevice *dev = findDevice(vpin); + if (!dev) return false; + return dev->_deviceState; +} + // check whether the pin supports notification. If so, then regular _read calls are not required. bool IODevice::hasCallback(VPIN vpin) { IODevice *dev = findDevice(vpin); diff --git a/IODevice.h b/IODevice.h index 4eb24e5..4803f56 100644 --- a/IODevice.h +++ b/IODevice.h @@ -27,17 +27,6 @@ // Define symbol DIAG_LOOPTIMES to enable CS loop execution time to be reported //#define DIAG_LOOPTIMES -// Define symbol IO_NO_HAL to reduce FLASH footprint when HAL features not required -// The HAL is disabled by default on Nano and Uno platforms, because of limited flash space. -#include "defines.h" -#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) - #if defined(DISABLE_DIAG) && defined(DISABLE_EEPROM) && defined(DISABLE_PROG) - #warning you have sacrificed DIAG for HAL - #else - #define IO_NO_HAL - #endif -#endif - // Define symbol IO_SWITCH_OFF_SERVO to set the PCA9685 output to 0 when an // animation has completed. This switches off the servo motor, preventing // the continuous buzz sometimes found on servos, and reducing the @@ -165,6 +154,9 @@ public: // exists checks whether there is a device owning the specified vpin static bool exists(VPIN vpin); + // getStatus returns the state of the device at the specified vpin + static uint8_t getStatus(VPIN vpin); + // Enable shared interrupt on specified pin for GPIO extender modules. The extender module // should pull down this pin when requesting a scan. The pin may be shared by multiple modules. // Without the shared interrupt, input states are scanned periodically to detect changes on @@ -388,6 +380,7 @@ private: uint8_t *_pinInUse; }; +#ifndef IO_NO_HAL ///////////////////////////////////////////////////////////////////////////////////////////////////// /* * IODevice subclass for EX-Turntable. @@ -416,10 +409,13 @@ private: void _begin() override; void _loop(unsigned long currentMicros) override; int _read(VPIN vpin) override; + void _broadcastStatus (VPIN vpin, uint8_t status); void _writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) override; void _display() override; uint8_t _stepperStatus; + uint8_t _previousStatus; }; +#endif ///////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/IO_EXTurntable.h b/IO_EXTurntable.cpp similarity index 79% rename from IO_EXTurntable.h rename to IO_EXTurntable.cpp index 29ce679..af220a3 100644 --- a/IO_EXTurntable.h +++ b/IO_EXTurntable.cpp @@ -20,20 +20,21 @@ /* * The IO_EXTurntable device driver is used to control a turntable via an Arduino with a stepper motor over I2C. * -* The EX-Turntable code lives in a separate repo (https://github.com/DCC-EX/Turntable-EX) and contains the stepper motor logic. +* The EX-Turntable code lives in a separate repo (https://github.com/DCC-EX/EX-Turntable) and contains the stepper motor logic. * -* This device driver sends a step position to Turntable-EX to indicate the step position to move to using either of these commands: +* This device driver sends a step position to EX-Turntable to indicate the step position to move to using either of these commands: * in the serial console * MOVETT(vpin, steps, activity) in EX-RAIL * Refer to the documentation for further information including the valid activities. */ -#ifndef IO_EXTurntable_h -#define IO_EXTurntable_h - #include "IODevice.h" #include "I2CManager.h" #include "DIAG.h" +#include "Turntables.h" +#include "CommandDistributor.h" + +#ifndef IO_NO_HAL void EXTurntable::create(VPIN firstVpin, int nPins, I2CAddress I2CAddress) { new EXTurntable(firstVpin, nPins, I2CAddress); @@ -44,6 +45,8 @@ EXTurntable::EXTurntable(VPIN firstVpin, int nPins, I2CAddress I2CAddress) { _firstVpin = firstVpin; _nPins = nPins; _I2CAddress = I2CAddress; + _stepperStatus = 0; + _previousStatus = 0; addDevice(this); } @@ -51,6 +54,7 @@ EXTurntable::EXTurntable(VPIN firstVpin, int nPins, I2CAddress I2CAddress) { void EXTurntable::_begin() { I2CManager.begin(); if (I2CManager.exists(_I2CAddress)) { + DIAG(F("EX-Turntable device found, I2C:%s"), _I2CAddress.toString()); #ifdef DIAG_IO _display(); #endif @@ -67,15 +71,17 @@ void EXTurntable::_loop(unsigned long currentMicros) { uint8_t readBuffer[1]; I2CManager.read(_I2CAddress, readBuffer, 1); _stepperStatus = readBuffer[0]; - // DIAG(F("Turntable-EX returned status: %d"), _stepperStatus); - delayUntil(currentMicros + 500000); // Wait 500ms before checking again, turntables turn slowly + if (_stepperStatus != _previousStatus && _stepperStatus == 0) { // Broadcast when a rotation finishes + _broadcastStatus(_firstVpin, _stepperStatus); + _previousStatus = _stepperStatus; + } + delayUntil(currentMicros + 100000); // Wait 100ms before checking again } // Read returns status as obtained in our loop. // Return false if our status value is invalid. int EXTurntable::_read(VPIN vpin) { if (_deviceState == DEVSTATE_FAILED) return 0; - // DIAG(F("_read status: %d"), _stepperStatus); if (_stepperStatus > 1) { return false; } else { @@ -83,6 +89,15 @@ int EXTurntable::_read(VPIN vpin) { } } +// If a status change has occurred for a turntable object, broadcast it +void EXTurntable::_broadcastStatus (VPIN vpin, uint8_t status) { + Turntable *tto = Turntable::getByVpin(vpin); + if (tto) { + tto->setMoving(status); + CommandDistributor::broadcastTurntable(tto->getId(), tto->getPosition(), status); + } +} + // writeAnalogue to send the steps and activity to Turntable-EX. // Sends 3 bytes containing the MSB and LSB of the step count, and activity. // value contains the steps, bit shifted to MSB + LSB. @@ -100,6 +115,7 @@ int EXTurntable::_read(VPIN vpin) { // Acc_Off = 9 // Turn accessory pin off void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) { if (_deviceState == DEVSTATE_FAILED) return; + if (value < 0) return; uint8_t stepsMSB = value >> 8; uint8_t stepsLSB = value & 0xFF; #ifdef DIAG_IO @@ -109,6 +125,8 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_ _I2CAddress.toString(), stepsMSB, stepsLSB, activity); #endif _stepperStatus = 1; // Tell the device driver Turntable-EX is busy + _previousStatus = _stepperStatus; + _broadcastStatus(vpin, _stepperStatus); // Broadcast when the rotation starts I2CManager.write(_I2CAddress, 3, stepsMSB, stepsLSB, activity); } diff --git a/Turntables.cpp b/Turntables.cpp new file mode 100644 index 0000000..ba143cb --- /dev/null +++ b/Turntables.cpp @@ -0,0 +1,268 @@ +/* + * © 2023 Peter Cole + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +#include "defines.h" +#include +#include "Turntables.h" +#include "StringFormatter.h" +#include "CommandDistributor.h" +#include "EXRAIL2.h" +#include "DCC.h" + +// No turntable support without HAL +#ifndef IO_NO_HAL + +/* + * Protected static data + */ +Turntable *Turntable::_firstTurntable = 0; + + +/* + * Public static data + */ +int Turntable::turntablelistHash = 0; + + +/* + * Protected static functions + */ +// Add new turntable to end of list + +void Turntable::add(Turntable *tto) { + if (!_firstTurntable) { + _firstTurntable = tto; + } else { + Turntable *ptr = _firstTurntable; + for ( ; ptr->_nextTurntable!=0; ptr=ptr->_nextTurntable) {} + ptr->_nextTurntable = tto; + } + turntablelistHash++; +} + +// Add a position +void Turntable::addPosition(uint8_t idx, uint16_t value, uint16_t angle) { + _turntablePositions.insert(idx, value, angle); +} + +// Get value for position +uint16_t Turntable::getPositionValue(uint8_t position) { + TurntablePosition* currentPosition = _turntablePositions.getHead(); + while (currentPosition) { + if (currentPosition->index == position) { + return currentPosition->data; + } + currentPosition = currentPosition->next; + } + return false; +} + +// Get value for position +uint16_t Turntable::getPositionAngle(uint8_t position) { + TurntablePosition* currentPosition = _turntablePositions.getHead(); + while (currentPosition) { + if (currentPosition->index == position) { + return currentPosition->angle; + } + currentPosition = currentPosition->next; + } + return false; +} + +// Get the count of positions associated with the turntable +uint8_t Turntable::getPositionCount() { + TurntablePosition* currentPosition = _turntablePositions.getHead(); + uint8_t count = 0; + while (currentPosition) { + count++; + currentPosition = currentPosition->next; + } + return count; +} + +/* + * Public static functions + */ +// Find turntable from list +Turntable *Turntable::get(uint16_t id) { + for (Turntable *tto = _firstTurntable; tto != nullptr; tto = tto->_nextTurntable) + if (tto->_turntableData.id == id) return tto; + return NULL; +} + +// Find turntable via Vpin +Turntable *Turntable::getByVpin(VPIN vpin) { + for (Turntable *tto = _firstTurntable; tto != nullptr; tto = tto->_nextTurntable) { + if (tto->isEXTT()) { + EXTTTurntable *exttTto = static_cast(tto); + if (exttTto->getVpin() == vpin) { + return tto; + } + } + } + return nullptr; +} + +// Get the current position for turntable with the specified ID +uint8_t Turntable::getPosition(uint16_t id) { + Turntable *tto = get(id); + if (!tto) return false; + return tto->getPosition(); +} + +// Got the moving state of the specified turntable +bool Turntable::ttMoving(uint16_t id) { + Turntable *tto = get(id); + if (!tto) return false; + return tto->isMoving(); +} + +// Initiate a turntable move +bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { +#if defined(DIAG_IO) + DIAG(F("Rotate turntable %d to position %d, activity %d)"), id, position, activity); +#endif + Turntable *tto = Turntable::get(id); + if (!tto) return false; + if (tto->isMoving()) return false; + bool ok = tto->setPositionInternal(position, activity); + + if (ok) { + // We only deal with broadcasts for DCC turntables here, EXTT in the device driver + if (!tto->isEXTT()) { + CommandDistributor::broadcastTurntable(id, position, false); + } + // Trigger EXRAIL rotateEvent for both types here if changed +#if defined(EXRAIL_ACTIVE) + bool rotated = false; + if (position != tto->_previousPosition) rotated = true; + RMFT2::rotateEvent(id, rotated); +#endif + } + return ok; +} + +/************************************************************************************* + * EXTTTurntable - EX-Turntable device. + * + *************************************************************************************/ +// Private constructor +EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin) : + Turntable(id, TURNTABLE_EXTT) +{ + _exttTurntableData.vpin = vpin; +} + +using DevState = IODevice::DeviceStateEnum; + +// Create function + Turntable *EXTTTurntable::create(uint16_t id, VPIN vpin) { +#ifndef IO_NO_HAL + Turntable *tto = get(id); + if (tto) { + if (tto->isType(TURNTABLE_EXTT)) { + EXTTTurntable *extt = (EXTTTurntable *)tto; + extt->_exttTurntableData.vpin = vpin; + return tto; + } + } + if (!IODevice::exists(vpin)) return nullptr; + if (IODevice::getStatus(vpin) == DevState::DEVSTATE_FAILED) return nullptr; + if (Turntable::getByVpin(vpin)) return nullptr; + tto = (Turntable *)new EXTTTurntable(id, vpin); + DIAG(F("Turntable 0x%x size %d size %d"), tto, sizeof(Turntable), sizeof(struct TurntableData)); + return tto; +#else + (void)id; + (void)vpin; + return NULL; +#endif + } + + void EXTTTurntable::print(Print *stream) { + StringFormatter::send(stream, F("\n"), _turntableData.id, _exttTurntableData.vpin); + } + + // EX-Turntable specific code for moving to the specified position + bool EXTTTurntable::setPositionInternal(uint8_t position, uint8_t activity) { +#ifndef IO_NO_HAL + int16_t value; + if (position == 0) { + value = 0; // Position 0 is just to send activities + } else { + if (activity > 1) return false; // If sending a position update, only phase changes valid (0|1) + value = getPositionValue(position); // Get position value from position list + } + if (position > 0 && !value) return false; // Return false if it's not a valid position + // Set position via device driver + _previousPosition = _turntableData.position; + _turntableData.position = position; + EXTurntable::writeAnalogue(_exttTurntableData.vpin, value, activity); +#else + (void)position; +#endif + return true; + } + +/************************************************************************************* + * DCCTurntable - DCC Turntable device. + * + *************************************************************************************/ +// Private constructor +DCCTurntable::DCCTurntable(uint16_t id) : Turntable(id, TURNTABLE_DCC) {} + +// Create function + Turntable *DCCTurntable::create(uint16_t id) { +#ifndef IO_NO_HAL + Turntable *tto = get(id); + if (!tto) { + tto = (Turntable *)new DCCTurntable(id); + DIAG(F("Turntable 0x%x size %d size %d"), tto, sizeof(Turntable), sizeof(struct TurntableData)); + } + return tto; +#else + (void)id; + return NULL; +#endif + } + + void DCCTurntable::print(Print *stream) { + StringFormatter::send(stream, F("\n"), _turntableData.id); + } + + // EX-Turntable specific code for moving to the specified position + bool DCCTurntable::setPositionInternal(uint8_t position, uint8_t activity) { +#ifndef IO_NO_HAL + int16_t value = getPositionValue(position); + if (position == 0 || !value) return false; // Return false if it's not a valid position + // Set position via device driver + int16_t addr=value>>3; + int16_t subaddr=(value>>1) & 0x03; + bool active=value & 0x01; + _previousPosition = _turntableData.position; + _turntableData.position = position; + DCC::setAccessory(addr, subaddr, active); +#else + (void)position; +#endif + return true; + } + +#endif diff --git a/Turntables.h b/Turntables.h new file mode 100644 index 0000000..6c15bab --- /dev/null +++ b/Turntables.h @@ -0,0 +1,243 @@ +/* + * © 2023 Peter Cole + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +#ifndef TURNTABLES_H +#define TURNTABLES_H + +#include +#include "IODevice.h" +#include "StringFormatter.h" + +// No turntable support without HAL +#ifndef IO_NO_HAL + +// Turntable type definitions +// EXTT = EX-Turntable +// DCC = DCC accessory turntables - to be added later +enum { + TURNTABLE_EXTT = 0, + TURNTABLE_DCC = 1, +}; + +/************************************************************************************* + * Turntable positions. + * + *************************************************************************************/ +struct TurntablePosition { + uint8_t index; + uint16_t data; + uint16_t angle; + TurntablePosition* next; + + TurntablePosition(uint8_t idx, uint16_t value, uint16_t angle) : index(idx), data(value), angle(angle), next(nullptr) {} +}; + +class TurntablePositionList { +public: + TurntablePositionList() : head(nullptr) {} + + void insert(uint8_t idx, uint16_t value, uint16_t angle) { + TurntablePosition* newPosition = new TurntablePosition(idx, value, angle); + if(!head) { + head = newPosition; + } else { + newPosition->next = head; + head = newPosition; + } + } + + TurntablePosition* getHead() { + return head; + } + +private: + TurntablePosition* head; + +}; + + +/************************************************************************************* + * Turntable - Base class for turntables. + * + *************************************************************************************/ + +class Turntable { +protected: + /* + * Object data + */ + + // Data common to all turntable types + struct TurntableData { + union { + struct { + bool hidden : 1; + bool turntableType : 1; + uint8_t position : 6; // Allows up to 63 positions including 0/home + }; + uint8_t flags; + }; + uint16_t id; + } _turntableData; + + // Pointer to next turntable object + Turntable *_nextTurntable = 0; + + // Linked list for positions + TurntablePositionList _turntablePositions; + + // Store the previous position to allow checking for changes + uint8_t _previousPosition = 0; + + // Store the current state of the turntable + bool _isMoving = false; + + /* + * Constructor + */ + Turntable(uint16_t id, uint8_t turntableType) { + _turntableData.id = id; + _turntableData.turntableType = turntableType; + _turntableData.hidden = false; + _turntableData.position = 0; + add(this); + } + + /* + * Static data + */ + static Turntable *_firstTurntable; + static int _turntablelistHash; + + /* + * Virtual functions + */ + virtual bool setPositionInternal(uint8_t position, uint8_t activity) = 0; + + /* + * Static functions + */ + static void add(Turntable *tto); + +public: + static Turntable *get(uint16_t id); + static Turntable *getByVpin(VPIN vpin); + + /* + * Static data + */ + static int turntablelistHash; + + /* + * Public base class functions + */ + inline uint8_t getPosition() { return _turntableData.position; } + inline bool isHidden() { return _turntableData.hidden; } + inline void setHidden(bool h) {_turntableData.hidden=h; } + inline bool isType(uint8_t type) { return _turntableData.turntableType == type; } + inline bool isEXTT() const { return _turntableData.turntableType == TURNTABLE_EXTT; } + inline uint16_t getId() { return _turntableData.id; } + inline Turntable *next() { return _nextTurntable; } + void printState(Print *stream); + void addPosition(uint8_t idx, uint16_t value, uint16_t angle); + uint16_t getPositionValue(uint8_t position); + uint16_t getPositionAngle(uint8_t position); + uint8_t getPositionCount(); + bool isMoving() { return _isMoving; } + void setMoving(bool moving) { _isMoving=moving; } + + /* + * Virtual functions + */ + virtual void print(Print *stream) { + (void)stream; // suppress compiler warnings + } + virtual ~Turntable() {} // Destructor + + + /* + * Public static functions + */ + inline static bool exists(uint16_t id) { return get(id) != 0; } + static bool setPosition(uint16_t id, uint8_t position, uint8_t activity=0); + static uint8_t getPosition(uint16_t id); + static bool ttMoving(uint16_t id); + inline static Turntable *first() { return _firstTurntable; } + static bool printAll(Print *stream) { + bool gotOne = false; + for (Turntable *tto = _firstTurntable; tto != 0; tto = tto->_nextTurntable) + if (!tto->isHidden()) { + gotOne = true; + StringFormatter::send(stream, F("\n"), tto->getId(), tto->getPosition()); + } + return gotOne; + } + +}; + +/************************************************************************************* + * EXTTTurntable - EX-Turntable device. + * + *************************************************************************************/ +class EXTTTurntable : public Turntable { +private: + // EXTTTurntableData contains device specific data + struct EXTTTurntableData { + VPIN vpin; + } _exttTurntableData; + + // Constructor + EXTTTurntable(uint16_t id, VPIN vpin); + +public: + // Create function + static Turntable *create(uint16_t id, VPIN vpin); + void print(Print *stream) override; + VPIN getVpin() const { return _exttTurntableData.vpin; } + +protected: + // EX-Turntable specific code for setting position + bool setPositionInternal(uint8_t position, uint8_t activity) override; + +}; + +/************************************************************************************* + * DCCTurntable - DCC accessory Turntable device. + * + *************************************************************************************/ +class DCCTurntable : public Turntable { +private: + // Constructor + DCCTurntable(uint16_t id); + +public: + // Create function + static Turntable *create(uint16_t id); + void print(Print *stream) override; + +protected: + // DCC specific code for setting position + bool setPositionInternal(uint8_t position, uint8_t activity=0) override; + +}; + +#endif + +#endif diff --git a/defines.h b/defines.h index f3822ca..49aff71 100644 --- a/defines.h +++ b/defines.h @@ -213,6 +213,19 @@ // #define WIFI_SERIAL_LINK_SPEED 115200 +//////////////////////////////////////////////////////////////////////////////// +// +// Define symbol IO_NO_HAL to reduce FLASH footprint when HAL features not required +// The HAL is disabled by default on Nano and Uno platforms, because of limited flash space. +// +#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) + #if defined(DISABLE_DIAG) && defined(DISABLE_EEPROM) && defined(DISABLE_PROG) + #warning you have sacrificed DIAG for HAL + #else + #define IO_NO_HAL + #endif +#endif + #if __has_include ( "myAutomation.h") #if defined(HAS_ENOUGH_MEMORY) || defined(DISABLE_EEPROM) || defined(DISABLE_PROG) #define EXRAIL_ACTIVE diff --git a/platformio.ini b/platformio.ini index 1a87770..ac2d598 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,7 +30,7 @@ include_dir = . [env] build_flags = -Wall -Wextra -monitor_filters = time +; monitor_filters = time ; lib_deps = adafruit/Adafruit ST7735 and ST7789 Library @ ^1.10.0 [env:samd21-dev-usb] diff --git a/version.h b/version.h index abcc577..e8ee138 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,10 @@ #include "StringFormatter.h" -#define VERSION "5.1.4" +#define VERSION "5.1.5" +// 5.1.5 - Added turntable object and EXRAIL commands +// - , , - turntable commands +// - DCC_TURNTABLE, EXTT_TURNTABLE, IFTTPOSITION, ONROTATE, ROTATE, ROTATE_DCC, TT_ADDPOSITION, WAITFORTT EXRAIL // 5.1.4 - Added ONOVERLOAD & AFTEROVERLOAD to EXRAIL // 5.1.3 - Make parser more fool proof // 5.1.2 - Bugfix: ESP32 30ms off time