From 7397a4089b9fb93a1d9289be1c04170e30e5400f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Sep 2021 00:31:05 +0200 Subject: [PATCH 01/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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/13] 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; }