From 7a9dee9bed5713502e7d653c041bdbafbba0b631 Mon Sep 17 00:00:00 2001 From: Sergei Kotlyachkov Date: Mon, 6 Nov 2023 21:39:29 -0500 Subject: [PATCH 1/3] Add AT check delay for longer init delays in ESP32 versions --- WifiInterface.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 8b2251a..7cbeb78 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -36,6 +36,11 @@ const unsigned long LOOP_TIMEOUT = 2000; bool WifiInterface::connected = false; Stream * WifiInterface::wifiStream; +#ifndef WIFI_AT_CHECK_TIMEOUT +// Some ESP32 AT firmware versions take time to initialize and do not respond to AT commands right away. +#define WIFI_AT_CHECK_TIMEOUT 2000 +#endif + #ifndef WIFI_CONNECT_TIMEOUT // Tested how long it takes to FAIL an unknown SSID on firmware 1.7.4. // The ES should fail a connect in 15 seconds, we don't want to fail BEFORE that @@ -192,7 +197,7 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password, } StringFormatter::send(wifiStream, F("AT\r\n")); // Is something here that understands AT? - if(!checkForOK(200, true)) + if(!checkForOK(WIFI_AT_CHECK_TIMEOUT, true)) return WIFI_NOAT; // No AT compatible WiFi module here StringFormatter::send(wifiStream, F("ATE1\r\n")); // Turn on the echo, se we can see what's happening From dba5e88d041e4ce59838cce196c3843f8f54ea70 Mon Sep 17 00:00:00 2001 From: Sergei Kotlyachkov Date: Mon, 6 Nov 2023 21:54:25 -0500 Subject: [PATCH 2/3] Add support for HBRIDGE Turnouts, allowing control of Kato turnouts that require reverse of polarity and short power application, easily configurable through 2-pin controlled Motor H-Bridge. --- DCCEXParser.cpp | 6 ++- EXRAIL2.cpp | 11 +++++- EXRAIL2.h | 3 +- EXRAIL2MacroReset.h | 2 + EXRAILMacros.h | 3 ++ Turnouts.cpp | 91 +++++++++++++++++++++++++++++++++++++++++++++ Turnouts.h | 36 ++++++++++++++++++ 7 files changed, 149 insertions(+), 3 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 04c0b6d..2febe41 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -162,6 +162,7 @@ const int16_t HASH_KEYWORD_T='T'; const int16_t HASH_KEYWORD_X='X'; const int16_t HASH_KEYWORD_LCN = 15137; const int16_t HASH_KEYWORD_HAL = 10853; +const int16_t HASH_KEYWORD_HBRIDGE=-20585; const int16_t HASH_KEYWORD_SHOW = -21309; const int16_t HASH_KEYWORD_ANIN = -10424; const int16_t HASH_KEYWORD_ANOUT = -26399; @@ -916,7 +917,10 @@ bool DCCEXParser::parseT(Print *stream, int16_t params, int16_t p[]) } else if (params == 3 && p[1] == HASH_KEYWORD_VPIN) { // if (!VpinTurnout::create(p[0], p[2])) return false; - } else + } else + if (params == 5 && p[1] == HASH_KEYWORD_HBRIDGE) { // + if (!HBridgeTurnout::create(p[0], p[2], p[3], p[4])) return false; + } else if (params >= 3 && p[1] == HASH_KEYWORD_DCC) { // 0<=addr<=511, 0<=subadd<=3 (like command). if (params==4 && p[2]>=0 && p[2]<512 && p[3]>=0 && p[3]<4) { // diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 0e17ea9..d16e955 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -238,7 +238,16 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { setTurnoutHiddenState(VpinTurnout::create(id,pin)); break; } - + + case OPCODE_HBRIDGETURNOUT: { + VPIN id=operand; + VPIN pin1=getOperand(progCounter, 1); + VPIN pin2=getOperand(progCounter, 2); + uint16_t delay=getOperand(progCounter, 3); + setTurnoutHiddenState(HBridgeTurnout::create(id,pin1, pin2, delay)); + break; + } + case OPCODE_AUTOSTART: // automatically create a task from here at startup. // Removed if (progCounter>0) check 4.2.31 because diff --git a/EXRAIL2.h b/EXRAIL2.h index 4d106e6..6fdc9f1 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -51,7 +51,8 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_POM, OPCODE_START,OPCODE_SETLOCO,OPCODE_SENDLOCO,OPCODE_FORGET, OPCODE_PAUSE, OPCODE_RESUME,OPCODE_POWEROFF,OPCODE_POWERON, - OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, OPCODE_PINTURNOUT, + OPCODE_ONCLOSE, OPCODE_ONTHROW, OPCODE_SERVOTURNOUT, + OPCODE_PINTURNOUT, OPCODE_HBRIDGETURNOUT, OPCODE_PRINT,OPCODE_DCCACTIVATE, OPCODE_ONACTIVATE,OPCODE_ONDEACTIVATE, OPCODE_ROSTER,OPCODE_KILLALL, diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 588a417..06be5af 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -62,6 +62,7 @@ #undef FWD #undef GREEN #undef HAL +#undef HBRIDGE_TURNOUT #undef IF #undef IFAMBER #undef IFCLOSED @@ -187,6 +188,7 @@ #define FWD(speed) #define GREEN(signal_id) #define HAL(haltype,params...) +#define HBRIDGE_TURNOUT(id,pin1,pin2,dly,description...) #define IF(sensor_id) #define IFAMBER(signal_id) #define IFCLOSED(turnout_id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 4bbabfc..80a0d13 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -174,6 +174,8 @@ void RMFT2::printMessage(uint16_t id) { #define TURNOUT(id,addr,subaddr,description...) O_DESC(id,description) #undef TURNOUTL #define TURNOUTL(id,addr,description...) O_DESC(id,description) +#undef HBRIDGE_TURNOUT +#define HBRIDGE_TURNOUT(id,pin1,pin2,delay_ms,description...) O_DESC(id,description) #undef PIN_TURNOUT #define PIN_TURNOUT(id,pin,description...) O_DESC(id,description) #undef SERVO_TURNOUT @@ -293,6 +295,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define FWD(speed) OPCODE_FWD,V(speed), #define GREEN(signal_id) OPCODE_GREEN,V(signal_id), #define HAL(haltype,params...) +#define HBRIDGE_TURNOUT(id,pin1,pin2,delay,description...) OPCODE_HBRIDGETURNOUT,V(id),OPCODE_PAD,V(pin1),OPCODE_PAD,V(pin2),OPCODE_PAD,V(delay), #define IF(sensor_id) OPCODE_IF,V(sensor_id), #define IFAMBER(signal_id) OPCODE_IFAMBER,V(signal_id), #define IFCLOSED(turnout_id) OPCODE_IFCLOSED,V(turnout_id), diff --git a/Turnouts.cpp b/Turnouts.cpp index 83603fc..a6b6aac 100644 --- a/Turnouts.cpp +++ b/Turnouts.cpp @@ -187,6 +187,10 @@ // VPIN turnout tt = VpinTurnout::load(&turnoutData); break; + case TURNOUT_HBRIDGE: + // HBRIDGE turnout + tt = HBridgeTurnout::load(&turnoutData); + break; default: // If we find anything else, then we don't know what it is or how long it is, // so we can't go any further through the EEPROM! @@ -477,6 +481,93 @@ #endif } +/************************************************************************************* + * HBridgeTurnout - Turnout controlled through a pair of HAL pins. + * Typically connected to Motor H-Bridge. Delay is used to quickly turn on/off power. + *************************************************************************************/ + + // Constructor + HBridgeTurnout::HBridgeTurnout(uint16_t id, VPIN pin1, VPIN pin2, uint16_t millisDelay, bool closed) : + Turnout(id, TURNOUT_HBRIDGE, closed) + { + _hbridgeTurnoutData.pin1 = pin1; + _hbridgeTurnoutData.pin2 = pin2; + _hbridgeTurnoutData.millisDelay = millisDelay; + } + + // Create function + /* static */ Turnout *HBridgeTurnout::create(uint16_t id, VPIN pin1, VPIN pin2, uint16_t millisDelay, bool closed) { + Turnout *tt = get(id); + if (tt) { + // Object already exists, check if it is usable + if (tt->isType(TURNOUT_HBRIDGE)) { + // Yes, so set parameters + HBridgeTurnout *hbt = (HBridgeTurnout *)tt; + hbt->_hbridgeTurnoutData.pin1 = pin1; + hbt->_hbridgeTurnoutData.pin2 = pin2; + hbt->_hbridgeTurnoutData.millisDelay = millisDelay; + // Don't touch the _closed parameter, retain the original value. + return tt; + } else { + // Incompatible object, delete and recreate + remove(id); + } + } + tt = (Turnout *)new HBridgeTurnout(id, pin1, pin2, millisDelay, closed); + return tt; + } + + // Load a VPIN turnout definition from EEPROM. The common Turnout data has already been read at this point. + /* static */ Turnout *HBridgeTurnout::load(struct TurnoutData *turnoutData) { +#ifndef DISABLE_EEPROM + HBridgeTurnoutData hbridgeTurnoutData; + // Read class-specific data from EEPROM + EEPROM.get(EEStore::pointer(), hbridgeTurnoutData); + EEStore::advance(sizeof(hbridgeTurnoutData)); + + // Create new object + HBridgeTurnout *tt = new HBridgeTurnout(turnoutData->id, hbridgeTurnoutData.pin1, + hbridgeTurnoutData.pin2, hbridgeTurnoutData.millisDelay, turnoutData->closed); + + return tt; +#else + (void)turnoutData; + return NULL; +#endif + } + + // Report 1 for thrown, 0 for closed. + void HBridgeTurnout::print(Print *stream) { + StringFormatter::send(stream, F("\n"), _turnoutData.id, _hbridgeTurnoutData.pin1, _hbridgeTurnoutData.pin2, + !_turnoutData.closed); + } + + void HBridgeTurnout::turnUpDown(VPIN pin) { + // HBridge turnouts require very small, prescribed time to keep pin1 or pin2 in HIGH state. + // Otherwise internal coil of the turnout will burn. + IODevice::write(pin, HIGH); + // HARD LIMIT to maximum 0.5 second to avoid burning the coil + delay(min(_hbridgeTurnoutData.millisDelay, 500)); + IODevice::write(pin, LOW); + } + + bool HBridgeTurnout::setClosedInternal(bool close) { + turnUpDown(close ? _hbridgeTurnoutData.pin2 : _hbridgeTurnoutData.pin1); + _turnoutData.closed = close; + return true; + } + + void HBridgeTurnout::save() { +#ifndef DISAB LE_EEPROM + // Write turnout definition and current position to EEPROM + // First write common servo data, then + // write the servo-specific data + EEPROM.put(EEStore::pointer(), _turnoutData); + EEStore::advance(sizeof(_turnoutData)); + EEPROM.put(EEStore::pointer(), _hbridgeTurnoutData); + EEStore::advance(sizeof(_hbridgeTurnoutData)); +#endif + } /************************************************************************************* * LCNTurnout - Turnout controlled by Loconet diff --git a/Turnouts.h b/Turnouts.h index 56b7f82..ec2086b 100644 --- a/Turnouts.h +++ b/Turnouts.h @@ -37,6 +37,7 @@ enum { TURNOUT_SERVO = 2, TURNOUT_VPIN = 3, TURNOUT_LCN = 4, + TURNOUT_HBRIDGE = 5, }; /************************************************************************************* @@ -284,6 +285,41 @@ protected: }; +/************************************************************************************* + * HBridgeTurnout - Turnout controlled through a pair of HAL pins. + * + * Hard limited to maximum 0.5 second to avoid burning the coil + * Typical millisDelay should be within between 50 and 100 + *************************************************************************************/ +class HBridgeTurnout : public Turnout { +private: + // HBridgeTurnoutData contains data specific to this subclass that is + // written to EEPROM when the turnout is saved. + struct HBridgeTurnoutData { + VPIN pin1; + VPIN pin2; + uint16_t millisDelay; + } _hbridgeTurnoutData; // 6 bytes + + // Constructor + HBridgeTurnout(uint16_t id, VPIN pin1, VPIN pin2, uint16_t millisDelay, bool closed); + +public: + // Create function + static Turnout *create(uint16_t id, VPIN pin1, VPIN pin2, uint16_t millisDelay, bool closed=true); + + // Load a HBRIDGE turnout definition from EEPROM. The common Turnout data has already been read at this point. + static Turnout *load(struct TurnoutData *turnoutData); + void print(Print *stream) override; + +protected: + bool setClosedInternal(bool close) override; + void save() override; + +private: + void turnUpDown(VPIN pin); + +}; /************************************************************************************* * LCNTurnout - Turnout controlled by Loconet From faf9f76c424304e98164480c53c54a9417ce4dfa Mon Sep 17 00:00:00 2001 From: Sergei Kotlyachkov Date: Mon, 6 Nov 2023 22:29:41 -0500 Subject: [PATCH 3/3] Remove space that was added by mistake when merging --- Turnouts.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Turnouts.cpp b/Turnouts.cpp index a6b6aac..4b44ad4 100644 --- a/Turnouts.cpp +++ b/Turnouts.cpp @@ -558,7 +558,7 @@ } void HBridgeTurnout::save() { -#ifndef DISAB LE_EEPROM +#ifndef DISABLE_EEPROM // Write turnout definition and current position to EEPROM // First write common servo data, then // write the servo-specific data