From 7397a4089b9fb93a1d9289be1c04170e30e5400f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Sep 2021 00:31:05 +0200 Subject: [PATCH 01/66] first waveform on esp --- CommandStation-EX.ino | 1 - DCC.cpp | 24 ++++++++++++------------ DCC.h | 4 +++- DCCEXParser.cpp | 2 +- DCCTimer.cpp | 28 +++++++++++++++++++++++----- MotorDriver.cpp | 6 +++--- MotorDriver.h | 4 +++- freeMemory.cpp | 17 ++++++++++++++++- 8 files changed, 61 insertions(+), 25 deletions(-) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index c8a3aed..c112c54 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -111,7 +111,6 @@ void loop() // Responsibility 1: Handle DCC background processes // (loco reminders and power checks) DCC::loop(); - // Responsibility 2: handle any incoming commands on USB connection serialParser.loop(Serial); diff --git a/DCC.cpp b/DCC.cpp index 226425b..286e7c3 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -304,14 +304,14 @@ const ackOp FLASH WRITE_BIT0_PROG[] = { W0,WACK, V0, WACK, // validate bit is 0 ITC1, // if acked, callback(1) - FAIL // callback (-1) + CALLFAIL // callback (-1) }; const ackOp FLASH WRITE_BIT1_PROG[] = { BASELINE, W1,WACK, V1, WACK, // validate bit is 1 ITC1, // if acked, callback(1) - FAIL // callback (-1) + CALLFAIL // callback (-1) }; const ackOp FLASH VERIFY_BIT0_PROG[] = { @@ -320,7 +320,7 @@ const ackOp FLASH VERIFY_BIT0_PROG[] = { ITC0, // if acked, callback(0) V1, WACK, // validate bit is 1 ITC1, - FAIL // callback (-1) + CALLFAIL // callback (-1) }; const ackOp FLASH VERIFY_BIT1_PROG[] = { BASELINE, @@ -328,7 +328,7 @@ const ackOp FLASH VERIFY_BIT1_PROG[] = { ITC1, // if acked, callback(1) V0, WACK, ITC0, - FAIL // callback (-1) + CALLFAIL // callback (-1) }; const ackOp FLASH READ_BIT_PROG[] = { @@ -337,7 +337,7 @@ const ackOp FLASH READ_BIT_PROG[] = { ITC1, // if acked, callback(1) V0, WACK, // validate bit is zero ITC0, // if acked callback 0 - FAIL // bit not readable + CALLFAIL // bit not readable }; const ackOp FLASH WRITE_BYTE_PROG[] = { @@ -345,7 +345,7 @@ const ackOp FLASH WRITE_BYTE_PROG[] = { WB,WACK,ITC1, // Write and callback(1) if ACK // handle decoders that dont ack a write VB,WACK,ITC1, // validate byte and callback(1) if correct - FAIL // callback (-1) + CALLFAIL // callback (-1) }; const ackOp FLASH VERIFY_BYTE_PROG[] = { @@ -370,7 +370,7 @@ const ackOp FLASH VERIFY_BYTE_PROG[] = { V0, WACK, MERGE, V0, WACK, MERGE, VB, WACK, ITCB, // verify merged byte and return it if acked ok - FAIL }; + CALLFAIL }; const ackOp FLASH READ_CV_PROG[] = { @@ -393,7 +393,7 @@ const ackOp FLASH READ_CV_PROG[] = { V0, WACK, MERGE, V0, WACK, MERGE, VB, WACK, ITCB, // verify merged byte and return it if acked ok - FAIL }; // verification failed + CALLFAIL }; // verification failed const ackOp FLASH LOCO_ID_PROG[] = { @@ -459,7 +459,7 @@ const ackOp FLASH LOCO_ID_PROG[] = { V0, WACK, MERGE, V0, WACK, MERGE, VB, WACK, ITCB, // verify merged byte and callback - FAIL + CALLFAIL }; const ackOp FLASH SHORT_LOCO_ID_PROG[] = { @@ -476,7 +476,7 @@ const ackOp FLASH SHORT_LOCO_ID_PROG[] = { SETBYTEL, // low byte of word WB,WACK, // some decoders don't ACK writes VB,WACK,ITCB, - FAIL + CALLFAIL }; const ackOp FLASH LONG_LOCO_ID_PROG[] = { @@ -500,7 +500,7 @@ const ackOp FLASH LONG_LOCO_ID_PROG[] = { SETBYTEL, // low byte of word WB,WACK, VB,WACK,ITC1, // callback(1) means Ok - FAIL + CALLFAIL }; void DCC::writeCVByte(int16_t cv, byte byteValue, ACK_CALLBACK callback) { @@ -828,7 +828,7 @@ void DCC::ackManagerLoop() { } break; - case FAIL: // callback(-1) + case CALLFAIL: // callback(-1) callback(-1); return; diff --git a/DCC.h b/DCC.h index 1bdd5f0..b473673 100644 --- a/DCC.h +++ b/DCC.h @@ -40,7 +40,7 @@ enum ackOp : byte ITCB, // If True callback(byte) ITCB7, // If True callback(byte &0x7F) NAKFAIL, // if false callback(-1) - FAIL, // callback(-1) + CALLFAIL, // callback(-1) STARTMERGE, // Clear bit and byte settings ready for merge pass MERGE, // Merge previous wack response with byte value and decrement bit number (use for readimng CV bytes) SETBIT, // sets bit number to next prog byte @@ -191,6 +191,8 @@ private: #define ARDUINO_TYPE "TEENSY40" #elif defined(ARDUINO_TEENSY41) #define ARDUINO_TYPE "TEENSY41" +#elif defined(ARDUINO_ARCH_ESP8266) +#define ARDUINO_TYPE "ESP8266" #else #error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH AN ARDUINO UNO, NANO 328, OR ARDUINO MEGA 1280/2560 #endif diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 2c3885a..8591844 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -30,7 +30,7 @@ #include "EEStore.h" #include "DIAG.h" -#include +//#include // 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 diff --git a/DCCTimer.cpp b/DCCTimer.cpp index 727caa1..0ea7491 100644 --- a/DCCTimer.cpp +++ b/DCCTimer.cpp @@ -44,7 +44,7 @@ #include "DCCTimer.h" const int DCC_SIGNAL_TIME=58; // this is the 58uS DCC 1-bit waveform half-cycle -const long CLOCK_CYCLES=(F_CPU / 1000000 * DCC_SIGNAL_TIME) >>1; +const long CLOCK_CYCLES=(F_CPU / 1000000 * DCC_SIGNAL_TIME); INTERRUPT_CALLBACK interruptHandler=0; @@ -53,11 +53,11 @@ INTERRUPT_CALLBACK interruptHandler=0; void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interruptHandler=callback; - noInterrupts(); + noInterrupts(); ADC0.CTRLC = (ADC0.CTRLC & 0b00110000) | 0b01000011; // speed up analogRead sample time TCB0.CTRLB = TCB_CNTMODE_INT_gc & ~TCB_CCMPEN_bm; // timer compare mode with output disabled TCB0.CTRLA = TCB_CLKSEL_CLKDIV2_gc; // 8 MHz ~ 0.125 us - TCB0.CCMP = CLOCK_CYCLES -1; // 1 tick less for timer reset + TCB0.CCMP = (CLOCK_CYCLES>>1) -1; // 1 tick less for timer reset TCB0.INTFLAGS = TCB_CAPT_bm; // clear interrupt request flag TCB0.INTCTRL = TCB_CAPT_bm; // Enable the interrupt TCB0.CNT = 0; @@ -146,6 +146,24 @@ void DCCTimer::read(uint8_t word, uint8_t *mac, uint8_t offset) { } #endif +#elif defined(ARDUINO_ARCH_ESP8266) +// ESP8266 !!!!!!!!!!!!!!!!!!!!! +void DCCTimer::begin(INTERRUPT_CALLBACK callback) { + interruptHandler=callback; + noInterrupts(); + timer1_attachInterrupt(interruptHandler); + timer1_write(CLOCK_CYCLES); + timer1_enable(TIM_DIV1, TIM_EDGE, TIM_LOOP); + interrupts(); +} +bool DCCTimer::isPWMPin(byte pin) { + return false; +} +void DCCTimer::setPWM(byte pin, bool high) { +} + + + #else // Arduino nano, uno, mega etc #if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__) @@ -159,10 +177,10 @@ void DCCTimer::read(uint8_t word, uint8_t *mac, uint8_t offset) { void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interruptHandler=callback; - noInterrupts(); + noInterrupts(); ADCSRA = (ADCSRA & 0b11111000) | 0b00000100; // speed up analogRead sample time TCCR1A = 0; - ICR1 = CLOCK_CYCLES; + ICR1 = CLOCK_CYCLES>>1; TCNT1 = 0; TCCR1B = _BV(WGM13) | _BV(CS10); // Mode 8, clock select 1 TIMSK1 = _BV(TOIE1); // Enable Software interrupt diff --git a/MotorDriver.cpp b/MotorDriver.cpp index f51ee04..769c7d6 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -176,13 +176,13 @@ int MotorDriver::mA2raw( unsigned int mA) { void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & result) { // DIAG(F("MotorDriver %S Pin=%d,"),type,pin); - (void) type; // avoid compiler warning if diag not used above. - uint8_t port = digitalPinToPort(pin); + (void) type; // avoid compiler warning if diag not used above. + PORTTYPE port = digitalPinToPort(pin); if (input) result.inout = portInputRegister(port); else result.inout = portOutputRegister(port); result.maskHIGH = digitalPinToBitMask(pin); result.maskLOW = ~result.maskHIGH; - // DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH); + DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH); } diff --git a/MotorDriver.h b/MotorDriver.h index 08db049..ffa8387 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -26,13 +26,15 @@ #define UNUSED_PIN 127 // inside int8_t #endif -#if defined(__IMXRT1062__) +#if defined(__IMXRT1062__) || defined (ARDUINO_ARCH_ESP8266) +typedef uint32_t PORTTYPE; struct FASTPIN { volatile uint32_t *inout; uint32_t maskHIGH; uint32_t maskLOW; }; #else +typedef uint8_t PORTTYPE; struct FASTPIN { volatile uint8_t *inout; uint8_t maskHIGH; diff --git a/freeMemory.cpp b/freeMemory.cpp index 5514e8d..6eb822e 100644 --- a/freeMemory.cpp +++ b/freeMemory.cpp @@ -27,6 +27,8 @@ extern "C" char* sbrk(int); #elif defined(__AVR__) extern char *__brkval; extern char *__malloc_heap_start; +#elif defined(ARDUINO_ARCH_ESP8266) +// fine as well #else #error Unsupported board type #endif @@ -34,7 +36,7 @@ extern char *__malloc_heap_start; static volatile int minimum_free_memory = __INT_MAX__; -#if !defined(__IMXRT1062__) +#if !defined(__IMXRT1062__) && !defined(ARDUINO_ARCH_ESP8266) static inline int freeMemory() { char top; #if defined(__arm__) @@ -55,7 +57,20 @@ int minimumFreeMemory() { return retval; } +#elif defined(ARDUINO_ARCH_ESP8266) +// ESP8266 +static inline int freeMemory() { + return ESP.getFreeHeap(); +} +// Return low memory value. +int minimumFreeMemory() { + noInterrupts(); // Disable interrupts + int retval = minimum_free_memory; + interrupts(); // interrupts + return retval; +} #else +// All types of TEENSYs #if defined(ARDUINO_TEENSY40) static const unsigned DTCM_START = 0x20000000UL; static const unsigned OCRAM_START = 0x20200000UL; From 34474cbf5c9dd9448a6678417bcb4558391104a4 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Sep 2021 09:23:52 +0200 Subject: [PATCH 02/66] WifiESP skeleton files --- WifiESP.cpp | 31 +++++++++++++++++++++++++++++++ WifiESP.h | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 65 insertions(+) create mode 100644 WifiESP.cpp create mode 100644 WifiESP.h diff --git a/WifiESP.cpp b/WifiESP.cpp new file mode 100644 index 0000000..5435ca0 --- /dev/null +++ b/WifiESP.cpp @@ -0,0 +1,31 @@ +/* + © 2021, Harald Barth. + + 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 "WifiESP.h" +#include "DIAG.h" + +bool WifiESP::setup(const FSH *wifiESSID, + const FSH *wifiPassword, + const FSH *hostname, + const int port, + const byte channel) { + +} +void WifiESP::loop() { +} diff --git a/WifiESP.h b/WifiESP.h new file mode 100644 index 0000000..d73c741 --- /dev/null +++ b/WifiESP.h @@ -0,0 +1,34 @@ +/* + * © 2021, Harald Barth. + * + * 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 WifiESP_h +#define WifiESP_h + +class WifiESP +{ + +public: + static bool setup(const FSH *wifiESSID, + const FSH *wifiPassword, + const FSH *hostname, + const int port, + const byte channel); + static void loop(); +private: +}; +#endif From b048879eaafcaee4fc616e725fbcdce7dc87db06 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 25 Sep 2021 23:18:10 +0200 Subject: [PATCH 03/66] Wifi active --- CommandStation-EX.ino | 7 ++- DCCEX.h | 1 + DCCTimer.cpp | 12 ++++- DCCWaveform.cpp | 15 ++---- MotorDriver.cpp | 11 ++--- MotorDriver.h | 17 ++++++- WifiESP.cpp | 106 ++++++++++++++++++++++++++++++++++++++++-- WifiESP.h | 9 ++-- config.example.h | 11 ++++- freeMemory.cpp | 4 +- 10 files changed, 157 insertions(+), 36 deletions(-) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index c112c54..2b89708 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -59,6 +59,7 @@ void setup() // Responsibility 1: Start the usb connection for diagnostics // This is normally Serial but uses SerialUSB on a SAMD processor Serial.begin(115200); + Serial.setDebugOutput(true); DIAG(F("License GPLv3 fsf.org (c) dcc-ex.com")); @@ -74,7 +75,7 @@ void setup() #if WIFI_ON WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL); #endif // WIFI_ON - + WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, 2560, 1); #if ETHERNET_ON EthernetInterface::setup(); #endif // ETHERNET_ON @@ -118,6 +119,7 @@ void loop() #if WIFI_ON WifiInterface::loop(); #endif + WifiESP::loop(); #if ETHERNET_ON EthernetInterface::loop(); #endif @@ -134,11 +136,12 @@ void loop() // Report any decrease in memory (will automatically trigger on first call) static int ramLowWatermark = __INT_MAX__; // replaced on first loop - +/* int freeNow = minimumFreeMemory(); if (freeNow < ramLowWatermark) { ramLowWatermark = freeNow; LCD(2,F("Free RAM=%5db"), ramLowWatermark); } + */ } diff --git a/DCCEX.h b/DCCEX.h index 1504490..f6668a6 100644 --- a/DCCEX.h +++ b/DCCEX.h @@ -31,6 +31,7 @@ #include "DCCEXParser.h" #include "version.h" #include "WifiInterface.h" +#include "WifiESP.h" #if ETHERNET_ON == true #include "EthernetInterface.h" #endif diff --git a/DCCTimer.cpp b/DCCTimer.cpp index 0ea7491..5228501 100644 --- a/DCCTimer.cpp +++ b/DCCTimer.cpp @@ -150,16 +150,24 @@ void DCCTimer::read(uint8_t word, uint8_t *mac, uint8_t offset) { // ESP8266 !!!!!!!!!!!!!!!!!!!!! void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interruptHandler=callback; + timer1_disable(); +// ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); +// ETS_FRC_TIMER1_NMI_INTR_ATTACH(interruptHandler); + timer1_attachInterrupt(interruptHandler); + timer1_enable(TIM_DIV1, TIM_EDGE, TIM_LOOP); + timer1_write(CLOCK_CYCLES); +/* noInterrupts(); timer1_attachInterrupt(interruptHandler); timer1_write(CLOCK_CYCLES); timer1_enable(TIM_DIV1, TIM_EDGE, TIM_LOOP); interrupts(); +*/ } -bool DCCTimer::isPWMPin(byte pin) { +IRAM_ATTR bool DCCTimer::isPWMPin(byte pin) { return false; } -void DCCTimer::setPWM(byte pin, bool high) { +void ICACHE_RAM_ATTR DCCTimer::setPWM(byte pin, bool high) { } diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index df88e5d..aaffc2a 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -53,31 +53,26 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { DCCTimer::begin(DCCWaveform::interruptHandler); } -void DCCWaveform::loop(bool ackManagerActive) { +void IRAM_ATTR DCCWaveform::loop(bool ackManagerActive) { mainTrack.checkPowerOverload(false); progTrack.checkPowerOverload(ackManagerActive); } -void DCCWaveform::interruptHandler() { +void IRAM_ATTR DCCWaveform::interruptHandler() { // call the timer edge sensitive actions for progtrack and maintrack // member functions would be cleaner but have more overhead byte sigMain=signalTransform[mainTrack.state]; byte sigProg=progTrackSyncMain? sigMain : signalTransform[progTrack.state]; - // Set the signal state for both tracks mainTrack.motorDriver->setSignal(sigMain); progTrack.motorDriver->setSignal(sigProg); - // Move on in the state engine mainTrack.state=stateTransform[mainTrack.state]; progTrack.state=stateTransform[progTrack.state]; - - // WAVE_PENDING means we dont yet know what the next bit is if (mainTrack.state==WAVE_PENDING) mainTrack.interrupt2(); if (progTrack.state==WAVE_PENDING) progTrack.interrupt2(); else if (progTrack.ackPending) progTrack.checkAck(); - } @@ -197,7 +192,7 @@ const bool DCCWaveform::signalTransform[]={ /* WAVE_LOW_0 -> */ LOW, /* WAVE_PENDING (should not happen) -> */ LOW}; -void DCCWaveform::interrupt2() { +void ICACHE_RAM_ATTR DCCWaveform::interrupt2() { // calculate the next bit to be sent: // set state WAVE_MID_1 for a 1=bit // or WAVE_HIGH_0 for a 0 bit. @@ -207,7 +202,7 @@ void DCCWaveform::interrupt2() { remainingPreambles--; // Update free memory diagnostic as we don't have anything else to do this time. // Allow for checkAck and its called functions using 22 bytes more. - updateMinimumFreeMemory(22); +// might break ESP8266 updateMinimumFreeMemory(22); return; } @@ -306,7 +301,7 @@ byte DCCWaveform::getAck() { return(0); // pending set off but not detected means no ACK. } -void DCCWaveform::checkAck() { +void ICACHE_RAM_ATTR DCCWaveform::checkAck() { // This function operates in interrupt() time so must be fast and can't DIAG if (sentResetsSincePacket > 6) { //ACK timeout ackCheckDuration=millis()-ackCheckStart; diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 769c7d6..c6c37cf 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -21,11 +21,6 @@ #include "DCCTimer.h" #include "DIAG.h" -#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH -#define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW -#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH) -#define isLOW(fastpin) (!isHIGH(fastpin)) - bool MotorDriver::usePWM=false; bool MotorDriver::commonFaultPin=false; @@ -109,8 +104,8 @@ void MotorDriver::setBrake(bool on) { if (on ^ invertBrake) setHIGH(fastBrakePin); else setLOW(fastBrakePin); } - -void MotorDriver::setSignal( bool high) { +/* +IRAM_ATTR void MotorDriver::setSignal( bool high) { if (usePWM) { DCCTimer::setPWM(signalPin,high); } @@ -125,7 +120,7 @@ void MotorDriver::setSignal( bool high) { } } } - +*/ #if defined(ARDUINO_TEENSY32) || defined(ARDUINO_TEENSY35)|| defined(ARDUINO_TEENSY36) volatile unsigned int overflow_count=0; #endif diff --git a/MotorDriver.h b/MotorDriver.h index ffa8387..06c19b1 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -42,12 +42,27 @@ struct FASTPIN { }; #endif +#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH +#define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW +#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH) +#define isLOW(fastpin) (!isHIGH(fastpin)) + class MotorDriver { public: MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin); virtual void setPower( bool on); - virtual void setSignal( bool high); + virtual void setSignal( bool high) { + if (high) { + setHIGH(fastSignalPin); + if (dualSignal) setLOW(fastSignalPin2); + } + else { + setLOW(fastSignalPin); + if (dualSignal) setHIGH(fastSignalPin2); + } + }; + virtual void setBrake( bool on); virtual int getCurrentRaw(); virtual unsigned int raw2mA( int raw); diff --git a/WifiESP.cpp b/WifiESP.cpp index 5435ca0..e99b1a9 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -17,15 +17,111 @@ along with CommandStation. If not, see . */ +#include +#include +#include + #include "WifiESP.h" #include "DIAG.h" +#include "RingStream.h" +#include "CommandDistributor.h" +#include -bool WifiESP::setup(const FSH *wifiESSID, - const FSH *wifiPassword, - const FSH *hostname, - const int port, - const byte channel) { +static std::vector clients; // a list to hold all clients +static AsyncServer *server; +static RingStream *outboundRing = new RingStream(2048); + +static void handleError(void* arg, AsyncClient* client, int8_t error) { + DIAG(F("connection error %s from client %s"), client->errorToString(error), client->remoteIP().toString().c_str()); +} + +static void handleData(void* arg, AsyncClient* client, void *data, size_t len) { + DIAG(F("data received from client %s"), client->remoteIP().toString().c_str()); + uint8_t clientId; + for (clientId=0; clientIdspace() >= (c=outboundRing->count()) && client->canSend()) { + char cmd[c+1]; + int i; + for (i=0;iread(); + } + cmd[i]=0; + client->add(cmd, strlen(cmd)); + client->send(); + } +} + +static void handleDisconnect(void* arg, AsyncClient* client) { + DIAG(F("client %s disconnected"), client->remoteIP().toString().c_str()); +} + +static void handleTimeOut(void* arg, AsyncClient* client, uint32_t time) { + DIAG(F("client ACK timeout ip: %s"), client->remoteIP().toString().c_str()); +} + + +static void handleNewClient(void* arg, AsyncClient* client) { + DIAG(F("New client has been connected to server, ip: %s"), client->remoteIP().toString().c_str()); + + // add to list + clients.push_back(client); + + // register events + client->onData(&handleData, NULL); + client->onError(&handleError, NULL); + client->onDisconnect(&handleDisconnect, NULL); + client->onTimeout(&handleTimeOut, NULL); + +} + +bool WifiESP::setup(const char *wifiESSID, + const char *wifiPassword, + const char *hostname, + int port, + const byte channel) { + DIAG(F("START")); + // connects to access point + wifi_set_sleep_type(NONE_SLEEP_T); + WiFi.mode(WIFI_STA); + WiFi.setAutoReconnect(true); + DIAG(F("BEGIN")); + WiFi.begin(wifiESSID, wifiPassword); + DIAG(F("STATUS")); + while (WiFi.status() != WL_CONNECTED) { + Serial.print('.'); + delay(500); + } + + DIAG(F("SERVER")); + + server = new AsyncServer(port); // start listening on tcp port + + DIAG(F("CLIENT")); + server->onClient(&handleNewClient, server); + DIAG(F("SBEGIN")); + + server->begin(); + + DIAG(F("ENDSETUP")); + + return true; } void WifiESP::loop() { + static unsigned long last = 0; + if (millis() - last > 60000) { + last = millis(); + DIAG(F("+")); + } + ESP.wdtFeed(); } diff --git a/WifiESP.h b/WifiESP.h index d73c741..971ccc7 100644 --- a/WifiESP.h +++ b/WifiESP.h @@ -16,16 +16,19 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ + #ifndef WifiESP_h #define WifiESP_h +#include "FSH.h" + class WifiESP { public: - static bool setup(const FSH *wifiESSID, - const FSH *wifiPassword, - const FSH *hostname, + static bool setup(const char *wifiESSID, + const char *wifiPassword, + const char *hostname, const int port, const byte channel); static void loop(); diff --git a/config.example.h b/config.example.h index 1d1977a..eb7e02b 100644 --- a/config.example.h +++ b/config.example.h @@ -41,7 +41,14 @@ The configuration file for DCC-EX Command Station // | // +-----------------------v // -#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD +//#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD + +#define ESP_MOTOR_SHIELD F("ESP"), \ + new MotorDriver(D3, D5, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.99, 2000, UNUSED_PIN),\ + new MotorDriver(D2, D6, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.99, 2000, UNUSED_PIN) + +#define MOTOR_SHIELD_TYPE ESP_MOTOR_SHIELD + ///////////////////////////////////////////////////////////////////////////////////// // // The IP port to talk to a WIFI or Ethernet shield. @@ -53,7 +60,7 @@ The configuration file for DCC-EX Command Station // NOTE: Only supported on Arduino Mega // Set to false if you not even want it on the Arduino Mega // -#define ENABLE_WIFI true +//#define ENABLE_WIFI true ///////////////////////////////////////////////////////////////////////////////////// // diff --git a/freeMemory.cpp b/freeMemory.cpp index 6eb822e..af7bd16 100644 --- a/freeMemory.cpp +++ b/freeMemory.cpp @@ -64,9 +64,7 @@ static inline int freeMemory() { } // Return low memory value. int minimumFreeMemory() { - noInterrupts(); // Disable interrupts int retval = minimum_free_memory; - interrupts(); // interrupts return retval; } #else @@ -118,7 +116,7 @@ int minimumFreeMemory() { // So even if all of the heap is freed, the reported minimum free // memory will not increase. // -void updateMinimumFreeMemory(unsigned char extraBytes) { +void ICACHE_RAM_ATTR updateMinimumFreeMemory(unsigned char extraBytes) { int spare = freeMemory()-extraBytes; if (spare < 0) spare = 0; if (spare < minimum_free_memory) minimum_free_memory = spare; From fa1d1619b607ddfbd9cd6f133dbe73cb6fbc7c13 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 26 Sep 2021 08:37:59 +0200 Subject: [PATCH 04/66] wifi sendData --- WifiESP.cpp | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/WifiESP.cpp b/WifiESP.cpp index e99b1a9..36040f1 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -46,20 +46,22 @@ static void handleData(void* arg, AsyncClient* client, void *data, size_t len) { byte cmd[len+1]; memcpy(cmd,data,len); cmd[len]=0; + outboundRing->mark(clientId); CommandDistributor::parse(clientId,cmd,outboundRing); + outboundRing->commit(); } +} + +bool sendData(uint8_t clientId, char* data, int count) { + AsyncClient *client = clients[clientId]; + // reply to client - int c; - if (client->space() >= (c=outboundRing->count()) && client->canSend()) { - char cmd[c+1]; - int i; - for (i=0;iread(); - } - cmd[i]=0; - client->add(cmd, strlen(cmd)); + if (client->space() >= count && client->canSend()) { + client->add(data, count); client->send(); + return true; } + return false; } static void handleDisconnect(void* arg, AsyncClient* client) { @@ -117,7 +119,12 @@ bool WifiESP::setup(const char *wifiESSID, return true; } + void WifiESP::loop() { + + // Do something with outboundRing + // call sendData + static unsigned long last = 0; if (millis() - last > 60000) { last = millis(); From 35cba02ee7990fa8d6bbcc05e3b8328b5db581f2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 26 Sep 2021 10:59:07 +0200 Subject: [PATCH 05/66] outboundRing uses sendData --- WifiESP.cpp | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/WifiESP.cpp b/WifiESP.cpp index 36040f1..fd0a4b2 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -124,7 +124,18 @@ void WifiESP::loop() { // Do something with outboundRing // call sendData - + int clientId=outboundRing->read(); + if (clientId>=0) { + int count=outboundRing->count(); + DIAG(F("Wifi reply client=%d, count=:%d"), clientId,count); + { + char buffer[count]; + for(uint8_t i=0;iread(); + sendData(clientId, buffer, count); + } + } + static unsigned long last = 0; if (millis() - last > 60000) { last = millis(); From 696d12fc5ed3246e009dbe6eea0beee2ba56dac5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 26 Sep 2021 11:57:15 +0200 Subject: [PATCH 06/66] test A0 --- config.example.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.example.h b/config.example.h index eb7e02b..f66e525 100644 --- a/config.example.h +++ b/config.example.h @@ -45,7 +45,7 @@ The configuration file for DCC-EX Command Station #define ESP_MOTOR_SHIELD F("ESP"), \ new MotorDriver(D3, D5, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.99, 2000, UNUSED_PIN),\ - new MotorDriver(D2, D6, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.99, 2000, UNUSED_PIN) + new MotorDriver(D2, D6, UNUSED_PIN, UNUSED_PIN, A0 , 2.99, 2000, UNUSED_PIN) #define MOTOR_SHIELD_TYPE ESP_MOTOR_SHIELD From a194b8965c1249266f95ce33b4ac7c90b3ffab14 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 27 Sep 2021 20:01:46 +0200 Subject: [PATCH 07/66] Ack read outside interrupt --- DCCWaveform.cpp | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index aaffc2a..3e7e98f 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -53,9 +53,22 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { DCCTimer::begin(DCCWaveform::interruptHandler); } +#define SLOW_ANALOG_READ +#ifdef SLOW_ANALOG_READ +// Flag to hold if we need to run ack checking in loop +static bool ackflag = 0; +#endif + void IRAM_ATTR DCCWaveform::loop(bool ackManagerActive) { mainTrack.checkPowerOverload(false); progTrack.checkPowerOverload(ackManagerActive); +#ifdef SLOW_ANALOG_READ + if (ackflag) { + progTrack.checkAck(); + // reset flag AFTER check is done + ackflag = 0; + } +#endif } void IRAM_ATTR DCCWaveform::interruptHandler() { @@ -70,9 +83,17 @@ void IRAM_ATTR DCCWaveform::interruptHandler() { mainTrack.state=stateTransform[mainTrack.state]; progTrack.state=stateTransform[progTrack.state]; // WAVE_PENDING means we dont yet know what the next bit is - if (mainTrack.state==WAVE_PENDING) mainTrack.interrupt2(); - if (progTrack.state==WAVE_PENDING) progTrack.interrupt2(); - else if (progTrack.ackPending) progTrack.checkAck(); + if (mainTrack.state==WAVE_PENDING) + mainTrack.interrupt2(); + if (progTrack.state==WAVE_PENDING) + progTrack.interrupt2(); +#ifdef SLOW_ANALOG_READ + else if (progTrack.ackPending && ackflag == 0) // We need AND we are not already checking + ackflag = 1; +#else + else if (progTrack.ackPending) + progTrack.checkAck(); +#endif } @@ -308,7 +329,7 @@ void ICACHE_RAM_ATTR DCCWaveform::checkAck() { ackPending = false; return; } - + int current=motorDriver->getCurrentRaw(); numAckSamples++; if (current > ackMaxCurrent) ackMaxCurrent=current; From afd4626988a1d46cfd78618ed2c9fc7e5af24e76 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 28 Sep 2021 17:20:44 +0200 Subject: [PATCH 08/66] send diag --- WifiESP.cpp | 45 ++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 40 insertions(+), 5 deletions(-) diff --git a/WifiESP.cpp b/WifiESP.cpp index fd0a4b2..93d1bc3 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -54,13 +54,27 @@ static void handleData(void* arg, AsyncClient* client, void *data, size_t len) { bool sendData(uint8_t clientId, char* data, int count) { AsyncClient *client = clients[clientId]; + size_t willsend = 0; // reply to client - if (client->space() >= count && client->canSend()) { - client->add(data, count); - client->send(); + if (client->canSend()) { + while (count > 0) { + willsend = client->add(data, count); // add checks for space() + if (willsend < count) { + DIAG(F("Willsend %d of count %d"), willsend, count); + } + if (client->send()) { + count = count - willsend; + data = data + willsend; + } else { + DIAG(F("Could not send promised %d"), count); + return false; + } + } + // Did send all bytes we wanted return true; } + DIAG(F("Aborting: Busy or space=0")); return false; } @@ -87,6 +101,25 @@ static void handleNewClient(void* arg, AsyncClient* client) { } +/* Things one _might_ want to do: + Disable soft watchdog: ESP.wdtDisable() + Enable soft watchdog: ESP.wdtEnable(X) ignores the value of X and enables it for fixed + time at least in version 3.0.2 of the esp8266 package. + +Internet says: + +I manage to complety disable the hardware watchdog on ESP8266 in order to run the benchmark CoreMark. + +void hw_wdt_disable(){ + *((volatile uint32_t*) 0x60000900) &= ~(1); // Hardware WDT OFF +} + +void hw_wdt_enable(){ + *((volatile uint32_t*) 0x60000900) |= 1; // Hardware WDT ON +} + +*/ + bool WifiESP::setup(const char *wifiESSID, const char *wifiPassword, const char *hostname, @@ -127,11 +160,13 @@ void WifiESP::loop() { int clientId=outboundRing->read(); if (clientId>=0) { int count=outboundRing->count(); - DIAG(F("Wifi reply client=%d, count=:%d"), clientId,count); + DIAG(F("Wifi reply client=%d, count=%d"), clientId,count); { - char buffer[count]; + char buffer[count+1]; for(uint8_t i=0;iread(); + buffer[count]=0; + DIAG(F("SEND:%s COUNT:%d"),buffer,count); sendData(clientId, buffer, count); } } From 1c7a5320d86331e1f3f654da0eb29f7440e51df1 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 28 Sep 2021 17:31:12 +0200 Subject: [PATCH 09/66] more send diag --- WifiESP.cpp | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/WifiESP.cpp b/WifiESP.cpp index 93d1bc3..a461e43 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -163,8 +163,15 @@ void WifiESP::loop() { DIAG(F("Wifi reply client=%d, count=%d"), clientId,count); { char buffer[count+1]; - for(uint8_t i=0;iread(); + for(uint8_t i=0;iread(); + if (c >= 0) + buffer[i] = (char)c; + else { + DIAG(F("Ringread fail at %d"),i); + break; + } + } buffer[count]=0; DIAG(F("SEND:%s COUNT:%d"),buffer,count); sendData(clientId, buffer, count); From 19b4893b5f64f40224939c6849b389dc1a5db5cb Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 28 Sep 2021 21:08:41 +0200 Subject: [PATCH 10/66] counter should be int, not uint8_t --- WifiESP.cpp | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/WifiESP.cpp b/WifiESP.cpp index a461e43..b46a13d 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -37,7 +37,7 @@ static void handleError(void* arg, AsyncClient* client, int8_t error) { } static void handleData(void* arg, AsyncClient* client, void *data, size_t len) { - DIAG(F("data received from client %s"), client->remoteIP().toString().c_str()); + //DIAG(F("data received from client %s"), client->remoteIP().toString().c_str()); uint8_t clientId; for (clientId=0; clientIdread(); if (clientId>=0) { int count=outboundRing->count(); - DIAG(F("Wifi reply client=%d, count=%d"), clientId,count); + //DIAG(F("Wifi reply client=%d, count=%d"), clientId,count); { char buffer[count+1]; - for(uint8_t i=0;iread(); if (c >= 0) buffer[i] = (char)c; @@ -173,8 +173,11 @@ void WifiESP::loop() { } } buffer[count]=0; - DIAG(F("SEND:%s COUNT:%d"),buffer,count); - sendData(clientId, buffer, count); + //DIAG(F("SEND:%s COUNT:%d"),buffer,count); + while (! sendData(clientId, buffer, count)) { + DIAG(F("senData fail")); + yield(); + } } } From 426b27f0dd01f800dc0cb31cf71782bbb7cb85f9 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 30 Sep 2021 22:55:14 +0200 Subject: [PATCH 11/66] Reworked use of ringbuffer --- RingStream.cpp | 15 ++++++++++++--- RingStream.h | 7 +++++-- WifiESP.cpp | 49 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 58 insertions(+), 13 deletions(-) diff --git a/RingStream.cpp b/RingStream.cpp index 2c28d1b..baa4cc1 100644 --- a/RingStream.cpp +++ b/RingStream.cpp @@ -45,10 +45,11 @@ size_t RingStream::write(uint8_t b) { return 1; } -int RingStream::read() { - if ((_pos_read==_pos_write) && !_overflow) return -1; // empty +int RingStream::read(byte advance) { + if ((_pos_read==_pos_write) && !_overflow) return -1; // empty + if (_pos_read == _mark) return -1; byte b=_buffer[_pos_read]; - _pos_read++; + _pos_read += advance; if (_pos_read==_len) _pos_read=0; _overflow=false; return b; @@ -68,6 +69,7 @@ int RingStream::freeSpace() { // mark start of message with client id (0...9) void RingStream::mark(uint8_t b) { + //DIAG(F("Mark1 len=%d count=%d pr=%d pw=%d m=%d"),_len, _count,_pos_read,_pos_write,_mark); _mark=_pos_write; write(b); // client id write((uint8_t)0); // count MSB placemarker @@ -81,7 +83,12 @@ uint8_t RingStream::peekTargetMark() { return _buffer[_mark]; } +void RingStream::info() { + DIAG(F("Info len=%d count=%d pr=%d pw=%d m=%d"),_len, _count,_pos_read,_pos_write,_mark); +} + bool RingStream::commit() { + //DIAG(F("Commit1 len=%d count=%d pr=%d pw=%d m=%d"),_len, _count,_pos_read,_pos_write,_mark); if (_overflow) { DIAG(F("RingStream(%d) commit(%d) OVERFLOW"),_len, _count); // just throw it away @@ -101,5 +108,7 @@ bool RingStream::commit() { _mark++; if (_mark==_len) _mark=0; _buffer[_mark]=lowByte(_count); + _mark=_len+1; + //DIAG(F("Commit2 len=%d count=%d pr=%d pw=%d m=%d"),_len, _count,_pos_read,_pos_write,_mark); return true; // commit worked } diff --git a/RingStream.h b/RingStream.h index 790c66e..974a235 100644 --- a/RingStream.h +++ b/RingStream.h @@ -28,14 +28,17 @@ class RingStream : public Print { virtual size_t write(uint8_t b); using Print::write; - int read(); + inline int read() { return read(1); }; + inline int peek() { return read(0); }; int count(); int freeSpace(); void mark(uint8_t b); bool commit(); uint8_t peekTargetMark(); - + void info(); + private: + int read(byte advance); int _len; int _pos_write; int _pos_read; diff --git a/WifiESP.cpp b/WifiESP.cpp index b46a13d..8948e3d 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -52,18 +52,22 @@ static void handleData(void* arg, AsyncClient* client, void *data, size_t len) { } } -bool sendData(uint8_t clientId, char* data, int count) { - AsyncClient *client = clients[clientId]; +//static AsyncClient *debugclient = NULL; + +bool sendData(AsyncClient *client, char* data, size_t count) { size_t willsend = 0; // reply to client if (client->canSend()) { while (count > 0) { - willsend = client->add(data, count); // add checks for space() + if (client->connected()) + willsend = client->add(data, count); // add checks for space() + else + willsend = 0; if (willsend < count) { DIAG(F("Willsend %d of count %d"), willsend, count); } - if (client->send()) { + if (client->connected() && client->send()) { count = count - willsend; data = data + willsend; } else { @@ -78,12 +82,23 @@ bool sendData(uint8_t clientId, char* data, int count) { return false; } +static void deleteClient(AsyncClient* client) { + uint8_t clientId; + for (clientId=0; clientIdremoteIP().toString().c_str()); + deleteClient(client); } static void handleTimeOut(void* arg, AsyncClient* client, uint32_t time) { DIAG(F("client ACK timeout ip: %s"), client->remoteIP().toString().c_str()); + deleteClient(client); } @@ -154,11 +169,27 @@ bool WifiESP::setup(const char *wifiESSID, } void WifiESP::loop() { - + AsyncClient *client = NULL; // Do something with outboundRing // call sendData - int clientId=outboundRing->read(); - if (clientId>=0) { + int clientId=outboundRing->peek(); + if (clientId >= 0) { + if (clientId > clients.size()) { + // something is wrong with the ringbuffer position + outboundRing->info(); + client = NULL; + } else { + client = clients[clientId]; + } +// if (client != debugclient) { +// DIAG(F("new client pointer = %x from id %d"), client, clientId); +// debugclient = client; +// } + } else { + client = NULL; + } + if (clientId>=0 && client && client->connected() && client->canSend()) { + outboundRing->read(); int count=outboundRing->count(); //DIAG(F("Wifi reply client=%d, count=%d"), clientId,count); { @@ -174,9 +205,11 @@ void WifiESP::loop() { } buffer[count]=0; //DIAG(F("SEND:%s COUNT:%d"),buffer,count); - while (! sendData(clientId, buffer, count)) { + uint8_t tries = 3; + while (! sendData(client, buffer, count)) { DIAG(F("senData fail")); yield(); + if (tries == 0) break; } } } From cf0c818138210fb6f3c9fe16512b340e24f864f0 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 1 Oct 2021 09:09:30 +0200 Subject: [PATCH 12/66] Cleanup ESP specific details --- CommandStation-EX.ino | 17 +++++++++++++---- DCCEX.h | 2 ++ DCCEXParser.cpp | 5 ++++- DCCTimer.cpp | 23 +++++++++++------------ DCCWaveform.cpp | 10 ++++++---- MotorDriver.cpp | 8 ++++---- MotorDriver.h | 17 ++++++++++------- WifiESP.cpp | 35 +++++++++++++++++++---------------- WifiESP.h | 2 +- defines.h | 12 ++++++++++-- freeMemory.cpp | 5 ++--- 11 files changed, 82 insertions(+), 54 deletions(-) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 2b89708..71c1d67 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -59,7 +59,9 @@ void setup() // Responsibility 1: Start the usb connection for diagnostics // This is normally Serial but uses SerialUSB on a SAMD processor Serial.begin(115200); +#ifdef ESP_DEBUG Serial.setDebugOutput(true); +#endif DIAG(F("License GPLv3 fsf.org (c) dcc-ex.com")); @@ -73,9 +75,12 @@ void setup() // Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi // Start Ethernet if it exists #if WIFI_ON +#ifndef ESP_FAMILY WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL); +#else + WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL); +#endif #endif // WIFI_ON - WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, 2560, 1); #if ETHERNET_ON EthernetInterface::setup(); #endif // ETHERNET_ON @@ -117,9 +122,12 @@ void loop() // Responsibility 3: Optionally handle any incoming WiFi traffic #if WIFI_ON +#ifndef ESP_FAMILY WifiInterface::loop(); -#endif +#else WifiESP::loop(); +#endif +#endif //WIFI_ON #if ETHERNET_ON EthernetInterface::loop(); #endif @@ -136,12 +144,13 @@ void loop() // Report any decrease in memory (will automatically trigger on first call) static int ramLowWatermark = __INT_MAX__; // replaced on first loop -/* +#ifdef ESP_FAMILY + updateMinimumFreeMemory(128); +#endif int freeNow = minimumFreeMemory(); if (freeNow < ramLowWatermark) { ramLowWatermark = freeNow; LCD(2,F("Free RAM=%5db"), ramLowWatermark); } - */ } diff --git a/DCCEX.h b/DCCEX.h index f6668a6..1c2c522 100644 --- a/DCCEX.h +++ b/DCCEX.h @@ -31,7 +31,9 @@ #include "DCCEXParser.h" #include "version.h" #include "WifiInterface.h" +#ifdef ESP_FAMILY #include "WifiESP.h" +#endif #if ETHERNET_ON == true #include "EthernetInterface.h" #endif diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 8591844..2bbcc5b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -17,6 +17,7 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ +#include "defines.h" #include "StringFormatter.h" #include "DCCEXParser.h" #include "DCC.h" @@ -30,7 +31,9 @@ #include "EEStore.h" #include "DIAG.h" -//#include +#ifndef ESP_FAMILY +#include +#endif // These keywords are used in the <1> command. The number is what you get if you use the keyword as a parameter. // To discover new keyword numbers , use the <$ YOURKEYWORD> command diff --git a/DCCTimer.cpp b/DCCTimer.cpp index 5228501..063a6ae 100644 --- a/DCCTimer.cpp +++ b/DCCTimer.cpp @@ -147,27 +147,26 @@ void DCCTimer::read(uint8_t word, uint8_t *mac, uint8_t offset) { #endif #elif defined(ARDUINO_ARCH_ESP8266) -// ESP8266 !!!!!!!!!!!!!!!!!!!!! + void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interruptHandler=callback; timer1_disable(); -// ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); -// ETS_FRC_TIMER1_NMI_INTR_ATTACH(interruptHandler); + + // There seem to be differnt ways to attach interrupt handler + // ETS_FRC_TIMER1_INTR_ATTACH(NULL, NULL); + // ETS_FRC_TIMER1_NMI_INTR_ATTACH(interruptHandler); + // Let us choose the one from the API timer1_attachInterrupt(interruptHandler); + + // not exactly sure of order: timer1_enable(TIM_DIV1, TIM_EDGE, TIM_LOOP); timer1_write(CLOCK_CYCLES); -/* - noInterrupts(); - timer1_attachInterrupt(interruptHandler); - timer1_write(CLOCK_CYCLES); - timer1_enable(TIM_DIV1, TIM_EDGE, TIM_LOOP); - interrupts(); -*/ } -IRAM_ATTR bool DCCTimer::isPWMPin(byte pin) { +// We do not support to use PWM to make the Waveform on ESP +bool IRAM_ATTR DCCTimer::isPWMPin(byte pin) { return false; } -void ICACHE_RAM_ATTR DCCTimer::setPWM(byte pin, bool high) { +void IRAM_ATTR DCCTimer::setPWM(byte pin, bool high) { } diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 3e7e98f..df599de 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -20,6 +20,7 @@ #pragma GCC optimize ("-O3") #include +#include "defines.h" #include "DCCWaveform.h" #include "DCCTimer.h" #include "DIAG.h" @@ -53,7 +54,6 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { DCCTimer::begin(DCCWaveform::interruptHandler); } -#define SLOW_ANALOG_READ #ifdef SLOW_ANALOG_READ // Flag to hold if we need to run ack checking in loop static bool ackflag = 0; @@ -213,7 +213,7 @@ const bool DCCWaveform::signalTransform[]={ /* WAVE_LOW_0 -> */ LOW, /* WAVE_PENDING (should not happen) -> */ LOW}; -void ICACHE_RAM_ATTR DCCWaveform::interrupt2() { +void IRAM_ATTR DCCWaveform::interrupt2() { // calculate the next bit to be sent: // set state WAVE_MID_1 for a 1=bit // or WAVE_HIGH_0 for a 0 bit. @@ -223,7 +223,9 @@ void ICACHE_RAM_ATTR DCCWaveform::interrupt2() { remainingPreambles--; // Update free memory diagnostic as we don't have anything else to do this time. // Allow for checkAck and its called functions using 22 bytes more. -// might break ESP8266 updateMinimumFreeMemory(22); +#ifndef ESP_FAMILY + updateMinimumFreeMemory(22); +#endif return; } @@ -322,7 +324,7 @@ byte DCCWaveform::getAck() { return(0); // pending set off but not detected means no ACK. } -void ICACHE_RAM_ATTR DCCWaveform::checkAck() { +void IRAM_ATTR DCCWaveform::checkAck() { // This function operates in interrupt() time so must be fast and can't DIAG if (sentResetsSincePacket > 6) { //ACK timeout ackCheckDuration=millis()-ackCheckStart; diff --git a/MotorDriver.cpp b/MotorDriver.cpp index c6c37cf..8bcb03c 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -104,8 +104,8 @@ void MotorDriver::setBrake(bool on) { if (on ^ invertBrake) setHIGH(fastBrakePin); else setLOW(fastBrakePin); } -/* -IRAM_ATTR void MotorDriver::setSignal( bool high) { + +void IRAM_ATTR MotorDriver::setSignal( bool high) { if (usePWM) { DCCTimer::setPWM(signalPin,high); } @@ -120,7 +120,7 @@ IRAM_ATTR void MotorDriver::setSignal( bool high) { } } } -*/ + #if defined(ARDUINO_TEENSY32) || defined(ARDUINO_TEENSY35)|| defined(ARDUINO_TEENSY36) volatile unsigned int overflow_count=0; #endif @@ -179,5 +179,5 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res result.inout = portOutputRegister(port); result.maskHIGH = digitalPinToBitMask(pin); result.maskLOW = ~result.maskHIGH; - DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH); + // DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH); } diff --git a/MotorDriver.h b/MotorDriver.h index 06c19b1..a020ef3 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -52,17 +52,20 @@ class MotorDriver { MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin); virtual void setPower( bool on); - virtual void setSignal( bool high) { - if (high) { + void setSignal( bool high);/* { + if (usePWM) { + DCCTimer::setPWM(signalPin,high); + } + + if (high) { setHIGH(fastSignalPin); if (dualSignal) setLOW(fastSignalPin2); - } - else { + } + else { setLOW(fastSignalPin); if (dualSignal) setHIGH(fastSignalPin2); - } - }; - + } + };*/ virtual void setBrake( bool on); virtual int getCurrentRaw(); virtual unsigned int raw2mA( int raw); diff --git a/WifiESP.cpp b/WifiESP.cpp index 8948e3d..d949679 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -17,6 +17,8 @@ along with CommandStation. If not, see . */ +#include "defines.h" +#ifdef ESP_FAMILY #include #include #include @@ -92,7 +94,7 @@ static void deleteClient(AsyncClient* client) { } } static void handleDisconnect(void* arg, AsyncClient* client) { - DIAG(F("client %s disconnected"), client->remoteIP().toString().c_str()); + DIAG(F("Client disconnected")); deleteClient(client); } @@ -103,11 +105,11 @@ static void handleTimeOut(void* arg, AsyncClient* client, uint32_t time) { static void handleNewClient(void* arg, AsyncClient* client) { - DIAG(F("New client has been connected to server, ip: %s"), client->remoteIP().toString().c_str()); + DIAG(F("New client %s"), client->remoteIP().toString().c_str()); // add to list clients.push_back(client); - + // register events client->onData(&handleData, NULL); client->onError(&handleError, NULL); @@ -118,7 +120,7 @@ static void handleNewClient(void* arg, AsyncClient* client) { /* Things one _might_ want to do: Disable soft watchdog: ESP.wdtDisable() - Enable soft watchdog: ESP.wdtEnable(X) ignores the value of X and enables it for fixed + Enable soft watchdog: ESP.wdtEnable(X) ignores the value of X and enables it for fixed time at least in version 3.0.2 of the esp8266 package. Internet says: @@ -140,30 +142,29 @@ bool WifiESP::setup(const char *wifiESSID, const char *hostname, int port, const byte channel) { - DIAG(F("START")); - // connects to access point + // We are server and should not sleep wifi_set_sleep_type(NONE_SLEEP_T); + // connects to access point WiFi.mode(WIFI_STA); WiFi.setAutoReconnect(true); - DIAG(F("BEGIN")); WiFi.begin(wifiESSID, wifiPassword); - DIAG(F("STATUS")); while (WiFi.status() != WL_CONNECTED) { Serial.print('.'); delay(500); } - - DIAG(F("SERVER")); + if (WiFi.status() == WL_CONNECTED) + DIAG(F("Wifi IP %s"),WiFi.localIP().toString().c_str()); + else { + DIAG(F("Wifi fail")); + // no idea to go on + return false; + } server = new AsyncServer(port); // start listening on tcp port - DIAG(F("CLIENT")); server->onClient(&handleNewClient, server); - DIAG(F("SBEGIN")); - server->begin(); - - DIAG(F("ENDSETUP")); + DIAG(F("Server up port %d"),port); return true; } @@ -213,11 +214,13 @@ void WifiESP::loop() { } } } - +#ifdef ESP_DEBUG static unsigned long last = 0; if (millis() - last > 60000) { last = millis(); DIAG(F("+")); } +#endif ESP.wdtFeed(); } +#endif //ESP_FAMILY diff --git a/WifiESP.h b/WifiESP.h index 971ccc7..47de4ed 100644 --- a/WifiESP.h +++ b/WifiESP.h @@ -1,6 +1,6 @@ /* * © 2021, Harald Barth. - * + * * This file is part of CommandStation-EX * * This is free software: you can redistribute it and/or modify diff --git a/defines.h b/defines.h index b018c54..afb18e8 100644 --- a/defines.h +++ b/defines.h @@ -1,5 +1,5 @@ /* - © 2020, Harald Barth. + © 2020,2021 Harald Barth. This file is part of CommandStation-EX @@ -18,12 +18,20 @@ */ +//////////////////////////////////////////////////////////////////////////////// +// +#if defined (ARDUINO_ARCH_ESP8266) +#define ESP_FAMILY +//#define ESP_DEBUG +#define SLOW_ANALOG_READ +#endif + //////////////////////////////////////////////////////////////////////////////// // // WIFI_ON: All prereqs for running with WIFI are met // Note: WIFI_CHANNEL may not exist in early config.h files so is added here if needed. -#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(TEENSYDUINO)) +#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(TEENSYDUINO) || defined (ESP_FAMILY)) #define WIFI_ON true #ifndef WIFI_CHANNEL #define WIFI_CHANNEL 1 diff --git a/freeMemory.cpp b/freeMemory.cpp index af7bd16..f2dc29d 100644 --- a/freeMemory.cpp +++ b/freeMemory.cpp @@ -28,7 +28,7 @@ extern "C" char* sbrk(int); extern char *__brkval; extern char *__malloc_heap_start; #elif defined(ARDUINO_ARCH_ESP8266) -// fine as well +// supported but nothing needed here #else #error Unsupported board type #endif @@ -116,9 +116,8 @@ int minimumFreeMemory() { // So even if all of the heap is freed, the reported minimum free // memory will not increase. // -void ICACHE_RAM_ATTR updateMinimumFreeMemory(unsigned char extraBytes) { +void updateMinimumFreeMemory(unsigned char extraBytes) { int spare = freeMemory()-extraBytes; if (spare < 0) spare = 0; if (spare < minimum_free_memory) minimum_free_memory = spare; } - From 0bb6b577fa32795d8479ee2d53abd06d3b415d22 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 1 Oct 2021 11:32:09 +0200 Subject: [PATCH 13/66] Wifi STA or AP mode --- WifiESP.cpp | 62 ++++++++++++++++++++++++++++++++++++++++++----------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/WifiESP.cpp b/WifiESP.cpp index d949679..f4e9773 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -22,6 +22,7 @@ #include #include #include +#include #include "WifiESP.h" #include "DIAG.h" @@ -137,25 +138,62 @@ void hw_wdt_enable(){ */ -bool WifiESP::setup(const char *wifiESSID, - const char *wifiPassword, +bool WifiESP::setup(const char *SSid, + const char *password, const char *hostname, int port, const byte channel) { + bool havePassword = true; + bool haveSSID = true; + bool wifiUp = false; + // We are server and should not sleep wifi_set_sleep_type(NONE_SLEEP_T); // connects to access point - WiFi.mode(WIFI_STA); - WiFi.setAutoReconnect(true); - WiFi.begin(wifiESSID, wifiPassword); - while (WiFi.status() != WL_CONNECTED) { - Serial.print('.'); - delay(500); + + const char *yourNetwork = "Your network "; + if (strncmp(yourNetwork, SSid, 13) == 0 || strncmp("", SSid, 13) == 0) + haveSSID = false; + if (strncmp(yourNetwork, password, 13) == 0 || strncmp("", password, 13) == 0) + havePassword = false; + + if (haveSSID && havePassword) { + WiFi.mode(WIFI_STA); + WiFi.setAutoReconnect(true); + WiFi.begin(SSid, password); + while (WiFi.status() != WL_CONNECTED) { + Serial.print('.'); + delay(500); + } + if (WiFi.status() == WL_CONNECTED) { + DIAG(F("Wifi STA IP %s"),WiFi.localIP().toString().c_str()); + wifiUp = true; + } } - if (WiFi.status() == WL_CONNECTED) - DIAG(F("Wifi IP %s"),WiFi.localIP().toString().c_str()); - else { - DIAG(F("Wifi fail")); + if (!haveSSID) { + // prepare all strings + String strSSID("DCC_"); + String strPass("PASS_"); + String strMac = WiFi.macAddress(); + strMac.remove(0,9); + strMac.replace(":",""); + strMac.replace(":",""); + strSSID.concat(strMac); + strPass.concat(strMac); + + WiFi.mode(WIFI_AP); + if (WiFi.softAP(strSSID.c_str(), + havePassword ? password : strPass.c_str(), + channel, false, 8)) { + DIAG(F("Wifi AP SSID %s PASS %s"),strSSID.c_str(),havePassword ? password : strPass.c_str()); + DIAG(F("Wifi AP IP %s"),WiFi.softAPIP().toString().c_str()); + wifiUp = true; + } + } + + + if (!wifiUp) { + DIAG(F("Wifi all fail")); // no idea to go on return false; } From 50bb1c950b00b29c29662f1cb5ce06395539fbe5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 3 Oct 2021 19:39:43 +0200 Subject: [PATCH 14/66] less warnings --- WifiESP.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/WifiESP.cpp b/WifiESP.cpp index f4e9773..2c6f169 100644 --- a/WifiESP.cpp +++ b/WifiESP.cpp @@ -36,10 +36,12 @@ static AsyncServer *server; static RingStream *outboundRing = new RingStream(2048); static void handleError(void* arg, AsyncClient* client, int8_t error) { + (void)arg; DIAG(F("connection error %s from client %s"), client->errorToString(error), client->remoteIP().toString().c_str()); } static void handleData(void* arg, AsyncClient* client, void *data, size_t len) { + (void)arg; //DIAG(F("data received from client %s"), client->remoteIP().toString().c_str()); uint8_t clientId; for (clientId=0; clientIdremoteIP().toString().c_str()); deleteClient(client); } static void handleNewClient(void* arg, AsyncClient* client) { + (void)arg; DIAG(F("New client %s"), client->remoteIP().toString().c_str()); // add to list @@ -213,7 +219,7 @@ void WifiESP::loop() { // call sendData int clientId=outboundRing->peek(); if (clientId >= 0) { - if (clientId > clients.size()) { + if ((unsigned int)clientId > clients.size()) { // something is wrong with the ringbuffer position outboundRing->info(); client = NULL; From 43191e225e2c2638f39e00c81ae21f7263e28568 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 4 Oct 2021 23:03:36 +0200 Subject: [PATCH 15/66] first stab at ESP32 --- DCCTimer.cpp | 19 +++++++++++++++++++ DCCWaveform.cpp | 7 ++++++- defines.h | 11 +++++++++++ 3 files changed, 36 insertions(+), 1 deletion(-) diff --git a/DCCTimer.cpp b/DCCTimer.cpp index 063a6ae..6b204fe 100644 --- a/DCCTimer.cpp +++ b/DCCTimer.cpp @@ -169,7 +169,26 @@ bool IRAM_ATTR DCCTimer::isPWMPin(byte pin) { void IRAM_ATTR DCCTimer::setPWM(byte pin, bool high) { } +#elif defined(ARDUINO_ARCH_ESP32) +// https://www.visualmicro.com/page/Timer-Interrupts-Explained.aspx +portMUX_TYPE timerMux = portMUX_INITIALIZER_UNLOCKED; + +void DCCTimer::begin(INTERRUPT_CALLBACK callback) { + interruptHandler = callback; + hw_timer_t *timer = NULL; + timer = timerBegin(0, 2, true); // prescaler can be 2 to 65536 so choose 2 + timerAttachInterrupt(timer, interruptHandler, true); + timerAlarmWrite(timer, CLOCK_CYCLES / 2, true); // divide by prescaler + timerAlarmEnable(timer); +} + +// We do not support to use PWM to make the Waveform on ESP +bool IRAM_ATTR DCCTimer::isPWMPin(byte pin) { + return false; +} +void IRAM_ATTR DCCTimer::setPWM(byte pin, bool high) { +} #else // Arduino nano, uno, mega etc diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index df599de..dbabc19 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -66,7 +66,9 @@ void IRAM_ATTR DCCWaveform::loop(bool ackManagerActive) { if (ackflag) { progTrack.checkAck(); // reset flag AFTER check is done + portENTER_CRITICAL(&timerMux); ackflag = 0; + portEXIT_CRITICAL(&timerMux); } #endif } @@ -88,8 +90,11 @@ void IRAM_ATTR DCCWaveform::interruptHandler() { if (progTrack.state==WAVE_PENDING) progTrack.interrupt2(); #ifdef SLOW_ANALOG_READ - else if (progTrack.ackPending && ackflag == 0) // We need AND we are not already checking + else if (progTrack.ackPending && ackflag == 0) { // We need AND we are not already checking + portENTER_CRITICAL(&timerMux); ackflag = 1; + portEXIT_CRITICAL(&timerMux); + } #else else if (progTrack.ackPending) progTrack.checkAck(); diff --git a/defines.h b/defines.h index afb18e8..a79ddca 100644 --- a/defines.h +++ b/defines.h @@ -26,6 +26,17 @@ #define SLOW_ANALOG_READ #endif +//////////////////////////////////////////////////////////////////////////////// +// +#if defined (ARDUINO_ARCH_ESP32) +#define ESP_FAMILY +#define SLOW_ANALOG_READ +#else +#define portENTER_CRITICAL(A) do {} while (0) +#define portEXIT_CRITICAL(A) do {} while (0) +#endif + + //////////////////////////////////////////////////////////////////////////////// // // WIFI_ON: All prereqs for running with WIFI are met From 0a10dbea0b433096548591d2760c4cc739d80b35 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 4 Oct 2021 23:12:47 +0200 Subject: [PATCH 16/66] not forget volatile --- DCCWaveform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index dbabc19..33f7392 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -56,7 +56,7 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { #ifdef SLOW_ANALOG_READ // Flag to hold if we need to run ack checking in loop -static bool ackflag = 0; +volatile bool ackflag = 0; #endif void IRAM_ATTR DCCWaveform::loop(bool ackManagerActive) { From 75dffd9dfab08b5ae6fd74b36152b113c86e37ef Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 5 Oct 2021 10:39:08 +0200 Subject: [PATCH 17/66] first ESP32 compile --- DCC.h | 2 ++ DCCEX.h | 7 ++++-- DCCEXParser.cpp | 8 +++++-- DCCTimer.h | 1 + DCCWaveform.cpp | 9 +++++--- MotorDriver.h | 3 ++- WifiESP32.cpp | 39 ++++++++++++++++++++++++++++++++++ WifiESP.h => WifiESP32.h | 8 ++++--- WifiESP.cpp => WifiESP8266.cpp | 4 ++-- WifiESP8266.h | 39 ++++++++++++++++++++++++++++++++++ WifiInterface.cpp | 6 ++++-- WifiInterface.h | 5 ++++- defines.h | 6 +++--- freeMemory.cpp | 10 ++++----- 14 files changed, 123 insertions(+), 24 deletions(-) create mode 100644 WifiESP32.cpp rename WifiESP.h => WifiESP32.h (89%) rename WifiESP.cpp => WifiESP8266.cpp (99%) create mode 100644 WifiESP8266.h diff --git a/DCC.h b/DCC.h index b473673..715a38d 100644 --- a/DCC.h +++ b/DCC.h @@ -193,6 +193,8 @@ private: #define ARDUINO_TYPE "TEENSY41" #elif defined(ARDUINO_ARCH_ESP8266) #define ARDUINO_TYPE "ESP8266" +#elif defined(ARDUINO_ARCH_ESP32) +#define ARDUINO_TYPE "ESP32" #else #error CANNOT COMPILE - DCC++ EX ONLY WORKS WITH AN ARDUINO UNO, NANO 328, OR ARDUINO MEGA 1280/2560 #endif diff --git a/DCCEX.h b/DCCEX.h index 1c2c522..8ff2f51 100644 --- a/DCCEX.h +++ b/DCCEX.h @@ -30,9 +30,12 @@ #include "DIAG.h" #include "DCCEXParser.h" #include "version.h" +#if defined(ARDUINO_ARCH_ESP8266) +#include "WifiESP8266.h" +#elif defined(ARDUINO_ARCH_ESP32) +#include "WifiESP32.h" +#else #include "WifiInterface.h" -#ifdef ESP_FAMILY -#include "WifiESP.h" #endif #if ETHERNET_ON == true #include "EthernetInterface.h" diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 2bbcc5b..c8d7fde 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -789,8 +789,12 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) case HASH_KEYWORD_RESET: { - wdt_enable( WDTO_15MS); // set Arduino watchdog timer for 15ms - delay(50); // wait for the prescaller time to expire +#ifndef ESP_FAMILY + wdt_enable( WDTO_15MS); // set Arduino watchdog timer for 15ms + delay(50); // wait for the prescaler time to expire +#else + /* XXX do right thing to reboot */ +#endif break; // and if we didnt restart } diff --git a/DCCTimer.h b/DCCTimer.h index b518701..d421828 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -37,4 +37,5 @@ class DCCTimer { private: }; +extern portMUX_TYPE timerMux; #endif diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 33f7392..439e781 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -255,7 +255,8 @@ void IRAM_ATTR DCCWaveform::interrupt2() { transmitRepeats--; } else if (packetPending) { - // Copy pending packet to transmit packet + portENTER_CRITICAL(&timerMux); + // Copy pending packet to transmit packet // a fixed length memcpy is faster than a variable length loop for these small lengths // for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b]; memcpy( transmitPacket, pendingPacket, sizeof(pendingPacket)); @@ -264,6 +265,7 @@ void IRAM_ATTR DCCWaveform::interrupt2() { transmitRepeats = pendingRepeats; packetPending = false; sentResetsSincePacket=0; + portEXIT_CRITICAL(&timerMux); } else { // Fortunately reset and idle packets are the same length @@ -282,7 +284,7 @@ void IRAM_ATTR DCCWaveform::interrupt2() { void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) { if (byteCount > MAX_PACKET_SIZE) return; // allow for chksum while (packetPending); - + portENTER_CRITICAL(&timerMux); byte checksum = 0; for (byte b = 0; b < byteCount; b++) { checksum ^= buffer[b]; @@ -294,6 +296,7 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea pendingRepeats = repeats; packetPending = true; sentResetsSincePacket=0; + portEXIT_CRITICAL(&timerMux); } // Operations applicable to PROG track ONLY. @@ -330,7 +333,7 @@ byte DCCWaveform::getAck() { } void IRAM_ATTR DCCWaveform::checkAck() { - // This function operates in interrupt() time so must be fast and can't DIAG + // This function operates in interrupt() time (not on ESP) so must be fast and can't DIAG if (sentResetsSincePacket > 6) { //ACK timeout ackCheckDuration=millis()-ackCheckStart; ackPending = false; diff --git a/MotorDriver.h b/MotorDriver.h index a020ef3..9193828 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -18,6 +18,7 @@ */ #ifndef MotorDriver_h #define MotorDriver_h +#include "defines.h" #include "FSH.h" // Virtualised Motor shield 1-track hardware Interface @@ -26,7 +27,7 @@ #define UNUSED_PIN 127 // inside int8_t #endif -#if defined(__IMXRT1062__) || defined (ARDUINO_ARCH_ESP8266) +#if defined(__IMXRT1062__) || defined(ESP_FAMILY) typedef uint32_t PORTTYPE; struct FASTPIN { volatile uint32_t *inout; diff --git a/WifiESP32.cpp b/WifiESP32.cpp new file mode 100644 index 0000000..93ec702 --- /dev/null +++ b/WifiESP32.cpp @@ -0,0 +1,39 @@ +/* + © 2021, Harald Barth. + + 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" +#if defined(ARDUINO_ARCH_ESP32) +#include +#include "WifiESP32.h" +#include "DIAG.h" +#include "RingStream.h" +#include "CommandDistributor.h" + +bool WifiESP::setup(const char *SSid, + const char *password, + const char *hostname, + int port, + const byte channel) { + return false; +} + +void WifiESP::loop() { + +} +#endif //ESP32 diff --git a/WifiESP.h b/WifiESP32.h similarity index 89% rename from WifiESP.h rename to WifiESP32.h index 47de4ed..100e393 100644 --- a/WifiESP.h +++ b/WifiESP32.h @@ -17,8 +17,9 @@ * along with CommandStation. If not, see . */ -#ifndef WifiESP_h -#define WifiESP_h +#if defined(ARDUINO_ARCH_ESP32) +#ifndef WifiESP32_h +#define WifiESP32_h #include "FSH.h" @@ -34,4 +35,5 @@ public: static void loop(); private: }; -#endif +#endif //WifiESP8266_h +#endif //ESP8266 diff --git a/WifiESP.cpp b/WifiESP8266.cpp similarity index 99% rename from WifiESP.cpp rename to WifiESP8266.cpp index 2c6f169..7b3dec5 100644 --- a/WifiESP.cpp +++ b/WifiESP8266.cpp @@ -18,13 +18,13 @@ */ #include "defines.h" -#ifdef ESP_FAMILY +#if defined(ARDUINO_ARCH_ESP8266) #include #include #include #include -#include "WifiESP.h" +#include "WifiESP8266.h" #include "DIAG.h" #include "RingStream.h" #include "CommandDistributor.h" diff --git a/WifiESP8266.h b/WifiESP8266.h new file mode 100644 index 0000000..6fd71dd --- /dev/null +++ b/WifiESP8266.h @@ -0,0 +1,39 @@ +/* + * © 2021, Harald Barth. + * + * 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 . + */ + +#if defined(ARDUINO_ARCH_ESP8266) +#ifndef WifiESP8266_h +#define WifiESP8266_h + +#include "FSH.h" + +class WifiESP +{ + +public: + static bool setup(const char *wifiESSID, + const char *wifiPassword, + const char *hostname, + const int port, + const byte channel); + static void loop(); +private: +}; +#endif //WifiESP8266_h +#endif //ESP8266 diff --git a/WifiInterface.cpp b/WifiInterface.cpp index bd7b7f2..cb40a79 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -17,9 +17,10 @@ You should have received a copy of the GNU General Public License along with CommandStation. If not, see . */ +#include "WifiInterface.h" /* config.h included there */ +#ifndef ESP_FAMILY #ifndef ARDUINO_AVR_UNO_WIFI_REV2 // This code is NOT compiled on a unoWifiRev2 processor which uses a different architecture -#include "WifiInterface.h" /* config.h included there */ #include #include "DIAG.h" #include "StringFormatter.h" @@ -370,4 +371,5 @@ void WifiInterface::loop() { } } -#endif +#endif //ARDUINO_AVR_UNO_WIFI_REV2 +#endif //ESP_FAMILY diff --git a/WifiInterface.h b/WifiInterface.h index 19f8a3a..d674bee 100644 --- a/WifiInterface.h +++ b/WifiInterface.h @@ -19,6 +19,8 @@ */ #ifndef WifiInterface_h #define WifiInterface_h +#include "defines.h" +#ifndef ESP_FAMILY #include "FSH.h" #include "DCCEXParser.h" #include @@ -50,4 +52,5 @@ private: static bool checkForOK(const unsigned int timeout, const FSH *waitfor, bool echo, bool escapeEcho = true); static bool connected; }; -#endif +#endif //ESP_FAMILY +#endif diff --git a/defines.h b/defines.h index a79ddca..7ad98fe 100644 --- a/defines.h +++ b/defines.h @@ -20,7 +20,7 @@ //////////////////////////////////////////////////////////////////////////////// // -#if defined (ARDUINO_ARCH_ESP8266) +#if defined(ARDUINO_ARCH_ESP8266) #define ESP_FAMILY //#define ESP_DEBUG #define SLOW_ANALOG_READ @@ -28,7 +28,7 @@ //////////////////////////////////////////////////////////////////////////////// // -#if defined (ARDUINO_ARCH_ESP32) +#if defined(ARDUINO_ARCH_ESP32) #define ESP_FAMILY #define SLOW_ANALOG_READ #else @@ -42,7 +42,7 @@ // WIFI_ON: All prereqs for running with WIFI are met // Note: WIFI_CHANNEL may not exist in early config.h files so is added here if needed. -#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(TEENSYDUINO) || defined (ESP_FAMILY)) +#if ENABLE_WIFI && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO) || defined(TEENSYDUINO) || defined(ESP_FAMILY)) #define WIFI_ON true #ifndef WIFI_CHANNEL #define WIFI_CHANNEL 1 diff --git a/freeMemory.cpp b/freeMemory.cpp index f2dc29d..4758f83 100644 --- a/freeMemory.cpp +++ b/freeMemory.cpp @@ -1,5 +1,5 @@ /* - * © 2020, Harald Barth + * © 2020,2021 Harald Barth * © 2021, Neil McKechnie * * This file is part of Asbelos DCC-EX @@ -27,7 +27,7 @@ extern "C" char* sbrk(int); #elif defined(__AVR__) extern char *__brkval; extern char *__malloc_heap_start; -#elif defined(ARDUINO_ARCH_ESP8266) +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) // supported but nothing needed here #else #error Unsupported board type @@ -36,7 +36,7 @@ extern char *__malloc_heap_start; static volatile int minimum_free_memory = __INT_MAX__; -#if !defined(__IMXRT1062__) && !defined(ARDUINO_ARCH_ESP8266) +#if !defined(__IMXRT1062__) && !defined(ARDUINO_ARCH_ESP8266) && !defined(ARDUINO_ARCH_ESP32) static inline int freeMemory() { char top; #if defined(__arm__) @@ -57,8 +57,8 @@ int minimumFreeMemory() { return retval; } -#elif defined(ARDUINO_ARCH_ESP8266) -// ESP8266 +#elif defined(ARDUINO_ARCH_ESP8266) || defined(ARDUINO_ARCH_ESP32) +// ESP8266 and ESP32 static inline int freeMemory() { return ESP.getFreeHeap(); } From d174c05127ec44de655fb32e489466b9300a28e6 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 5 Oct 2021 21:53:02 +0200 Subject: [PATCH 18/66] Wifi connect and waveform --- DCCTimer.cpp | 2 +- WifiESP32.cpp | 123 ++++++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+), 1 deletion(-) diff --git a/DCCTimer.cpp b/DCCTimer.cpp index 6b204fe..5de7e6a 100644 --- a/DCCTimer.cpp +++ b/DCCTimer.cpp @@ -179,7 +179,7 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { hw_timer_t *timer = NULL; timer = timerBegin(0, 2, true); // prescaler can be 2 to 65536 so choose 2 timerAttachInterrupt(timer, interruptHandler, true); - timerAlarmWrite(timer, CLOCK_CYCLES / 2, true); // divide by prescaler + timerAlarmWrite(timer, CLOCK_CYCLES / 6, true); // divide by prescaler*3 (Clockbase is 80Mhz and not F_CPU 240Mhz) timerAlarmEnable(timer); } diff --git a/WifiESP32.cpp b/WifiESP32.cpp index 93ec702..10dbdb2 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -25,15 +25,138 @@ #include "RingStream.h" #include "CommandDistributor.h" +static std::vector clients; // a list to hold all clients +static WiFiServer *server = NULL; +static RingStream *outboundRing = new RingStream(2048); + bool WifiESP::setup(const char *SSid, const char *password, const char *hostname, int port, const byte channel) { + bool havePassword = true; + bool haveSSID = true; + bool wifiUp = false; + + const char *yourNetwork = "Your network "; + if (strncmp(yourNetwork, SSid, 13) == 0 || strncmp("", SSid, 13) == 0) + haveSSID = false; + if (strncmp(yourNetwork, password, 13) == 0 || strncmp("", password, 13) == 0) + havePassword = false; + + if (haveSSID && havePassword) { + WiFi.mode(WIFI_STA); + WiFi.setAutoReconnect(true); + WiFi.begin(SSid, password); + while (WiFi.status() != WL_CONNECTED) { + Serial.print('.'); + delay(500); + } + if (WiFi.status() == WL_CONNECTED) { + DIAG(F("Wifi STA IP %s"),WiFi.localIP().toString().c_str()); + wifiUp = true; + } + } + if (!haveSSID) { + // prepare all strings + String strSSID("DCC_"); + String strPass("PASS_"); + String strMac = WiFi.macAddress(); + strMac.remove(0,9); + strMac.replace(":",""); + strMac.replace(":",""); + strSSID.concat(strMac); + strPass.concat(strMac); + + WiFi.mode(WIFI_AP); + if (WiFi.softAP(strSSID.c_str(), + havePassword ? password : strPass.c_str(), + channel, false, 8)) { + DIAG(F("Wifi AP SSID %s PASS %s"),strSSID.c_str(),havePassword ? password : strPass.c_str()); + DIAG(F("Wifi AP IP %s"),WiFi.softAPIP().toString().c_str()); + wifiUp = true; + } + } + + + if (!wifiUp) { + DIAG(F("Wifi all fail")); + // no idea to go on + return false; + } + server = new WiFiServer(port); // start listening on tcp port + server->begin(); + DIAG(F("Server up port %d"),port); + + return true; return false; } void WifiESP::loop() { + int clientId; //tmp loop var + if (WiFi.status() == WL_CONNECTED /* || what for AP? */) { + if (server->hasClient()) { + // loop over all clients and remove inactive + for (clientId=0; clientIdavailable()) { + clients.push_back(client); + DIAG(F("New client %s"), client.remoteIP().toString().c_str()); + } + } + // loop over all connected clients + for (clientId=0; clientId 0) { + // read data from client + byte cmd[len+1]; + for(int i=0; imark(clientId); + CommandDistributor::parse(clientId,cmd,outboundRing); + outboundRing->commit(); + } + } + } // all clients + + // something to write out? + clientId=outboundRing->peek(); + if (clientId >= 0) { + if ((unsigned int)clientId > clients.size()) { + // something is wrong with the ringbuffer position + outboundRing->info(); + } else { + // we have data to send in outboundRing + if(clients[clientId].connected()) { + outboundRing->read(); // read over peek() + int count=outboundRing->count(); + { + char buffer[count+1]; + for(int i=0;iread(); + if (c >= 0) + buffer[i] = (char)c; + else { + DIAG(F("Ringread fail at %d"),i); + break; + } + } + buffer[count]=0; + clients[clientId].write(buffer,count); + } + } + } + } + } //connected } #endif //ESP32 From 26bd3ac342da5795bfaf4a41ca8cda1b41c99380 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 5 Oct 2021 21:55:13 +0200 Subject: [PATCH 19/66] Example ESP motor shields --- config.example.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/config.example.h b/config.example.h index f66e525..76b6610 100644 --- a/config.example.h +++ b/config.example.h @@ -43,11 +43,15 @@ The configuration file for DCC-EX Command Station // //#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD -#define ESP_MOTOR_SHIELD F("ESP"), \ +#define ESP8266_MOTOR_SHIELD F("ESP8266"), \ new MotorDriver(D3, D5, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.99, 2000, UNUSED_PIN),\ new MotorDriver(D2, D6, UNUSED_PIN, UNUSED_PIN, A0 , 2.99, 2000, UNUSED_PIN) -#define MOTOR_SHIELD_TYPE ESP_MOTOR_SHIELD +#define ESP32_MOTOR_SHIELD F("ESP32"), \ + new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 36, 2.99, 2000, UNUSED_PIN),\ + new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 37, 2.99, 2000, UNUSED_PIN) + +#define MOTOR_SHIELD_TYPE ESP8266_MOTOR_SHIELD ///////////////////////////////////////////////////////////////////////////////////// // From faeb3194dbc429523bafe5db5eb649df783e2c3f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 22 Oct 2021 08:21:44 +0200 Subject: [PATCH 20/66] ESP32 motorshield as default --- config.example.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/config.example.h b/config.example.h index 76b6610..5b9a228 100644 --- a/config.example.h +++ b/config.example.h @@ -51,7 +51,7 @@ The configuration file for DCC-EX Command Station new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 36, 2.99, 2000, UNUSED_PIN),\ new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 37, 2.99, 2000, UNUSED_PIN) -#define MOTOR_SHIELD_TYPE ESP8266_MOTOR_SHIELD +#define MOTOR_SHIELD_TYPE ESP32_MOTOR_SHIELD ///////////////////////////////////////////////////////////////////////////////////// // From 8a0ddb0d748cf37feccf30076ff03699cf9cf8b0 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 22 Oct 2021 08:35:29 +0200 Subject: [PATCH 21/66] ESP32 I/O info --- config.example.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/config.example.h b/config.example.h index 5b9a228..5c6da06 100644 --- a/config.example.h +++ b/config.example.h @@ -47,9 +47,15 @@ The configuration file for DCC-EX Command Station new MotorDriver(D3, D5, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.99, 2000, UNUSED_PIN),\ new MotorDriver(D2, D6, UNUSED_PIN, UNUSED_PIN, A0 , 2.99, 2000, UNUSED_PIN) +// ADC1 CH4 = GPIO32 +// ADC1 CH5 = GPIO33 +// Adjust pin usage according to info in +// https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/ +// https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ +// Adjust conversion factor according to your voltage divider #define ESP32_MOTOR_SHIELD F("ESP32"), \ - new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 36, 2.99, 2000, UNUSED_PIN),\ - new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 37, 2.99, 2000, UNUSED_PIN) + new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 32, 2.99, 2000, UNUSED_PIN),\ + new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 33, 2.99, 2000, UNUSED_PIN) #define MOTOR_SHIELD_TYPE ESP32_MOTOR_SHIELD From b6cfc39d23c8a84602102de5d0108d8b58d0d155 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 24 Oct 2021 12:09:54 +0200 Subject: [PATCH 22/66] ESP32 watchdog workaround (with diag code) --- WifiESP32.cpp | 52 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) diff --git a/WifiESP32.cpp b/WifiESP32.cpp index 10dbdb2..b11f906 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -24,6 +24,45 @@ #include "DIAG.h" #include "RingStream.h" #include "CommandDistributor.h" +/* +#include "soc/rtc_wdt.h" +#include "esp_task_wdt.h" +*/ + +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +void feedTheDog0(){ + // feed dog 0 + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + TIMERG0.wdt_feed=1; // feed dog + TIMERG0.wdt_wprotect=0; // write protect + // feed dog 1 + //TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + //TIMERG1.wdt_feed=1; // feed dog + //TIMERG1.wdt_wprotect=0; // write protect +} + +/* +void enableCoreWDT(byte core){ + TaskHandle_t idle = xTaskGetIdleTaskHandleForCPU(core); + if(idle == NULL){ + DIAG(F("Get idle rask on core %d failed"),core); + } else { + if(esp_task_wdt_add(idle) != ESP_OK){ + DIAG(F("Failed to add Core %d IDLE task to WDT"),core); + } else { + DIAG(F("Added Core %d IDLE task to WDT"),core); + } + } +} + +void disableCoreWDT(byte core){ + TaskHandle_t idle = xTaskGetIdleTaskHandleForCPU(core); + if(idle == NULL || esp_task_wdt_delete(idle) != ESP_OK){ + DIAG(F("Failed to remove Core %d IDLE task from WDT"),core); + } +} +*/ static std::vector clients; // a list to hold all clients static WiFiServer *server = NULL; @@ -38,6 +77,10 @@ bool WifiESP::setup(const char *SSid, bool haveSSID = true; bool wifiUp = false; + // tests + // enableCoreWDT(1); + // disableCoreWDT(0); + const char *yourNetwork = "Your network "; if (strncmp(yourNetwork, SSid, 13) == 0 || strncmp("", SSid, 13) == 0) haveSSID = false; @@ -158,5 +201,14 @@ void WifiESP::loop() { } } } //connected + yield(); + // when loop() is running on core0 we must + // feed the core0 wdt ourselves as yield() + // is not necessarily yielding to a low + // prio task. On core1 this is not a problem + // as there the wdt is disabled by the + // arduio IDE startup routines. + if (xPortGetCoreID() == 0) + feedTheDog0(); } #endif //ESP32 From 05eb0d763a93b25ce39f3fa6876d71e7bccdd59c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 24 Oct 2021 12:59:28 +0200 Subject: [PATCH 23/66] explain ESP32 watchdog --- esp32wdt.txt | 67 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 67 insertions(+) create mode 100644 esp32wdt.txt diff --git a/esp32wdt.txt b/esp32wdt.txt new file mode 100644 index 0000000..dbfb4ef --- /dev/null +++ b/esp32wdt.txt @@ -0,0 +1,67 @@ +The ESP-IDF has interrupt watchdogs and task watchdogs. Normally on +each core there is a very low prio idle task (IDLE0, ILDE1) that feeds +the watchdog (an internal timer) and if that timer expires a +reboot/reset happens. This thought to enure that even the lowest prio +tasks get run ever. + +Now enter the Arduino IDE generated task loop(). When there are two cores, +loop() is run on core1. If loop runs continiously, IDLE1 is never run and +triggers the watchdog. It is said that this can be previented by one +of the following: + +1. Call delay(X) with big enough X +2. Call yield() + +While the delay() method works, big enough X to run idle seem to be in +the ms range and there are definitely applications that can not accept +a several ms long pause in loop(). + +The yield() method does not work because it only seems not to yield to +a low prio task like IDLE1 in all circumstances. + +Then the makers of the Arduino IDE did get the brilliant idea to +disable that IDLE1 calls the watchdog. Then loop() can spin on core1 +and other tasks (like wifi or interrupts or whatever) can run on core0 +and are watched by the IDLE0 watchdog. All swell and well. Almost. + +Enter: SINGLE CORE ESP32 + +As the IDLE0 watchdog is not disabled it will fire when loop() runs on +core0. The next idea is to feed the watchdog from loop() just +alongside the yield. There is a function called esp_task_wdt_feed(), +so can that be used to feed the watchdog? Yes and no. While it +will feed the watchdog, there is as well as check in the ESP-IDF +that the watchdog is fed from ALL tasks that should feed it. So +if the setup is that IDLE0 should feed the watchdog, we can not +get away by calling esp_task_wdt_feed() from loop(). BUMMER! + +But there seems to be a way around this. The watchdog is implemented +by low level timers/counters and these are accessible. So we can feed +the dog behind the back of the ESP-IDF: + +#include "soc/timer_group_struct.h" +#include "soc/timer_group_reg.h" +void feedTheDog(){ + // feed dog 0 + TIMERG0.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + TIMERG0.wdt_feed=1; // feed dog + TIMERG0.wdt_wprotect=0; // write protect + // feed dog 1 + TIMERG1.wdt_wprotect=TIMG_WDT_WKEY_VALUE; // write enable + TIMERG1.wdt_feed=1; // feed dog + TIMERG1.wdt_wprotect=0; // write protect +} + +As I do not have a single core ESP32 I tested this by enabling the +IDLE1 watchdog (which normally is disabled) and checking that I +do get watchdog resets. Then I call feedTheDog() from loop() +and the resets disappear. So I guess the feeding operation is +successful. For a single core ESP32 of course only dog0 has +to be fed. + +Feed dog directly behind back of the ESP-IDF routines: +https://forum.arduino.cc/t/esp32-a-better-way-than-vtaskdelay-to-get-around-watchdog-crash/596889/13 +Disable/Endable WDT code: +https://github.com/espressif/arduino-esp32/commit/b8f8502f +Get/set taskid on cores: +https://techtutorialsx.com/2017/05/09/esp32-get-task-execution-core/ From 7d7b337f82d149eebbe92fa2e5cb67c9305c20c5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 24 Oct 2021 19:38:07 +0200 Subject: [PATCH 24/66] on ESP32 currently WIFI should be on --- config.example.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/config.example.h b/config.example.h index 5c6da06..7338051 100644 --- a/config.example.h +++ b/config.example.h @@ -70,7 +70,9 @@ The configuration file for DCC-EX Command Station // NOTE: Only supported on Arduino Mega // Set to false if you not even want it on the Arduino Mega // -//#define ENABLE_WIFI true +// Currently ESP32 single core only works with WIFI ON because of Watchdog code +// and if you have an ESP32 you probably want WIFI anyway. +#define ENABLE_WIFI true ///////////////////////////////////////////////////////////////////////////////////// // From 31059a615c20bfb1814685ea0e9b9f09383c8fe3 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 27 Oct 2021 23:03:37 +0200 Subject: [PATCH 25/66] use ESP-IDF ADC functions instead of analogRead() which breaks waveform --- MotorDriver.cpp | 12 ++++++++++++ config.example.h | 15 ++++++++++----- 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 8bcb03c..8959f42 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -20,6 +20,10 @@ #include "MotorDriver.h" #include "DCCTimer.h" #include "DIAG.h" +#if defined(ARDUINO_ARCH_ESP32) +#include +#define pinToADC1Channel(X) (adc1_channel_t)(((X) > 35) ? (X)-36 : (X)-28) +#endif bool MotorDriver::usePWM=false; bool MotorDriver::commonFaultPin=false; @@ -55,7 +59,13 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8 currentPin=current_pin; if (currentPin!=UNUSED_PIN) { pinMode(currentPin, INPUT); +#if defined(ARDUINO_ARCH_ESP32) + adc1_config_width(ADC_WIDTH_BIT_12); + adc1_config_channel_atten(pinToADC1Channel(currentPin),ADC_ATTEN_DB_11); + senseOffset = adc1_get_raw(pinToADC1Channel(currentPin)); +#else senseOffset=analogRead(currentPin); // value of sensor at zero current +#endif } faultPin=fault_pin; @@ -150,6 +160,8 @@ int MotorDriver::getCurrentRaw() { current = analogRead(currentPin)-senseOffset; overflow_count = 0; SREG = sreg_backup; /* restore interrupt state */ +#elif defined(ARDUINO_ARCH_ESP32) + current = adc1_get_raw(pinToADC1Channel(currentPin))-senseOffset; #else current = analogRead(currentPin)-senseOffset; #endif diff --git a/config.example.h b/config.example.h index 7338051..2d345e2 100644 --- a/config.example.h +++ b/config.example.h @@ -43,19 +43,24 @@ The configuration file for DCC-EX Command Station // //#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD +// https://randomnerdtutorials.com/esp8266-pinout-reference-gpios/ +// 4 high at boot #define ESP8266_MOTOR_SHIELD F("ESP8266"), \ new MotorDriver(D3, D5, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.99, 2000, UNUSED_PIN),\ new MotorDriver(D2, D6, UNUSED_PIN, UNUSED_PIN, A0 , 2.99, 2000, UNUSED_PIN) -// ADC1 CH4 = GPIO32 -// ADC1 CH5 = GPIO33 +// ESP32 ADC1 only supported GPIO pins 32 to 39, for example +// ADC1 CH4 = GPIO32, ADC1 CH5 = GPIO33, ADC1 CH0 = GPIO36 +// // Adjust pin usage according to info in // https://randomnerdtutorials.com/esp32-adc-analog-read-arduino-ide/ // https://randomnerdtutorials.com/esp32-pinout-reference-gpios/ -// Adjust conversion factor according to your voltage divider +// +// Adjust conversion factor according to your voltage divider. +// #define ESP32_MOTOR_SHIELD F("ESP32"), \ - new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 32, 2.99, 2000, UNUSED_PIN),\ - new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 33, 2.99, 2000, UNUSED_PIN) + new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 32, 2.00, 2000, UNUSED_PIN),\ + new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 33, 2.00, 2000, UNUSED_PIN) #define MOTOR_SHIELD_TYPE ESP32_MOTOR_SHIELD From 9d74b0f6a55d33aee7c5be4d31b0b35b40d359ba Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 29 Oct 2021 22:19:23 +0200 Subject: [PATCH 26/66] set pinMode analog --- MotorDriver.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 8959f42..11d56d8 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -58,12 +58,13 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8 currentPin=current_pin; if (currentPin!=UNUSED_PIN) { - pinMode(currentPin, INPUT); #if defined(ARDUINO_ARCH_ESP32) + pinMode(currentPin, ANALOG); adc1_config_width(ADC_WIDTH_BIT_12); adc1_config_channel_atten(pinToADC1Channel(currentPin),ADC_ATTEN_DB_11); senseOffset = adc1_get_raw(pinToADC1Channel(currentPin)); #else + pinMode(currentPin, INPUT); senseOffset=analogRead(currentPin); // value of sensor at zero current #endif } From 278f7618f4c6018dc9a4e812871f1cfb381d2e92 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 31 Oct 2021 00:10:58 +0200 Subject: [PATCH 27/66] do something i AP mode --- WifiESP32.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/WifiESP32.cpp b/WifiESP32.cpp index b11f906..cc3d335 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -67,6 +67,7 @@ void disableCoreWDT(byte core){ static std::vector clients; // a list to hold all clients static WiFiServer *server = NULL; static RingStream *outboundRing = new RingStream(2048); +static bool APmode = false; bool WifiESP::setup(const char *SSid, const char *password, @@ -118,6 +119,7 @@ bool WifiESP::setup(const char *SSid, DIAG(F("Wifi AP SSID %s PASS %s"),strSSID.c_str(),havePassword ? password : strPass.c_str()); DIAG(F("Wifi AP IP %s"),WiFi.softAPIP().toString().c_str()); wifiUp = true; + APmode = true; } } @@ -138,7 +140,8 @@ bool WifiESP::setup(const char *SSid, void WifiESP::loop() { int clientId; //tmp loop var - if (WiFi.status() == WL_CONNECTED /* || what for AP? */) { + // really no good way to check for LISTEN especially in AP mode? + if (APmode || WiFi.status() == WL_CONNECTED) { if (server->hasClient()) { // loop over all clients and remove inactive for (clientId=0; clientId Date: Sun, 31 Oct 2021 00:40:35 +0200 Subject: [PATCH 28/66] more diag messages --- WifiESP32.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/WifiESP32.cpp b/WifiESP32.cpp index cc3d335..56765ba 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -99,6 +99,8 @@ bool WifiESP::setup(const char *SSid, if (WiFi.status() == WL_CONNECTED) { DIAG(F("Wifi STA IP %s"),WiFi.localIP().toString().c_str()); wifiUp = true; + } else { + DIAG(F("Could not connect to Wifi SSID %s"),SSid); } } if (!haveSSID) { @@ -120,12 +122,14 @@ bool WifiESP::setup(const char *SSid, DIAG(F("Wifi AP IP %s"),WiFi.softAPIP().toString().c_str()); wifiUp = true; APmode = true; + } else { + DIAG(F("Could not set up AP with Wifi SSID %s"),strSSID.c_str()); } } if (!wifiUp) { - DIAG(F("Wifi all fail")); + DIAG(F("Wifi setup all fail (STA and AP mode)")); // no idea to go on return false; } From c5b283bd8ccc59351dfb6231ecd46c2ca8640412 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 31 Oct 2021 01:10:13 +0200 Subject: [PATCH 29/66] should compile for all boards --- DCCEXParser.cpp | 2 +- DCCTimer.h | 9 +++++++++ DCCWaveform.cpp | 2 +- 3 files changed, 11 insertions(+), 2 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index c8d7fde..c83c0e0 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -17,10 +17,10 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ +#include "DCC.h" // includes "Motordriver.h" and #include "defines.h" #include "StringFormatter.h" #include "DCCEXParser.h" -#include "DCC.h" #include "DCCWaveform.h" #include "Turnouts.h" #include "Outputs.h" diff --git a/DCCTimer.h b/DCCTimer.h index d421828..0af3614 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -37,5 +37,14 @@ class DCCTimer { private: }; +#if defined(ARDUINO_ARCH_ESP32) extern portMUX_TYPE timerMux; #endif + +#if !(defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_ESP8266)) +#ifndef IRAM_ATTR +#define IRAM_ATTR +#endif +#endif + +#endif //DCCTimer.h diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 439e781..a8b83e4 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ - #pragma GCC optimize ("-O3") +#pragma GCC optimize ("-O3") #include #include "defines.h" From c87a80928bfefef00883fb68412df7d6ecbdb075 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 31 Oct 2021 22:06:22 +0100 Subject: [PATCH 30/66] special tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 6ffec91..8fdecc0 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "50fcbc0" +#define GITHUB_SHA ESP32-20211031-11:05" From a109ba4e019001c8ff297403c5c91bdd03600188 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 31 Oct 2021 23:35:28 +0100 Subject: [PATCH 31/66] unknown locos should have speed forward --- DCC.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCC.cpp b/DCC.cpp index 286e7c3..ee1918d 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -137,7 +137,7 @@ uint8_t DCC::getThrottleSpeed(int cab) { bool DCC::getThrottleDirection(int cab) { int reg=lookupSpeedTable(cab); - if (reg<0) return false ; + if (reg<0) return true; return (speedTable[reg].speedCode & 0x80) !=0; } From 837b0a9fb63412b497d721f7f602d581a31dfa50 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 31 Oct 2021 23:46:25 +0100 Subject: [PATCH 32/66] typo --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 8fdecc0..c2b4c7e 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA ESP32-20211031-11:05" +#define GITHUB_SHA "ESP32-20211031-11:05" From 77ee57eb83ebf4a7decff17b31e1537938230d5a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 2 Nov 2021 17:50:32 +0100 Subject: [PATCH 33/66] give up eventually --- WifiESP32.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/WifiESP32.cpp b/WifiESP32.cpp index 56765ba..c907448 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -77,6 +77,7 @@ bool WifiESP::setup(const char *SSid, bool havePassword = true; bool haveSSID = true; bool wifiUp = false; + uint8_t tries = 40; // tests // enableCoreWDT(1); @@ -92,8 +93,9 @@ bool WifiESP::setup(const char *SSid, WiFi.mode(WIFI_STA); WiFi.setAutoReconnect(true); WiFi.begin(SSid, password); - while (WiFi.status() != WL_CONNECTED) { + while (WiFi.status() != WL_CONNECTED && tries) { Serial.print('.'); + tries--; delay(500); } if (WiFi.status() == WL_CONNECTED) { From 836ccc143e98c783a6eb86d21d13fcf8d21bb951 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 3 Nov 2021 09:45:30 +0100 Subject: [PATCH 34/66] check power overload only when not ack check --- DCCEX.h | 9 +++++---- DCCWaveform.cpp | 7 +++++-- 2 files changed, 10 insertions(+), 6 deletions(-) diff --git a/DCCEX.h b/DCCEX.h index 8ff2f51..fa7f8b8 100644 --- a/DCCEX.h +++ b/DCCEX.h @@ -44,9 +44,10 @@ #include "LCN.h" #include "freeMemory.h" -#if __has_include ( "myAutomation.h") - #include "RMFT.h" - #define RMFT_ACTIVE -#endif +// not yet in this branch +//#if __has_include ( "myAutomation.h") +// #include "RMFT.h" +// #define RMFT_ACTIVE +//#endif #endif diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index a8b83e4..94cd91d 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -60,8 +60,6 @@ volatile bool ackflag = 0; #endif void IRAM_ATTR DCCWaveform::loop(bool ackManagerActive) { - mainTrack.checkPowerOverload(false); - progTrack.checkPowerOverload(ackManagerActive); #ifdef SLOW_ANALOG_READ if (ackflag) { progTrack.checkAck(); @@ -69,8 +67,13 @@ void IRAM_ATTR DCCWaveform::loop(bool ackManagerActive) { portENTER_CRITICAL(&timerMux); ackflag = 0; portEXIT_CRITICAL(&timerMux); + } else { + progTrack.checkPowerOverload(ackManagerActive); } +#else + progTrack.checkPowerOverload(ackManagerActive); #endif + mainTrack.checkPowerOverload(false); } void IRAM_ATTR DCCWaveform::interruptHandler() { From 4901f12fcd0aec0af4acc5ce6048ee134063a24e Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 6 Nov 2021 02:40:49 +0100 Subject: [PATCH 35/66] make own task on core0 for WifiESP::loop() on ESP32 --- CommandStation-EX.ino | 3 ++- WifiESP32.cpp | 13 +++++++++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 71c1d67..57b281b 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -124,7 +124,8 @@ void loop() #if WIFI_ON #ifndef ESP_FAMILY WifiInterface::loop(); -#else +#endif +#if defined(ARDUINO_ARCH_ESP8266) // on ESP32 own task WifiESP::loop(); #endif #endif //WIFI_ON diff --git a/WifiESP32.cpp b/WifiESP32.cpp index c907448..0aaacd3 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -69,6 +69,12 @@ static WiFiServer *server = NULL; static RingStream *outboundRing = new RingStream(2048); static bool APmode = false; +void wifiLoop(void *){ + for(;;){ + WifiESP::loop(); + } +} + bool WifiESP::setup(const char *SSid, const char *password, const char *hostname, @@ -139,6 +145,13 @@ bool WifiESP::setup(const char *SSid, server->begin(); DIAG(F("Server up port %d"),port); + xTaskCreatePinnedToCore(wifiLoop, /* Task function. */ + "wifiLoop",/* name of task. */ + 10000, /* Stack size of task */ + NULL, /* parameter of the task */ + 1, /* priority of the task */ + NULL, /* Task handle to keep track of created task */ + 0); /* pin task to core 0 */ return true; return false; } From 877db433a471757dec8b2ac2445886cd4ef9bb5a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 6 Nov 2021 02:59:57 +0100 Subject: [PATCH 36/66] make task startup nicer --- WifiESP32.cpp | 26 +++++++++++++++++--------- 1 file changed, 17 insertions(+), 9 deletions(-) diff --git a/WifiESP32.cpp b/WifiESP32.cpp index 0aaacd3..49b2116 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -143,17 +143,25 @@ bool WifiESP::setup(const char *SSid, } server = new WiFiServer(port); // start listening on tcp port server->begin(); - DIAG(F("Server up port %d"),port); + // server started here - xTaskCreatePinnedToCore(wifiLoop, /* Task function. */ - "wifiLoop",/* name of task. */ - 10000, /* Stack size of task */ - NULL, /* parameter of the task */ - 1, /* priority of the task */ - NULL, /* Task handle to keep track of created task */ - 0); /* pin task to core 0 */ + //start loop task + if (pdPASS != xTaskCreatePinnedToCore( + wifiLoop, /* Task function. */ + "wifiLoop",/* name of task. */ + 10000, /* Stack size of task */ + NULL, /* parameter of the task */ + 1, /* priority of the task */ + NULL, /* Task handle to keep track of created task */ + 0)) { /* pin task to core 0 */ + DIAG(F("Could not create wifiLoop task")); + return false; + } + + // report server started after wifiLoop creation + // when everything looks good + DIAG(F("Server up port %d"),port); return true; - return false; } void WifiESP::loop() { From d7e46ac625d9d1308ede9913eb9b98162dd17861 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 6 Nov 2021 03:04:50 +0100 Subject: [PATCH 37/66] set version --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index c2b4c7e..98ed837 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "ESP32-20211031-11:05" +#define GITHUB_SHA "ESP32-20211106-03:03" From 55c7a0a1e823cabd198a270cf6421b2f31147dc0 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 6 Nov 2021 23:51:32 +0100 Subject: [PATCH 38/66] protect ringstream --- RingStream.cpp | 16 +++++++++++++++- RingStream.h | 3 +++ 2 files changed, 18 insertions(+), 1 deletion(-) diff --git a/RingStream.cpp b/RingStream.cpp index baa4cc1..0f91f78 100644 --- a/RingStream.cpp +++ b/RingStream.cpp @@ -30,28 +30,36 @@ RingStream::RingStream( const uint16_t len) _overflow=false; _mark=0; _count=0; +#if defined(ARDUINO_ARCH_ESP32) + portMUX_TYPE _bufMux = portMUX_INITIALIZER_UNLOCKED; +#endif } size_t RingStream::write(uint8_t b) { if (_overflow) return 0; + portENTER_CRITICAL(&_bufMux); _buffer[_pos_write] = b; ++_pos_write; if (_pos_write==_len) _pos_write=0; if (_pos_write==_pos_read) { _overflow=true; + portEXIT_CRITICAL(&_bufMux); return 0; } _count++; + portEXIT_CRITICAL(&_bufMux); return 1; } int RingStream::read(byte advance) { if ((_pos_read==_pos_write) && !_overflow) return -1; // empty if (_pos_read == _mark) return -1; + portENTER_CRITICAL(&_bufMux); byte b=_buffer[_pos_read]; _pos_read += advance; if (_pos_read==_len) _pos_read=0; _overflow=false; + portEXIT_CRITICAL(&_bufMux); return b; } @@ -70,11 +78,13 @@ int RingStream::freeSpace() { // mark start of message with client id (0...9) void RingStream::mark(uint8_t b) { //DIAG(F("Mark1 len=%d count=%d pr=%d pw=%d m=%d"),_len, _count,_pos_read,_pos_write,_mark); + portENTER_CRITICAL(&_bufMux); _mark=_pos_write; write(b); // client id write((uint8_t)0); // count MSB placemarker write((uint8_t)0); // count LSB placemarker _count=0; + portEXIT_CRITICAL(&_bufMux); } // peekTargetMark is used by the parser stash routines to know which client @@ -89,16 +99,19 @@ void RingStream::info() { bool RingStream::commit() { //DIAG(F("Commit1 len=%d count=%d pr=%d pw=%d m=%d"),_len, _count,_pos_read,_pos_write,_mark); + portENTER_CRITICAL(&_bufMux); if (_overflow) { DIAG(F("RingStream(%d) commit(%d) OVERFLOW"),_len, _count); // just throw it away _pos_write=_mark; _overflow=false; - return false; // commit failed + portEXIT_CRITICAL(&_bufMux); + return false; // commit failed } if (_count==0) { // ignore empty response _pos_write=_mark; + portEXIT_CRITICAL(&_bufMux); return true; // true=commit ok } // Go back to the _mark and inject the count 1 byte later @@ -110,5 +123,6 @@ bool RingStream::commit() { _buffer[_mark]=lowByte(_count); _mark=_len+1; //DIAG(F("Commit2 len=%d count=%d pr=%d pw=%d m=%d"),_len, _count,_pos_read,_pos_write,_mark); + portEXIT_CRITICAL(&_bufMux); return true; // commit worked } diff --git a/RingStream.h b/RingStream.h index 974a235..6d53171 100644 --- a/RingStream.h +++ b/RingStream.h @@ -46,6 +46,9 @@ class RingStream : public Print { int _mark; int _count; byte * _buffer; +#if defined(ARDUINO_ARCH_ESP32) + portMUX_TYPE _bufMux; +#endif }; #endif From c02e976c9facd9de515038a945ea1fb57cc0d760 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 7 Nov 2021 00:12:11 +0100 Subject: [PATCH 39/66] protect ringstream typo fix --- GITHUB_SHA.h | 2 +- RingStream.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 98ed837..7548d9a 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "ESP32-20211106-03:03" +#define GITHUB_SHA "ESP32-20211107-00:09" diff --git a/RingStream.cpp b/RingStream.cpp index 0f91f78..1fa9ec6 100644 --- a/RingStream.cpp +++ b/RingStream.cpp @@ -31,7 +31,7 @@ RingStream::RingStream( const uint16_t len) _mark=0; _count=0; #if defined(ARDUINO_ARCH_ESP32) - portMUX_TYPE _bufMux = portMUX_INITIALIZER_UNLOCKED; + _bufMux = portMUX_INITIALIZER_UNLOCKED; #endif } From 5cbf0c2cad6ffb38786fb0559f7f8545991e7d4e Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 7 Nov 2021 00:21:15 +0100 Subject: [PATCH 40/66] defines.h needed to get ESP32 macro on non-ESP32 --- RingStream.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/RingStream.cpp b/RingStream.cpp index 1fa9ec6..70b66c2 100644 --- a/RingStream.cpp +++ b/RingStream.cpp @@ -18,6 +18,7 @@ */ #include "RingStream.h" +#include "defines.h" #include "DIAG.h" RingStream::RingStream( const uint16_t len) From 4668e116f4fb26a7937116de103b156495970004 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 14 Nov 2021 13:10:16 +0100 Subject: [PATCH 41/66] preambles running --- DCCRMT.cpp | 138 ++++++++++++++++++++++++++++++++++++++++++++++++ DCCRMT.h | 51 ++++++++++++++++++ DCCWaveform.cpp | 7 +++ WifiESP32.cpp | 4 +- 4 files changed, 199 insertions(+), 1 deletion(-) create mode 100644 DCCRMT.cpp create mode 100644 DCCRMT.h diff --git a/DCCRMT.cpp b/DCCRMT.cpp new file mode 100644 index 0000000..5186ecf --- /dev/null +++ b/DCCRMT.cpp @@ -0,0 +1,138 @@ +/* + * © 2021, Harald Barth. + * + * This file is part of DCC-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 "DIAG.h" +#include "DCCRMT.h" +#include "soc/periph_defs.h" +#include "driver/periph_ctrl.h" + +#if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,2,0) +#error wrong IDF version +#endif + +void setDCCBit1(rmt_item32_t* item) { + item->level0 = 1; + item->duration0 = DCC_1_HALFPERIOD; + item->level1 = 0; + item->duration1 = DCC_1_HALFPERIOD; +} + +void setDCCBit0(rmt_item32_t* item) { + item->level0 = 1; + item->duration0 = DCC_0_HALFPERIOD; + item->level1 = 0; + item->duration1 = DCC_0_HALFPERIOD; +} + +void IRAM_ATTR interrupt(rmt_channel_t channel, void *t) { + BaseType_t wtf = pdFALSE; + RMTPin *tt = (RMTPin *)t; + //DIAG(F("interrupt %d"), tt->idleLen); + tt->RMTinterrupt(channel,t); + rmt_tx_start(channel,true); + portYIELD_FROM_ISR(wtf); +} + +RMTPin::RMTPin(byte pin, byte ch, byte plen) { + + // preamble + preambleLen = plen+1; + preamble = (rmt_item32_t*)malloc(preambleLen*sizeof(rmt_item32_t)); + for (byte n=0; npreambleNext) { + rmt_fill_tx_items(channel, obj->preamble, obj->preambleLen, 0); + //obj->preambleNext = false; + } else { + if (obj->dataNext) { + rmt_fill_tx_items(channel, obj->packetBits, obj->packetLen, 0); + } else { + // here we should not get as now we need to send idle packet + rmt_fill_tx_items(channel, obj->idle, obj->idleLen, 0); + } + obj->preambleNext = true; + } + rmt_tx_start(channel,true); + DIAG(F("START")); + */ +} diff --git a/DCCRMT.h b/DCCRMT.h new file mode 100644 index 0000000..58f77de --- /dev/null +++ b/DCCRMT.h @@ -0,0 +1,51 @@ +/* + * © 2021, Harald Barth. + * + * This file is part of DCC-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 . + */ + +#pragma once +#include +#include "driver/rmt.h" +#include "soc/rmt_reg.h" +#include "soc/rmt_struct.h" + +#define DCC_1_HALFPERIOD 4640 // 1 / 80000000 * 4640 = 58us +#define DCC_0_HALFPERIOD 8000 + +class RMTPin { + public: + RMTPin(byte pin, byte ch, byte plen); + void IRAM_ATTR RMTinterrupt(rmt_channel_t, void *t); + + static RMTPin mainRMTPin; + static RMTPin progRMTPin; + + // private: + + rmt_channel_t channel; + // 3 types of data to send, preamble and then idle or data + // if this is prog track, idle will contain reset instead + rmt_item32_t *idle; + byte idleLen; + rmt_item32_t *preamble; + byte preambleLen; + rmt_item32_t packetBits[64]; + byte packetLen; + // flags + volatile bool preambleNext = true; // alternate between preamble and content + volatile bool dataNext = false; // do we have real data available or send idle +}; diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 94cd91d..505583f 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -25,6 +25,7 @@ #include "DCCTimer.h" #include "DIAG.h" #include "freeMemory.h" +#include "DCCRMT.h" DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true); DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false); @@ -37,6 +38,9 @@ volatile uint8_t DCCWaveform::numAckSamples=0; uint8_t DCCWaveform::trailingEdgeCounter=0; void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { + + RMTPin *p = new RMTPin(21, 0, PREAMBLE_BITS_MAIN); + mainTrack.motorDriver=mainDriver; progTrack.motorDriver=progDriver; progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static @@ -47,11 +51,14 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { && (mainDriver->getFaultPin() != UNUSED_PIN)); // Only use PWM if both pins are PWM capable. Otherwise JOIN does not work MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable(); + /* if (MotorDriver::usePWM) DIAG(F("Signal pin config: high accuracy waveform")); else DIAG(F("Signal pin config: normal accuracy waveform")); DCCTimer::begin(DCCWaveform::interruptHandler); + */ + DIAG(F("No waveform")); } #ifdef SLOW_ANALOG_READ diff --git a/WifiESP32.cpp b/WifiESP32.cpp index 49b2116..48d40d6 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -17,6 +17,7 @@ along with CommandStation. If not, see . */ +#include #include "defines.h" #if defined(ARDUINO_ARCH_ESP32) #include @@ -231,7 +232,7 @@ void WifiESP::loop() { } } } //connected - yield(); + // when loop() is running on core0 we must // feed the core0 wdt ourselves as yield() // is not necessarily yielding to a low @@ -240,5 +241,6 @@ void WifiESP::loop() { // arduio IDE startup routines. if (xPortGetCoreID() == 0) feedTheDog0(); + yield(); } #endif //ESP32 From 97065e892dedcbc04130803b700fa83783c5c893 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 14 Nov 2021 14:48:32 +0100 Subject: [PATCH 42/66] transmit preamble and idle --- DCCRMT.cpp | 64 ++++++++++++++++++++----------------------------- DCCRMT.h | 4 ++-- DCCWaveform.cpp | 3 --- 3 files changed, 28 insertions(+), 43 deletions(-) diff --git a/DCCRMT.cpp b/DCCRMT.cpp index 5186ecf..abe1cf8 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -41,34 +41,37 @@ void setDCCBit0(rmt_item32_t* item) { item->duration1 = DCC_0_HALFPERIOD; } +void setEOT(rmt_item32_t* item) { + item->val = 0; +} + void IRAM_ATTR interrupt(rmt_channel_t channel, void *t) { - BaseType_t wtf = pdFALSE; RMTPin *tt = (RMTPin *)t; - //DIAG(F("interrupt %d"), tt->idleLen); - tt->RMTinterrupt(channel,t); - rmt_tx_start(channel,true); - portYIELD_FROM_ISR(wtf); + tt->RMTinterrupt(channel); } RMTPin::RMTPin(byte pin, byte ch, byte plen) { // preamble - preambleLen = plen+1; + preambleLen = plen+2; // plen 1 bits, one 0 bit and one EOF marker preamble = (rmt_item32_t*)malloc(preambleLen*sizeof(rmt_item32_t)); for (byte n=0; npreambleNext) { - rmt_fill_tx_items(channel, obj->preamble, obj->preambleLen, 0); - //obj->preambleNext = false; +void IRAM_ATTR RMTPin::RMTinterrupt(rmt_channel_t channel) { + + if (preambleNext) { + rmt_fill_tx_items(channel, preamble, preambleLen, 0); + preambleNext = false; } else { - if (obj->dataNext) { - rmt_fill_tx_items(channel, obj->packetBits, obj->packetLen, 0); + if (dataNext) { + rmt_fill_tx_items(channel, packetBits, packetLen, 0); } else { // here we should not get as now we need to send idle packet - rmt_fill_tx_items(channel, obj->idle, obj->idleLen, 0); + rmt_fill_tx_items(channel, idle, idleLen, 0); } - obj->preambleNext = true; + preambleNext = true; } rmt_tx_start(channel,true); - DIAG(F("START")); - */ } diff --git a/DCCRMT.h b/DCCRMT.h index 58f77de..78eaaa3 100644 --- a/DCCRMT.h +++ b/DCCRMT.h @@ -29,12 +29,12 @@ class RMTPin { public: RMTPin(byte pin, byte ch, byte plen); - void IRAM_ATTR RMTinterrupt(rmt_channel_t, void *t); + void IRAM_ATTR RMTinterrupt(rmt_channel_t); static RMTPin mainRMTPin; static RMTPin progRMTPin; - // private: + private: rmt_channel_t channel; // 3 types of data to send, preamble and then idle or data diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 505583f..ee3e9f1 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -51,14 +51,11 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { && (mainDriver->getFaultPin() != UNUSED_PIN)); // Only use PWM if both pins are PWM capable. Otherwise JOIN does not work MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable(); - /* if (MotorDriver::usePWM) DIAG(F("Signal pin config: high accuracy waveform")); else DIAG(F("Signal pin config: normal accuracy waveform")); DCCTimer::begin(DCCWaveform::interruptHandler); - */ - DIAG(F("No waveform")); } #ifdef SLOW_ANALOG_READ From 71117bc7a1fed796bd95d2331ac1627b03c60060 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 14 Nov 2021 14:49:55 +0100 Subject: [PATCH 43/66] special version --- GITHUB_SHA.h | 2 +- version.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 7548d9a..50fb4ee 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "ESP32-20211107-00:09" +#define GITHUB_SHA "ESP32-2021114-14:49" diff --git a/version.h b/version.h index 09211fa..cf06ed8 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,7 @@ #include "StringFormatter.h" -#define VERSION "3.1.6" +#define VERSION "3.1.90" // 3.1.6 Make output ID two bytes and guess format/size of registered outputs found in EEPROM // 3.1.5 Fix LCD corruption on power-up // 3.1.4 Refactor OLED and LCD drivers and remove unused code From 10209ed6f353762a54fb29e3c82f0ec58d8f1241 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 14 Nov 2021 15:35:26 +0100 Subject: [PATCH 44/66] remove uneccessary workaround, compensate for interrupt length --- DCCRMT.cpp | 27 +++++++++++++++++++-------- GITHUB_SHA.h | 2 +- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/DCCRMT.cpp b/DCCRMT.cpp index abe1cf8..4a5ec59 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -20,8 +20,9 @@ #include "defines.h" #include "DIAG.h" #include "DCCRMT.h" -#include "soc/periph_defs.h" -#include "driver/periph_ctrl.h" + +//#include "soc/periph_defs.h" +//#include "driver/periph_ctrl.h" #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,2,0) #error wrong IDF version @@ -41,6 +42,13 @@ void setDCCBit0(rmt_item32_t* item) { item->duration1 = DCC_0_HALFPERIOD; } +void setDCCBit0Last(rmt_item32_t* item) { + item->level0 = 1; + item->duration0 = DCC_0_HALFPERIOD + DCC_0_HALFPERIOD/10; + item->level1 = 0; + item->duration1 = DCC_0_HALFPERIOD; +} + void setEOT(rmt_item32_t* item) { item->val = 0; } @@ -56,9 +64,9 @@ RMTPin::RMTPin(byte pin, byte ch, byte plen) { preambleLen = plen+2; // plen 1 bits, one 0 bit and one EOF marker preamble = (rmt_item32_t*)malloc(preambleLen*sizeof(rmt_item32_t)); for (byte n=0; n Date: Mon, 15 Nov 2021 22:28:30 +0100 Subject: [PATCH 45/66] Transmit DCC packet to loco --- DCCRMT.cpp | 75 ++++++++++++++++++++++++++++++++++++------------- DCCRMT.h | 13 +++++---- DCCWaveform.cpp | 14 +++++++-- DCCWaveform.h | 3 ++ GITHUB_SHA.h | 2 +- 5 files changed, 78 insertions(+), 29 deletions(-) diff --git a/DCCRMT.cpp b/DCCRMT.cpp index 4a5ec59..5287e16 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -55,7 +55,7 @@ void setEOT(rmt_item32_t* item) { void IRAM_ATTR interrupt(rmt_channel_t channel, void *t) { RMTPin *tt = (RMTPin *)t; - tt->RMTinterrupt(channel); + tt->RMTinterrupt(); } RMTPin::RMTPin(byte pin, byte ch, byte plen) { @@ -65,7 +65,7 @@ RMTPin::RMTPin(byte pin, byte ch, byte plen) { preamble = (rmt_item32_t*)malloc(preambleLen*sizeof(rmt_item32_t)); for (byte n=0; n 0) // we have still old work to do + return false; + byte bitcounter = 0; + for(byte n=0; n 0) + dataRepeat--; + return; } diff --git a/DCCRMT.h b/DCCRMT.h index 78eaaa3..c197757 100644 --- a/DCCRMT.h +++ b/DCCRMT.h @@ -29,8 +29,10 @@ class RMTPin { public: RMTPin(byte pin, byte ch, byte plen); - void IRAM_ATTR RMTinterrupt(rmt_channel_t); - + void IRAM_ATTR RMTinterrupt(); + void RMTprefill(); + bool fillData(const byte buffer[], byte byteCount, byte repeatCount); + static RMTPin mainRMTPin; static RMTPin progRMTPin; @@ -43,9 +45,10 @@ class RMTPin { byte idleLen; rmt_item32_t *preamble; byte preambleLen; - rmt_item32_t packetBits[64]; - byte packetLen; + rmt_item32_t *data; + byte dataLen; // flags volatile bool preambleNext = true; // alternate between preamble and content - volatile bool dataNext = false; // do we have real data available or send idle + volatile bool dataReady = false; // do we have real data available or send idle + volatile byte dataRepeat = 0; }; diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index ee3e9f1..0e32c70 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -25,7 +25,6 @@ #include "DCCTimer.h" #include "DIAG.h" #include "freeMemory.h" -#include "DCCRMT.h" DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true); DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false); @@ -39,7 +38,7 @@ uint8_t DCCWaveform::trailingEdgeCounter=0; void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { - RMTPin *p = new RMTPin(21, 0, PREAMBLE_BITS_MAIN); + mainTrack.rmtPin = new RMTPin(21, 0, PREAMBLE_BITS_MAIN); mainTrack.motorDriver=mainDriver; progTrack.motorDriver=progDriver; @@ -64,6 +63,13 @@ volatile bool ackflag = 0; #endif void IRAM_ATTR DCCWaveform::loop(bool ackManagerActive) { + + if (mainTrack.packetPendingRMT) { + mainTrack.rmtPin->fillData(mainTrack.pendingPacket, mainTrack.pendingLength, mainTrack.pendingRepeats); + mainTrack.packetPendingRMT=false; + // sentResetsSincePacket = 0 // later when progtrack + } + #ifdef SLOW_ANALOG_READ if (ackflag) { progTrack.checkAck(); @@ -122,6 +128,7 @@ const byte bitMask[] = {0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) { isMainTrack = isMain; packetPending = false; + packetPendingRMT = false; memcpy(transmitPacket, idlePacket, sizeof(idlePacket)); state = WAVE_START; // The +1 below is to allow the preamble generator to create the stop bit @@ -290,7 +297,7 @@ void IRAM_ATTR DCCWaveform::interrupt2() { // Wait until there is no packet pending, then make this pending void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) { if (byteCount > MAX_PACKET_SIZE) return; // allow for chksum - while (packetPending); + while (packetPending||packetPendingRMT); portENTER_CRITICAL(&timerMux); byte checksum = 0; for (byte b = 0; b < byteCount; b++) { @@ -302,6 +309,7 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea pendingLength = byteCount + 1; pendingRepeats = repeats; packetPending = true; + packetPendingRMT = true; sentResetsSincePacket=0; portEXIT_CRITICAL(&timerMux); } diff --git a/DCCWaveform.h b/DCCWaveform.h index 29d6a29..9822a87 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -20,6 +20,7 @@ #ifndef DCCWaveform_h #define DCCWaveform_h +#include "DCCRMT.h" #include "MotorDriver.h" // Wait times for power management. Unit: milliseconds @@ -82,6 +83,7 @@ class DCCWaveform { } void schedulePacket(const byte buffer[], byte byteCount, byte repeats); volatile bool packetPending; + volatile bool packetPendingRMT; volatile byte sentResetsSincePacket; volatile bool autoPowerOff=false; void setAckBaseline(); //prog track only @@ -122,6 +124,7 @@ class DCCWaveform { bool isMainTrack; MotorDriver* motorDriver; + RMTPin* rmtPin; // Transmission controller byte transmitPacket[MAX_PACKET_SIZE+1]; // +1 for checksum byte transmitLength; diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index e47a6b3..512e544 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "ESP32-2021114-15:35" +#define GITHUB_SHA "ESP32-2021115-22:27" From 114686d124105803c0b81e109812764a41df59f5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 15 Nov 2021 23:10:23 +0100 Subject: [PATCH 46/66] cleanup comments --- DCCRMT.cpp | 33 ++++++++++----------------------- DCCRMT.h | 3 ++- DCCWaveform.cpp | 2 +- GITHUB_SHA.h | 2 +- 4 files changed, 14 insertions(+), 26 deletions(-) diff --git a/DCCRMT.cpp b/DCCRMT.cpp index 5287e16..c07805b 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -21,9 +21,6 @@ #include "DIAG.h" #include "DCCRMT.h" -//#include "soc/periph_defs.h" -//#include "driver/periph_ctrl.h" - #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,2,0) #error wrong IDF version #endif @@ -82,8 +79,8 @@ RMTPin::RMTPin(byte pin, byte ch, byte plen) { setEOT(idle + 28); // EOT marker // data: max packet size today is 5 + checksum - dataLen = (5+1)*9+2; // Each byte has one bit extra and one 0 bit and one EOF marker - data = (rmt_item32_t*)malloc(dataLen*sizeof(rmt_item32_t)); + maxDataLen = (5+1)*9+2; // Each byte has one bit extra and one 0 bit and one EOF marker + data = (rmt_item32_t*)malloc(maxDataLen*sizeof(rmt_item32_t)); rmt_config_t config; // Configure the RMT channel for TX @@ -97,10 +94,6 @@ RMTPin::RMTPin(byte pin, byte ch, byte plen) { // 11*9 + extrazero + EOT = 124 // 2 mem block of 64 RMT items should be enough - // this was not our problem https://esp32.com/viewtopic.php?t=5252 - //periph_module_disable(PERIPH_RMT_MODULE); - //periph_module_enable(PERIPH_RMT_MODULE); - ESP_ERROR_CHECK(rmt_config(&config)); // NOTE: ESP_INTR_FLAG_IRAM is *NOT* included in this bitmask ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, ESP_INTR_FLAG_LOWMED|ESP_INTR_FLAG_SHARED)); @@ -110,11 +103,6 @@ RMTPin::RMTPin(byte pin, byte ch, byte plen) { rmt_register_tx_end_callback(interrupt, this); rmt_set_tx_intr_en(channel, true); - // rmt_set_source_clk() // not needed as APB only supported currently - - - //rmt_register_tx_end_callback() - DIAG(F("Starting channel %d signal generator"), config.channel); // send one bit to kickstart the signal, remaining data will come from the @@ -133,9 +121,13 @@ void RMTPin::RMTprefill() { const byte transmitMask[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; -bool RMTPin::fillData(const byte buffer[], byte byteCount, byte repeatCount=1) { +bool RMTPin::RMTfillData(const byte buffer[], byte byteCount, byte repeatCount=1) { if (dataReady == true || dataRepeat > 0) // we have still old work to do return false; + if (byteCount*9+2 > maxDataLen) // this would overun our allocated memory for data + return false; // something very broken, can not convert packet + + // convert bytes to RMT stream of "bits" byte bitcounter = 0; for(byte n=0; n 0) + if (dataRepeat > 0) // if a repeat count was specified, work on that dataRepeat--; return; } diff --git a/DCCRMT.h b/DCCRMT.h index c197757..2e119e6 100644 --- a/DCCRMT.h +++ b/DCCRMT.h @@ -31,7 +31,7 @@ class RMTPin { RMTPin(byte pin, byte ch, byte plen); void IRAM_ATTR RMTinterrupt(); void RMTprefill(); - bool fillData(const byte buffer[], byte byteCount, byte repeatCount); + bool RMTfillData(const byte buffer[], byte byteCount, byte repeatCount); static RMTPin mainRMTPin; static RMTPin progRMTPin; @@ -47,6 +47,7 @@ class RMTPin { byte preambleLen; rmt_item32_t *data; byte dataLen; + byte maxDataLen; // flags volatile bool preambleNext = true; // alternate between preamble and content volatile bool dataReady = false; // do we have real data available or send idle diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 0e32c70..24f6050 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -65,7 +65,7 @@ volatile bool ackflag = 0; void IRAM_ATTR DCCWaveform::loop(bool ackManagerActive) { if (mainTrack.packetPendingRMT) { - mainTrack.rmtPin->fillData(mainTrack.pendingPacket, mainTrack.pendingLength, mainTrack.pendingRepeats); + mainTrack.rmtPin->RMTfillData(mainTrack.pendingPacket, mainTrack.pendingLength, mainTrack.pendingRepeats); mainTrack.packetPendingRMT=false; // sentResetsSincePacket = 0 // later when progtrack } diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 512e544..c2c1e55 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "ESP32-2021115-22:27" +#define GITHUB_SHA "ESP32-2021115-23:10" From a69b7ee1131a746127b7db725f521b5d14688b05 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 18 Nov 2021 23:57:53 +0100 Subject: [PATCH 47/66] change to RMT loop mode --- DCCRMT.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/DCCRMT.cpp b/DCCRMT.cpp index c07805b..21002cf 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -100,6 +100,7 @@ RMTPin::RMTPin(byte pin, byte ch, byte plen) { DIAG(F("Register interrupt on core %d"), xPortGetCoreID()); + ESP_ERROR_CHECK(rmt_set_tx_loop_mode(channel, true)); rmt_register_tx_end_callback(interrupt, this); rmt_set_tx_intr_en(channel, true); @@ -148,7 +149,8 @@ bool RMTPin::RMTfillData(const byte buffer[], byte byteCount, byte repeatCount=1 } void IRAM_ATTR RMTPin::RMTinterrupt() { - rmt_tx_start(channel,true); // preamble is always loaded, stat right away + //no rmt_tx_start(channel,true) as we run in loop mode + //preamble is always loaded at beginning of buffer if (dataReady) { // if we have new data, fill while preamble is running rmt_fill_tx_items(channel, data, dataLen, preambleLen-1); dataReady = false; From 55a789d65a808f29e153427b6001650bee826ddf Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 19 Nov 2021 00:03:21 +0100 Subject: [PATCH 48/66] set RMT clock to microseconds --- DCCRMT.cpp | 2 +- DCCRMT.h | 6 ++++-- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/DCCRMT.cpp b/DCCRMT.cpp index 21002cf..a0d8756 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -87,7 +87,7 @@ RMTPin::RMTPin(byte pin, byte ch, byte plen) { bzero(&config, sizeof(rmt_config_t)); config.rmt_mode = RMT_MODE_TX; config.channel = channel = (rmt_channel_t)ch; - config.clk_div = 1; // use 80Mhz clock directly + config.clk_div = RMT_CLOCK_DIVIDER; config.gpio_num = (gpio_num_t)pin; config.mem_block_num = 2; // With longest DCC packet 11 inc checksum (future expansion) // number of bits needed is 22preamble + start + diff --git a/DCCRMT.h b/DCCRMT.h index 2e119e6..bd235b7 100644 --- a/DCCRMT.h +++ b/DCCRMT.h @@ -23,8 +23,10 @@ #include "soc/rmt_reg.h" #include "soc/rmt_struct.h" -#define DCC_1_HALFPERIOD 4640 // 1 / 80000000 * 4640 = 58us -#define DCC_0_HALFPERIOD 8000 +// make calculations easy and set up for microseconds +#define RMT_CLOCK_DIVIDER 80 +#define DCC_1_HALFPERIOD 58 //4640 // 1 / 80000000 * 4640 = 58us +#define DCC_0_HALFPERIOD 100 //8000 class RMTPin { public: From 50b854c5266d02ec9ff9fa0d5715f89347d2c6b6 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 19 Nov 2021 00:34:56 +0100 Subject: [PATCH 49/66] remove extra zero bit --- DCCRMT.cpp | 25 ++++++++++++++++--------- GITHUB_SHA.h | 2 +- 2 files changed, 17 insertions(+), 10 deletions(-) diff --git a/DCCRMT.cpp b/DCCRMT.cpp index a0d8756..a561917 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -17,9 +17,13 @@ * along with CommandStation. If not, see . */ +#include "config.h" #include "defines.h" #include "DIAG.h" #include "DCCRMT.h" +#include "DCCWaveform.h" // for MAX_PACKET_SIZE + +#define DATA_LEN(X) ((X)*9+1) // Each byte has one bit extra and we have one EOF marker #if ESP_IDF_VERSION < ESP_IDF_VERSION_VAL(4,2,0) #error wrong IDF version @@ -39,11 +43,12 @@ void setDCCBit0(rmt_item32_t* item) { item->duration1 = DCC_0_HALFPERIOD; } -void setDCCBit0Last(rmt_item32_t* item) { +// special long zero to trigger scope +void setDCCBit0Long(rmt_item32_t* item) { item->level0 = 1; item->duration0 = DCC_0_HALFPERIOD + DCC_0_HALFPERIOD/10; item->level1 = 0; - item->duration1 = DCC_0_HALFPERIOD; + item->duration1 = DCC_0_HALFPERIOD + DCC_0_HALFPERIOD/10; } void setEOT(rmt_item32_t* item) { @@ -62,11 +67,15 @@ RMTPin::RMTPin(byte pin, byte ch, byte plen) { preamble = (rmt_item32_t*)malloc(preambleLen*sizeof(rmt_item32_t)); for (byte n=0; n 0) // we have still old work to do return false; - if (byteCount*9+2 > maxDataLen) // this would overun our allocated memory for data + if (DATA_LEN(byteCount) > maxDataLen) // this would overun our allocated memory for data return false; // something very broken, can not convert packet // convert bytes to RMT stream of "bits" @@ -140,7 +148,6 @@ bool RMTPin::RMTfillData(const byte buffer[], byte byteCount, byte repeatCount=1 setDCCBit0(data + bitcounter++); // zero at end of each byte } setDCCBit1(data + bitcounter-1); // overwrite previous zero bit with one bit - setDCCBit0Last(data + bitcounter++); // extra 0 bit after end bit setEOT(data + bitcounter++); // EOT marker dataLen = bitcounter; dataReady = true; diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index c2c1e55..6bcd087 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "ESP32-2021115-23:10" +#define GITHUB_SHA "ESP32-2021119-00:26" From c8e5123c0a0016099f0572acffbb51cbfc92ae84 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 21 Nov 2021 00:51:59 +0100 Subject: [PATCH 50/66] fix compile errors on ESP32 --- IO_MCP23008.h | 8 +++++++- IO_PCF8574.h | 6 +++--- RMFTMacros.h | 26 +++++++++++++------------- 3 files changed, 23 insertions(+), 17 deletions(-) diff --git a/IO_MCP23008.h b/IO_MCP23008.h index 18ff12f..1b8471e 100644 --- a/IO_MCP23008.h +++ b/IO_MCP23008.h @@ -22,6 +22,12 @@ #include "IO_GPIOBase.h" +#if defined(ARDUINO_ARCH_ESP32) // min seems to be missing from that package +#ifndef min +#define min(a,b) ((a)<(b)?(a):(b)) +#endif +#endif + class MCP23008 : public GPIOBase { public: static void create(VPIN firstVpin, uint8_t nPins, uint8_t I2CAddress, int interruptPin=-1) { @@ -97,4 +103,4 @@ private: }; -#endif \ No newline at end of file +#endif diff --git a/IO_PCF8574.h b/IO_PCF8574.h index dea5e2c..8403a1a 100644 --- a/IO_PCF8574.h +++ b/IO_PCF8574.h @@ -75,7 +75,7 @@ private: if (immediate) { uint8_t buffer[1]; I2CManager.read(_I2CAddress, buffer, 1); - _portInputState = ((uint16_t)buffer) & 0xff; + _portInputState = buffer[0]; } else { requestBlock.wait(); // Wait for preceding operation to complete // Issue new request to read GPIO register @@ -86,7 +86,7 @@ private: // This function is invoked when an I/O operation on the requestBlock completes. void _processCompletion(uint8_t status) override { if (status == I2C_STATUS_OK) - _portInputState = ((uint16_t)inputBuffer[0]) & 0xff; + _portInputState = inputBuffer[0]; else _portInputState = 0xff; } @@ -99,4 +99,4 @@ private: uint8_t inputBuffer[1]; }; -#endif \ No newline at end of file +#endif diff --git a/RMFTMacros.h b/RMFTMacros.h index 01bfb46..8c98acd 100644 --- a/RMFTMacros.h +++ b/RMFTMacros.h @@ -226,16 +226,16 @@ const int StringMacroTracker1=__COUNTER__; // Define macros for route code creation #define V(val) ((int16_t)(val))&0x00FF,((int16_t)(val)>>8)&0x00FF -#define NOP 0,0 +#define NOOPERAND 0,0 #define ALIAS(name,value) #define EXRAIL const FLASH byte RMFT2::RouteCode[] = { #define AUTOMATION(id, description) OPCODE_AUTOMATION, V(id), #define ROUTE(id, description) OPCODE_ROUTE, V(id), #define SEQUENCE(id) OPCODE_SEQUENCE, V(id), -#define ENDTASK OPCODE_ENDTASK,NOP, -#define DONE OPCODE_ENDTASK,NOP, -#define ENDEXRAIL OPCODE_ENDTASK,NOP,OPCODE_ENDEXRAIL,NOP }; +#define ENDTASK OPCODE_ENDTASK,NOOPERAND, +#define DONE OPCODE_ENDTASK,NOOPERAND, +#define ENDEXRAIL OPCODE_ENDTASK,NOOPERAND,OPCODE_ENDEXRAIL,NOOPERAND }; #define AFTER(sensor_id) OPCODE_AT,V(sensor_id),OPCODE_AFTER,V(sensor_id), #define AMBER(signal_id) OPCODE_AMBER,V(signal_id), @@ -245,7 +245,7 @@ const int StringMacroTracker1=__COUNTER__; #define DELAY(ms) OPCODE_DELAY,V(ms/100L), #define DELAYMINS(mindelay) OPCODE_DELAYMINS,V(mindelay), #define DELAYRANDOM(mindelay,maxdelay) OPCODE_DELAY,V(mindelay/100L),OPCODE_RANDWAIT,V((maxdelay-mindelay)/100L), -#define ENDIF OPCODE_ENDIF,NOP, +#define ENDIF OPCODE_ENDIF,NOOPERAND, #define ESTOP OPCODE_SPEED,V(1), #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), @@ -258,23 +258,23 @@ const int StringMacroTracker1=__COUNTER__; #define IFNOT(sensor_id) OPCODE_IFNOT,V(sensor_id), #define IFRANDOM(percent) OPCODE_IFRANDOM,V(percent), #define IFRESERVE(block) OPCODE_IFRESERVE,V(block), -#define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,NOP, -#define JOIN OPCODE_JOIN,NOP, +#define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,NOOPERAND, +#define JOIN OPCODE_JOIN,NOOPERAND, #define LATCH(sensor_id) OPCODE_LATCH,V(sensor_id), #define LCD(id,msg) PRINT(msg) #define LCN(msg) PRINT(msg) #define ONCLOSE(turnout_id) OPCODE_ONCLOSE,V(turnout_id), #define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id), -#define PAUSE OPCODE_PAUSE,NOP, +#define PAUSE OPCODE_PAUSE,NOOPERAND, #define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value), -#define POWEROFF OPCODE_POWEROFF,NOP, +#define POWEROFF OPCODE_POWEROFF,NOOPERAND, #define PRINT(msg) OPCODE_PRINT,V(__COUNTER__ - StringMacroTracker2), -#define READ_LOCO OPCODE_READ_LOCO1,NOP,OPCODE_READ_LOCO2,NOP, +#define READ_LOCO OPCODE_READ_LOCO1,NOOPERAND,OPCODE_READ_LOCO2,NOOPERAND, #define RED(signal_id) OPCODE_RED,V(signal_id), #define RESERVE(blockid) OPCODE_RESERVE,V(blockid), #define RESET(pin) OPCODE_RESET,V(pin), -#define RESUME OPCODE_RESUME,NOP, -#define RETURN OPCODE_RETURN,NOP, +#define RESUME OPCODE_RESUME,NOOPERAND, +#define RETURN OPCODE_RETURN,NOOPERAND, #define REV(speed) OPCODE_REV,V(speed), #define SENDLOCO(cab,route) OPCODE_SENDLOCO,V(cab),OPCODE_PAD,V(route), #define SERIAL(msg) PRINT(msg) @@ -293,7 +293,7 @@ const int StringMacroTracker1=__COUNTER__; #define PIN_TURNOUT(id,pin) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin), #define THROW(id) OPCODE_THROW,V(id), #define TURNOUT(id,addr,subaddr) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr), -#define UNJOIN OPCODE_UNJOIN,NOP, +#define UNJOIN OPCODE_UNJOIN,NOOPERAND, #define UNLATCH(sensor_id) OPCODE_UNLATCH,V(sensor_id), #define WAITFOR(pin) OPCODE_WAITFOR,V(pin), #define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func), From 2632d44ec9b4e6310f003a293a891c6c69915b13 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 21 Nov 2021 21:28:56 +0100 Subject: [PATCH 51/66] remove packetPendingRMT from wrong if --- DCCWaveform.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 094e395..91f0a5d 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -302,7 +302,7 @@ void IRAM_ATTR DCCWaveform::interrupt2() { // Wait until there is no packet pending, then make this pending void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) { if (byteCount > MAX_PACKET_SIZE) return; // allow for chksum - while (packetPending||packetPendingRMT); + while (packetPending); portENTER_CRITICAL(&timerMux); byte checksum = 0; for (byte b = 0; b < byteCount; b++) { From f7e90e7b738b943f078fa95e7ad50cf16002f2e5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 21 Nov 2021 22:53:17 +0100 Subject: [PATCH 52/66] MotorDriverContainer (multi-motordriver) start --- CommandStation-EX.ino | 6 +----- DCC.cpp | 10 ++++++---- DCC.h | 2 +- DCCWaveform.cpp | 13 ++++++++++--- MotorDriver.cpp | 24 ++++++++++++++++++++++++ MotorDriver.h | 21 +++++++++++++++++++++ 6 files changed, 63 insertions(+), 13 deletions(-) diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index ee7245b..872dc2b 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -85,11 +85,7 @@ void setup() #endif // ETHERNET_ON // Responsibility 3: Start the DCC engine. - // Note: this provides DCC with two motor drivers, main and prog, which handle the motor shield(s) - // Standard supported devices have pre-configured macros but custome hardware installations require - // detailed pin mappings and may also require modified subclasses of the MotorDriver to implement specialist logic. - // STANDARD_MOTOR_SHIELD, POLOLU_MOTOR_SHIELD, FIREBOX_MK1, FIREBOX_MK1S are pre defined in MotorShields.h - DCC::begin(MOTOR_SHIELD_TYPE); + DCC::begin(); // Start RMFT (ignored if no automnation) RMFT::begin(); diff --git a/DCC.cpp b/DCC.cpp index 76bb917..4d30b98 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -26,6 +26,9 @@ #include "FSH.h" #include "IODevice.h" +#include "MotorDriver.h" +extern MotorDriverContainer mDC; + // This module is responsible for converting API calls into // messages to be sent to the waveform generator. // It has no visibility of the hardware, timers, interrupts @@ -49,9 +52,8 @@ FSH* DCC::shieldName=NULL; byte DCC::joinRelay=UNUSED_PIN; byte DCC::globalSpeedsteps=128; -void DCC::begin(const FSH * motorShieldName, MotorDriver * mainDriver, MotorDriver* progDriver) { - shieldName=(FSH *)motorShieldName; - StringFormatter::send(Serial,F("\n"), F(VERSION), F(ARDUINO_TYPE), shieldName, F(GITHUB_SHA)); +void DCC::begin() { + StringFormatter::send(Serial,F("\n"), F(VERSION), F(ARDUINO_TYPE), mDC.getMotorShieldName(), F(GITHUB_SHA)); // Initialise HAL layer before reading EEprom. IODevice::begin(); @@ -60,7 +62,7 @@ void DCC::begin(const FSH * motorShieldName, MotorDriver * mainDriver, MotorDriv (void)EEPROM; // tell compiler not to warn this is unused EEStore::init(); - DCCWaveform::begin(mainDriver,progDriver); + DCCWaveform::begin(mDC.mainTrack(),mDC.progTrack()); } void DCC::setJoinRelayPin(byte joinRelayPin) { diff --git a/DCC.h b/DCC.h index cb03949..19b4e02 100644 --- a/DCC.h +++ b/DCC.h @@ -77,7 +77,7 @@ const byte MAX_LOCOS = 50; class DCC { public: - static void begin(const FSH * motorShieldName, MotorDriver *mainDriver, MotorDriver *progDriver); + static void begin(); static void setJoinRelayPin(byte joinRelayPin); static void loop(); diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 91f0a5d..a9f09f9 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -92,8 +92,10 @@ void IRAM_ATTR DCCWaveform::interruptHandler() { byte sigMain=signalTransform[mainTrack.state]; byte sigProg=progTrackSyncMain? sigMain : signalTransform[progTrack.state]; // Set the signal state for both tracks - mainTrack.motorDriver->setSignal(sigMain); - progTrack.motorDriver->setSignal(sigProg); + if (mainTrack.motorDriver) + mainTrack.motorDriver->setSignal(sigMain); + if (progTrack.motorDriver) + progTrack.motorDriver->setSignal(sigProg); // Move on in the state engine mainTrack.state=stateTransform[mainTrack.state]; progTrack.state=stateTransform[progTrack.state]; @@ -148,12 +150,14 @@ POWERMODE DCCWaveform::getPowerMode() { void DCCWaveform::setPowerMode(POWERMODE mode) { powerMode = mode; bool ison = (mode == POWERMODE::ON); - motorDriver->setPower( ison); + if (motorDriver) + motorDriver->setPower( ison); sentResetsSincePacket=0; } void DCCWaveform::checkPowerOverload(bool ackManagerActive) { + if (!motorDriver) return; if (millis() - lastSampleTaken < sampleDelay) return; lastSampleTaken = millis(); int tripValue= motorDriver->getRawCurrentTripValue(); @@ -323,6 +327,7 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea // (yes I know I could have subclassed the main track but...) void DCCWaveform::setAckBaseline() { + if (!motorDriver) return; if (isMainTrack) return; int baseline=motorDriver->getCurrentRaw(); ackThreshold= baseline + motorDriver->mA2raw(ackLimitmA); @@ -345,6 +350,7 @@ void DCCWaveform::setAckPending() { } byte DCCWaveform::getAck() { + if (!motorDriver) return 0; if (ackPending) return (2); // still waiting if (Diag::ACK) DIAG(F("%S after %dmS max=%d/%dmA pulse=%duS samples=%d gaps=%d"),ackDetected?F("ACK"):F("NO-ACK"), ackCheckDuration, ackMaxCurrent,motorDriver->raw2mA(ackMaxCurrent), ackPulseDuration, numAckSamples, numAckGaps); @@ -355,6 +361,7 @@ byte DCCWaveform::getAck() { #pragma GCC push_options #pragma GCC optimize ("-O3") void IRAM_ATTR DCCWaveform::checkAck() { + if (!motorDriver) return; // This function operates in interrupt() time so must be fast and can't DIAG if (sentResetsSincePacket > 6) { //ACK timeout ackCheckDuration=millis()-ackCheckStart; diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 11d56d8..2675249 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -17,6 +17,8 @@ * along with CommandStation. If not, see . */ #include +#include "config.h" +#include "defines.h" #include "MotorDriver.h" #include "DCCTimer.h" #include "DIAG.h" @@ -194,3 +196,25 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res result.maskLOW = ~result.maskHIGH; // DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH); } + +MotorDriverContainer::MotorDriverContainer(const FSH * motorShieldName, + MotorDriver *m0, + MotorDriver *m1, + MotorDriver *m2, + MotorDriver *m3, + MotorDriver *m4, + MotorDriver *m5, + MotorDriver *m6, + MotorDriver *m7) { + mD[0]=m0; + mD[1]=m1; + mD[2]=m2; + mD[3]=m3; + mD[4]=m4; + mD[5]=m5; + mD[6]=m6; + mD[7]=m7; + shieldName = (FSH *)motorShieldName; +} + +MotorDriverContainer mDC(MOTOR_SHIELD_TYPE); diff --git a/MotorDriver.h b/MotorDriver.h index 9193828..cd311a8 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -106,4 +106,25 @@ class MotorDriver { } #endif }; + +class MotorDriverContainer { +public: + MotorDriverContainer(const FSH * motorShieldName, + MotorDriver *m0=NULL, + MotorDriver *m1=NULL, + MotorDriver *m2=NULL, + MotorDriver *m3=NULL, + MotorDriver *m4=NULL, + MotorDriver *m5=NULL, + MotorDriver *m6=NULL, + MotorDriver *m7=NULL); + // void SetCapability(byte n, byte cap, char [] name); + inline FSH *getMotorShieldName() { return shieldName; }; + inline MotorDriver *mainTrack() { return mD[0]; }; //start fixed + inline MotorDriver *progTrack() { return mD[1]; }; + +private: + MotorDriver *mD[8]; + FSH *shieldName; +}; #endif From 35ee03537d91712b33094cadab9ac53910d29b79 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 21 Nov 2021 22:56:14 +0100 Subject: [PATCH 53/66] version --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 7663d2e..4fe3dd6 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "ESP32-2021120-11:30" +#define GITHUB_SHA "ESP32-motordriver-2021121-22:55" From c00d3a825df838b22381aeb9e30dc68e215de9b1 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 22 Nov 2021 03:24:15 +0100 Subject: [PATCH 54/66] Shield RMT stuff with ifdef ESP32 --- DCCRMT.cpp | 2 ++ DCCRMT.h | 2 ++ 2 files changed, 4 insertions(+) diff --git a/DCCRMT.cpp b/DCCRMT.cpp index a561917..294663b 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -19,6 +19,7 @@ #include "config.h" #include "defines.h" +#if defined(ARDUINO_ARCH_ESP32) #include "DIAG.h" #include "DCCRMT.h" #include "DCCWaveform.h" // for MAX_PACKET_SIZE @@ -166,3 +167,4 @@ void IRAM_ATTR RMTPin::RMTinterrupt() { dataRepeat--; return; } +#endif //ESP32 diff --git a/DCCRMT.h b/DCCRMT.h index bd235b7..078a652 100644 --- a/DCCRMT.h +++ b/DCCRMT.h @@ -19,6 +19,7 @@ #pragma once #include +#if defined(ARDUINO_ARCH_ESP32) #include "driver/rmt.h" #include "soc/rmt_reg.h" #include "soc/rmt_struct.h" @@ -55,3 +56,4 @@ class RMTPin { volatile bool dataReady = false; // do we have real data available or send idle volatile byte dataRepeat = 0; }; +#endif //ESP32 From 82df3a21dc1abf595810858af33ad0655cb7f63b Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 22 Nov 2021 03:55:00 +0100 Subject: [PATCH 55/66] Rename RMTPin to RMTChannel --- DCCRMT.cpp | 10 +++++----- DCCRMT.h | 8 ++++---- DCCWaveform.cpp | 2 +- DCCWaveform.h | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/DCCRMT.cpp b/DCCRMT.cpp index 294663b..108bac9 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -57,11 +57,11 @@ void setEOT(rmt_item32_t* item) { } void IRAM_ATTR interrupt(rmt_channel_t channel, void *t) { - RMTPin *tt = (RMTPin *)t; + RMTChannel *tt = (RMTChannel *)t; tt->RMTinterrupt(); } -RMTPin::RMTPin(byte pin, byte ch, byte plen) { +RMTChannel::RMTChannel(byte pin, byte ch, byte plen) { // preamble preambleLen = plen+2; // plen 1 bits, one 0 bit and one EOF marker @@ -124,14 +124,14 @@ RMTPin::RMTPin(byte pin, byte ch, byte plen) { RMTinterrupt(); } -void RMTPin::RMTprefill() { +void RMTChannel::RMTprefill() { rmt_fill_tx_items(channel, preamble, preambleLen, 0); rmt_fill_tx_items(channel, idle, idleLen, preambleLen-1); } const byte transmitMask[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; -bool RMTPin::RMTfillData(const byte buffer[], byte byteCount, byte repeatCount=1) { +bool RMTChannel::RMTfillData(const byte buffer[], byte byteCount, byte repeatCount=1) { if (dataReady == true || dataRepeat > 0) // we have still old work to do return false; if (DATA_LEN(byteCount) > maxDataLen) // this would overun our allocated memory for data @@ -156,7 +156,7 @@ bool RMTPin::RMTfillData(const byte buffer[], byte byteCount, byte repeatCount=1 return true; } -void IRAM_ATTR RMTPin::RMTinterrupt() { +void IRAM_ATTR RMTChannel::RMTinterrupt() { //no rmt_tx_start(channel,true) as we run in loop mode //preamble is always loaded at beginning of buffer if (dataReady) { // if we have new data, fill while preamble is running diff --git a/DCCRMT.h b/DCCRMT.h index 078a652..33339c4 100644 --- a/DCCRMT.h +++ b/DCCRMT.h @@ -29,15 +29,15 @@ #define DCC_1_HALFPERIOD 58 //4640 // 1 / 80000000 * 4640 = 58us #define DCC_0_HALFPERIOD 100 //8000 -class RMTPin { +class RMTChannel { public: - RMTPin(byte pin, byte ch, byte plen); + RMTChannel(byte pin, byte ch, byte plen); void IRAM_ATTR RMTinterrupt(); void RMTprefill(); bool RMTfillData(const byte buffer[], byte byteCount, byte repeatCount); - static RMTPin mainRMTPin; - static RMTPin progRMTPin; + static RMTChannel mainRMTChannel; + static RMTChannel progRMTChannel; private: diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index a9f09f9..1cf3011 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -38,7 +38,7 @@ uint8_t DCCWaveform::trailingEdgeCounter=0; void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { - mainTrack.rmtPin = new RMTPin(21, 0, PREAMBLE_BITS_MAIN); + mainTrack.rmtPin = new RMTChannel(21, 0, PREAMBLE_BITS_MAIN); mainTrack.motorDriver=mainDriver; progTrack.motorDriver=progDriver; diff --git a/DCCWaveform.h b/DCCWaveform.h index 9822a87..ef8773a 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -124,7 +124,7 @@ class DCCWaveform { bool isMainTrack; MotorDriver* motorDriver; - RMTPin* rmtPin; + RMTChannel* rmtPin; // Transmission controller byte transmitPacket[MAX_PACKET_SIZE+1]; // +1 for checksum byte transmitLength; From ed2aa4c1d8fd5b314ae06b012b6c74f02cbe2c3a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 22 Nov 2021 04:01:48 +0100 Subject: [PATCH 56/66] remove virtual --- MotorDriver.h | 25 ++++++------------------- 1 file changed, 6 insertions(+), 19 deletions(-) diff --git a/MotorDriver.h b/MotorDriver.h index cd311a8..a4a389d 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -52,25 +52,12 @@ class MotorDriver { public: MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin); - virtual void setPower( bool on); - void setSignal( bool high);/* { - if (usePWM) { - DCCTimer::setPWM(signalPin,high); - } - - if (high) { - setHIGH(fastSignalPin); - if (dualSignal) setLOW(fastSignalPin2); - } - else { - setLOW(fastSignalPin); - if (dualSignal) setHIGH(fastSignalPin2); - } - };*/ - virtual void setBrake( bool on); - virtual int getCurrentRaw(); - virtual unsigned int raw2mA( int raw); - virtual int mA2raw( unsigned int mA); + void setPower( bool on); + void setSignal( bool high); + void setBrake( bool on); + int getCurrentRaw(); + unsigned int raw2mA( int raw); + int mA2raw( unsigned int mA); inline int getRawCurrentTripValue() { return rawCurrentTripValue; } From c711be798079883a5d215c9cbec0573033f1c7c0 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 22 Nov 2021 23:26:04 +0100 Subject: [PATCH 57/66] DCCTrack::schedulePacket allows multiple different motordrivers side by side --- DCC.cpp | 30 +++++++++++++++--------- DCCEXParser.cpp | 3 ++- DCCPacket.h | 12 ++++++++++ DCCRMT.cpp | 20 ++++++++++++---- DCCRMT.h | 4 +++- DCCTrack.cpp | 38 ++++++++++++++++++++++++++++++ DCCTrack.h | 20 ++++++++++++++++ DCCWaveform.cpp | 24 ++++++++++--------- DCCWaveform.h | 10 ++++---- GITHUB_SHA.h | 2 +- IO_GPIOBase.h | 6 ++++- IO_MCP23008.h | 6 ----- MotorDriver.cpp | 61 +++++++++++++++++++++++++++++++++++++++---------- MotorDriver.h | 31 +++++++++++++++++++++++-- 14 files changed, 211 insertions(+), 56 deletions(-) create mode 100644 DCCPacket.h create mode 100644 DCCTrack.cpp create mode 100644 DCCTrack.h diff --git a/DCC.cpp b/DCC.cpp index 4d30b98..fe0fbb4 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -20,6 +20,7 @@ #include "DIAG.h" #include "DCC.h" #include "DCCWaveform.h" +#include "DCCTrack.h" #include "EEStore.h" #include "GITHUB_SHA.h" #include "version.h" @@ -62,7 +63,13 @@ void DCC::begin() { (void)EEPROM; // tell compiler not to warn this is unused EEStore::init(); - DCCWaveform::begin(mDC.mainTrack(),mDC.progTrack()); + DCCWaveform::begin(mDC.mainTrack(),mDC.progTrack()); + DCCTrack::mainTrack.addDriver(mDC.mainTrack()); + DCCTrack::progTrack.addDriver(mDC.progTrack()); + + MotorDriver *md; + mDC.add(2, md = new MotorDriver(16, 21, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN)); + DCCTrack::mainTrack.addDriver(md); } void DCC::setJoinRelayPin(byte joinRelayPin) { @@ -118,7 +125,7 @@ void DCC::setThrottle2( uint16_t cab, byte speedCode) { } - DCCWaveform::mainTrack.schedulePacket(b, nB, 0); + DCCTrack::mainTrack.schedulePacket(b, nB, 0); } void DCC::setFunctionInternal(int cab, byte byte1, byte byte2) { @@ -132,7 +139,7 @@ void DCC::setFunctionInternal(int cab, byte byte1, byte byte2) { if (byte1!=0) b[nB++] = byte1; b[nB++] = byte2; - DCCWaveform::mainTrack.schedulePacket(b, nB, 0); + DCCTrack::mainTrack.schedulePacket(b, nB, 0); } uint8_t DCC::getThrottleSpeed(int cab) { @@ -167,7 +174,7 @@ void DCC::setFn( int cab, int16_t functionNumber, bool on) { b[nB++] = (functionNumber & 0x7F) | (on ? 0x80 : 0); // low order bits and state flag b[nB++] = functionNumber >>7 ; // high order bits } - DCCWaveform::mainTrack.schedulePacket(b, nB, 4); + DCCTrack::mainTrack.schedulePacket(b, nB, 3); return; } @@ -254,7 +261,7 @@ void DCC::setAccessory(int address, byte number, bool activate) { b[0] = address % 64 + 128; // first byte is of the form 10AAAAAA, where AAAAAA represent 6 least signifcant bits of accessory address b[1] = ((((address / 64) % 8) << 4) + (number % 4 << 1) + activate % 2) ^ 0xF8; // second byte is of the form 1AAACDDD, where C should be 1, and the least significant D represent activate/deactivate - DCCWaveform::mainTrack.schedulePacket(b, 2, 4); // Repeat the packet four times + DCCTrack::mainTrack.schedulePacket(b, 2, 3); // Repeat the packet four times (3 2 1 0) } // @@ -272,7 +279,7 @@ void DCC::writeCVByteMain(int cab, int cv, byte bValue) { b[nB++] = cv2(cv); b[nB++] = bValue; - DCCWaveform::mainTrack.schedulePacket(b, nB, 4); + DCCTrack::mainTrack.schedulePacket(b, nB, 3); } // @@ -293,7 +300,7 @@ void DCC::writeCVBitMain(int cab, int cv, byte bNum, bool bValue) { b[nB++] = cv2(cv); b[nB++] = WRITE_BIT | (bValue ? BIT_ON : BIT_OFF) | bNum; - DCCWaveform::mainTrack.schedulePacket(b, nB, 4); + DCCTrack::mainTrack.schedulePacket(b, nB, 3); } void DCC::setProgTrackSyncMain(bool on) { @@ -571,6 +578,7 @@ byte DCC::loopStatus=0; void DCC::loop() { DCCWaveform::loop(ackManagerProg!=NULL); // power overload checks + mDC.loop(); ackManagerLoop(); // maintain prog track ack manager issueReminders(); } @@ -771,7 +779,7 @@ void DCC::ackManagerLoop() { if (Diag::ACK) DIAG(F("W%d cv=%d bit=%d"),opcode==W1, ackManagerCv,ackManagerBitNum); byte instruction = WRITE_BIT | (opcode==W1 ? BIT_ON : BIT_OFF) | ackManagerBitNum; byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction }; - DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); + DCCTrack::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); DCCWaveform::progTrack.setAckPending(); callbackState=AFTER_WRITE; } @@ -782,7 +790,7 @@ void DCC::ackManagerLoop() { if (checkResets( RESET_MIN)) return; if (Diag::ACK) DIAG(F("WB cv=%d value=%d"),ackManagerCv,ackManagerByte); byte message[] = {cv1(WRITE_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte}; - DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); + DCCTrack::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); DCCWaveform::progTrack.setAckPending(); callbackState=AFTER_WRITE; } @@ -793,7 +801,7 @@ void DCC::ackManagerLoop() { if (checkResets( RESET_MIN)) return; if (Diag::ACK) DIAG(F("VB cv=%d value=%d"),ackManagerCv,ackManagerByte); byte message[] = { cv1(VERIFY_BYTE, ackManagerCv), cv2(ackManagerCv), ackManagerByte}; - DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); + DCCTrack::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); DCCWaveform::progTrack.setAckPending(); } break; @@ -805,7 +813,7 @@ void DCC::ackManagerLoop() { if (Diag::ACK) DIAG(F("V%d cv=%d bit=%d"),opcode==V1, ackManagerCv,ackManagerBitNum); byte instruction = VERIFY_BIT | (opcode==V0?BIT_OFF:BIT_ON) | ackManagerBitNum; byte message[] = {cv1(BIT_MANIPULATE, ackManagerCv), cv2(ackManagerCv), instruction }; - DCCWaveform::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); + DCCTrack::progTrack.schedulePacket(message, sizeof(message), PROG_REPEATS); DCCWaveform::progTrack.setAckPending(); } break; diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 0757c29..7a8ed3b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -22,6 +22,7 @@ #include "StringFormatter.h" #include "DCCEXParser.h" #include "DCCWaveform.h" +#include "DCCTrack.h" #include "Turnouts.h" #include "Outputs.h" #include "Sensors.h" @@ -413,7 +414,7 @@ void DCCEXParser::parse(Print *stream, byte *com, RingStream * ringStream) packet[i]=(byte)p[i+1]; if (Diag::CMD) DIAG(F("packet[%d]=%d (0x%x)"), i, packet[i], packet[i]); } - (opcode=='M'?DCCWaveform::mainTrack:DCCWaveform::progTrack).schedulePacket(packet,params,3); + (opcode=='M'?DCCTrack::mainTrack:DCCTrack::progTrack).schedulePacket(packet,params,3); } return; diff --git a/DCCPacket.h b/DCCPacket.h new file mode 100644 index 0000000..261cff3 --- /dev/null +++ b/DCCPacket.h @@ -0,0 +1,12 @@ +#pragma once + +const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload size WITHOUT checksum. + +class dccPacket { + public: + byte data[MAX_PACKET_SIZE+1]; // space for checksum if needed + byte length : 4; // future proof up to 15 + byte repeat : 4; // hopefully 15 enough for ever + //byte priority : 2; // 0 repeats; 1 mobile function ; 2 accessory ; 3 mobile speed +}; + diff --git a/DCCRMT.cpp b/DCCRMT.cpp index 108bac9..95d0e9a 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -131,15 +131,25 @@ void RMTChannel::RMTprefill() { const byte transmitMask[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; -bool RMTChannel::RMTfillData(const byte buffer[], byte byteCount, byte repeatCount=1) { +//bool RMTChannel::RMTfillData(const byte buffer[], byte byteCount, byte repeatCount=0) { +bool RMTChannel::RMTfillData(dccPacket packet) { + // dataReady: Signals to then interrupt routine. It is set when + // we have data in the channel buffer which can be copied out + // to the HW. dataRepeat on the other hand signals back to + // the caller of this function if the data has been sent enough + // times (0 to 3 means 1 to 4 times in total). if (dataReady == true || dataRepeat > 0) // we have still old work to do return false; - if (DATA_LEN(byteCount) > maxDataLen) // this would overun our allocated memory for data - return false; // something very broken, can not convert packet + if (DATA_LEN(packet.length) > maxDataLen) { // this would overun our allocated memory for data + DIAG(F("Can not convert DCC bytes # %d to DCC bits %d, buffer too small"), packet.length, maxDataLen); + return false; // something very broken, can not convert packet + } + byte *buffer = packet.data; + // convert bytes to RMT stream of "bits" byte bitcounter = 0; - for(byte n=0; n #if defined(ARDUINO_ARCH_ESP32) +#include "DCCPacket.h" #include "driver/rmt.h" #include "soc/rmt_reg.h" #include "soc/rmt_struct.h" @@ -34,7 +35,8 @@ class RMTChannel { RMTChannel(byte pin, byte ch, byte plen); void IRAM_ATTR RMTinterrupt(); void RMTprefill(); - bool RMTfillData(const byte buffer[], byte byteCount, byte repeatCount); + bool RMTfillData(dccPacket packet); + //bool RMTfillData(const byte buffer[], byte byteCount, byte repeatCount); static RMTChannel mainRMTChannel; static RMTChannel progRMTChannel; diff --git a/DCCTrack.cpp b/DCCTrack.cpp new file mode 100644 index 0000000..e0896d0 --- /dev/null +++ b/DCCTrack.cpp @@ -0,0 +1,38 @@ + +#include "defines.h" +#include "DCCTrack.h" +#include "DIAG.h" + +DCCTrack::DCCTrack(DCCWaveform *w) { + waveform = w; +} + +void DCCTrack::schedulePacket(const byte buffer[], byte byteCount, byte repeats) { + dccPacket packet; + + // add checksum now, makes stuff easier later + byte checksum = 0; + for (byte b = 0; b < byteCount; b++) { + checksum ^= buffer[b]; + packet.data[b] = buffer[b]; + } + packet.data[byteCount] = checksum; + packet.length = byteCount + 1; + packet.repeat = repeats; + schedulePacket(packet); +}; + +void DCCTrack::schedulePacket(dccPacket packet) { + bool once=true; + for (const auto& driver: mD) { + if (driver->type() == RMT_MAIN || driver->type() == RMT_PROG) { + //DIAG(F("DCCTrack::schedulePacket RMT l=%d d=%x"),packet.length, packet.data[0]); + driver->schedulePacket(packet); + } + if (driver->type() == TIMERINTERRUPT && waveform && once) { + //DIAG(F("DCCTrack::schedulePacket WAVE l=%d d=%x"),packet.length, packet.data[0]); + waveform->schedulePacket(packet); + once=false; + } + } +} diff --git a/DCCTrack.h b/DCCTrack.h new file mode 100644 index 0000000..86ce98d --- /dev/null +++ b/DCCTrack.h @@ -0,0 +1,20 @@ + +#pragma once +#include +#include "DCCPacket.h" +#include "DCCWaveform.h" + +class DCCTrack { + public: + DCCTrack(DCCWaveform *w); + void schedulePacket(const byte buffer[], byte byteCount, byte repeats); + void schedulePacket(dccPacket packet); + inline void addDriver(MotorDriver *m) { mD.push_back(m); }; + static DCCTrack mainTrack; + static DCCTrack progTrack; + private: + DCCWaveform *waveform; + std::vectormD; +}; + + diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 1cf3011..85bf40b 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -22,6 +22,7 @@ #include "defines.h" #include "DCCWaveform.h" +#include "DCCTrack.h" #include "DCCTimer.h" #include "DIAG.h" #include "freeMemory.h" @@ -29,6 +30,9 @@ DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true); DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false); +DCCTrack DCCTrack::mainTrack(&DCCWaveform::mainTrack); +DCCTrack DCCTrack::progTrack(&DCCWaveform::progTrack); + bool DCCWaveform::progTrackSyncMain=false; bool DCCWaveform::progTrackBoosted=false; int DCCWaveform::progTripValue=0; @@ -38,8 +42,6 @@ uint8_t DCCWaveform::trailingEdgeCounter=0; void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { - mainTrack.rmtPin = new RMTChannel(21, 0, PREAMBLE_BITS_MAIN); - mainTrack.motorDriver=mainDriver; progTrack.motorDriver=progDriver; progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static @@ -62,11 +64,11 @@ volatile bool ackflag = 0; void IRAM_ATTR DCCWaveform::loop(bool ackManagerActive) { - if (mainTrack.packetPendingRMT) { - mainTrack.rmtPin->RMTfillData(mainTrack.pendingPacket, mainTrack.pendingLength, mainTrack.pendingRepeats); - mainTrack.packetPendingRMT=false; + //if (mainTrack.packetPendingRMT) { + // mainTrack.rmtPin->RMTfillData(mainTrack.pendingPacket, mainTrack.pendingLength, mainTrack.pendingRepeats); + // mainTrack.packetPendingRMT=false; // sentResetsSincePacket = 0 // later when progtrack - } + //} #ifdef SLOW_ANALOG_READ if (ackflag) { @@ -305,17 +307,17 @@ void IRAM_ATTR DCCWaveform::interrupt2() { // Wait until there is no packet pending, then make this pending void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repeats) { - if (byteCount > MAX_PACKET_SIZE) return; // allow for chksum + if (byteCount > MAX_PACKET_SIZE+1) return; // has chksum while (packetPending); portENTER_CRITICAL(&timerMux); - byte checksum = 0; + //byte checksum = 0; for (byte b = 0; b < byteCount; b++) { - checksum ^= buffer[b]; + //checksum ^= buffer[b]; pendingPacket[b] = buffer[b]; } // buffer is MAX_PACKET_SIZE but pendingPacket is one bigger - pendingPacket[byteCount] = checksum; - pendingLength = byteCount + 1; + //pendingPacket[byteCount] = checksum; + pendingLength = byteCount /*+ 1*/; pendingRepeats = repeats; packetPending = true; packetPendingRMT = true; diff --git a/DCCWaveform.h b/DCCWaveform.h index ef8773a..4f61fa4 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -28,10 +28,8 @@ const int POWER_SAMPLE_ON_WAIT = 100; const int POWER_SAMPLE_OFF_WAIT = 1000; const int POWER_SAMPLE_OVERLOAD_WAIT = 20; -// Number of preamble bits. -const int PREAMBLE_BITS_MAIN = 16; -const int PREAMBLE_BITS_PROG = 22; -const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload size WITHOUT checksum. +//const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload size WITHOUT checksum. +#include "DCCPacket.h" // The WAVE_STATE enum is deliberately numbered because a change of order would be catastrophic // to the transform array. @@ -81,6 +79,9 @@ class DCCWaveform { } return tripmA; } + inline void schedulePacket(dccPacket packet) { + schedulePacket(packet.data, packet.length, packet.repeat); + }; void schedulePacket(const byte buffer[], byte byteCount, byte repeats); volatile bool packetPending; volatile bool packetPendingRMT; @@ -124,7 +125,6 @@ class DCCWaveform { bool isMainTrack; MotorDriver* motorDriver; - RMTChannel* rmtPin; // Transmission controller byte transmitPacket[MAX_PACKET_SIZE+1]; // +1 for checksum byte transmitLength; diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 4fe3dd6..fba9fe3 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "ESP32-motordriver-2021121-22:55" +#define GITHUB_SHA "ESP32-motordriver-2021122-23:21" diff --git a/IO_GPIOBase.h b/IO_GPIOBase.h index 4269a70..3bc75af 100644 --- a/IO_GPIOBase.h +++ b/IO_GPIOBase.h @@ -69,6 +69,10 @@ protected: I2CRB requestBlock; FSH *_deviceName; +#if defined(ARDUINO_ARCH_ESP32) + // workaround: Has somehow no min function for all types + static inline T min(T a, int b) { return a < b ? a : b; }; +#endif }; // Because class GPIOBase is a template, the implementation (below) must be contained within the same @@ -246,4 +250,4 @@ int GPIOBase::_read(VPIN vpin) { return (_portInputState & mask) ? 0 : 1; // Invert state (5v=0, 0v=1) } -#endif \ No newline at end of file +#endif diff --git a/IO_MCP23008.h b/IO_MCP23008.h index 1b8471e..fa19d01 100644 --- a/IO_MCP23008.h +++ b/IO_MCP23008.h @@ -22,12 +22,6 @@ #include "IO_GPIOBase.h" -#if defined(ARDUINO_ARCH_ESP32) // min seems to be missing from that package -#ifndef min -#define min(a,b) ((a)<(b)?(a):(b)) -#endif -#endif - class MCP23008 : public GPIOBase { public: static void create(VPIN firstVpin, uint8_t nPins, uint8_t I2CAddress, int interruptPin=-1) { diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 2675249..cb8e55d 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -31,22 +31,33 @@ bool MotorDriver::usePWM=false; bool MotorDriver::commonFaultPin=false; MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, - byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin) { + byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin, + driverType dt) { + dtype = dt; powerPin=power_pin; getFastPin(F("POWER"),powerPin,fastPowerPin); pinMode(powerPin, OUTPUT); - - signalPin=signal_pin; - getFastPin(F("SIG"),signalPin,fastSignalPin); - pinMode(signalPin, OUTPUT); - - signalPin2=signal_pin2; - if (signalPin2!=UNUSED_PIN) { - dualSignal=true; - getFastPin(F("SIG2"),signalPin2,fastSignalPin2); - pinMode(signalPin2, OUTPUT); + + if (dtype == RMT_MAIN) { + signalPin=signal_pin; +#if defined(ARDUINO_ARCH_ESP32) + rmtChannel = new RMTChannel(signalPin, 0, PREAMBLE_BITS_MAIN); +#endif + dualSignal=false; + } else if (dtype == TIMERINTERRUPT) { + signalPin=signal_pin; + getFastPin(F("SIG"),signalPin,fastSignalPin); + pinMode(signalPin, OUTPUT); + + signalPin2=signal_pin2; + if (signalPin2!=UNUSED_PIN) { + dualSignal=true; + getFastPin(F("SIG2"),signalPin2,fastSignalPin2); + pinMode(signalPin2, OUTPUT); + } else { + dualSignal=false; + } } - else dualSignal=false; brakePin=brake_pin; if (brake_pin!=UNUSED_PIN){ @@ -197,6 +208,21 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res // DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH); } +bool MotorDriver::schedulePacket(dccPacket packet) { + if(!rmtChannel) return true; // fake success if functionality is not there + + outQueue.push(packet); + if (outQueue.size() > 10) { + DIAG(F("Warning: outQueue > 10")); + } + return true; +} + +void MotorDriver::loop() { + if (rmtChannel && !outQueue.empty() && rmtChannel->RMTfillData(outQueue.front())) + outQueue.pop(); +} + MotorDriverContainer::MotorDriverContainer(const FSH * motorShieldName, MotorDriver *m0, MotorDriver *m1, @@ -217,4 +243,15 @@ MotorDriverContainer::MotorDriverContainer(const FSH * motorShieldName, shieldName = (FSH *)motorShieldName; } +void MotorDriverContainer::loop() { + static byte i = 0; + + // loops over MotorDrivers which have loop tasks + if (mD[i]) + if (mD[i]->type() == RMT_MAIN || mD[i]->type() == RMT_PROG) + mD[i]->loop(); + i++; + if(i > 7) i=0; +} + MotorDriverContainer mDC(MOTOR_SHIELD_TYPE); diff --git a/MotorDriver.h b/MotorDriver.h index a4a389d..77f645a 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -16,12 +16,20 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ + #ifndef MotorDriver_h #define MotorDriver_h #include "defines.h" #include "FSH.h" -// Virtualised Motor shield 1-track hardware Interface +#if defined(ARDUINO_ARCH_ESP32) +#include +#include "DCCRMT.h" +#endif + +// Number of preamble bits (moved here so MotorDriver and Waveform know) +const int PREAMBLE_BITS_MAIN = 16; +const int PREAMBLE_BITS_PROG = 22; #ifndef UNUSED_PIN // sync define with the one in MotorDrivers.h #define UNUSED_PIN 127 // inside int8_t @@ -48,10 +56,13 @@ struct FASTPIN { #define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH) #define isLOW(fastpin) (!isHIGH(fastpin)) +enum driverType { TIMERINTERRUPT, RMT_MAIN, RMT_PROG, DC_ENA, DC_BRAKE }; + class MotorDriver { public: MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, - byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin); + byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin, + driverType t=TIMERINTERRUPT); void setPower( bool on); void setSignal( bool high); void setBrake( bool on); @@ -68,6 +79,12 @@ class MotorDriver { inline byte getFaultPin() { return faultPin; } +#if defined(ARDUINO_ARCH_ESP32) + void loop(); + inline driverType type() { return dtype; }; + bool schedulePacket(dccPacket packet); +#endif + private: void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result); void getFastPin(const FSH* type,int pin, FASTPIN & result) { @@ -92,6 +109,11 @@ class MotorDriver { if (doit) __enable_irq(); } #endif +#if defined(ARDUINO_ARCH_ESP32) + RMTChannel* rmtChannel; + std::queue outQueue; + driverType dtype; +#endif }; class MotorDriverContainer { @@ -105,10 +127,15 @@ public: MotorDriver *m5=NULL, MotorDriver *m6=NULL, MotorDriver *m7=NULL); + inline void add(byte n, MotorDriver *m) { + if (n>8) return; + mD[n] = m; + }; // void SetCapability(byte n, byte cap, char [] name); inline FSH *getMotorShieldName() { return shieldName; }; inline MotorDriver *mainTrack() { return mD[0]; }; //start fixed inline MotorDriver *progTrack() { return mD[1]; }; + void loop(); private: MotorDriver *mD[8]; From feebe67ecbaee84a7a7afd5a05d1686ef05b44d1 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 30 Jul 2022 10:40:45 +0200 Subject: [PATCH 58/66] fix compiler Werror --- EEStore.cpp | 4 ++-- I2CManager_Wire.h | 12 +++++------- 2 files changed, 7 insertions(+), 9 deletions(-) diff --git a/EEStore.cpp b/EEStore.cpp index 3a13be7..24be0f8 100644 --- a/EEStore.cpp +++ b/EEStore.cpp @@ -43,7 +43,7 @@ void EEStore::init() { if (strncmp(eeStore->data.id, EESTORE_ID, sizeof(EESTORE_ID)) != 0) { // if not, create blank eeStore structure (no // turnouts, no sensors) and save it back to EEPROM - strncpy(eeStore->data.id, EESTORE_ID, sizeof(EESTORE_ID)); + strncpy(eeStore->data.id, EESTORE_ID, sizeof(EESTORE_ID)+0); eeStore->data.nTurnouts = 0; eeStore->data.nSensors = 0; eeStore->data.nOutputs = 0; @@ -92,7 +92,7 @@ int EEStore::pointer() { return (eeAddress); } /////////////////////////////////////////////////////////////////////////////// void EEStore::dump(int num) { - byte b; + byte b = 0; DIAG(F("Addr 0x char")); for (int n = 0; n < num; n++) { EEPROM.get(n, b); diff --git a/I2CManager_Wire.h b/I2CManager_Wire.h index fb41f86..19e682e 100644 --- a/I2CManager_Wire.h +++ b/I2CManager_Wire.h @@ -98,22 +98,20 @@ uint8_t I2CManagerClass::read(uint8_t address, uint8_t readBuffer[], uint8_t rea * returned in the I2CRB as for the asynchronous version. ***************************************************************************/ void I2CManagerClass::queueRequest(I2CRB *req) { - uint8_t status; switch (req->operation) { case OPERATION_READ: - status = read(req->i2cAddress, req->readBuffer, req->readLen, NULL, 0, req); + req->status = read(req->i2cAddress, req->readBuffer, req->readLen, NULL, 0, req); break; case OPERATION_SEND: - status = write(req->i2cAddress, req->writeBuffer, req->writeLen, req); + req->status = write(req->i2cAddress, req->writeBuffer, req->writeLen, req); break; case OPERATION_SEND_P: - status = write_P(req->i2cAddress, req->writeBuffer, req->writeLen, req); + req->status = write_P(req->i2cAddress, req->writeBuffer, req->writeLen, req); break; case OPERATION_REQUEST: - status = read(req->i2cAddress, req->readBuffer, req->readLen, req->writeBuffer, req->writeLen, req); + req->status = read(req->i2cAddress, req->readBuffer, req->readLen, req->writeBuffer, req->writeLen, req); break; } - req->status = status; } /*************************************************************************** @@ -125,4 +123,4 @@ void I2CManagerClass::loop() {} void I2CManagerClass::checkForTimeout() {} -#endif \ No newline at end of file +#endif From a5d47e0c2ce1a59ca03b90d73d6651e370d52259 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 28 Nov 2021 17:07:21 +0100 Subject: [PATCH 59/66] make mDC a singleton static member of MotorDriverContainer --- DCC.cpp | 18 +++++++++++------- DCCWaveform.cpp | 7 +++++++ MotorDriver.cpp | 11 ++++++++++- MotorDriver.h | 3 +++ 4 files changed, 31 insertions(+), 8 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index fe0fbb4..9f5d53e 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -28,7 +28,6 @@ #include "IODevice.h" #include "MotorDriver.h" -extern MotorDriverContainer mDC; // This module is responsible for converting API calls into // messages to be sent to the waveform generator. @@ -54,7 +53,8 @@ byte DCC::joinRelay=UNUSED_PIN; byte DCC::globalSpeedsteps=128; void DCC::begin() { - StringFormatter::send(Serial,F("\n"), F(VERSION), F(ARDUINO_TYPE), mDC.getMotorShieldName(), F(GITHUB_SHA)); + StringFormatter::send(Serial,F("\n"), F(VERSION), F(ARDUINO_TYPE), + MotorDriverContainer::mDC.getMotorShieldName(), F(GITHUB_SHA)); // Initialise HAL layer before reading EEprom. IODevice::begin(); @@ -63,13 +63,17 @@ void DCC::begin() { (void)EEPROM; // tell compiler not to warn this is unused EEStore::init(); - DCCWaveform::begin(mDC.mainTrack(),mDC.progTrack()); - DCCTrack::mainTrack.addDriver(mDC.mainTrack()); - DCCTrack::progTrack.addDriver(mDC.progTrack()); + DCCWaveform::begin(MotorDriverContainer::mDC.mainTrack(),MotorDriverContainer::mDC.progTrack()); + DCCTrack::mainTrack.addDriver(MotorDriverContainer::mDC.mainTrack()); + DCCTrack::progTrack.addDriver(MotorDriverContainer::mDC.progTrack()); MotorDriver *md; - mDC.add(2, md = new MotorDriver(16, 21, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN)); + MotorDriverContainer::mDC.add(2, md = new MotorDriver(16, 21, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN)); DCCTrack::mainTrack.addDriver(md); + /* + std::vector v = MotorDriverContainer::mDC.getDriverType(RMT_MAIN); + for (const auto& d: v) DCCTrack::mainTrack.addDriver(d); + */ } void DCC::setJoinRelayPin(byte joinRelayPin) { @@ -578,7 +582,7 @@ byte DCC::loopStatus=0; void DCC::loop() { DCCWaveform::loop(ackManagerProg!=NULL); // power overload checks - mDC.loop(); + MotorDriverContainer::mDC.loop(); ackManagerLoop(); // maintain prog track ack manager issueReminders(); } diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 85bf40b..92b98eb 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -27,9 +27,16 @@ #include "DIAG.h" #include "freeMemory.h" +// The two Waveforms which defines what happens when the +// interrupt driven DCC signal is generated. This is tied +// to the timer interrupts of the hardware. DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true); DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false); + +// The two different DCC _kinds_ of signals we want to be able +// to genrate at the same time. When timer interupts are used, +// these need the respective waveform DCCTrack DCCTrack::mainTrack(&DCCWaveform::mainTrack); DCCTrack DCCTrack::progTrack(&DCCWaveform::progTrack); diff --git a/MotorDriver.cpp b/MotorDriver.cpp index cb8e55d..70643e5 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -254,4 +254,13 @@ void MotorDriverContainer::loop() { if(i > 7) i=0; } -MotorDriverContainer mDC(MOTOR_SHIELD_TYPE); +std::vector MotorDriverContainer::getDriverType(driverType t) { + std::vector v; + for(byte i=0; i<8; i++) { + if (mD[i] && mD[i]->type() == t) + v.push_back(mD[i]); + } + return v; +} + +MotorDriverContainer MotorDriverContainer::mDC(MOTOR_SHIELD_TYPE); diff --git a/MotorDriver.h b/MotorDriver.h index 77f645a..c84bdca 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -19,6 +19,7 @@ #ifndef MotorDriver_h #define MotorDriver_h +#include #include "defines.h" #include "FSH.h" @@ -127,6 +128,7 @@ public: MotorDriver *m5=NULL, MotorDriver *m6=NULL, MotorDriver *m7=NULL); + static MotorDriverContainer mDC; inline void add(byte n, MotorDriver *m) { if (n>8) return; mD[n] = m; @@ -136,6 +138,7 @@ public: inline MotorDriver *mainTrack() { return mD[0]; }; //start fixed inline MotorDriver *progTrack() { return mD[1]; }; void loop(); + std::vector getDriverType(driverType t); private: MotorDriver *mD[8]; From 342b9798f0e8af26ad8db59769a9864c8ee752bd Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 24 Nov 2021 09:00:23 +0100 Subject: [PATCH 60/66] define GETFLASHP for pointers --- FSH.h | 2 ++ SSD1306Ascii.cpp | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/FSH.h b/FSH.h index 7961b7c..30f3d79 100644 --- a/FSH.h +++ b/FSH.h @@ -41,6 +41,7 @@ typedef char FSH; #define GETFLASH(addr) (*(const unsigned char *)(addr)) #define GETFLASHW(addr) (*(const unsigned short *)(addr)) +#define GETFLASHP(addr) (*(void * const *)(addr)) #define FLASH #define strlen_P strlen #define strcpy_P strcpy @@ -48,6 +49,7 @@ typedef char FSH; typedef __FlashStringHelper FSH; #define GETFLASH(addr) pgm_read_byte_near(addr) #define GETFLASHW(addr) pgm_read_word_near(addr) +#define GETFLASHP(addr) pgm_read_ptr_near(addr) #define FLASH PROGMEM #endif #endif diff --git a/SSD1306Ascii.cpp b/SSD1306Ascii.cpp index 17fd5a2..348d64f 100644 --- a/SSD1306Ascii.cpp +++ b/SSD1306Ascii.cpp @@ -137,7 +137,7 @@ void SSD1306AsciiWire::begin(const DevType* dev, uint8_t i2cAddr) { m_i2cAddr = i2cAddr; m_col = 0; m_row = 0; - const uint8_t* table = (const uint8_t*)GETFLASHW(&dev->initcmds); + const uint8_t* table = (const uint8_t*)GETFLASHP(&dev->initcmds); uint8_t size = GETFLASH(&dev->initSize); m_displayWidth = GETFLASH(&dev->lcdWidth); m_displayHeight = GETFLASH(&dev->lcdHeight); From 6c940615f6e5d8b95aec68883134e2b817587b83 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 28 Nov 2021 23:36:47 +0100 Subject: [PATCH 61/66] make mDC a vector in the Container and bugfixes --- DCC.cpp | 18 +++++++++-------- DCCTrack.cpp | 2 +- DCCTrack.h | 5 ++++- DCCWaveform.cpp | 32 +++++++++++++++++++------------ MotorDriver.cpp | 51 +++++++++++++++++++++++++++++-------------------- MotorDriver.h | 41 +++++++++++++++++++++++++++++++-------- 6 files changed, 98 insertions(+), 51 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 9f5d53e..6c640fb 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -56,24 +56,26 @@ void DCC::begin() { StringFormatter::send(Serial,F("\n"), F(VERSION), F(ARDUINO_TYPE), MotorDriverContainer::mDC.getMotorShieldName(), F(GITHUB_SHA)); + /* +NOT YES, PIN CONFLICTS // Initialise HAL layer before reading EEprom. IODevice::begin(); + */ + //MotorDriverContainer::mDC.add(new MotorDriver(16, 21, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN)); // Load stuff from EEprom (void)EEPROM; // tell compiler not to warn this is unused EEStore::init(); + MotorDriverContainer::mDC.diag(); DCCWaveform::begin(MotorDriverContainer::mDC.mainTrack(),MotorDriverContainer::mDC.progTrack()); - DCCTrack::mainTrack.addDriver(MotorDriverContainer::mDC.mainTrack()); - DCCTrack::progTrack.addDriver(MotorDriverContainer::mDC.progTrack()); - MotorDriver *md; - MotorDriverContainer::mDC.add(2, md = new MotorDriver(16, 21, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN)); - DCCTrack::mainTrack.addDriver(md); - /* - std::vector v = MotorDriverContainer::mDC.getDriverType(RMT_MAIN); + // Add main and prog drivers to the main and prog packet sources (dcc-tracks). + std::vector v; + v = MotorDriverContainer::mDC.getDriverType(RMT_MAIN|TIMER_MAIN); for (const auto& d: v) DCCTrack::mainTrack.addDriver(d); - */ + v = MotorDriverContainer::mDC.getDriverType(RMT_PROG|TIMER_PROG); + for (const auto& d: v) DCCTrack::progTrack.addDriver(d); } void DCC::setJoinRelayPin(byte joinRelayPin) { diff --git a/DCCTrack.cpp b/DCCTrack.cpp index e0896d0..f2f05a6 100644 --- a/DCCTrack.cpp +++ b/DCCTrack.cpp @@ -29,7 +29,7 @@ void DCCTrack::schedulePacket(dccPacket packet) { //DIAG(F("DCCTrack::schedulePacket RMT l=%d d=%x"),packet.length, packet.data[0]); driver->schedulePacket(packet); } - if (driver->type() == TIMERINTERRUPT && waveform && once) { + if (driver->type() & (TIMER_MAIN | TIMER_PROG) && waveform && once) { //DIAG(F("DCCTrack::schedulePacket WAVE l=%d d=%x"),packet.length, packet.data[0]); waveform->schedulePacket(packet); once=false; diff --git a/DCCTrack.h b/DCCTrack.h index 86ce98d..5a8d3de 100644 --- a/DCCTrack.h +++ b/DCCTrack.h @@ -3,13 +3,16 @@ #include #include "DCCPacket.h" #include "DCCWaveform.h" +#include "DIAG.h" class DCCTrack { public: DCCTrack(DCCWaveform *w); void schedulePacket(const byte buffer[], byte byteCount, byte repeats); void schedulePacket(dccPacket packet); - inline void addDriver(MotorDriver *m) { mD.push_back(m); }; + inline void addDriver(MotorDriver *m) { mD.push_back(m); + DIAG(F("Track: mDType=%d count=%d"),m->type(), mD.size()); + }; static DCCTrack mainTrack; static DCCTrack progTrack; private: diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 92b98eb..22bd42b 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -49,19 +49,27 @@ uint8_t DCCWaveform::trailingEdgeCounter=0; void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) { - mainTrack.motorDriver=mainDriver; - progTrack.motorDriver=progDriver; - progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static - mainTrack.setPowerMode(POWERMODE::OFF); - progTrack.setPowerMode(POWERMODE::OFF); - // Fault pin config for odd motor boards (example pololu) - MotorDriver::commonFaultPin = ((mainDriver->getFaultPin() == progDriver->getFaultPin()) - && (mainDriver->getFaultPin() != UNUSED_PIN)); - // Only use PWM if both pins are PWM capable. Otherwise JOIN does not work - MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable(); - DIAG(F("Signal pin config: %S accuracy waveform"), + if(mainDriver) { + mainTrack.motorDriver=mainDriver; + mainTrack.setPowerMode(POWERMODE::OFF); + } + if(progDriver) { + progTrack.motorDriver=progDriver; + progTripValue = progDriver->mA2raw(TRIP_CURRENT_PROG); // need only calculate once hence static + progTrack.setPowerMode(POWERMODE::OFF); + } + if(mainDriver && progDriver) { + // Fault pin config for odd motor boards (example pololu) + MotorDriver::commonFaultPin = ((mainDriver->getFaultPin() == progDriver->getFaultPin()) + && (mainDriver->getFaultPin() != UNUSED_PIN)); + // Only use PWM if both pins are PWM capable. Otherwise JOIN does not work + MotorDriver::usePWM= mainDriver->isPWMCapable() && progDriver->isPWMCapable(); + } + if(mainDriver || progDriver) { + DIAG(F("Signal pin config: %S accuracy waveform"), MotorDriver::usePWM ? F("high") : F("normal") ); - DCCTimer::begin(DCCWaveform::interruptHandler); + } + DCCTimer::begin(DCCWaveform::interruptHandler); } #ifdef SLOW_ANALOG_READ diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 70643e5..ee878ec 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -44,7 +44,7 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8 rmtChannel = new RMTChannel(signalPin, 0, PREAMBLE_BITS_MAIN); #endif dualSignal=false; - } else if (dtype == TIMERINTERRUPT) { + } else if (dtype & (TIMER_MAIN | TIMER_PROG)) { signalPin=signal_pin; getFastPin(F("SIG"),signalPin,fastSignalPin); pinMode(signalPin, OUTPUT); @@ -212,8 +212,9 @@ bool MotorDriver::schedulePacket(dccPacket packet) { if(!rmtChannel) return true; // fake success if functionality is not there outQueue.push(packet); - if (outQueue.size() > 10) { - DIAG(F("Warning: outQueue > 10")); + uint16_t size = outQueue.size(); + if (size > 10) { + DIAG(F("Warning: outQueue %d > 10"),size); } return true; } @@ -232,33 +233,41 @@ MotorDriverContainer::MotorDriverContainer(const FSH * motorShieldName, MotorDriver *m5, MotorDriver *m6, MotorDriver *m7) { - mD[0]=m0; - mD[1]=m1; - mD[2]=m2; - mD[3]=m3; - mD[4]=m4; - mD[5]=m5; - mD[6]=m6; - mD[7]=m7; + // THIS AUTOMATIC DOES NOT WORK YET. TIMER_MAIN AND TIMER_PROG required in CONSTRUCTOR + // AND CAN NOT BE ADDED LATER + if (m0) { + if (m0->type() == TYPE_UNKNOWN) + m0->setType(TIMER_MAIN); + mD.push_back(m0); + } + if (m1) { + if (m1->type() == TYPE_UNKNOWN) + m1->setType(TIMER_PROG); + mD.push_back(m1); + } + if (m2) mD.push_back(m2); + if (m3) mD.push_back(m3); + if (m4) mD.push_back(m4); + if (m5) mD.push_back(m5); + if (m6) mD.push_back(m6); + if (m7) mD.push_back(m7); shieldName = (FSH *)motorShieldName; } void MotorDriverContainer::loop() { - static byte i = 0; - // loops over MotorDrivers which have loop tasks - if (mD[i]) - if (mD[i]->type() == RMT_MAIN || mD[i]->type() == RMT_PROG) - mD[i]->loop(); - i++; - if(i > 7) i=0; + if (mD.empty()) + return; + for(const auto& d: mD) + if (d->type() & (RMT_MAIN | RMT_PROG)) + d->loop(); } std::vector MotorDriverContainer::getDriverType(driverType t) { std::vector v; - for(byte i=0; i<8; i++) { - if (mD[i] && mD[i]->type() == t) - v.push_back(mD[i]); + for(const auto& d: mD){ + if (d->type() & t) + v.push_back(d); } return v; } diff --git a/MotorDriver.h b/MotorDriver.h index c84bdca..0121e99 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -22,6 +22,7 @@ #include #include "defines.h" #include "FSH.h" +#include "DIAG.h" #if defined(ARDUINO_ARCH_ESP32) #include @@ -57,13 +58,20 @@ struct FASTPIN { #define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH) #define isLOW(fastpin) (!isHIGH(fastpin)) -enum driverType { TIMERINTERRUPT, RMT_MAIN, RMT_PROG, DC_ENA, DC_BRAKE }; +typedef byte driverType; +const driverType TYPE_UNKNOWN=0; +const driverType TIMER_MAIN=1; +const driverType TIMER_PROG=2; +const driverType RMT_MAIN=4; +const driverType RMT_PROG=16; +const driverType DC_ENA=32; +const driverType DC_BRAKE=64; class MotorDriver { public: MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin, - driverType t=TIMERINTERRUPT); + driverType t=TYPE_UNKNOWN); void setPower( bool on); void setSignal( bool high); void setBrake( bool on); @@ -83,6 +91,7 @@ class MotorDriver { #if defined(ARDUINO_ARCH_ESP32) void loop(); inline driverType type() { return dtype; }; + inline void setType(driverType t) { dtype = t; }; bool schedulePacket(dccPacket packet); #endif @@ -129,19 +138,35 @@ public: MotorDriver *m6=NULL, MotorDriver *m7=NULL); static MotorDriverContainer mDC; - inline void add(byte n, MotorDriver *m) { - if (n>8) return; - mD[n] = m; + inline void add(MotorDriver *m) { + mD.push_back(m); + DIAG(F("Container: mDType=%d count=%d"),m->type(), mD.size()); }; // void SetCapability(byte n, byte cap, char [] name); inline FSH *getMotorShieldName() { return shieldName; }; - inline MotorDriver *mainTrack() { return mD[0]; }; //start fixed - inline MotorDriver *progTrack() { return mD[1]; }; + inline void diag() { + if (mD.empty()) { + DIAG(F("Container empty")); + return; + } + for(const auto& d: mD) + DIAG(F("Container: mDType=%d count=%d"),d->type(), mD.size()); + }; + inline MotorDriver *mainTrack() { + std::vector v = getDriverType(TIMER_MAIN); + if(v.empty()) return NULL; + return v.front(); + }; + inline MotorDriver *progTrack() { + std::vector v = getDriverType(TIMER_PROG); + if(v.empty()) return NULL; + return v.front(); + }; void loop(); std::vector getDriverType(driverType t); private: - MotorDriver *mD[8]; + std::vectormD; FSH *shieldName; }; #endif From 237846f190c4ef21480120db950e297dfd0a28e2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 29 Nov 2021 00:14:24 +0100 Subject: [PATCH 62/66] clean up diag, multiple gpio pin test worked --- DCC.cpp | 7 +++---- DCCRMT.cpp | 9 +++++++++ DCCTrack.h | 4 ++-- GITHUB_SHA.h | 2 +- MotorDriver.h | 3 +-- config.example.h | 9 ++++++--- 6 files changed, 22 insertions(+), 12 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 6c640fb..74397ef 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -56,13 +56,12 @@ void DCC::begin() { StringFormatter::send(Serial,F("\n"), F(VERSION), F(ARDUINO_TYPE), MotorDriverContainer::mDC.getMotorShieldName(), F(GITHUB_SHA)); - /* -NOT YES, PIN CONFLICTS + // BE AWARE, USES I2C PINS, MAY LEAD TO PIN CONFLICTS // Initialise HAL layer before reading EEprom. IODevice::begin(); - */ - //MotorDriverContainer::mDC.add(new MotorDriver(16, 21, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN)); + //example how to use add: + //MotorDriverContainer::mDC.add(new MotorDriver(16, 21, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN)); // Load stuff from EEprom (void)EEPROM; // tell compiler not to warn this is unused EEStore::init(); diff --git a/DCCRMT.cpp b/DCCRMT.cpp index 95d0e9a..6a81c25 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -23,6 +23,7 @@ #include "DIAG.h" #include "DCCRMT.h" #include "DCCWaveform.h" // for MAX_PACKET_SIZE +#include "soc/gpio_sig_map.h" #define DATA_LEN(X) ((X)*9+1) // Each byte has one bit extra and we have one EOF marker @@ -104,6 +105,14 @@ RMTChannel::RMTChannel(byte pin, byte ch, byte plen) { // 2 mem block of 64 RMT items should be enough ESP_ERROR_CHECK(rmt_config(&config)); + /* + // test: config another gpio pin + gpio_num_t gpioNum = (gpio_num_t)(pin-1); + PIN_FUNC_SELECT(GPIO_PIN_MUX_REG[gpioNum], PIN_FUNC_GPIO); + gpio_set_direction(gpioNum, GPIO_MODE_OUTPUT); + gpio_matrix_out(gpioNum, RMT_SIG_OUT0_IDX, 0, 0); + */ + // NOTE: ESP_INTR_FLAG_IRAM is *NOT* included in this bitmask ESP_ERROR_CHECK(rmt_driver_install(config.channel, 0, ESP_INTR_FLAG_LOWMED|ESP_INTR_FLAG_SHARED)); diff --git a/DCCTrack.h b/DCCTrack.h index 5a8d3de..6c22ed9 100644 --- a/DCCTrack.h +++ b/DCCTrack.h @@ -10,8 +10,8 @@ class DCCTrack { DCCTrack(DCCWaveform *w); void schedulePacket(const byte buffer[], byte byteCount, byte repeats); void schedulePacket(dccPacket packet); - inline void addDriver(MotorDriver *m) { mD.push_back(m); - DIAG(F("Track: mDType=%d count=%d"),m->type(), mD.size()); + inline void addDriver(MotorDriver *m) { + mD.push_back(m); }; static DCCTrack mainTrack; static DCCTrack progTrack; diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index fba9fe3..3b6ebdf 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "ESP32-motordriver-2021122-23:21" +#define GITHUB_SHA "ESP32-motordriver-2021129-00:12" diff --git a/MotorDriver.h b/MotorDriver.h index 0121e99..da9e0d9 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -140,7 +140,6 @@ public: static MotorDriverContainer mDC; inline void add(MotorDriver *m) { mD.push_back(m); - DIAG(F("Container: mDType=%d count=%d"),m->type(), mD.size()); }; // void SetCapability(byte n, byte cap, char [] name); inline FSH *getMotorShieldName() { return shieldName; }; @@ -150,7 +149,7 @@ public: return; } for(const auto& d: mD) - DIAG(F("Container: mDType=%d count=%d"),d->type(), mD.size()); + DIAG(F("Container: mDType=%d"),d->type()); }; inline MotorDriver *mainTrack() { std::vector v = getDriverType(TIMER_MAIN); diff --git a/config.example.h b/config.example.h index 8452589..5904ad2 100644 --- a/config.example.h +++ b/config.example.h @@ -45,9 +45,12 @@ The configuration file for DCC-EX Command Station // https://randomnerdtutorials.com/esp8266-pinout-reference-gpios/ // 4 high at boot -#define ESP8266_MOTOR_SHIELD F("ESP8266"), \ - new MotorDriver(D3, D5, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.99, 2000, UNUSED_PIN),\ - new MotorDriver(D2, D6, UNUSED_PIN, UNUSED_PIN, A0 , 2.99, 2000, UNUSED_PIN) +// BUG: WE STILL NEED AT LEAST ONE TIMER_* motor shield defined, otherwise the packet scheduling does not work +// BUG: WE DO NOT HAVE ANY RMT_PROG CAPABILITY yet. +#define ESP32_MOTOR_SHIELD F("ESP32"), \ + NULL /*new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 36, 2.00, 2000, UNUSED_PIN, TIMER_MAIN)*/, \ + new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 39, 2.00, 2000, UNUSED_PIN, TIMER_PROG), \ + new MotorDriver(16, 23, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN) // ESP32 ADC1 only supported GPIO pins 32 to 39, for example // ADC1 CH4 = GPIO32, ADC1 CH5 = GPIO33, ADC1 CH0 = GPIO36 From 83300387d25de3a5624c03220e50c18822098eb8 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 30 Jul 2022 15:57:35 +0200 Subject: [PATCH 63/66] working pin assignment in config.example.h --- config.example.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/config.example.h b/config.example.h index 5904ad2..0cf036a 100644 --- a/config.example.h +++ b/config.example.h @@ -62,8 +62,9 @@ The configuration file for DCC-EX Command Station // Adjust conversion factor according to your voltage divider. // #define ESP32_MOTOR_SHIELD F("ESP32"), \ - new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 32, 2.00, 2000, UNUSED_PIN),\ - new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 33, 2.00, 2000, UNUSED_PIN) + NULL /* new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 32, 2.00, 2000, UNUSED_PIN) */ , \ + new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 39, 2.00, 2000, UNUSED_PIN, TIMER_PROG), \ + new MotorDriver(16, 23, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN) #define MOTOR_SHIELD_TYPE ESP32_MOTOR_SHIELD From 37f44709f98f20fd0949a2d46ead0e063fb176d7 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 4 Dec 2021 20:07:34 +0100 Subject: [PATCH 64/66] RMT prog track channel start --- DCCPacket.h | 4 ++++ DCCRMT.cpp | 19 ++++++++++++------- DCCRMT.h | 8 +++++++- MotorDriver.cpp | 2 +- MotorDriver.h | 4 ---- 5 files changed, 24 insertions(+), 13 deletions(-) diff --git a/DCCPacket.h b/DCCPacket.h index 261cff3..9ac480a 100644 --- a/DCCPacket.h +++ b/DCCPacket.h @@ -2,6 +2,10 @@ const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload size WITHOUT checksum. +// Number of preamble bits (moved here so MotorDriver and Waveform know) +const int PREAMBLE_BITS_MAIN = 16; +const int PREAMBLE_BITS_PROG = 22; + class dccPacket { public: byte data[MAX_PACKET_SIZE+1]; // space for checksum if needed diff --git a/DCCRMT.cpp b/DCCRMT.cpp index 6a81c25..e26dd18 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -62,7 +62,7 @@ void IRAM_ATTR interrupt(rmt_channel_t channel, void *t) { tt->RMTinterrupt(); } -RMTChannel::RMTChannel(byte pin, byte ch, byte plen) { +RMTChannel::RMTChannel(byte pin, byte ch, byte plen, bool isMain) { // preamble preambleLen = plen+2; // plen 1 bits, one 0 bit and one EOF marker @@ -79,12 +79,17 @@ RMTChannel::RMTChannel(byte pin, byte ch, byte plen) { // idle idleLen = 28; idle = (rmt_item32_t*)malloc(idleLen*sizeof(rmt_item32_t)); - for (byte n=0; n<8; n++) // 0 to 7 - setDCCBit1(idle + n); - for (byte n=8; n<18; n++) // 8, 9 to 16, 17 - setDCCBit0(idle + n); - for (byte n=18; n<26; n++) // 18 to 25 - setDCCBit1(idle + n); + if (isMain) { + for (byte n=0; n<8; n++) // 0 to 7 + setDCCBit1(idle + n); + for (byte n=8; n<18; n++) // 8, 9 to 16, 17 + setDCCBit0(idle + n); + for (byte n=18; n<26; n++) // 18 to 25 + setDCCBit1(idle + n); + } else { + for (byte n=0; n<26; n++) // all zero + setDCCBit0(idle + n); + } setDCCBit1(idle + 26); // end bit setEOT(idle + 27); // EOT marker diff --git a/DCCRMT.h b/DCCRMT.h index 8d8a93c..94234e7 100644 --- a/DCCRMT.h +++ b/DCCRMT.h @@ -32,7 +32,13 @@ class RMTChannel { public: - RMTChannel(byte pin, byte ch, byte plen); + inline RMTChannel(byte pin, bool isMain) { + if (isMain) + RMTChannel(pin, 0, PREAMBLE_BITS_MAIN, 1); + else + RMTChannel(pin, 2, PREAMBLE_BITS_PROG, 0); + }; + RMTChannel(byte pin, byte ch, byte plen, bool isProg); void IRAM_ATTR RMTinterrupt(); void RMTprefill(); bool RMTfillData(dccPacket packet); diff --git a/MotorDriver.cpp b/MotorDriver.cpp index ee878ec..5f3d68a 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -41,7 +41,7 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8 if (dtype == RMT_MAIN) { signalPin=signal_pin; #if defined(ARDUINO_ARCH_ESP32) - rmtChannel = new RMTChannel(signalPin, 0, PREAMBLE_BITS_MAIN); + rmtChannel = new RMTChannel(signalPin, true); // true: isMain #endif dualSignal=false; } else if (dtype & (TIMER_MAIN | TIMER_PROG)) { diff --git a/MotorDriver.h b/MotorDriver.h index da9e0d9..65f97f2 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -29,10 +29,6 @@ #include "DCCRMT.h" #endif -// Number of preamble bits (moved here so MotorDriver and Waveform know) -const int PREAMBLE_BITS_MAIN = 16; -const int PREAMBLE_BITS_PROG = 22; - #ifndef UNUSED_PIN // sync define with the one in MotorDrivers.h #define UNUSED_PIN 127 // inside int8_t #endif From 67e8c04314a51cd04ed1499d92c1c07cb0cbd88e Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 30 Jul 2022 21:11:51 +0200 Subject: [PATCH 65/66] in principle schedules packets --- DCC.cpp | 8 +++++++- DCCRMT.cpp | 25 ++++++++++++++++++------- DCCRMT.h | 10 ++-------- MotorDriver.cpp | 24 +++++++++++++++++++----- MotorDriver.h | 6 +++++- 5 files changed, 51 insertions(+), 22 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 74397ef..bc9160f 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -71,7 +71,13 @@ void DCC::begin() { // Add main and prog drivers to the main and prog packet sources (dcc-tracks). std::vector v; - v = MotorDriverContainer::mDC.getDriverType(RMT_MAIN|TIMER_MAIN); + + v = MotorDriverContainer::mDC.getDriverType(RMT_MAIN); + + v.front()->setChannel(new RMTChannel(v.front()->getSignalPin(), true)); + + + for (const auto& d: v) DCCTrack::mainTrack.addDriver(d); v = MotorDriverContainer::mDC.getDriverType(RMT_PROG|TIMER_PROG); for (const auto& d: v) DCCTrack::progTrack.addDriver(d); diff --git a/DCCRMT.cpp b/DCCRMT.cpp index e26dd18..1ac1aa4 100644 --- a/DCCRMT.cpp +++ b/DCCRMT.cpp @@ -62,8 +62,17 @@ void IRAM_ATTR interrupt(rmt_channel_t channel, void *t) { tt->RMTinterrupt(); } -RMTChannel::RMTChannel(byte pin, byte ch, byte plen, bool isMain) { - +RMTChannel::RMTChannel(byte pin, bool isMain) { + byte ch; + byte plen; + if (isMain) { + ch = 0; + plen = PREAMBLE_BITS_MAIN; + } else { + ch = 2; + plen = PREAMBLE_BITS_PROG; + } + // preamble preambleLen = plen+2; // plen 1 bits, one 0 bit and one EOF marker preamble = (rmt_item32_t*)malloc(preambleLen*sizeof(rmt_item32_t)); @@ -146,17 +155,19 @@ void RMTChannel::RMTprefill() { const byte transmitMask[] = {0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01}; //bool RMTChannel::RMTfillData(const byte buffer[], byte byteCount, byte repeatCount=0) { -bool RMTChannel::RMTfillData(dccPacket packet) { +int RMTChannel::RMTfillData(dccPacket packet) { // dataReady: Signals to then interrupt routine. It is set when // we have data in the channel buffer which can be copied out // to the HW. dataRepeat on the other hand signals back to // the caller of this function if the data has been sent enough // times (0 to 3 means 1 to 4 times in total). - if (dataReady == true || dataRepeat > 0) // we have still old work to do - return false; + if (dataReady == true) + return 1000; + if (dataRepeat > 0) // we have still old work to do + return dataRepeat; if (DATA_LEN(packet.length) > maxDataLen) { // this would overun our allocated memory for data DIAG(F("Can not convert DCC bytes # %d to DCC bits %d, buffer too small"), packet.length, maxDataLen); - return false; // something very broken, can not convert packet + return -1; // something very broken, can not convert packet } byte *buffer = packet.data; @@ -177,7 +188,7 @@ bool RMTChannel::RMTfillData(dccPacket packet) { dataLen = bitcounter; dataReady = true; dataRepeat = packet.repeat+1; // repeatCount of 0 means send once - return true; + return 0; } void IRAM_ATTR RMTChannel::RMTinterrupt() { diff --git a/DCCRMT.h b/DCCRMT.h index 94234e7..6e75219 100644 --- a/DCCRMT.h +++ b/DCCRMT.h @@ -32,16 +32,10 @@ class RMTChannel { public: - inline RMTChannel(byte pin, bool isMain) { - if (isMain) - RMTChannel(pin, 0, PREAMBLE_BITS_MAIN, 1); - else - RMTChannel(pin, 2, PREAMBLE_BITS_PROG, 0); - }; - RMTChannel(byte pin, byte ch, byte plen, bool isProg); + RMTChannel(byte pin, bool isMain); void IRAM_ATTR RMTinterrupt(); void RMTprefill(); - bool RMTfillData(dccPacket packet); + int RMTfillData(dccPacket packet); //bool RMTfillData(const byte buffer[], byte byteCount, byte repeatCount); static RMTChannel mainRMTChannel; diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 5f3d68a..5b6d6c3 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -38,11 +38,14 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8 getFastPin(F("POWER"),powerPin,fastPowerPin); pinMode(powerPin, OUTPUT); - if (dtype == RMT_MAIN) { + if (dtype & (RMT_MAIN | RMT_PROG)) { signalPin=signal_pin; + /* #if defined(ARDUINO_ARCH_ESP32) - rmtChannel = new RMTChannel(signalPin, true); // true: isMain + //rmtChannel = new RMTChannel(signalPin, 0, PREAMBLE_BITS_MAIN, true); // true: isMain + rmtChannel = new RMTChannel(signalPin, dtype == RMT_MAIN); // true: isMain #endif + */ dualSignal=false; } else if (dtype & (TIMER_MAIN | TIMER_PROG)) { signalPin=signal_pin; @@ -209,7 +212,10 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res } bool MotorDriver::schedulePacket(dccPacket packet) { - if(!rmtChannel) return true; // fake success if functionality is not there + if(!rmtChannel) { + DIAG(F("no rmt Channel")); + return true; // fake success if functionality is not there + } outQueue.push(packet); uint16_t size = outQueue.size(); @@ -220,8 +226,16 @@ bool MotorDriver::schedulePacket(dccPacket packet) { } void MotorDriver::loop() { - if (rmtChannel && !outQueue.empty() && rmtChannel->RMTfillData(outQueue.front())) - outQueue.pop(); + int r; + if (rmtChannel && !outQueue.empty()) { + r = rmtChannel->RMTfillData(outQueue.front()); + if (r == 0) { + DIAG(F("r=OK")); + outQueue.pop(); + } + else + DIAG(F("r=%d"), r); + } } MotorDriverContainer::MotorDriverContainer(const FSH * motorShieldName, diff --git a/MotorDriver.h b/MotorDriver.h index 65f97f2..95d00fe 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -83,12 +83,16 @@ class MotorDriver { static bool commonFaultPin; // This is a stupid motor shield which has only a common fault pin for both outputs inline byte getFaultPin() { return faultPin; - } + }; + inline byte getSignalPin() { + return signalPin; + }; #if defined(ARDUINO_ARCH_ESP32) void loop(); inline driverType type() { return dtype; }; inline void setType(driverType t) { dtype = t; }; bool schedulePacket(dccPacket packet); + inline void setChannel(RMTChannel * r) { rmtChannel = r; }; #endif private: From 7ce74cfdf81ee3e9b1230cd120f2e8204cf51360 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 30 Jul 2022 21:33:25 +0200 Subject: [PATCH 66/66] pin assignment for uno form factor board --- config.example.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/config.example.h b/config.example.h index 0cf036a..c1c0c2d 100644 --- a/config.example.h +++ b/config.example.h @@ -63,8 +63,8 @@ The configuration file for DCC-EX Command Station // #define ESP32_MOTOR_SHIELD F("ESP32"), \ NULL /* new MotorDriver(16, 17, UNUSED_PIN, UNUSED_PIN, 32, 2.00, 2000, UNUSED_PIN) */ , \ - new MotorDriver(18, 19, UNUSED_PIN, UNUSED_PIN, 39, 2.00, 2000, UNUSED_PIN, TIMER_PROG), \ - new MotorDriver(16, 23, UNUSED_PIN, UNUSED_PIN, UNUSED_PIN, 2.00, 2000, UNUSED_PIN, RMT_MAIN) + new MotorDriver(23/*11*/, 18/*13*/, UNUSED_PIN, UNUSED_PIN, 39, 2.00, 2000, UNUSED_PIN, TIMER_PROG), \ + new MotorDriver(25/* 3*/, 19/*12*/, UNUSED_PIN, UNUSED_PIN, 36, 2.00, 2000, UNUSED_PIN, RMT_MAIN) #define MOTOR_SHIELD_TYPE ESP32_MOTOR_SHIELD