1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-07-29 18:33:44 +02:00

Compare commits

...

13 Commits

Author SHA1 Message Date
Harald Barth
0bb6b577fa Wifi STA or AP mode 2021-10-01 11:32:09 +02:00
Harald Barth
cf0c818138 Cleanup ESP specific details 2021-10-01 09:09:30 +02:00
Harald Barth
426b27f0dd Reworked use of ringbuffer 2021-09-30 22:55:14 +02:00
Harald Barth
19b4893b5f counter should be int, not uint8_t 2021-09-28 21:08:41 +02:00
Harald Barth
1c7a5320d8 more send diag 2021-09-28 17:31:12 +02:00
Harald Barth
afd4626988 send diag 2021-09-28 17:20:44 +02:00
Harald Barth
a194b8965c Ack read outside interrupt 2021-09-27 20:01:46 +02:00
Harald Barth
696d12fc5e test A0 2021-09-26 11:57:15 +02:00
Harald Barth
35cba02ee7 outboundRing uses sendData 2021-09-26 10:59:07 +02:00
Harald Barth
fa1d1619b6 wifi sendData 2021-09-26 08:37:59 +02:00
Harald Barth
b048879eaa Wifi active 2021-09-25 23:18:10 +02:00
Harald Barth
34474cbf5c WifiESP skeleton files 2021-09-21 09:23:52 +02:00
Harald Barth
7397a4089b first waveform on esp 2021-09-21 00:31:05 +02:00
16 changed files with 473 additions and 56 deletions

View File

@@ -59,6 +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"));
@@ -72,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
#if ETHERNET_ON
EthernetInterface::setup();
#endif // ETHERNET_ON
@@ -111,14 +117,17 @@ 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);
// Responsibility 3: Optionally handle any incoming WiFi traffic
#if WIFI_ON
#ifndef ESP_FAMILY
WifiInterface::loop();
#else
WifiESP::loop();
#endif
#endif //WIFI_ON
#if ETHERNET_ON
EthernetInterface::loop();
#endif
@@ -135,7 +144,9 @@ 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)
{

24
DCC.cpp
View File

@@ -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;

4
DCC.h
View File

@@ -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

View File

@@ -31,6 +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

View File

@@ -17,6 +17,7 @@
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
#include "defines.h"
#include "StringFormatter.h"
#include "DCCEXParser.h"
#include "DCC.h"
@@ -30,7 +31,9 @@
#include "EEStore.h"
#include "DIAG.h"
#ifndef ESP_FAMILY
#include <avr/wdt.h>
#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

View File

@@ -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;
@@ -57,7 +57,7 @@ INTERRUPT_CALLBACK interruptHandler=0;
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,31 @@ void DCCTimer::read(uint8_t word, uint8_t *mac, uint8_t offset) {
}
#endif
#elif defined(ARDUINO_ARCH_ESP8266)
void DCCTimer::begin(INTERRUPT_CALLBACK callback) {
interruptHandler=callback;
timer1_disable();
// 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);
}
// 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
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
@@ -162,7 +187,7 @@ void DCCTimer::read(uint8_t word, uint8_t *mac, uint8_t offset) {
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

View File

@@ -20,6 +20,7 @@
#pragma GCC optimize ("-O3")
#include <Arduino.h>
#include "defines.h"
#include "DCCWaveform.h"
#include "DCCTimer.h"
#include "DIAG.h"
@@ -53,31 +54,46 @@ void DCCWaveform::begin(MotorDriver * mainDriver, MotorDriver * progDriver) {
DCCTimer::begin(DCCWaveform::interruptHandler);
}
void DCCWaveform::loop(bool ackManagerActive) {
#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 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();
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
}
@@ -197,7 +213,7 @@ const bool DCCWaveform::signalTransform[]={
/* WAVE_LOW_0 -> */ LOW,
/* WAVE_PENDING (should not happen) -> */ LOW};
void 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.
@@ -207,7 +223,9 @@ 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.
#ifndef ESP_FAMILY
updateMinimumFreeMemory(22);
#endif
return;
}
@@ -306,7 +324,7 @@ byte DCCWaveform::getAck() {
return(0); // pending set off but not detected means no ACK.
}
void 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;

View File

@@ -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;
@@ -110,7 +105,7 @@ void MotorDriver::setBrake(bool on) {
else setLOW(fastBrakePin);
}
void MotorDriver::setSignal( bool high) {
void IRAM_ATTR MotorDriver::setSignal( bool high) {
if (usePWM) {
DCCTimer::setPWM(signalPin,high);
}
@@ -177,7 +172,7 @@ 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);
PORTTYPE port = digitalPinToPort(pin);
if (input)
result.inout = portInputRegister(port);
else

View File

@@ -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;
@@ -40,12 +42,30 @@ 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);
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);

View File

@@ -45,10 +45,11 @@ size_t RingStream::write(uint8_t b) {
return 1;
}
int RingStream::read() {
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
}

View File

@@ -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;

264
WifiESP.cpp Normal file
View File

@@ -0,0 +1,264 @@
/*
© 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 <https://www.gnu.org/licenses/>.
*/
#include "defines.h"
#ifdef ESP_FAMILY
#include <ESP8266WiFi.h>
#include <ESPAsyncTCP.h>
#include <vector>
#include <string>
#include "WifiESP.h"
#include "DIAG.h"
#include "RingStream.h"
#include "CommandDistributor.h"
#include <string.h>
static std::vector<AsyncClient*> 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; clientId<clients.size(); clientId++){
if (clients[clientId] == client) break;
}
if (clientId < clients.size()) {
byte cmd[len+1];
memcpy(cmd,data,len);
cmd[len]=0;
outboundRing->mark(clientId);
CommandDistributor::parse(clientId,cmd,outboundRing);
outboundRing->commit();
}
}
//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) {
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->connected() && 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;
}
static void deleteClient(AsyncClient* client) {
uint8_t clientId;
for (clientId=0; clientId<clients.size(); clientId++){
if (clients[clientId] == client) break;
}
if (clientId < clients.size()) {
clients[clientId] = NULL;
}
}
static void handleDisconnect(void* arg, AsyncClient* client) {
DIAG(F("Client disconnected"));
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);
}
static void handleNewClient(void* arg, AsyncClient* client) {
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);
client->onDisconnect(&handleDisconnect, NULL);
client->onTimeout(&handleTimeOut, NULL);
}
/* 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 *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
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 AsyncServer(port); // start listening on tcp port
server->onClient(&handleNewClient, server);
server->begin();
DIAG(F("Server up port %d"),port);
return true;
}
void WifiESP::loop() {
AsyncClient *client = NULL;
// Do something with outboundRing
// call sendData
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);
{
char buffer[count+1];
for(int i=0;i<count;i++) {
int c = outboundRing->read();
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);
uint8_t tries = 3;
while (! sendData(client, buffer, count)) {
DIAG(F("senData fail"));
yield();
if (tries == 0) break;
}
}
}
#ifdef ESP_DEBUG
static unsigned long last = 0;
if (millis() - last > 60000) {
last = millis();
DIAG(F("+"));
}
#endif
ESP.wdtFeed();
}
#endif //ESP_FAMILY

37
WifiESP.h Normal file
View File

@@ -0,0 +1,37 @@
/*
* © 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 <https://www.gnu.org/licenses/>.
*/
#ifndef WifiESP_h
#define WifiESP_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

View File

@@ -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, A0 , 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
/////////////////////////////////////////////////////////////////////////////////////
//

View File

@@ -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

View File

@@ -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)
// supported but nothing needed here
#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,18 @@ int minimumFreeMemory() {
return retval;
}
#elif defined(ARDUINO_ARCH_ESP8266)
// ESP8266
static inline int freeMemory() {
return ESP.getFreeHeap();
}
// Return low memory value.
int minimumFreeMemory() {
int retval = minimum_free_memory;
return retval;
}
#else
// All types of TEENSYs
#if defined(ARDUINO_TEENSY40)
static const unsigned DTCM_START = 0x20000000UL;
static const unsigned OCRAM_START = 0x20200000UL;
@@ -108,4 +121,3 @@ void updateMinimumFreeMemory(unsigned char extraBytes) {
if (spare < 0) spare = 0;
if (spare < minimum_free_memory) minimum_free_memory = spare;
}