1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-26 17:46:14 +01:00

Wifi reliability (#45)

* First pass at wifi inbound FSA
* Fixup detector loop
* Remove asyncBanned
Unused, uninitialized
* Move wifi setup loop out of .ino
Wifi auto detect Serial 1,2,3
* Correct capitalization
* Uno compiles clean
* Command distributor
Moved command execution routing out of Wifi code for future use by Ethernet interface.
Co-authored-by: Fred <fndecker@gmail.com>
This commit is contained in:
Asbelos 2020-10-05 18:42:31 +01:00 committed by GitHub
parent abd83bf7d6
commit 3f06fb08df
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 440 additions and 219 deletions

54
CommandDistributor.cpp Normal file
View File

@ -0,0 +1,54 @@
#include <Arduino.h>
#include "CommandDistributor.h"
#include "WiThrottle.h"
DCCEXParser * CommandDistributor::parser=0;
bool CommandDistributor::parse(byte clientId,byte * buffer, Print * streamer) {
// SIDE EFFECT WARNING:::
// We know that parser will read the entire buffer before starting to write to it.
// Otherwise we would have to copy the buffer elsewhere and RAM is in short supply.
bool closeAfter=false;
// Intercept HTTP requests
if (isHTTP(buffer)) {
if (httpCallback) httpCallback(streamer, buffer);
closeAfter = true;
}
else if (buffer[0] == '<') {
if (!parser) parser = new DCCEXParser();
parser->parse(streamer, buffer, true); // tell JMRI parser that ACKS are blocking because we can't handle the async
}
else WiThrottle::getThrottle(clientId)->parse(*streamer, buffer);
return closeAfter;
}
bool CommandDistributor::isHTTP(byte * buffer) {
// POST GET PUT PATCH DELETE
// You may think a simple strstr() is better... but not when ram & time is in short supply
switch (buffer[0]) {
case 'P':
if (buffer[1] == 'U' && buffer[2] == 'T' && buffer[3] == ' ' ) return true;
if (buffer[1] == 'O' && buffer[2] == 'S' && buffer[3] == 'T' && buffer[4] == ' ') return true;
if (buffer[1] == 'A' && buffer[2] == 'T' && buffer[3] == 'C' && buffer[4] == 'H' && buffer[5] == ' ') return true;
return false;
case 'G':
if (buffer[1] == 'E' && buffer[2] == 'T' && buffer[3] == ' ' ) return true;
return false;
case 'D':
if (buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'E' && buffer[4] == 'T' && buffer[5] == 'E' && buffer[6] == ' ') return true;
return false;
default:
return false;
}
}
void CommandDistributor::setHTTPCallback(HTTP_CALLBACK callback) {
httpCallback = callback;
}
HTTP_CALLBACK CommandDistributor::httpCallback=0;

20
CommandDistributor.h Normal file
View File

@ -0,0 +1,20 @@
#ifndef CommandDistributor_h
#define CommandDistributor_h
#include "DCCEXParser.h"
typedef void (*HTTP_CALLBACK)(Print *stream, byte *cmd);
class CommandDistributor {
public :
static void setHTTPCallback(HTTP_CALLBACK callback);
static bool parse(byte clientId,byte* buffer, Print * streamer);
private:
static HTTP_CALLBACK httpCallback;
static bool isHTTP(byte * buffer);
static DCCEXParser * parser;
};
#endif

View File

@ -68,32 +68,9 @@ void setup()
Serial.begin(115200);
// Start the WiFi interface on a MEGA, Uno cannot currently handle WiFi
// NOTE: References to Serial1 are for the serial port used to connect
// your wifi chip/shield.
#if WIFI_ON
bool wifiUp = false;
const __FlashStringHelper *wifiESSID = F(WIFI_SSID);
const __FlashStringHelper *wifiPassword = F(WIFI_PASSWORD);
const __FlashStringHelper *dccex = F(WIFI_HOSTNAME);
const uint16_t port = IP_PORT;
Serial1.begin(WIFI_SERIAL_LINK_SPEED);
wifiUp = WifiInterface::setup(Serial1, wifiESSID, wifiPassword, dccex, port);
#if NUM_SERIAL > 1
if (!wifiUp)
{
Serial2.begin(WIFI_SERIAL_LINK_SPEED);
wifiUp = WifiInterface::setup(Serial2, wifiESSID, wifiPassword, dccex, port);
}
#if NUM_SERIAL > 2
if (!wifiUp)
{
Serial3.begin(WIFI_SERIAL_LINK_SPEED);
wifiUp = WifiInterface::setup(Serial3, wifiESSID, wifiPassword, dccex, port);
}
#endif // >2
#endif // >1
#ifdef WIFI_ON
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT);
#endif // WIFI_ON
// Responsibility 3: Start the DCC engine.

View File

@ -623,7 +623,7 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[])
// CALLBACKS must be static
bool DCCEXParser::stashCallback(Print *stream, int p[MAX_PARAMS])
{
if (stashBusy || asyncBanned)
if (stashBusy )
return false;
stashBusy = true;
stashStream = stream;

View File

@ -38,7 +38,6 @@ struct DCCEXParser
static const int MAX_BUFFER=50; // longest command sent in
byte bufferLength=0;
bool inCommandPayload=false;
bool asyncBanned; // true when called with stream that must complete before returning
byte buffer[MAX_BUFFER+2];
int splitValues( int result[MAX_PARAMS], const byte * command);

214
WifiInboundHandler.cpp Normal file
View File

@ -0,0 +1,214 @@
#include <Arduino.h>
#include "WifiInboundHandler.h"
#include "CommandDistributor.h"
#include "DIAG.h"
WifiInboundHandler * WifiInboundHandler::singleton;
void WifiInboundHandler::setup(Stream * ESStream) {
singleton=new WifiInboundHandler(ESStream);
}
void WifiInboundHandler::loop() {
singleton->loop1();
}
WifiInboundHandler::WifiInboundHandler(Stream * ESStream) {
wifiStream=ESStream;
for (int clientId=0;clientId<MAX_CLIENTS;clientId++) {
clientStatus[clientId]=UNUSED;
// Note buffer is 1 byte longer than MemStream is told
// so that we can always inject a '\0' at stream->available()
clientBuffer[clientId]=new byte[MAX_WIFI_BUFFER+1];
clientStream[clientId]=new MemStream(clientBuffer[clientId], MAX_WIFI_BUFFER);
}
clientPendingCIPSEND=-1;
}
// Handle any inbound transmission
// +IPD,x,lll:data is stored in streamer[x]
// Other input returns
void WifiInboundHandler::loop1() {
// First handle all inbound traffic events
if (loop2()!=INBOUND_IDLE) return;
// if nothing is already CIPSEND pending, we can CIPSEND one reply
if (clientPendingCIPSEND<0) {
for (int clientId=0;clientId<MAX_CLIENTS;clientId++) {
if (clientStatus[clientId]==REPLY_PENDING) {
clientPendingCIPSEND=clientId;
if (Diag::WIFI) DIAG( F("\nWiFi: [[CIPSEND=%d,%d]]"), clientId, clientStream[clientId]->available());
StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), clientId, clientStream[clientId]->available());
clientStatus[clientId]=CIPSEND_PENDING;
return;
}
}
}
// if something waiting to close we can call one of them
for (int clientId=0;clientId<MAX_CLIENTS;clientId++) {
if (clientStatus[clientId]==CLOSE_AFTER_SEND) {
if (Diag::WIFI) DIAG(F("AT+CIPCLOSE=%d\r\n"), clientId);
StringFormatter::send(wifiStream, F("AT+CIPCLOSE=%d\r\n"), clientId);
clientStatus[clientId]=UNUSED;
return;
}
if (clientStatus[clientId]==READY_TO_PROCESS) {
processCommand(clientId);
return;
}
}
}
// This is a Finite State Automation (FSA) handling the inbound bytes from an ES AT command processor
WifiInboundHandler::INBOUND_STATE WifiInboundHandler::loop2() {
while (wifiStream->available()) {
int ch = wifiStream->read();
// echo the char to the diagnostic stream in escaped format
if (Diag::WIFI) {
// DIAG(F(" %d/"), loopState);
StringFormatter::printEscape(ch); // DIAG in disguise
}
switch (loopState) {
case ANYTHING: // looking for +IPD, > , busy , n,CONNECTED, n,CLOSED
if (ch == '+') {
loopState = IPD;
break;
}
if (ch=='>') {
if (Diag::WIFI) DIAG(F("[[XMIT %d]]"),clientStream[clientPendingCIPSEND]->available());
wifiStream->write(clientBuffer[clientPendingCIPSEND], clientStream[clientPendingCIPSEND]->available());
clientStatus[clientPendingCIPSEND]=clientCloseAfterReply[clientPendingCIPSEND]? CLOSE_AFTER_SEND: UNUSED;
clientPendingCIPSEND=-1;
loopState=SKIPTOEND;
break;
}
if (ch=='R') { // Received ... bytes
loopState=SKIPTOEND;
break;
}
if (ch=='b') { // This is a busy indicator... probabaly must restart a CIPSEND
if (clientPendingCIPSEND>=0) {
clientStatus[clientPendingCIPSEND]=REPLY_PENDING;
clientPendingCIPSEND=-1;
}
loopState=SKIPTOEND;
break;
}
if (ch>='0' && ch<=('0'+MAX_CLIENTS)) {
runningClientId=ch-'0';
loopState=GOT_CLIENT_ID;
break;
}
break;
case IPD: // Looking for I in +IPD
loopState = (ch == 'I') ? IPD1 : SKIPTOEND;
break;
case IPD1: // Looking for P in +IPD
loopState = (ch == 'P') ? IPD2 : SKIPTOEND;
break;
case IPD2: // Looking for D in +IPD
loopState = (ch == 'D') ? IPD3 : SKIPTOEND;
break;
case IPD3: // Looking for , After +IPD
loopState = (ch == ',') ? IPD4_CLIENT : SKIPTOEND;
break;
case IPD4_CLIENT: // reading connection id
if (ch >= '0' || ch <('0'+MAX_CLIENTS)){
runningClientId=ch-'0';
loopState=IPD5;
}
else loopState=SKIPTOEND;
break;
case IPD5: // Looking for , After +IPD,client
loopState = (ch == ',') ? IPD6_LENGTH : SKIPTOEND;
dataLength=0; // ready to start collecting the length
break;
case IPD6_LENGTH: // reading for length
if (ch == ':') {
if (dataLength==0) {
loopState=ANYTHING;
break;
}
clientStream[runningClientId]->flush(); // prepare streamer for input
clientStatus[runningClientId]=INBOUND_ARRIVING;
loopState=IPD_DATA;
break;
}
dataLength = dataLength * 10 + (ch - '0');
break;
case IPD_DATA: // reading data
clientStream[runningClientId]->write(ch); // NOTE: The MemStream will throw away bytes that do not fit in the buffer.
// This protects against buffer overflows even with things as innocent
// as a browser which send massive, irrlevent HTTP headers.
dataLength--;
if (dataLength == 0) {
clientStatus[runningClientId]=READY_TO_PROCESS;
loopState = ANYTHING;
}
break;
case GOT_CLIENT_ID: // got x before CLOSE or CONNECTED
loopState=(ch==',') ? GOT_CLIENT_ID2: SKIPTOEND;
break;
case GOT_CLIENT_ID2: // got "x," before CLOSE or CONNECTED
loopState=(ch=='C') ? GOT_CLIENT_ID3: SKIPTOEND;
break;
case GOT_CLIENT_ID3: // got "x C" before CLOSE or CONNECTED (which is ignored)
if(ch=='L') clientStatus[runningClientId]=UNUSED;
loopState=SKIPTOEND;
break;
case SKIPTOEND: // skipping for /n
if (ch=='\n') loopState=ANYTHING;
break;
} // switch
} // available
return (loopState==ANYTHING) ? INBOUND_IDLE: INBOUND_BUSY;
}
void WifiInboundHandler::processCommand(byte clientId) {
clientStatus[clientId]=PROCESSING;
byte * buffer=clientBuffer[clientId];
MemStream * streamer=clientStream[clientId];
buffer[streamer->available()]='\0';
if (Diag::WIFI) DIAG(F("\n%l Wifi(%d)<-[%e]\n"), millis(),clientId, buffer);
streamer->setBufferContentPosition(0, 0); // reset write position to start of buffer
clientCloseAfterReply[clientId]=CommandDistributor::parse(clientId,buffer,streamer);
if (streamer->available() == 0) {
clientStatus[clientId]=UNUSED;
}
else {
buffer[streamer->available()]='\0'; // mark end of buffer, so it can be used as a string later
if (Diag::WIFI) DIAG(F("%l WiFi(%d)->[%e] l(%d)\n"), millis(), clientId, buffer, streamer->available());
clientStatus[clientId]=REPLY_PENDING;
}
}

72
WifiInboundHandler.h Normal file
View File

@ -0,0 +1,72 @@
#ifndef WifiInboundHandler_h
#define WifiInboundHandler_h
#include "MemStream.h"
#include "DCCEXParser.h"
#include "DIAG.h"
class WifiInboundHandler {
public:
static void setup(Stream * ESStream);
static void loop();
private:
static WifiInboundHandler * singleton;
static const byte MAX_CLIENTS=5;
static const byte MAX_WIFI_BUFFER=255;
enum INBOUND_STATE {
INBOUND_BUSY, // keep calling in loop()
INBOUND_IDLE // Nothing happening, outbound may xcall CIPSEND
};
enum LOOP_STATE {
ANYTHING, // ready for +IPD, n CLOSED, n CONNECTED, busy etc...
SKIPTOEND, // skip to newline
// +IPD,client,length:data
IPD, // got +
IPD1, // got +I
IPD2, // got +IP
IPD3, // got +IPD
IPD4_CLIENT, // got +IPD, reading cient id
IPD5, // got +IPD,c
IPD6_LENGTH, // got +IPD,c, reading length
IPD_DATA, // got +IPD,c,ll,: collecting data
GOT_CLIENT_ID, // clientid prefix to CONNECTED / CLOSED
GOT_CLIENT_ID2, // clientid prefix to CONNECTED / CLOSED
GOT_CLIENT_ID3 // clientid prefix to CONNECTED / CLOSED
};
enum CLIENT_STATUS {
UNUSED, // client slot not in use
INBOUND_ARRIVING, // data is arriving
READY_TO_PROCESS, // data has arrived, may call parser now
PROCESSING, // command in progress
REPLY_PENDING, // reply is ready to CIPSEND
CIPSEND_PENDING, // CIPSEND waiting for >
CLOSE_PENDING, // CLOSE received
CLOSE_AFTER_SEND // Send CLOSE after CIPSEND completed
};
WifiInboundHandler(Stream * ESStream);
void loop1();
INBOUND_STATE loop2();
void processCommand(byte clientId);
Stream * wifiStream;
DCCEXParser *parser;
LOOP_STATE loopState=ANYTHING;
int runningClientId; // latest client inbound processing data or CLOSE
int dataLength; // dataLength of +IPD
byte * clientBuffer[MAX_CLIENTS];
MemStream * clientStream[MAX_CLIENTS];
CLIENT_STATUS clientStatus[MAX_CLIENTS];
bool clientCloseAfterReply[MAX_CLIENTS];
int clientPendingCIPSEND=-1;
};
#endif

View File

@ -23,26 +23,75 @@
#include "DIAG.h"
#include "StringFormatter.h"
#include "WiThrottle.h"
#include "WifiInboundHandler.h"
const char PROGMEM READY_SEARCH[] = "\r\nready\r\n";
const char PROGMEM OK_SEARCH[] = "\r\nOK\r\n";
const char PROGMEM END_DETAIL_SEARCH[] = "@ 1000";
const char PROGMEM PROMPT_SEARCH[] = ">";
const char PROGMEM SEND_OK_SEARCH[] = "\r\nSEND OK\r\n";
const char PROGMEM IPD_SEARCH[] = "+IPD";
const unsigned long LOOP_TIMEOUT = 2000;
bool WifiInterface::connected = false;
bool WifiInterface::closeAfter = false;
DCCEXParser WifiInterface::parser;
byte WifiInterface::loopstate = 0;
unsigned long WifiInterface::loopTimeoutStart = 0;
int WifiInterface::datalength = 0;
int WifiInterface::connectionId;
byte WifiInterface::buffer[MAX_WIFI_BUFFER+1];
MemStream * WifiInterface::streamer;
Stream * WifiInterface::wifiStream = NULL;
HTTP_CALLBACK WifiInterface::httpCallback = 0;
Stream * WifiInterface::wifiStream;
////////////////////////////////////////////////////////////////////////////////
//
// Figure out number of serial ports depending on hardware
//
#if defined(ARDUINO_AVR_UNO)
#define NUM_SERIAL 0
#endif
#if (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560))
#define NUM_SERIAL 3
#endif
#ifndef NUM_SERIAL
#define NUM_SERIAL 1
#endif
bool WifiInterface::setup(long serial_link_speed,
const __FlashStringHelper *wifiESSID,
const __FlashStringHelper *wifiPassword,
const __FlashStringHelper *hostname,
const int port) {
bool wifiUp = false;
#if NUM_SERIAL == 0
// no warning about unused parameters.
(void) serial_link_speed;
(void) wifiESSID;
(void) wifiPassword;
(void) hostname;
(void) port;
#endif
#if NUM_SERIAL > 0
Serial1.begin(serial_link_speed);
wifiUp = setup(Serial1, wifiESSID, wifiPassword, hostname, port);
#endif
// Other serials are tried, depending on hardware.
#if NUM_SERIAL > 1
if (!wifiUp)
{
Serial2.begin(serial_link_speed);
wifiUp = setup(Serial2, wifiESSID, wifiPassword, hostname, port);
}
#endif
#if NUM_SERIAL > 2
if (!wifiUp)
{
Serial3.begin(serial_link_speed);
wifiUp = setup(Serial3, wifiESSID, wifiPassword, hostname, port);
}
#endif
return wifiUp;
}
bool WifiInterface::setup(Stream & setupStream, const __FlashStringHelper* SSid, const __FlashStringHelper* password,
const __FlashStringHelper* hostname, int port) {
@ -59,8 +108,9 @@ bool WifiInterface::setup(Stream & setupStream, const __FlashStringHelper* SSid
StringFormatter::send(wifiStream, F("ATE0\r\n")); // turn off the echo
checkForOK(200, OK_SEARCH, true);
}
streamer=new MemStream(buffer, MAX_WIFI_BUFFER);
parser.setAtCommandCallback(ATCommand);
DCCEXParser::setAtCommandCallback(ATCommand);
WifiInboundHandler::setup(wifiStream);
DIAG(F("\n++ Wifi Setup %S ++\n"), connected ? F("OK") : F("FAILED"));
return connected;
@ -78,7 +128,7 @@ bool WifiInterface::setup2(const __FlashStringHelper* SSid, const __FlashStringH
// If there is, just shortcut the setup and continue to read the data as normal.
if (checkForOK(200,IPD_SEARCH, true)) {
DIAG(F("\nPreconfigured Wifi already running with data waiting\n"));
loopstate=4; // carry on from correct place
// loopstate=4; // carry on from correct place... or not as the case may be
return true;
}
@ -215,9 +265,7 @@ void WifiInterface::ATCommand(const byte * command) {
}
}
void WifiInterface::setHTTPCallback(HTTP_CALLBACK callback) {
httpCallback = callback;
}
bool WifiInterface::checkForOK( const unsigned int timeout, const char * waitfor, bool echo, bool escapeEcho) {
unsigned long startTime = millis();
@ -244,162 +292,10 @@ bool WifiInterface::checkForOK( const unsigned int timeout, const char * waitfor
return false;
}
bool WifiInterface::isHTTP() {
// POST GET PUT PATCH DELETE
// You may think a simple strstr() is better... but not when ram & time is in short supply
switch (buffer[0]) {
case 'P':
if (buffer[1] == 'U' && buffer[2] == 'T' && buffer[3] == ' ' ) return true;
if (buffer[1] == 'O' && buffer[2] == 'S' && buffer[3] == 'T' && buffer[4] == ' ') return true;
if (buffer[1] == 'A' && buffer[2] == 'T' && buffer[3] == 'C' && buffer[4] == 'H' && buffer[5] == ' ') return true;
return false;
case 'G':
if (buffer[1] == 'E' && buffer[2] == 'T' && buffer[3] == ' ' ) return true;
return false;
case 'D':
if (buffer[1] == 'E' && buffer[2] == 'L' && buffer[3] == 'E' && buffer[4] == 'T' && buffer[5] == 'E' && buffer[6] == ' ') return true;
return false;
default:
return false;
}
}
void WifiInterface::loop() {
if (!connected) return;
WiThrottle::loop(); // check heartbeats
// read anything into a buffer, collecting info on the way
while (loopstate != 99 && wifiStream->available()) {
int ch = wifiStream->read();
// echo the char to the diagnostic stream in escaped format
if (Diag::WIFI) StringFormatter::printEscape(ch); // DIAG in disguise
switch (loopstate) {
case 0: // looking for +IPD
connectionId = 0;
if (ch == '+') loopstate = 1;
break;
case 1: // Looking for I in +IPD
loopstate = (ch == 'I') ? 2 : 0;
break;
case 2: // Looking for P in +IPD
loopstate = (ch == 'P') ? 3 : 0;
break;
case 3: // Looking for D in +IPD
loopstate = (ch == 'D') ? 4 : 0;
break;
case 4: // Looking for , After +IPD
loopstate = (ch == ',') ? 5 : 0;
break;
case 5: // reading connection id
if (ch == ',') loopstate = 6;
else connectionId = 10 * connectionId + (ch - '0');
break;
case 6: // reading for length
if (ch == ':') loopstate = (datalength == 0) ? 99 : 7; // 99 is getout without reading next char
else datalength = datalength * 10 + (ch - '0');
streamer->flush(); // basically sets write point at start of buffer
break;
case 7: // reading data
streamer->write(ch); // NOTE: The MemStream will throw away bytes that do not fit in the buffer.
// This protects against buffer overflows even with things as innocent
// as a browser which send massive, irrlevent HTTP headers.
datalength--;
if (datalength == 0) {
buffer[streamer->available()]='\0'; // mark end of buffer, so it can be used as a string later
loopstate = 99;
if (connected) {
WiThrottle::loop();
WifiInboundHandler::loop();
}
break;
case 10: // Waiting for > so we can send reply
if (millis() - loopTimeoutStart > LOOP_TIMEOUT) {
if (Diag::WIFI) DIAG(F("\nWifi TIMEOUT on wait for > prompt or ERROR\n"));
loopstate = 0; // go back to +IPD
break;
}
if (ch == '>') {
// DIAG(F("\n> [%e]\n"),buffer);
wifiStream->print((char *) buffer);
loopTimeoutStart = millis();
loopstate = closeAfter ? 11 : 0;
break;
}
if (ch == '.') { // busy during send, delay and retry
loopstate = 12; // look for SEND OK finished
break;
}
break;
case 11: // Waiting for SEND OK or ERROR to complete so we can closeAfter
if (millis() - loopTimeoutStart > LOOP_TIMEOUT) {
if (Diag::WIFI) DIAG(F("\nWifi TIMEOUT on wait for SEND OK or ERROR\n"));
loopstate = 0; // go back to +IPD
break;
}
if (ch == 'K') { // assume its in SEND OK
if (Diag::WIFI) DIAG(F("\n Wifi AT+CIPCLOSE=%d\r\n"), connectionId);
StringFormatter::send(wifiStream, F("AT+CIPCLOSE=%d\r\n"), connectionId);
loopstate = 0; // wait for +IPD
}
break;
case 12: // Waiting for OK after send busy
if (ch == '+') { // Uh-oh IPD problem
if (Diag::WIFI) DIAG(F("\n\n Wifi ASYNC CLASH - LOST REPLY\n"));
connectionId = 0;
loopstate = 1;
}
if (ch == 'K') { // assume its in SEND OK
if (Diag::WIFI) DIAG(F("\n\n Wifi BUSY RETRYING.. AT+CIPSEND=%d,%d\r\n"), connectionId, streamer->available());
StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), connectionId, streamer->available());
loopTimeoutStart = millis();
loopstate = 10; // non-blocking loop waits for > before sending
break;
}
break;
} // switch
} // while
if (loopstate != 99) return;
// AT this point we have read an incoming message into the buffer
if (Diag::WIFI) DIAG(F("\n%l Wifi(%d)<-[%e]\n"), millis(),connectionId, buffer);
streamer->setBufferContentPosition(0, 0); // reset write position to start of buffer
// SIDE EFFECT WARNING:::
// We know that parser will read the entire buffer before starting to write to it.
// Otherwise we would have to copy the buffer elsewhere and RAM is in short supply.
closeAfter = false;
// Intercept HTTP requests
if (isHTTP()) {
if (httpCallback) httpCallback(streamer, buffer);
else {
StringFormatter::send(streamer, F("HTTP/1.1 404 Not Found\nContent-Type: text/html\nConnnection: close\n\n"));
StringFormatter::send(streamer, F("<html><body>This is <b>not</b> a web server.<br/></body></html>"));
}
closeAfter = true;
}
else if (buffer[0] == '<') parser.parse(streamer, buffer, true); // tell JMRI parser that ACKS are blocking because we can't handle the async
else WiThrottle::getThrottle(connectionId)->parse(*streamer, buffer);
if (streamer->available() == 0) {
// No reply
if (closeAfter) {
if (Diag::WIFI) DIAG(F("AT+CIPCLOSE=%d\r\n"), connectionId);
StringFormatter::send(wifiStream, F("AT+CIPCLOSE=%d\r\n"), connectionId);
}
loopstate = 0; // go back to waiting for +IPD
return;
}
// prepare to send reply
buffer[streamer->available()]='\0'; // mark end of buffer, so it can be used as a string later
if (Diag::WIFI) DIAG(F("%l WiFi(%d)->[%e] l(%d)\n"), millis(), connectionId, buffer, streamer->available());
if (Diag::WIFI) DIAG(F("AT+CIPSEND=%d,%d\r\n"), connectionId, streamer->available());
StringFormatter::send(wifiStream, F("AT+CIPSEND=%d,%d\r\n"), connectionId, streamer->available());
loopTimeoutStart = millis();
loopstate = 10; // non-blocking loop waits for > before sending
}

View File

@ -24,26 +24,26 @@
#include <Arduino.h>
#include <avr/pgmspace.h>
typedef void (*HTTP_CALLBACK)(Print *stream, byte *cmd);
class WifiInterface
{
public:
static bool setup(Stream &setupStream, const __FlashStringHelper *SSSid, const __FlashStringHelper *password,
const __FlashStringHelper *hostname, int port);
static bool setup(long serial_link_speed,
const __FlashStringHelper *wifiESSID,
const __FlashStringHelper *wifiPassword,
const __FlashStringHelper *hostname,
const int port = 2560);
static void loop();
static void ATCommand(const byte *command);
static void setHTTPCallback(HTTP_CALLBACK callback);
private:
static bool setup(Stream &setupStream, const __FlashStringHelper *SSSid, const __FlashStringHelper *password,
const __FlashStringHelper *hostname, int port);
static Stream *wifiStream;
static DCCEXParser parser;
static bool setup2(const __FlashStringHelper *SSSid, const __FlashStringHelper *password,
const __FlashStringHelper *hostname, int port);
static bool checkForOK(const unsigned int timeout, const char *waitfor, bool echo, bool escapeEcho = true);
static bool isHTTP();
static HTTP_CALLBACK httpCallback;
static bool connected;
static bool closeAfter;
static byte loopstate;

View File

@ -36,14 +36,3 @@
// Currently only devices which can communicate at 115200 are supported.
//
#define WIFI_SERIAL_LINK_SPEED 115200
////////////////////////////////////////////////////////////////////////////////
//
// Figure out number of serial ports depending on hardware
//
#if (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560))
#define NUM_SERIAL 3
#endif
#ifndef NUM_SERIAL
#define NUM_SERIAL 1
#endif