mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-26 17:46:14 +01:00
Inital integration - StringFormatter to be fixed
This commit is contained in:
parent
4dee6da29e
commit
740ba6859a
73
CVReader.cpp
Normal file
73
CVReader.cpp
Normal file
|
@ -0,0 +1,73 @@
|
||||||
|
/*
|
||||||
|
* © 2020, Gregor Baues, Chris Harlow. All rights reserved.
|
||||||
|
*
|
||||||
|
* This is a basic, no frills CVreader example of a DCC++ compatible setup.
|
||||||
|
* There are more advanced examples in the examples folder i
|
||||||
|
*/
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
// #include "DCCEX.h"
|
||||||
|
#include "MemoryFree.h"
|
||||||
|
#include "DIAG.h"
|
||||||
|
|
||||||
|
#include "NetworkInterface.h"
|
||||||
|
|
||||||
|
// DCCEXParser serialParser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief User define callback for HTTP requests. The Network interface will provide for each http request a parsed request object
|
||||||
|
* and the client who send the request are provided. Its up to the user to use the req as he sees fits. Below is just a scaffold to
|
||||||
|
* demonstrate the workings.
|
||||||
|
*
|
||||||
|
* @param req Parsed request object
|
||||||
|
* @param client Originator of the request to reply to
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
void httpRequestHandler(ParsedRequest *req, Client* client) {
|
||||||
|
DIAG(F("\nParsed Request:"));
|
||||||
|
DIAG(F("\nMethod: [%s]"), req->method);
|
||||||
|
DIAG(F("\nURI: [%s]"), req->uri);
|
||||||
|
DIAG(F("\nHTTP version: [%s]"), req->version);
|
||||||
|
DIAG(F("\nParameter count:[%d]\n"), *req->paramCount);
|
||||||
|
|
||||||
|
// result = doSomething(); // obtain result to be send back; fully prepare the serialized HTTP response!
|
||||||
|
|
||||||
|
// client->write(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void setup()
|
||||||
|
{
|
||||||
|
|
||||||
|
Serial.begin(115200);
|
||||||
|
while (!Serial)
|
||||||
|
{
|
||||||
|
; // wait for serial port to connect. just in case
|
||||||
|
}
|
||||||
|
|
||||||
|
// DCC::begin(STANDARD_MOTOR_SHIELD);
|
||||||
|
DIAG(F("\nFree RAM before network init: [%d]\n"),freeMemory());
|
||||||
|
DIAG(F("\nNetwork Setup In Progress ...\n"));
|
||||||
|
NetworkInterface::setup(WIFI, TCP, 8888); // specify WIFI or ETHERNET depending on if you have Wifi or an EthernetShield; Wifi has to be on Serial1 UDP or TCP for the protocol
|
||||||
|
NetworkInterface::setHttpCallback(httpRequestHandler); // The network interface will provide and HTTP request object which can be used as well to send the reply. cf. example above
|
||||||
|
|
||||||
|
// NetworkInterface::setup(WIFI, MQTT, 8888); // sending over MQTT.
|
||||||
|
// NetworkInterface::setup(WIFI, UDP, 8888); // Setup without port will use the by default port 2560 :: DOES NOT WORK
|
||||||
|
// NetworkInterface::setup(WIFI); // setup without port and protocol will use by default TCP on port 2560
|
||||||
|
// NetworkInterface::setup(); // all defaults ETHERNET, TCP on port 2560
|
||||||
|
|
||||||
|
DIAG(F("\nNetwork Setup done ..."));
|
||||||
|
|
||||||
|
|
||||||
|
DIAG(F("\nFree RAM after network init: [%d]\n"),freeMemory());
|
||||||
|
DIAG(F("\nReady for DCC Commands ..."));
|
||||||
|
}
|
||||||
|
|
||||||
|
void loop()
|
||||||
|
{
|
||||||
|
// DCC::loop();
|
||||||
|
NetworkInterface::loop();
|
||||||
|
|
||||||
|
// serialParser.loop(Serial);
|
||||||
|
}
|
2
DCCEX.h
2
DCCEX.h
|
@ -10,7 +10,7 @@
|
||||||
#include "DCCEXParser.h"
|
#include "DCCEXParser.h"
|
||||||
#include "version.h"
|
#include "version.h"
|
||||||
#include "WifiInterface.h"
|
#include "WifiInterface.h"
|
||||||
#include "EthernetInterface.h"
|
#include "NetworkInterface.h"
|
||||||
#include "LCD_Implementation.h"
|
#include "LCD_Implementation.h"
|
||||||
#include "freeMemory.h"
|
#include "freeMemory.h"
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
|
23
DIAG copy.h
Normal file
23
DIAG copy.h
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* © 2020, Chris Harlow. All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of Asbelos DCC API
|
||||||
|
*
|
||||||
|
* 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 DIAG_h
|
||||||
|
#define DIAG_h
|
||||||
|
#include "StringFormatter.h"
|
||||||
|
#define DIAG StringFormatter::diag
|
||||||
|
#endif
|
|
@ -1,304 +0,0 @@
|
||||||
/*
|
|
||||||
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of DCC-EX/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/>.
|
|
||||||
*
|
|
||||||
* Ethernet Interface added by Gregor Baues
|
|
||||||
*/
|
|
||||||
|
|
||||||
#include "EthernetInterface.h"
|
|
||||||
#include "DIAG.h"
|
|
||||||
#include "StringFormatter.h"
|
|
||||||
|
|
||||||
//#include <SPI.h>
|
|
||||||
#include <Ethernet.h>
|
|
||||||
#include <EthernetUdp.h>
|
|
||||||
|
|
||||||
|
|
||||||
// Support Functions
|
|
||||||
/**
|
|
||||||
* @brief Aquire IP Address from DHCP; if that fails try a statically configured address
|
|
||||||
*
|
|
||||||
* @return true
|
|
||||||
* @return false
|
|
||||||
*/
|
|
||||||
bool EthernetInterface::setupConnection()
|
|
||||||
{
|
|
||||||
|
|
||||||
singleton=this;
|
|
||||||
|
|
||||||
DIAG(F("\nInitialize Ethernet with DHCP:"));
|
|
||||||
server = EthernetServer(LISTEN_PORT); // Ethernet Server listening on default port LISTEN_PORT
|
|
||||||
ip = IPAddress(IP_ADDRESS); // init with fixed IP address needed to get to the server
|
|
||||||
connected = false; // Connection status
|
|
||||||
streamer= new MemStream(buffer, MAX_ETH_BUFFER, MAX_ETH_BUFFER, true); // streamer who writes the results to the buffer
|
|
||||||
|
|
||||||
if (Ethernet.begin(EthernetInterface::mac) == 0)
|
|
||||||
{
|
|
||||||
DIAG(F("\nFailed to configure Ethernet using DHCP ... Trying with fixed IP"));
|
|
||||||
Ethernet.begin(EthernetInterface::mac, EthernetInterface::ip); // default ip address
|
|
||||||
|
|
||||||
if (Ethernet.hardwareStatus() == EthernetNoHardware)
|
|
||||||
{
|
|
||||||
DIAG(F("\nEthernet shield was not found. Sorry, can't run without hardware. :("));
|
|
||||||
return false;
|
|
||||||
};
|
|
||||||
if (Ethernet.linkStatus() == LinkOFF)
|
|
||||||
{
|
|
||||||
DIAG(F("\nEthernet cable is not connected."));
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
ip = Ethernet.localIP(); // reassign the obtained ip address
|
|
||||||
|
|
||||||
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
|
|
||||||
DIAG(F("\nListening on port: [%d]"), port);
|
|
||||||
dnsip = Ethernet.dnsServerIP();
|
|
||||||
DIAG(F("\nDNS server IP address: [%d.%d.%d.%d] "), ip[0], ip[1], ip[2], ip[3]);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handles command requests recieved via UDP. UDP is a connection less, unreliable protocol as it doesn't maintain state but fast.
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void EthernetInterface::udpHandler() {
|
|
||||||
singleton->udpHandler2();
|
|
||||||
}
|
|
||||||
void EthernetInterface::udpHandler2()
|
|
||||||
{
|
|
||||||
|
|
||||||
int packetSize = Udp.parsePacket();
|
|
||||||
if (packetSize)
|
|
||||||
{
|
|
||||||
DIAG(F("\nReceived packet of size:[%d]\n"), packetSize);
|
|
||||||
IPAddress remote = Udp.remoteIP();
|
|
||||||
DIAG(F("From: [%d.%d.%d.%d:"), remote[0], remote[1], remote[2], remote[3]);
|
|
||||||
char portBuffer[6];
|
|
||||||
DIAG(F("%s]\n"), utoa(Udp.remotePort(), portBuffer, 10)); // DIAG has issues with unsigend int's so go through utoa
|
|
||||||
|
|
||||||
// read the packet into packetBufffer
|
|
||||||
Udp.read(packetBuffer, UDP_TX_PACKET_MAX_SIZE);
|
|
||||||
|
|
||||||
DIAG(F("Command: [%s]\n"), packetBuffer);
|
|
||||||
|
|
||||||
streamer->flush();
|
|
||||||
|
|
||||||
Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());
|
|
||||||
|
|
||||||
ethParser.parse(streamer, (byte *)packetBuffer, true); // set to true so it is sync cf. WifiInterface
|
|
||||||
|
|
||||||
if (streamer->available() == 0)
|
|
||||||
{
|
|
||||||
DIAG(F("\nNo response\n"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
// send the reply
|
|
||||||
DIAG(F("Response: %s\n"), (char *)buffer);
|
|
||||||
Udp.write((char *)buffer);
|
|
||||||
Udp.endPacket();
|
|
||||||
}
|
|
||||||
|
|
||||||
memset(packetBuffer, 0, UDP_TX_PACKET_MAX_SIZE); // reset PacktBuffer
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Handles command requests recieved via TCP. Supports up to the max# of simultaneous requests which is 8. The connection gets closed as soon as we finished processing
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void EthernetInterface::tcpHandler()
|
|
||||||
{
|
|
||||||
singleton->tcpHandler2();
|
|
||||||
}
|
|
||||||
void EthernetInterface::tcpHandler2()
|
|
||||||
{
|
|
||||||
// get client from the server
|
|
||||||
EthernetClient client = getServer().accept();
|
|
||||||
|
|
||||||
// check for new client
|
|
||||||
if (client)
|
|
||||||
{
|
|
||||||
for (byte i = 0; i < MAX_SOCK_NUM; i++)
|
|
||||||
{
|
|
||||||
if (!clients[i])
|
|
||||||
{
|
|
||||||
// On accept() the EthernetServer doesn't track the client anymore
|
|
||||||
// so we store it in our client array
|
|
||||||
clients[i] = client;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check for incoming data from all possible clients
|
|
||||||
for (byte i = 0; i < MAX_SOCK_NUM; i++)
|
|
||||||
{
|
|
||||||
if (clients[i] && clients[i].available() > 0)
|
|
||||||
{
|
|
||||||
// read bytes from a client
|
|
||||||
int count = clients[i].read(buffer, MAX_ETH_BUFFER);
|
|
||||||
buffer[count] = '\0'; // terminate the string properly
|
|
||||||
DIAG(F("\nReceived packet of size:[%d]\n"), count);
|
|
||||||
DIAG(F("From Client #: [%d]\n"), i);
|
|
||||||
DIAG(F("Command: [%s]\n"), buffer);
|
|
||||||
|
|
||||||
// as we use buffer for recv and send we have to reset the write position
|
|
||||||
streamer->setBufferContentPosition(0, 0);
|
|
||||||
|
|
||||||
ethParser.parse(streamer, buffer, true); // set to true to that the execution in DCC is sync
|
|
||||||
|
|
||||||
if (streamer->available() == 0)
|
|
||||||
{
|
|
||||||
DIAG(F("No response\n"));
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buffer[streamer->available()] = '\0'; // mark end of buffer, so it can be used as a string later
|
|
||||||
DIAG(F("Response: %s\n"), (char *)buffer);
|
|
||||||
if (clients[i].connected())
|
|
||||||
{
|
|
||||||
clients[i].write(buffer, streamer->available());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// stop any clients which disconnect
|
|
||||||
for (byte i = 0; i < MAX_SOCK_NUM; i++)
|
|
||||||
{
|
|
||||||
if (clients[i] && !clients[i].connected())
|
|
||||||
{
|
|
||||||
DIAG(F("Disconnect client #%d \n"), i);
|
|
||||||
clients[i].stop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Class Functions
|
|
||||||
/**
|
|
||||||
* @brief Setup Ethernet Connection
|
|
||||||
*
|
|
||||||
* @param pt Protocol used
|
|
||||||
* @param localPort Port number for the connection
|
|
||||||
*/
|
|
||||||
void EthernetInterface::setup(protocolType pt, uint16_t localPort)
|
|
||||||
{
|
|
||||||
DIAG(F("\n++++++ Ethernet Setup In Progress ++++++++\n"));
|
|
||||||
port = localPort;
|
|
||||||
if (setupConnection())
|
|
||||||
{
|
|
||||||
DIAG(F("\nProtocol: [%s]\n"), pt ? "UDP" : "TCP");
|
|
||||||
switch (pt)
|
|
||||||
{
|
|
||||||
case UDP:
|
|
||||||
{
|
|
||||||
if (Udp.begin(localPort))
|
|
||||||
{
|
|
||||||
connected = true;
|
|
||||||
protocolHandler = udpHandler;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
DIAG(F("\nUDP client failed to start"));
|
|
||||||
connected = false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
case TCP:
|
|
||||||
{
|
|
||||||
Ethernet.begin(mac, ip);
|
|
||||||
EthernetServer server(localPort);
|
|
||||||
setServer(server);
|
|
||||||
server.begin();
|
|
||||||
connected = true;
|
|
||||||
protocolHandler = tcpHandler;
|
|
||||||
break;
|
|
||||||
};
|
|
||||||
default:
|
|
||||||
{
|
|
||||||
DIAG(F("Unkown Ethernet protocol; Setup failed"));
|
|
||||||
connected = false;
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
connected = false;
|
|
||||||
};
|
|
||||||
DIAG(F("\n++++++ Ethernet Setup %S ++++++++\n"), connected ? F("OK") : F("FAILED"));
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Setup Ethernet on default port and user choosen protocol
|
|
||||||
*
|
|
||||||
* @param pt Protocol UDP or TCP
|
|
||||||
*/
|
|
||||||
void EthernetInterface::setup(protocolType pt)
|
|
||||||
{
|
|
||||||
setup(pt, LISTEN_PORT);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Ethernet setup with defaults TCP / Listen Port
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void EthernetInterface::setup()
|
|
||||||
{
|
|
||||||
setup(TCP, LISTEN_PORT);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Main loop for the EthernetInterface
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
void EthernetInterface::loop()
|
|
||||||
{
|
|
||||||
switch (Ethernet.maintain())
|
|
||||||
{
|
|
||||||
case 1:
|
|
||||||
//renewed fail
|
|
||||||
DIAG(F("\nError: renewed fail"));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 2:
|
|
||||||
//renewed success
|
|
||||||
DIAG(F("\nRenewed success: "));
|
|
||||||
ip = Ethernet.localIP(); // reassign the obtained ip address
|
|
||||||
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"),ip[0], ip[1], ip[2], ip[3]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 3:
|
|
||||||
//rebind fail
|
|
||||||
DIAG(F("Error: rebind fail"));
|
|
||||||
break;
|
|
||||||
|
|
||||||
case 4:
|
|
||||||
//rebind success
|
|
||||||
DIAG(F("Rebind success"));
|
|
||||||
ip = Ethernet.localIP(); // reassign the obtained ip address
|
|
||||||
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
//nothing happened
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
protocolHandler();
|
|
||||||
}
|
|
|
@ -1,107 +0,0 @@
|
||||||
/*
|
|
||||||
* © 2020,Gregor Baues, Chris Harlow. All rights reserved.
|
|
||||||
*
|
|
||||||
* This file is part of DCC-EX/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/>.
|
|
||||||
*
|
|
||||||
* Ethernet Interface added by Gregor Baues
|
|
||||||
*/
|
|
||||||
|
|
||||||
#ifndef EthernetInterface_h
|
|
||||||
#define EthernetInterface_h
|
|
||||||
|
|
||||||
#include "DCCEXParser.h"
|
|
||||||
#include "MemStream.h"
|
|
||||||
#include <Arduino.h>
|
|
||||||
#include <avr/pgmspace.h>
|
|
||||||
#include <Ethernet.h>
|
|
||||||
|
|
||||||
/* some generated mac addresses as EthernetShields don't have one by default in HW.
|
|
||||||
* Sometimes they come on a sticker on the EthernetShield then use this address otherwise
|
|
||||||
* just choose one from below or generate one yourself. Only condition is that there is no
|
|
||||||
* other device on your network with the same Mac address.
|
|
||||||
*
|
|
||||||
* 52:b8:8a:8e:ce:21
|
|
||||||
* e3:e9:73:e1:db:0d
|
|
||||||
* 54:2b:13:52:ac:0c
|
|
||||||
* c2:d8:d4:7d:7c:cb
|
|
||||||
* 86:cf:fa:9f:07:79
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Network Configuration
|
|
||||||
*
|
|
||||||
*/
|
|
||||||
#define MAC_ADDRESS { 0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21 } // MAC address of your networking card found on the sticker on your card or take one from above
|
|
||||||
#define IP_ADDRESS 10, 0, 0, 101 // Just in case we don't get an adress from DHCP try a static one; make sure
|
|
||||||
// this one is not used elsewhere and corresponds to your network layout
|
|
||||||
#define LISTEN_PORT 3366 // default listen port for the server
|
|
||||||
#define MAX_ETH_BUFFER 250
|
|
||||||
|
|
||||||
typedef void (*HTTP_CALLBACK)(Print * stream, byte * cmd);
|
|
||||||
|
|
||||||
enum protocolType {
|
|
||||||
TCP,
|
|
||||||
UDP
|
|
||||||
};
|
|
||||||
|
|
||||||
typedef void (*protocolCallback)();
|
|
||||||
|
|
||||||
class EthernetInterface {
|
|
||||||
|
|
||||||
private:
|
|
||||||
EthernetServer server;
|
|
||||||
|
|
||||||
public:
|
|
||||||
DCCEXParser ethParser;
|
|
||||||
bool connected;
|
|
||||||
byte mac[6];
|
|
||||||
IPAddress ip;
|
|
||||||
uint16_t port;
|
|
||||||
IPAddress dnsip;
|
|
||||||
|
|
||||||
void setup(protocolType pt, uint16_t lp); // specific port nummber
|
|
||||||
void setup(protocolType pt); // uses default port number
|
|
||||||
void setup(); // all defaults (protocol/port)
|
|
||||||
|
|
||||||
protocolCallback protocolHandler;
|
|
||||||
|
|
||||||
void loop();
|
|
||||||
|
|
||||||
private:
|
|
||||||
static EthernetInterface * singleton;
|
|
||||||
|
|
||||||
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; // buffer to hold incoming UDP packet,
|
|
||||||
uint8_t buffer[MAX_ETH_BUFFER]; // buffer provided to the streamer to be filled with the reply (used by TCP also for the recv)
|
|
||||||
MemStream * streamer; // streamer who writes the results to the buffer
|
|
||||||
EthernetClient clients[MAX_SOCK_NUM]; // accept up to MAX_SOCK_NUM client connections at the same time; This depends on the chipset used on the Shield
|
|
||||||
|
|
||||||
bool setupConnection();
|
|
||||||
static void udpHandler();
|
|
||||||
static void tcpHandler();
|
|
||||||
void udpHandler2();
|
|
||||||
void tcpHandler2();
|
|
||||||
EthernetUDP Udp;
|
|
||||||
|
|
||||||
EthernetServer getServer() {
|
|
||||||
return server;
|
|
||||||
};
|
|
||||||
void setServer(EthernetServer s) {
|
|
||||||
server = s;
|
|
||||||
};
|
|
||||||
};
|
|
||||||
|
|
||||||
#endif
|
|
113
EthernetSetup.cpp
Normal file
113
EthernetSetup.cpp
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
/*
|
||||||
|
* © 2020 Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 <Arduino.h>
|
||||||
|
#include "DIAG.h"
|
||||||
|
#include "EthernetSetup.h"
|
||||||
|
|
||||||
|
EthernetServer* EthernetSetup::setup()
|
||||||
|
{
|
||||||
|
|
||||||
|
DIAG(F("\nInitialize Ethernet with DHCP"));
|
||||||
|
if (Ethernet.begin(mac) == 0)
|
||||||
|
{
|
||||||
|
DIAG(F("\nFailed to configure Ethernet using DHCP ... Trying with fixed IP"));
|
||||||
|
Ethernet.begin(mac, IPAddress(IP_ADDRESS)); // default ip address
|
||||||
|
|
||||||
|
if (Ethernet.hardwareStatus() == EthernetNoHardware)
|
||||||
|
{
|
||||||
|
DIAG(F("\nEthernet shield was not found. Sorry, can't run without hardware. :("));
|
||||||
|
return 0;
|
||||||
|
};
|
||||||
|
if (Ethernet.linkStatus() == LinkOFF)
|
||||||
|
{
|
||||||
|
DIAG(F("\nEthernet cable is not connected."));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
maxConnections = MAX_SOCK_NUM;
|
||||||
|
|
||||||
|
if (Ethernet.hardwareStatus() == EthernetW5100)
|
||||||
|
{
|
||||||
|
DIAG(F("\nW5100 Ethernet controller detected."));
|
||||||
|
maxConnections = 4; // Max supported officaly by the W5100 but i have been running over 8 as well. Perf has to be evaluated though comparing 4 vs. 8 connections
|
||||||
|
}
|
||||||
|
else if (Ethernet.hardwareStatus() == EthernetW5200)
|
||||||
|
{
|
||||||
|
DIAG(F("\nW5200 Ethernet controller detected."));
|
||||||
|
maxConnections = 8;
|
||||||
|
}
|
||||||
|
else if (Ethernet.hardwareStatus() == EthernetW5500)
|
||||||
|
{
|
||||||
|
DIAG(F("W5500 Ethernet controller detected."));
|
||||||
|
maxConnections = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIAG(F("\nNetwork Protocol: [%s]"), protocol ? "UDP" : "TCP");
|
||||||
|
switch (protocol)
|
||||||
|
{
|
||||||
|
case UDP:
|
||||||
|
{
|
||||||
|
if (udp.begin(port))
|
||||||
|
{
|
||||||
|
maxConnections = 1; // there is only one UDP object listening for incomming data
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DIAG(F("\nUDP client failed to start"));
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case TCP:
|
||||||
|
{
|
||||||
|
server = new EthernetServer(port);
|
||||||
|
server->begin();
|
||||||
|
connected = true;
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case MQTT:
|
||||||
|
{
|
||||||
|
// do the MQTT setup stuff ...
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
DIAG(F("\nUnkown Ethernet protocol; Setup failed"));
|
||||||
|
connected = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (connected)
|
||||||
|
{
|
||||||
|
ip = Ethernet.localIP();
|
||||||
|
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
|
||||||
|
DIAG(F("\nListening on port: [%d]"), port);
|
||||||
|
dnsip = Ethernet.dnsServerIP();
|
||||||
|
DIAG(F("\nDNS server IP address: [%d.%d.%d.%d] "), dnsip[0], dnsip[1], dnsip[2], dnsip[3]);
|
||||||
|
DIAG(F("\nNumber of connections: [%d]"), maxConnections);
|
||||||
|
if( protocol == UDP ) return 0; // no server here as we use UDB
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
EthernetSetup::EthernetSetup() {}
|
||||||
|
EthernetSetup::EthernetSetup(uint16_t p, protocolType pt ) { port = p; protocol = pt; }
|
||||||
|
EthernetSetup::~EthernetSetup() {}
|
||||||
|
|
41
EthernetSetup.h
Normal file
41
EthernetSetup.h
Normal file
|
@ -0,0 +1,41 @@
|
||||||
|
/*
|
||||||
|
* © 2020 Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 EthernetSetup_h
|
||||||
|
#define EthernetSetup_h
|
||||||
|
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include "NetworkSetup.h"
|
||||||
|
|
||||||
|
class EthernetSetup: public NetworkSetup {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
EthernetServer* server;
|
||||||
|
EthernetUDP udp;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// EthernetServer *setup(uint16_t port);
|
||||||
|
EthernetServer *setup();
|
||||||
|
|
||||||
|
EthernetSetup();
|
||||||
|
EthernetSetup(uint16_t port, protocolType protocol);
|
||||||
|
~EthernetSetup();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
432
HttpRequest.cpp
Normal file
432
HttpRequest.cpp
Normal file
|
@ -0,0 +1,432 @@
|
||||||
|
/*
|
||||||
|
* © 2012 Francisco G. Paletta, © 2020 Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 "Arduino.h"
|
||||||
|
#include "HttpRequest.h"
|
||||||
|
#include "NetworkInterface.h"
|
||||||
|
#include "DIAG.h"
|
||||||
|
|
||||||
|
// public interface to the parsed request
|
||||||
|
// static ParsedRequest req;
|
||||||
|
|
||||||
|
HttpRequest::HttpRequest()
|
||||||
|
{
|
||||||
|
resetRequest();
|
||||||
|
req.method = method;
|
||||||
|
req.uri = uri;
|
||||||
|
req.version = version;
|
||||||
|
req.paramCount = ¶mCount;
|
||||||
|
/**
|
||||||
|
* @todo add list of parameters
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
}
|
||||||
|
|
||||||
|
ParsedRequest HttpRequest::getParsedRequest()
|
||||||
|
{
|
||||||
|
return req;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpRequest::resetRequest()
|
||||||
|
{
|
||||||
|
|
||||||
|
freeParamMem(firstParam);
|
||||||
|
freeCookieMem(firstCookie);
|
||||||
|
|
||||||
|
parseStatus = HTTP_PARSE_INIT;
|
||||||
|
method[0] = '\0';
|
||||||
|
uri[0] = '\0';
|
||||||
|
version[0] = '\0';
|
||||||
|
firstParam = NULL;
|
||||||
|
firstCookie = NULL;
|
||||||
|
paramCount = 0;
|
||||||
|
cookieCount = 0;
|
||||||
|
tmpParamName[0] = '\0';
|
||||||
|
tmpParamValue[0] = '\0';
|
||||||
|
tmpAttribName[0] = '\0';
|
||||||
|
tmpAttribValue[0] = '\0';
|
||||||
|
tmpCookieName[0] = '\0';
|
||||||
|
tmpCookieValue[0] = '\0';
|
||||||
|
dataBlockLength = 0;
|
||||||
|
dataCount = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpRequest::freeParamMem(Params *paramNode)
|
||||||
|
{
|
||||||
|
if (paramNode != NULL)
|
||||||
|
{
|
||||||
|
freeParamMem(paramNode->next);
|
||||||
|
delete paramNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpRequest::freeCookieMem(Cookies *cookieNode)
|
||||||
|
{
|
||||||
|
if (cookieNode != NULL)
|
||||||
|
{
|
||||||
|
freeCookieMem(cookieNode->next);
|
||||||
|
delete cookieNode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpRequest::parseRequest(char c)
|
||||||
|
{
|
||||||
|
|
||||||
|
char cStr[2];
|
||||||
|
cStr[0] = c;
|
||||||
|
cStr[1] = '\0';
|
||||||
|
switch (parseStatus)
|
||||||
|
{
|
||||||
|
|
||||||
|
case HTTP_METHOD:
|
||||||
|
if (c == ' ')
|
||||||
|
parseStatus = HTTP_URI;
|
||||||
|
else if (strlen(method) < HTTP_REQ_METHOD_LENGTH - 1)
|
||||||
|
strcat(method, cStr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_URI:
|
||||||
|
// DIAG(F("HTTP_URI: %c\n"), c);
|
||||||
|
if (c == ' ')
|
||||||
|
parseStatus = HTTP_VERSION;
|
||||||
|
else if (c == '?')
|
||||||
|
parseStatus = HTTP_GET_NAME;
|
||||||
|
else if (strlen(uri) < HTTP_REQ_URI_LENGTH - 1)
|
||||||
|
strcat(uri, cStr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_GET_NAME:
|
||||||
|
// DIAG(F("HTTP_GET_NAME: %c\n"), c);
|
||||||
|
if (c == ' ')
|
||||||
|
parseStatus = HTTP_VERSION;
|
||||||
|
else if (c != '=')
|
||||||
|
{
|
||||||
|
if (strlen(tmpParamName) < HTTP_REQ_PARAM_NAME_LENGTH - 1)
|
||||||
|
strcat(tmpParamName, cStr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
parseStatus = HTTP_GET_VALUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_GET_VALUE:
|
||||||
|
// DIAG(F("HTTP_GET_VALUE: %c\n"), c);
|
||||||
|
if (c == '&')
|
||||||
|
{
|
||||||
|
addParam();
|
||||||
|
parseStatus = HTTP_GET_NAME;
|
||||||
|
}
|
||||||
|
else if (c == ' ')
|
||||||
|
{
|
||||||
|
addParam();
|
||||||
|
parseStatus = HTTP_VERSION;
|
||||||
|
}
|
||||||
|
else if (strlen(tmpParamValue) < HTTP_REQ_PARAM_VALUE_LENGTH - 1)
|
||||||
|
strcat(tmpParamValue, cStr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_VERSION:
|
||||||
|
// DIAG(F("HTTP_VERSION: %c\n"), c);
|
||||||
|
if (c == '\n')
|
||||||
|
parseStatus = HTTP_NEW_LINE;
|
||||||
|
else if (c != '\r' && strlen(version) < HTTP_REQ_VERSION_LENGTH - 1)
|
||||||
|
strcat(version, cStr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_NEW_LINE:
|
||||||
|
// DIAG(F("HTTP_NEW_LINE: %c\n"), c);
|
||||||
|
if (c != '\r' && c != '\n')
|
||||||
|
{
|
||||||
|
parseStatus = HTTP_ATTRIB_NAME;
|
||||||
|
tmpAttribName[0] = '\0';
|
||||||
|
tmpAttribValue[0] = '\0';
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
if (strcmp(method, "POST") == 0 && dataBlockLength > 0)
|
||||||
|
parseStatus = HTTP_POST_NAME;
|
||||||
|
else
|
||||||
|
// DIAG(F("HTTP_REQUEST_END: %c\n"), c);
|
||||||
|
parseStatus = HTTP_REQUEST_END;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_ATTRIB_NAME:
|
||||||
|
// DIAG(F("HTTP_ATTRIB_NAME: %c\n"), c);
|
||||||
|
if (c == '\n')
|
||||||
|
parseStatus = HTTP_NEW_LINE;
|
||||||
|
else if (c != ':' && c != ' ' && c != '\r')
|
||||||
|
{
|
||||||
|
if (strlen(tmpAttribName) < HTTP_REQ_ATTRIB_NAME_LENGTH - 1)
|
||||||
|
strcat(tmpAttribName, cStr);
|
||||||
|
}
|
||||||
|
else if (c == ' ')
|
||||||
|
{
|
||||||
|
if (strcmp(tmpAttribName, "Cookie") == 0)
|
||||||
|
parseStatus = HTTP_COOKIE_NAME;
|
||||||
|
else
|
||||||
|
parseStatus = HTTP_ATTRIB_VALUE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_ATTRIB_VALUE:
|
||||||
|
// DIAG(F("HTTP_ATTRIB_VALUE: %c\n"), c);
|
||||||
|
if (c == '\n')
|
||||||
|
{
|
||||||
|
addAttrib();
|
||||||
|
parseStatus = HTTP_NEW_LINE;
|
||||||
|
}
|
||||||
|
else if (c != '\r')
|
||||||
|
if (strlen(tmpAttribValue) < HTTP_REQ_ATTRIB_VALUE_LENGTH - 1)
|
||||||
|
strcat(tmpAttribValue, cStr);
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_POST_NAME:
|
||||||
|
dataCount++;
|
||||||
|
if (c != '=')
|
||||||
|
{
|
||||||
|
if (strlen(tmpParamName) < HTTP_REQ_PARAM_NAME_LENGTH - 1)
|
||||||
|
strcat(tmpParamName, cStr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
parseStatus = HTTP_POST_VALUE;
|
||||||
|
if (dataCount > dataBlockLength)
|
||||||
|
{
|
||||||
|
addParam();
|
||||||
|
// DIAG(F("HTTP_REQUEST_END: %c\n"), c);
|
||||||
|
parseStatus = HTTP_REQUEST_END;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_POST_VALUE:
|
||||||
|
dataCount++;
|
||||||
|
if (c == '&')
|
||||||
|
{
|
||||||
|
addParam();
|
||||||
|
parseStatus = HTTP_POST_NAME;
|
||||||
|
}
|
||||||
|
else if (strlen(tmpParamValue) < HTTP_REQ_PARAM_VALUE_LENGTH - 1)
|
||||||
|
strcat(tmpParamValue, cStr);
|
||||||
|
if (dataCount > dataBlockLength)
|
||||||
|
{
|
||||||
|
addParam();
|
||||||
|
// DIAG(F("HTTP_REQUEST_END: %c\n"), c);
|
||||||
|
parseStatus = HTTP_REQUEST_END;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_COOKIE_NAME:
|
||||||
|
if (c == '\n')
|
||||||
|
parseStatus = HTTP_NEW_LINE;
|
||||||
|
else if (c != '=')
|
||||||
|
{
|
||||||
|
if (c != ' ' && strlen(tmpCookieName) < HTTP_REQ_COOKIE_NAME_LENGTH - 1)
|
||||||
|
strcat(tmpCookieName, cStr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
parseStatus = HTTP_COOKIE_VALUE;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case HTTP_COOKIE_VALUE:
|
||||||
|
if (c == ';')
|
||||||
|
{
|
||||||
|
addCookie();
|
||||||
|
parseStatus = HTTP_COOKIE_NAME;
|
||||||
|
}
|
||||||
|
else if (c == '\n')
|
||||||
|
{
|
||||||
|
addCookie();
|
||||||
|
parseStatus = HTTP_NEW_LINE;
|
||||||
|
}
|
||||||
|
else if (c != '\r' && c != ' ' && strlen(tmpCookieValue) < HTTP_REQ_COOKIE_VALUE_LENGTH - 1)
|
||||||
|
strcat(tmpCookieValue, cStr);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool HttpRequest::endOfRequest()
|
||||||
|
{
|
||||||
|
if (parseStatus == HTTP_REQUEST_END)
|
||||||
|
return true;
|
||||||
|
else
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpRequest::addParam()
|
||||||
|
{
|
||||||
|
|
||||||
|
Params **cursor;
|
||||||
|
|
||||||
|
cursor = &firstParam;
|
||||||
|
while ((*cursor) != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp((*cursor)->name, tmpParamName) == 0)
|
||||||
|
break;
|
||||||
|
cursor = &((*cursor)->next);
|
||||||
|
}
|
||||||
|
if ((*cursor) == NULL)
|
||||||
|
{
|
||||||
|
// DIAG(F("New Param: %s\n"), tmpParamName);
|
||||||
|
(*cursor) = new Params;
|
||||||
|
strcpy((*cursor)->name, tmpParamName);
|
||||||
|
strcpy((*cursor)->value, tmpParamValue);
|
||||||
|
(*cursor)->next = NULL;
|
||||||
|
paramCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpParamName[0] = '\0';
|
||||||
|
tmpParamValue[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpRequest::addCookie()
|
||||||
|
{
|
||||||
|
|
||||||
|
Cookies **cursor;
|
||||||
|
|
||||||
|
cursor = &firstCookie;
|
||||||
|
while ((*cursor) != NULL)
|
||||||
|
{
|
||||||
|
if (strcmp((*cursor)->name, tmpCookieName) == 0)
|
||||||
|
break;
|
||||||
|
cursor = &((*cursor)->next);
|
||||||
|
}
|
||||||
|
if ((*cursor) == NULL)
|
||||||
|
{
|
||||||
|
(*cursor) = new Cookies;
|
||||||
|
strcpy((*cursor)->name, tmpCookieName);
|
||||||
|
strcpy((*cursor)->value, tmpCookieValue);
|
||||||
|
(*cursor)->next = NULL;
|
||||||
|
cookieCount++;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpCookieName[0] = '\0';
|
||||||
|
tmpCookieValue[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
void HttpRequest::addAttrib()
|
||||||
|
{
|
||||||
|
|
||||||
|
if (strcmp(tmpAttribName, "Content-Length") == 0)
|
||||||
|
dataBlockLength = atoi(tmpAttribValue);
|
||||||
|
|
||||||
|
tmpAttribName[0] = '\0';
|
||||||
|
tmpAttribValue[0] = '\0';
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HttpRequest::getParam(uint8_t paramNum, char *name, char *value)
|
||||||
|
{
|
||||||
|
uint8_t i = 0;
|
||||||
|
|
||||||
|
Params **cursor;
|
||||||
|
|
||||||
|
cursor = &firstParam;
|
||||||
|
while ((*cursor) != NULL)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
if (i == paramNum)
|
||||||
|
{
|
||||||
|
strcpy(name, (*cursor)->name);
|
||||||
|
strcpy(value, (*cursor)->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cursor = &((*cursor)->next);
|
||||||
|
}
|
||||||
|
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
Params* HttpRequest::getParam(uint8_t paramNum)
|
||||||
|
{
|
||||||
|
uint8_t i = 0;
|
||||||
|
|
||||||
|
Params **cursor;
|
||||||
|
|
||||||
|
cursor = &firstParam;
|
||||||
|
while ((*cursor) != NULL)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
if (i == paramNum)
|
||||||
|
{
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cursor = &((*cursor)->next);
|
||||||
|
}
|
||||||
|
return *cursor;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HttpRequest::getParam(char *name, char *value)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t pos = 0, i = 0;
|
||||||
|
Params **cursor;
|
||||||
|
|
||||||
|
cursor = &firstParam;
|
||||||
|
while ((*cursor) != NULL)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
if (strcmp((*cursor)->name, name) == 0)
|
||||||
|
{
|
||||||
|
strcpy(value, (*cursor)->value);
|
||||||
|
pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cursor = &((*cursor)->next);
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HttpRequest::getCookie(uint8_t cookieNum, char *name, char *value)
|
||||||
|
{
|
||||||
|
uint8_t i = 0;
|
||||||
|
|
||||||
|
Cookies **cursor;
|
||||||
|
|
||||||
|
cursor = &firstCookie;
|
||||||
|
while ((*cursor) != NULL)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
if (i == cookieNum)
|
||||||
|
{
|
||||||
|
strcpy(name, (*cursor)->name);
|
||||||
|
strcpy(value, (*cursor)->value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cursor = &((*cursor)->next);
|
||||||
|
}
|
||||||
|
return i;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t HttpRequest::getCookie(char *name, char *value)
|
||||||
|
{
|
||||||
|
|
||||||
|
uint8_t pos = 0, i = 0;
|
||||||
|
Cookies **cursor;
|
||||||
|
|
||||||
|
cursor = &firstCookie;
|
||||||
|
while ((*cursor) != NULL)
|
||||||
|
{
|
||||||
|
i++;
|
||||||
|
if (strcmp((*cursor)->name, name) == 0)
|
||||||
|
{
|
||||||
|
strcpy(value, (*cursor)->value);
|
||||||
|
pos = i;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
cursor = &((*cursor)->next);
|
||||||
|
}
|
||||||
|
return pos;
|
||||||
|
}
|
123
HttpRequest.h
Normal file
123
HttpRequest.h
Normal file
|
@ -0,0 +1,123 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* © 2012 Francisco G. Paletta, © 2020 Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 HttpRequest_h
|
||||||
|
#define HttpRequest_h
|
||||||
|
|
||||||
|
#include "Ethernet.h"
|
||||||
|
|
||||||
|
// Buffer lengths
|
||||||
|
#define HTTP_REQ_METHOD_LENGTH 10 //10 is enough
|
||||||
|
#define HTTP_REQ_URI_LENGTH 32 //adjust if you have long path/file names
|
||||||
|
#define HTTP_REQ_VERSION_LENGTH 10 //10 is enough
|
||||||
|
#define HTTP_REQ_PARAM_NAME_LENGTH 16 //adjust to meet your needs
|
||||||
|
#define HTTP_REQ_PARAM_VALUE_LENGTH 16 //adjust to meet your needs
|
||||||
|
#define HTTP_REQ_ATTRIB_NAME_LENGTH 16 //enough to track attribute name
|
||||||
|
#define HTTP_REQ_ATTRIB_VALUE_LENGTH 16 //enough to track "Content-Length" value
|
||||||
|
#define HTTP_REQ_COOKIE_NAME_LENGTH 10 //adjust to meet your needs
|
||||||
|
#define HTTP_REQ_COOKIE_VALUE_LENGTH 16 //adjust to meet your needs
|
||||||
|
|
||||||
|
// Parsing status
|
||||||
|
#define HTTP_PARSE_INIT 0 //Initial Parser Status
|
||||||
|
#define HTTP_METHOD 0 //Parse the Method: GET POST UPDATE etc
|
||||||
|
#define HTTP_URI 1 //Parse the URI
|
||||||
|
#define HTTP_GET_NAME 11 //Parse the GET parameter NAME
|
||||||
|
#define HTTP_GET_VALUE 12 //Parse the GET parameter VALUE
|
||||||
|
#define HTTP_VERSION 2 //Parse the version: HTTP1.1
|
||||||
|
#define HTTP_NEW_LINE 3 //Starts reading a new line
|
||||||
|
#define HTTP_ATTRIB_NAME 41 //Read the attibutes NAME
|
||||||
|
#define HTTP_ATTRIB_VALUE 42 //Read the attribute VALUE
|
||||||
|
#define HTTP_POST_NAME 51 //Read the POST parameter NAME
|
||||||
|
#define HTTP_POST_VALUE 52 //Read the POST paramenter VALUE
|
||||||
|
#define HTTP_COOKIE_NAME 61 //Read the COOKIE NAME
|
||||||
|
#define HTTP_COOKIE_VALUE 62 //Read the COOKIE VALUE
|
||||||
|
#define HTTP_REQUEST_END 99 //Finished reading the HTTP Request
|
||||||
|
|
||||||
|
// returned to the callback
|
||||||
|
struct ParsedRequest {
|
||||||
|
char* method;
|
||||||
|
char* uri;
|
||||||
|
char* version;
|
||||||
|
uint8_t* paramCount;
|
||||||
|
// uint8_t (*getByIndex)(int, char*, char*);
|
||||||
|
// uint8_t (*getByName)(char*, char*);
|
||||||
|
};
|
||||||
|
|
||||||
|
struct Params
|
||||||
|
{
|
||||||
|
char name[HTTP_REQ_PARAM_NAME_LENGTH];
|
||||||
|
char value[HTTP_REQ_PARAM_VALUE_LENGTH];
|
||||||
|
Params *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
class HttpRequest
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
// no Cookies
|
||||||
|
struct Cookies
|
||||||
|
{
|
||||||
|
char name[HTTP_REQ_COOKIE_NAME_LENGTH];
|
||||||
|
char value[HTTP_REQ_COOKIE_VALUE_LENGTH];
|
||||||
|
Cookies *next;
|
||||||
|
};
|
||||||
|
|
||||||
|
uint8_t parseStatus;
|
||||||
|
Params *firstParam;
|
||||||
|
Cookies *firstCookie;
|
||||||
|
|
||||||
|
char tmpParamName[HTTP_REQ_PARAM_NAME_LENGTH];
|
||||||
|
char tmpParamValue[HTTP_REQ_PARAM_VALUE_LENGTH];
|
||||||
|
char tmpAttribName[HTTP_REQ_ATTRIB_NAME_LENGTH];
|
||||||
|
char tmpAttribValue[HTTP_REQ_ATTRIB_NAME_LENGTH];
|
||||||
|
char tmpCookieName[HTTP_REQ_COOKIE_NAME_LENGTH]; // no use
|
||||||
|
char tmpCookieValue[HTTP_REQ_COOKIE_VALUE_LENGTH]; // no use
|
||||||
|
|
||||||
|
uint16_t dataBlockLength, dataCount;
|
||||||
|
|
||||||
|
void addParam();
|
||||||
|
void addAttrib();
|
||||||
|
void addCookie(); // no use
|
||||||
|
void freeParamMem(Params *paramNode);
|
||||||
|
void freeCookieMem(Cookies *cookieNode);
|
||||||
|
|
||||||
|
char method[HTTP_REQ_METHOD_LENGTH]; // user
|
||||||
|
char uri[HTTP_REQ_URI_LENGTH]; // user
|
||||||
|
char version[HTTP_REQ_VERSION_LENGTH]; // user
|
||||||
|
|
||||||
|
uint8_t paramCount; // user
|
||||||
|
uint8_t cookieCount; // no use - no cookie support
|
||||||
|
|
||||||
|
uint8_t getParam(uint8_t paramNum, char *name, char *value); // user
|
||||||
|
uint8_t getParam(char *name, char *value); // user
|
||||||
|
uint8_t getCookie(uint8_t cookieNum, char *name, char *value); // no use
|
||||||
|
uint8_t getCookie(char *name, char *value); // no use
|
||||||
|
|
||||||
|
ParsedRequest req;
|
||||||
|
|
||||||
|
public:
|
||||||
|
HttpRequest();
|
||||||
|
void resetRequest();
|
||||||
|
void parseRequest(char c);
|
||||||
|
bool endOfRequest();
|
||||||
|
ParsedRequest getParsedRequest();
|
||||||
|
Params* getParam(uint8_t paramNum);
|
||||||
|
void (* callback)(ParsedRequest *req, Client *client);
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
52
MemoryFree.cpp
Normal file
52
MemoryFree.cpp
Normal file
|
@ -0,0 +1,52 @@
|
||||||
|
#if (ARDUINO >= 100)
|
||||||
|
#include <Arduino.h>
|
||||||
|
#else
|
||||||
|
#include <WProgram.h>
|
||||||
|
#endif
|
||||||
|
|
||||||
|
extern unsigned int __heap_start;
|
||||||
|
extern void *__brkval;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* The free list structure as maintained by the
|
||||||
|
* avr-libc memory allocation routines.
|
||||||
|
*/
|
||||||
|
struct __freelist
|
||||||
|
{
|
||||||
|
size_t sz;
|
||||||
|
struct __freelist *nx;
|
||||||
|
};
|
||||||
|
|
||||||
|
/* The head of the free list structure */
|
||||||
|
extern struct __freelist *__flp;
|
||||||
|
|
||||||
|
#include "MemoryFree.h"
|
||||||
|
|
||||||
|
/* Calculates the size of the free list */
|
||||||
|
int freeListSize()
|
||||||
|
{
|
||||||
|
struct __freelist* current;
|
||||||
|
int total = 0;
|
||||||
|
for (current = __flp; current; current = current->nx)
|
||||||
|
{
|
||||||
|
total += 2; /* Add two bytes for the memory block's header */
|
||||||
|
total += (int) current->sz;
|
||||||
|
}
|
||||||
|
|
||||||
|
return total;
|
||||||
|
}
|
||||||
|
|
||||||
|
int freeMemory()
|
||||||
|
{
|
||||||
|
int free_memory;
|
||||||
|
if ((int)__brkval == 0)
|
||||||
|
{
|
||||||
|
free_memory = ((int)&free_memory) - ((int)&__heap_start);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
free_memory = ((int)&free_memory) - ((int)__brkval);
|
||||||
|
free_memory += freeListSize();
|
||||||
|
}
|
||||||
|
return free_memory;
|
||||||
|
}
|
14
MemoryFree.h
Normal file
14
MemoryFree.h
Normal file
|
@ -0,0 +1,14 @@
|
||||||
|
#ifndef MEMORY_FREE_H
|
||||||
|
#define MEMORY_FREE_H
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
int freeMemory();
|
||||||
|
|
||||||
|
#ifdef __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#endif
|
137
NetworkInterface.cpp
Normal file
137
NetworkInterface.cpp
Normal file
|
@ -0,0 +1,137 @@
|
||||||
|
/*
|
||||||
|
* © 2020, Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 <Arduino.h>
|
||||||
|
|
||||||
|
#include "DIAG.h"
|
||||||
|
#include "NetworkInterface.h"
|
||||||
|
// #include "Singelton.h"
|
||||||
|
// #include "WifiTransport.h"
|
||||||
|
#include "EthernetSetup.h"
|
||||||
|
#include "WifiSetup.h"
|
||||||
|
|
||||||
|
HttpCallback NetworkInterface::httpCallback;
|
||||||
|
|
||||||
|
Transport<WiFiServer, WiFiClient, WiFiUDP> *NetworkInterface::wifiTransport;
|
||||||
|
Transport<EthernetServer, EthernetClient, EthernetUDP> *NetworkInterface::ethernetTransport;
|
||||||
|
transportType t;
|
||||||
|
|
||||||
|
void NetworkInterface::setup(transportType transport, protocolType protocol, uint16_t port)
|
||||||
|
{
|
||||||
|
|
||||||
|
bool ok = false;
|
||||||
|
|
||||||
|
DIAG(F("\n[%s] Transport Setup In Progress ...\n"), transport ? "Ethernet" : "Wifi");
|
||||||
|
|
||||||
|
// configure the Transport and get Ethernet/Wifi server up and running
|
||||||
|
|
||||||
|
t = transport;
|
||||||
|
switch (transport)
|
||||||
|
{
|
||||||
|
case WIFI:
|
||||||
|
{
|
||||||
|
WifiSetup wSetup(port, protocol);
|
||||||
|
|
||||||
|
wifiTransport = new Transport<WiFiServer, WiFiClient, WiFiUDP>();
|
||||||
|
ok = wSetup.setup();
|
||||||
|
if (ok)
|
||||||
|
{
|
||||||
|
wifiTransport->server = wSetup.getServer();
|
||||||
|
wifiTransport->port = port;
|
||||||
|
wifiTransport->protocol = protocol;
|
||||||
|
wifiTransport->transport = transport;
|
||||||
|
wifiTransport->maxConnections = wSetup.maxConnections;
|
||||||
|
ok = wifiTransport->setup();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case ETHERNET:
|
||||||
|
{
|
||||||
|
EthernetSetup eSetup(port, protocol);
|
||||||
|
|
||||||
|
ethernetTransport = new Transport<EthernetServer, EthernetClient, EthernetUDP>();
|
||||||
|
ethernetTransport->server = eSetup.setup(); // returns (NULL) 0 if we run over UDP
|
||||||
|
ethernetTransport->port = port;
|
||||||
|
ethernetTransport->protocol = protocol;
|
||||||
|
ethernetTransport->transport = transport;
|
||||||
|
ethernetTransport->maxConnections = eSetup.maxConnections; // that has been determined during the ethernet/wifi setup
|
||||||
|
ok = ethernetTransport->setup(); // start the transport i.e. setup all the client connections; We don't need the setup object anymore from here on
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
DIAG(F("\nERROR: Unknown Transport")); // Something went wrong
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DIAG(F("\n\n[%s] Transport %s ..."), transport ? "Ethernet" : "Wifi", ok ? "OK" : "Failed");
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkInterface::setup(transportType tt, protocolType pt)
|
||||||
|
{
|
||||||
|
NetworkInterface::setup(tt, pt, LISTEN_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkInterface::setup(transportType tt)
|
||||||
|
{
|
||||||
|
NetworkInterface::setup(tt, TCP, LISTEN_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkInterface::setup()
|
||||||
|
{
|
||||||
|
NetworkInterface::setup(ETHERNET, TCP, LISTEN_PORT);
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkInterface::loop()
|
||||||
|
{
|
||||||
|
switch (t)
|
||||||
|
{
|
||||||
|
case WIFI:
|
||||||
|
{
|
||||||
|
if (wifiTransport->isConnected()){
|
||||||
|
wifiTransport->loop();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case ETHERNET:
|
||||||
|
{
|
||||||
|
if (ethernetTransport->isConnected()) {
|
||||||
|
ethernetTransport->loop();
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void NetworkInterface::setHttpCallback(HttpCallback callback)
|
||||||
|
{
|
||||||
|
httpCallback = callback;
|
||||||
|
}
|
||||||
|
HttpCallback NetworkInterface::getHttpCallback()
|
||||||
|
{
|
||||||
|
return httpCallback;
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkInterface::NetworkInterface()
|
||||||
|
{
|
||||||
|
// DIAG(F("NetworkInterface created "));
|
||||||
|
}
|
||||||
|
|
||||||
|
NetworkInterface::~NetworkInterface()
|
||||||
|
{
|
||||||
|
// DIAG(F("NetworkInterface destroyed"));
|
||||||
|
}
|
63
NetworkInterface.h
Normal file
63
NetworkInterface.h
Normal file
|
@ -0,0 +1,63 @@
|
||||||
|
/*
|
||||||
|
* © 2020, Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 NetworkInterface_h
|
||||||
|
#define NetworkInterface_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
|
||||||
|
#include "Transport.h"
|
||||||
|
#include "HttpRequest.h"
|
||||||
|
|
||||||
|
typedef enum protocolType {
|
||||||
|
TCP,
|
||||||
|
UDP,
|
||||||
|
MQTT
|
||||||
|
} protocolType;
|
||||||
|
|
||||||
|
typedef enum transportType {
|
||||||
|
WIFI, // using an AT (Version >= V1.7) command enabled ESP8266 not to be used in conjunction with the WifiInterface though! not tested for conflicts
|
||||||
|
ETHERNET // using the EthernetShield
|
||||||
|
} transoprtType;
|
||||||
|
|
||||||
|
// typedef void (*HttpCallback)(ParsedRequest *req, Client *client);
|
||||||
|
using HttpCallback = void(*)(ParsedRequest *req, Client *client);
|
||||||
|
|
||||||
|
class NetworkInterface
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
|
||||||
|
static Transport<WiFiServer,WiFiClient,WiFiUDP>* wifiTransport;
|
||||||
|
static Transport<EthernetServer,EthernetClient,EthernetUDP>* ethernetTransport;
|
||||||
|
static HttpCallback httpCallback;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
static void setHttpCallback(HttpCallback callback);
|
||||||
|
static HttpCallback getHttpCallback();
|
||||||
|
static void setup(transportType t, protocolType p, uint16_t port); // specific port nummber
|
||||||
|
static void setup(transportType t, protocolType p); // uses default port number
|
||||||
|
static void setup(transportType t); // defaults for protocol/port
|
||||||
|
|
||||||
|
static void setup(); // defaults for all as above plus CABLE (i.e. using EthernetShield ) as default
|
||||||
|
static void loop();
|
||||||
|
|
||||||
|
NetworkInterface();
|
||||||
|
~NetworkInterface();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
23
NetworkSetup.cpp
Normal file
23
NetworkSetup.cpp
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
/*
|
||||||
|
* © 2020 Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 <Arduino.h>
|
||||||
|
#include "DIAG.h"
|
||||||
|
#include "NetworkSetup.h"
|
||||||
|
|
||||||
|
NetworkSetup::NetworkSetup() {}
|
||||||
|
NetworkSetup::~NetworkSetup() {}
|
61
NetworkSetup.h
Normal file
61
NetworkSetup.h
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
/*
|
||||||
|
* © 2020 Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 NetworkSetup_h
|
||||||
|
#define NetworkSetup_h
|
||||||
|
|
||||||
|
#include "Ethernet.h"
|
||||||
|
#include "NetworkInterface.h"
|
||||||
|
|
||||||
|
/* some generated mac addresses as EthernetShields don't have one by default in HW.
|
||||||
|
* Sometimes they come on a sticker on the EthernetShield then use this address otherwise
|
||||||
|
* just choose one from below or generate one yourself. Only condition is that there is no
|
||||||
|
* other device on your network with the same Mac address.
|
||||||
|
*
|
||||||
|
* 52:b8:8a:8e:ce:21
|
||||||
|
* e3:e9:73:e1:db:0d
|
||||||
|
* 54:2b:13:52:ac:0c
|
||||||
|
* c2:d8:d4:7d:7c:cb
|
||||||
|
* 86:cf:fa:9f:07:79
|
||||||
|
*/
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Network Configuration
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
#define MAC_ADDRESS \
|
||||||
|
{ \
|
||||||
|
0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21 \
|
||||||
|
} // MAC address of your networking card found on the sticker on your card or take one from above
|
||||||
|
#define IP_ADDRESS 10, 0, 0, 101 // Just in case we don't get an adress from DHCP try a static one;
|
||||||
|
|
||||||
|
class NetworkSetup
|
||||||
|
{
|
||||||
|
private:
|
||||||
|
public:
|
||||||
|
IPAddress dnsip;
|
||||||
|
IPAddress ip;
|
||||||
|
uint8_t mac[6] = MAC_ADDRESS;
|
||||||
|
uint8_t maxConnections;
|
||||||
|
bool connected; // semantics is that the server has successfullt started or not; client connections will be started in the Transport object
|
||||||
|
protocolType protocol;
|
||||||
|
uint16_t port = LISTEN_PORT; // Default port
|
||||||
|
|
||||||
|
NetworkSetup();
|
||||||
|
~NetworkSetup();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -31,8 +31,6 @@
|
||||||
#define __FlashStringHelper char
|
#define __FlashStringHelper char
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "LCDDisplay.h"
|
|
||||||
|
|
||||||
bool Diag::ACK=false;
|
bool Diag::ACK=false;
|
||||||
bool Diag::CMD=false;
|
bool Diag::CMD=false;
|
||||||
bool Diag::WIFI=false;
|
bool Diag::WIFI=false;
|
||||||
|
@ -46,14 +44,6 @@ void StringFormatter::diag( const __FlashStringHelper* input...) {
|
||||||
send2(diagSerial,input,args);
|
send2(diagSerial,input,args);
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringFormatter::lcd(byte row, const __FlashStringHelper* input...) {
|
|
||||||
if (!LCDDisplay::lcdDisplay) return;
|
|
||||||
LCDDisplay::lcdDisplay->setRow(row);
|
|
||||||
va_list args;
|
|
||||||
va_start(args, input);
|
|
||||||
send2(LCDDisplay::lcdDisplay,input,args);
|
|
||||||
}
|
|
||||||
|
|
||||||
void StringFormatter::send(Print * stream, const __FlashStringHelper* input...) {
|
void StringFormatter::send(Print * stream, const __FlashStringHelper* input...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
|
@ -66,6 +56,7 @@ void StringFormatter::send(Print & stream, const __FlashStringHelper* input...)
|
||||||
send2(&stream,input,args);
|
send2(&stream,input,args);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StringFormatter::send2(Print * stream,const __FlashStringHelper* format, va_list args) {
|
void StringFormatter::send2(Print * stream,const __FlashStringHelper* format, va_list args) {
|
||||||
|
|
||||||
// thanks to Jan Turoň https://arduino.stackexchange.com/questions/56517/formatting-strings-in-arduino-for-output
|
// thanks to Jan Turoň https://arduino.stackexchange.com/questions/56517/formatting-strings-in-arduino-for-output
|
||||||
|
@ -75,13 +66,6 @@ void StringFormatter::send2(Print * stream,const __FlashStringHelper* format, va
|
||||||
char c=pgm_read_byte_near(flash+i);
|
char c=pgm_read_byte_near(flash+i);
|
||||||
if (c=='\0') return;
|
if (c=='\0') return;
|
||||||
if(c!='%') { stream->print(c); continue; }
|
if(c!='%') { stream->print(c); continue; }
|
||||||
|
|
||||||
bool formatContinues=false;
|
|
||||||
byte formatWidth=0;
|
|
||||||
bool formatLeft=false;
|
|
||||||
do {
|
|
||||||
|
|
||||||
formatContinues=false;
|
|
||||||
i++;
|
i++;
|
||||||
c=pgm_read_byte_near(flash+i);
|
c=pgm_read_byte_near(flash+i);
|
||||||
switch(c) {
|
switch(c) {
|
||||||
|
@ -89,34 +73,14 @@ void StringFormatter::send2(Print * stream,const __FlashStringHelper* format, va
|
||||||
case 'c': stream->print((char) va_arg(args, int)); break;
|
case 'c': stream->print((char) va_arg(args, int)); break;
|
||||||
case 's': stream->print(va_arg(args, char*)); break;
|
case 's': stream->print(va_arg(args, char*)); break;
|
||||||
case 'e': printEscapes(stream,va_arg(args, char*)); break;
|
case 'e': printEscapes(stream,va_arg(args, char*)); break;
|
||||||
case 'E': printEscapes(stream,(const __FlashStringHelper*)va_arg(args, char*)); break;
|
|
||||||
case 'S': stream->print((const __FlashStringHelper*)va_arg(args, char*)); break;
|
case 'S': stream->print((const __FlashStringHelper*)va_arg(args, char*)); break;
|
||||||
case 'd': printPadded(stream,va_arg(args, int), formatWidth, formatLeft); break;
|
case 'd': stream->print(va_arg(args, int), DEC); break;
|
||||||
case 'l': printPadded(stream,va_arg(args, long), formatWidth, formatLeft); break;
|
case 'l': stream->print(va_arg(args, long), DEC); break;
|
||||||
case 'b': stream->print(va_arg(args, int), BIN); break;
|
case 'b': stream->print(va_arg(args, int), BIN); break;
|
||||||
case 'o': stream->print(va_arg(args, int), OCT); break;
|
case 'o': stream->print(va_arg(args, int), OCT); break;
|
||||||
case 'x': stream->print(va_arg(args, int), HEX); break;
|
case 'x': stream->print(va_arg(args, int), HEX); break;
|
||||||
case 'f': stream->print(va_arg(args, double), 2); break;
|
case 'f': stream->print(va_arg(args, double), 2); break;
|
||||||
//format width prefix
|
|
||||||
case '-':
|
|
||||||
formatLeft=true;
|
|
||||||
formatContinues=true;
|
|
||||||
break;
|
|
||||||
case '0':
|
|
||||||
case '1':
|
|
||||||
case '2':
|
|
||||||
case '3':
|
|
||||||
case '4':
|
|
||||||
case '5':
|
|
||||||
case '6':
|
|
||||||
case '7':
|
|
||||||
case '8':
|
|
||||||
case '9':
|
|
||||||
formatWidth=formatWidth * 10 + (c-'0');
|
|
||||||
formatContinues=true;
|
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
} while(formatContinues);
|
|
||||||
}
|
}
|
||||||
va_end(args);
|
va_end(args);
|
||||||
}
|
}
|
||||||
|
@ -130,17 +94,6 @@ void StringFormatter::printEscapes(Print * stream,char * input) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void StringFormatter::printEscapes(Print * stream, const __FlashStringHelper * input) {
|
|
||||||
|
|
||||||
if (!stream) return;
|
|
||||||
char* flash=(char*)input;
|
|
||||||
for(int i=0; ; ++i) {
|
|
||||||
char c=pgm_read_byte_near(flash+i);
|
|
||||||
printEscape(stream,c);
|
|
||||||
if (c=='\0') return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void StringFormatter::printEscape( char c) {
|
void StringFormatter::printEscape( char c) {
|
||||||
printEscape(diagSerial,c);
|
printEscape(diagSerial,c);
|
||||||
}
|
}
|
||||||
|
@ -156,27 +109,4 @@ void StringFormatter::printEscape(Print * stream, char c) {
|
||||||
default: stream->print(c);
|
default: stream->print(c);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
void StringFormatter::printPadded(Print* stream, long value, byte width, bool formatLeft) {
|
|
||||||
if (width==0) {
|
|
||||||
stream->print(value, DEC);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
int digits=(value <= 0)? 1: 0; // zero and negative need extra digot
|
|
||||||
long v=value;
|
|
||||||
while (v) {
|
|
||||||
v /= 10;
|
|
||||||
digits++;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (formatLeft) stream->print(value, DEC);
|
|
||||||
while(digits<width) {
|
|
||||||
stream->print(' ');
|
|
||||||
digits++;
|
|
||||||
}
|
|
||||||
if (!formatLeft) stream->print(value, DEC);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@
|
||||||
#ifndef StringFormatter_h
|
#ifndef StringFormatter_h
|
||||||
#define StringFormatter_h
|
#define StringFormatter_h
|
||||||
#include <Arduino.h>
|
#include <Arduino.h>
|
||||||
|
#include "TransportProcessor.h"
|
||||||
|
|
||||||
#if defined(ARDUINO_ARCH_SAMD)
|
#if defined(ARDUINO_ARCH_SAMD)
|
||||||
// Some processors use a gcc compiler that renames va_list!!!
|
// Some processors use a gcc compiler that renames va_list!!!
|
||||||
|
@ -27,7 +28,6 @@
|
||||||
#define __FlashStringHelper char
|
#define __FlashStringHelper char
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#include "LCDDisplay.h"
|
|
||||||
class Diag {
|
class Diag {
|
||||||
public:
|
public:
|
||||||
static bool ACK;
|
static bool ACK;
|
||||||
|
@ -43,19 +43,26 @@ class StringFormatter
|
||||||
static void send(Print & serial, const __FlashStringHelper* input...);
|
static void send(Print & serial, const __FlashStringHelper* input...);
|
||||||
|
|
||||||
static void printEscapes(Print * serial,char * input);
|
static void printEscapes(Print * serial,char * input);
|
||||||
static void printEscapes(Print * serial,const __FlashStringHelper* input);
|
|
||||||
static void printEscape(Print * serial, char c);
|
static void printEscape(Print * serial, char c);
|
||||||
|
|
||||||
// DIAG support
|
// DIAG support
|
||||||
static Print * diagSerial;
|
static Print * diagSerial;
|
||||||
static void diag( const __FlashStringHelper* input...);
|
static void diag( const __FlashStringHelper* input...);
|
||||||
static void lcd(byte row, const __FlashStringHelper* input...);
|
|
||||||
static void printEscapes(char * input);
|
static void printEscapes(char * input);
|
||||||
static void printEscape( char c);
|
static void printEscape( char c);
|
||||||
|
|
||||||
|
|
||||||
|
static void setDiagOut(Connection *c) {
|
||||||
|
if ( c->client->connected() ) {
|
||||||
|
diagSerial = c->client;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
static void resetDiagOut() {
|
||||||
|
diagSerial = &Serial;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static void send2(Print * serial, const __FlashStringHelper* input,va_list args);
|
static void send2(Print * serial, const __FlashStringHelper* input,va_list args);
|
||||||
static void printPadded(Print* stream, long value, byte width, bool formatLeft);
|
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
173
Transport.cpp
Normal file
173
Transport.cpp
Normal file
|
@ -0,0 +1,173 @@
|
||||||
|
/*
|
||||||
|
* © 2020, Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 <Arduino.h>
|
||||||
|
|
||||||
|
#include "DIAG.h"
|
||||||
|
#include "NetworkInterface.h"
|
||||||
|
#include "Transport.h"
|
||||||
|
|
||||||
|
extern bool diagNetwork;
|
||||||
|
extern uint8_t diagNetworkClient;
|
||||||
|
|
||||||
|
template<class S, class C, class U>
|
||||||
|
bool Transport<S,C,U>::setup() {
|
||||||
|
if (protocol == TCP) {
|
||||||
|
connectionPool(server); // server should have started here so create the connection pool only for TCP though
|
||||||
|
}
|
||||||
|
t = new TransportProcessor();
|
||||||
|
connected = true; // server & clients which will recieve/send data have all e setup and are available
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class S, class C, class U>
|
||||||
|
void Transport<S,C,U>::loop() {
|
||||||
|
switch (protocol)
|
||||||
|
{
|
||||||
|
case UDP:
|
||||||
|
{
|
||||||
|
udpHandler();
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case TCP:
|
||||||
|
{
|
||||||
|
tcpSessionHandler(server); // for session oriented coms
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case MQTT:
|
||||||
|
{
|
||||||
|
// MQTT
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class S, class C, class U>
|
||||||
|
void Transport<S, C, U>::connectionPool(S *server)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < Transport::maxConnections; i++)
|
||||||
|
{
|
||||||
|
clients[i] = server->accept();
|
||||||
|
connections[i].client = &clients[i];
|
||||||
|
connections[i].id = i;
|
||||||
|
DIAG(F("\nConnection pool: [%d:%x]"), i, clients[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class S, class C, class U>
|
||||||
|
void Transport<S, C, U>::udpHandler()
|
||||||
|
{
|
||||||
|
// DIAG(F("UdpHandler\n"));
|
||||||
|
int packetSize = udp->parsePacket();
|
||||||
|
if (packetSize)
|
||||||
|
{
|
||||||
|
DIAG(F("\nReceived packet of size:[%d]\n"), packetSize);
|
||||||
|
IPAddress remote = udp->remoteIP();
|
||||||
|
DIAG(F("From: [%d.%d.%d.%d:"), remote[0], remote[1], remote[2], remote[3]);
|
||||||
|
char portBuffer[6];
|
||||||
|
DIAG(F("%s]\n"), utoa(udp->remotePort(), portBuffer, 10)); // DIAG has issues with unsigend int's so go through utoa
|
||||||
|
|
||||||
|
// read the packet into packetBufffer
|
||||||
|
// udp.read(buffer, MAX_ETH_BUFFER); /////////// Put into the TransportProcessor
|
||||||
|
// terminate buffer properly
|
||||||
|
// buffer[packetSize] = '\0';
|
||||||
|
|
||||||
|
// DIAG(F("Command: [%s]\n"),buffer);
|
||||||
|
// execute the command via the parser
|
||||||
|
// check if we have a response if yes then
|
||||||
|
// send the reply
|
||||||
|
// udp.beginPacket(udp.remoteIP(), udp.remotePort());
|
||||||
|
// parse(&udp, (byte *)buffer, true); //////////// Put into the TransportProcessor
|
||||||
|
// udp.endPacket();
|
||||||
|
|
||||||
|
// clear out the PacketBuffer
|
||||||
|
// memset(buffer, 0, MAX_ETH_BUFFER); // reset PacktBuffer
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief As tcpHandler but this time the connections are kept open (thus creating a statefull session) as long as the client doesn't disconnect. A connection
|
||||||
|
* pool has been setup beforehand and determines the number of available sessions depending on the network hardware. Commands crossing packet boundaries will be captured
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
template<class S, class C, class U>
|
||||||
|
void Transport<S,C,U>::tcpSessionHandler(S* server)
|
||||||
|
{
|
||||||
|
// get client from the server
|
||||||
|
C client = server->accept();
|
||||||
|
|
||||||
|
// check for new client
|
||||||
|
if (client)
|
||||||
|
{
|
||||||
|
for (byte i = 0; i < Transport<S,C,U>::maxConnections; i++)
|
||||||
|
{
|
||||||
|
if (!clients[i])
|
||||||
|
{
|
||||||
|
// On accept() the EthernetServer doesn't track the client anymore
|
||||||
|
// so we store it in our client array
|
||||||
|
clients[i] = client;
|
||||||
|
DIAG(F("\nNew Client: [%d:%x]"), i, clients[i]);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// check for incoming data from all possible clients
|
||||||
|
for (byte i = 0; i < Transport<S,C,U>::maxConnections; i++)
|
||||||
|
{
|
||||||
|
if (clients[i] && clients[i].available() > 0)
|
||||||
|
{
|
||||||
|
// readStream(i);
|
||||||
|
t->readStream(&connections[i]);
|
||||||
|
}
|
||||||
|
// stop any clients which disconnect
|
||||||
|
for (byte i = 0; i < Transport<S,C,U>::maxConnections; i++)
|
||||||
|
{
|
||||||
|
if (clients[i] && !clients[i].connected())
|
||||||
|
{
|
||||||
|
DIAG(F("\nDisconnect client #%d"), i);
|
||||||
|
clients[i].stop();
|
||||||
|
connections[i].isProtocolDefined = false;
|
||||||
|
if (diagNetworkClient == i && diagNetwork) {
|
||||||
|
diagNetwork = false;
|
||||||
|
StringFormatter::resetDiagOut();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class S, class C, class U>
|
||||||
|
Transport<S,C,U>::Transport()
|
||||||
|
{
|
||||||
|
// DIAG(F("Transport created "));
|
||||||
|
}
|
||||||
|
|
||||||
|
template<class S, class C, class U>
|
||||||
|
Transport<S,C,U>::~Transport()
|
||||||
|
{
|
||||||
|
// DIAG(F("Transport destroyed"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// explicitly instatiate to get the relevant copies for ethernet / wifi build @compile time
|
||||||
|
template class Transport<EthernetServer,EthernetClient,EthernetUDP>;
|
||||||
|
template class Transport<WiFiServer, WiFiClient, WiFiUDP>;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* Scratch pad Section
|
||||||
|
*/
|
||||||
|
|
83
Transport.h
Normal file
83
Transport.h
Normal file
|
@ -0,0 +1,83 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* © 2020, Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 Transport_h
|
||||||
|
#define Transport_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <WiFiEspAT.h>
|
||||||
|
|
||||||
|
#include <NetworkInterface.h>
|
||||||
|
#include "TransportProcessor.h"
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_SOCK_NUM 8 // Maximum number of sockets allowed for any WizNet based EthernetShield. The W5100 only supports 4
|
||||||
|
#define MAX_WIFI_SOCK 5 // ESP8266 doesn't support more than 5 connections in //
|
||||||
|
#define LISTEN_PORT 2560 // default listen port for the server
|
||||||
|
|
||||||
|
|
||||||
|
#define MAC_ADDRESS \
|
||||||
|
{ \
|
||||||
|
0x52, 0xB8, 0x8A, 0x8E, 0xCE, 0x21 \
|
||||||
|
} // MAC address of your networking card found on the sticker on your card or take one from above
|
||||||
|
#define IP_ADDRESS 10, 0, 0, 101 // Just in case we don't get an adress from DHCP try a static one;
|
||||||
|
|
||||||
|
// Emulate Serial1 on pins 6/7 if not present
|
||||||
|
#if defined(ARDUINO_ARCH_AVR) && !defined(HAVE_HWSERIAL1)
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
SoftwareSerial Serial1(6, 7); // RX, TX
|
||||||
|
#define AT_BAUD_RATE 9600
|
||||||
|
#else
|
||||||
|
#define AT_BAUD_RATE 115200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
template <class S, class C, class U> class Transport
|
||||||
|
{
|
||||||
|
|
||||||
|
private:
|
||||||
|
C clients[MAX_SOCK_NUM]; // Client objects created by the connectionPool
|
||||||
|
Connection connections[MAX_SOCK_NUM]; // All the connections build by the connectionPool
|
||||||
|
bool connected = false;
|
||||||
|
TransportProcessor* t; // pointer to the object which handles the incomming flow
|
||||||
|
|
||||||
|
void udpHandler(); // Reads from a Udp socket - todo add incomming queue for processing when the flow is faster than we can process commands
|
||||||
|
void tcpSessionHandler(S* server); // tcpSessionHandler -> connections are maintained open until close by the client
|
||||||
|
void connectionPool(S* server); // allocates the Sockets at setup time and creates the Connections
|
||||||
|
|
||||||
|
public:
|
||||||
|
uint16_t port;
|
||||||
|
uint8_t protocol; // TCP or UDP
|
||||||
|
uint8_t transport; // WIFI or ETHERNET
|
||||||
|
S* server; // WiFiServer or EthernetServer
|
||||||
|
U* udp; // UDP socket object
|
||||||
|
uint8_t maxConnections; // number of supported connections depending on the network equipment used
|
||||||
|
|
||||||
|
bool setup();
|
||||||
|
void loop();
|
||||||
|
|
||||||
|
bool isConnected() {
|
||||||
|
return connected;
|
||||||
|
}
|
||||||
|
|
||||||
|
Transport<S,C,U>();
|
||||||
|
~Transport<S,C,U>();
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !Transport_h
|
419
TransportProcessor.cpp
Normal file
419
TransportProcessor.cpp
Normal file
|
@ -0,0 +1,419 @@
|
||||||
|
/*
|
||||||
|
* © 2020, Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 <Arduino.h>
|
||||||
|
|
||||||
|
#include "DIAG.h"
|
||||||
|
// #include "DCCEXParser.h"
|
||||||
|
|
||||||
|
#include "NetworkInterface.h"
|
||||||
|
#include "HttpRequest.h"
|
||||||
|
#include "TransportProcessor.h"
|
||||||
|
|
||||||
|
// DCCEXParser ethParser;
|
||||||
|
|
||||||
|
static uint8_t buffer[MAX_ETH_BUFFER];
|
||||||
|
static char command[MAX_JMRI_CMD] = {0};
|
||||||
|
static uint8_t reply[MAX_ETH_BUFFER];
|
||||||
|
|
||||||
|
HttpRequest httpReq;
|
||||||
|
uint16_t _rseq[MAX_SOCK_NUM] = {0};
|
||||||
|
uint16_t _sseq[MAX_SOCK_NUM] = {0};
|
||||||
|
|
||||||
|
char protocolName[4][11] = {"JMRI", "WITHROTTLE", "HTTP", "UNKNOWN"}; // change for Progmem
|
||||||
|
bool diagNetwork = false;
|
||||||
|
uint8_t diagNetworkClient = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief creates a HttpRequest object for the user callback. Some conditions apply esp reagrding the length of the items in the Request
|
||||||
|
* can be found in @file HttpRequest.h
|
||||||
|
*
|
||||||
|
* @param client Client object from whom we receievd the data
|
||||||
|
* @param c id of the Client object
|
||||||
|
*/
|
||||||
|
void httpProcessor(Connection* c)
|
||||||
|
{
|
||||||
|
uint8_t i, l = 0;
|
||||||
|
ParsedRequest preq;
|
||||||
|
l = strlen((char *)buffer);
|
||||||
|
for (i = 0; i < l; i++)
|
||||||
|
{
|
||||||
|
httpReq.parseRequest((char)buffer[i]);
|
||||||
|
}
|
||||||
|
if (httpReq.endOfRequest())
|
||||||
|
{
|
||||||
|
preq = httpReq.getParsedRequest();
|
||||||
|
httpReq.callback(&preq, c->client);
|
||||||
|
httpReq.resetRequest();
|
||||||
|
} // else do nothing and continue with the next packet
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Set the App Protocol. The detection id done upon the very first message recieved. The client will then be bound to that protocol. Its very brittle
|
||||||
|
* as e.g. The N message as first message for WiThrottle is not a requirement by the protocol; If any client talking Withrottle doesn't implement this the detection
|
||||||
|
* will default to JMRI. For HTTP we base this only on a subset of th HTTP verbs which can be used.
|
||||||
|
*
|
||||||
|
* @param a First character of the recieved buffer upon first connection
|
||||||
|
* @param b Second character of the recieved buffer upon first connection
|
||||||
|
* @return appProtocol
|
||||||
|
*/
|
||||||
|
appProtocol setAppProtocol(char a, char b, Connection *c)
|
||||||
|
{
|
||||||
|
appProtocol p;
|
||||||
|
switch (a)
|
||||||
|
{
|
||||||
|
case 'G': // GET
|
||||||
|
case 'C': // CONNECT
|
||||||
|
case 'O': // OPTIONS
|
||||||
|
case 'T': // TRACE
|
||||||
|
{
|
||||||
|
p = HTTP;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'D': // DELETE or D plux hex value
|
||||||
|
{
|
||||||
|
if (b == 'E')
|
||||||
|
{
|
||||||
|
p = HTTP;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p = WITHROTTLE;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'P':
|
||||||
|
{
|
||||||
|
if (b == 'T' || b == 'R')
|
||||||
|
{
|
||||||
|
p = WITHROTTLE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p = HTTP; // PUT / PATCH / POST
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'H':
|
||||||
|
{
|
||||||
|
if (b == 'U')
|
||||||
|
{
|
||||||
|
p = WITHROTTLE;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
p = HTTP; // HEAD
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'M':
|
||||||
|
case '*':
|
||||||
|
case 'R':
|
||||||
|
case 'Q': // That doesn't make sense as it's the Q or close on app level
|
||||||
|
case 'N':
|
||||||
|
{
|
||||||
|
p = WITHROTTLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '<':
|
||||||
|
{
|
||||||
|
p = DCCEX;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case '#': {
|
||||||
|
p = DCCEX;
|
||||||
|
DIAG(F("\nDiagnostics routed to network client\n"));
|
||||||
|
StringFormatter::setDiagOut(c);
|
||||||
|
diagNetwork = true;
|
||||||
|
diagNetworkClient = c->id;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
// here we don't know
|
||||||
|
p = UNKNOWN_PROTOCOL;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DIAG(F("\nClient speaks: [%s]\n"), protocolName[p]);
|
||||||
|
return p;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Parses the buffer to extract commands to be executed
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
// void TransportProcessor::processStream(Connection *c)
|
||||||
|
void processStream(Connection *c)
|
||||||
|
{
|
||||||
|
uint8_t i, j, k, l = 0;
|
||||||
|
|
||||||
|
memset(command, 0, MAX_JMRI_CMD); // clear out the command
|
||||||
|
DIAG(F("\nBuffer: [%e]\n"), buffer);
|
||||||
|
// copy overflow into the command
|
||||||
|
if ((i = strlen(c->overflow)) != 0)
|
||||||
|
{
|
||||||
|
// DIAG(F("\nCopy overflow to command: %e"), c->overflow);
|
||||||
|
strncpy(command, c->overflow, i);
|
||||||
|
k = i;
|
||||||
|
}
|
||||||
|
// reset the overflow
|
||||||
|
memset(c->overflow, 0, MAX_OVERFLOW);
|
||||||
|
|
||||||
|
// check if there is again an overflow and copy if needed
|
||||||
|
if ((i = strlen((char *)buffer)) == MAX_ETH_BUFFER - 1)
|
||||||
|
{ // only then we shall be in an overflow situation
|
||||||
|
// DIAG(F("\nPossible overflow situation detected: %d "), i);
|
||||||
|
j = i;
|
||||||
|
while (buffer[i] != c->delimiter)
|
||||||
|
{ // what if there is none: ?
|
||||||
|
// DIAG(F("%c"),(char) buffer[i]);
|
||||||
|
i--;
|
||||||
|
}
|
||||||
|
i++; // start of the buffer to copy
|
||||||
|
l = i;
|
||||||
|
k = j - i; // length to copy
|
||||||
|
|
||||||
|
for (j = 0; j < k; j++, i++)
|
||||||
|
{
|
||||||
|
c->overflow[j] = buffer[i];
|
||||||
|
// DIAG(F("\n%d %d %d %c"),k,j,i, buffer[i]); // c->overflow[j]);
|
||||||
|
}
|
||||||
|
buffer[l] = '\0'; // terminate buffer just after the last '>'
|
||||||
|
// DIAG(F("\nNew buffer: [%s] New overflow: [%s]\n"), (char*) buffer, c->overflow );
|
||||||
|
}
|
||||||
|
|
||||||
|
// breakup the buffer using its changed length
|
||||||
|
i = 0;
|
||||||
|
k = strlen(command); // current length of the command buffer telling us where to start copy in
|
||||||
|
l = strlen((char *)buffer);
|
||||||
|
// DIAG(F("\nCommand buffer: [%s]:[%d:%d:%d]\n"), command, i, l, k );
|
||||||
|
while (i < l)
|
||||||
|
{
|
||||||
|
// DIAG(F("\nl: %d k: %d , i: %d"), l, k, i);
|
||||||
|
command[k] = buffer[i];
|
||||||
|
if (buffer[i] == c->delimiter)
|
||||||
|
{ // closing bracket need to fix if there is none before an opening bracket ?
|
||||||
|
|
||||||
|
command[k+1] = '\0';
|
||||||
|
|
||||||
|
DIAG(F("Command: [%d:%e]\n"),_rseq[c->id], command);
|
||||||
|
|
||||||
|
// parse(client, buffer, true);
|
||||||
|
// sendReply(c->client, command, c);
|
||||||
|
// memset(command, 0, MAX_JMRI_CMD); // clear out the command
|
||||||
|
|
||||||
|
_rseq[c->id]++;
|
||||||
|
j = 0;
|
||||||
|
k = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
k++;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void echoProcessor(Connection *c)
|
||||||
|
{
|
||||||
|
memset(reply, 0, MAX_ETH_BUFFER);
|
||||||
|
sprintf((char *)reply, "ERROR: malformed content in [%s]", buffer);
|
||||||
|
if (c->client->connected())
|
||||||
|
{
|
||||||
|
c->client->write(reply, strlen((char *)reply));
|
||||||
|
_sseq[c->id]++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
void jmriProcessor(Connection *c)
|
||||||
|
{
|
||||||
|
processStream(c);
|
||||||
|
|
||||||
|
}
|
||||||
|
void withrottleProcessor(Connection *c)
|
||||||
|
{
|
||||||
|
processStream(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Reads what is available on the incomming TCP stream and hands it over to the protocol handler.
|
||||||
|
*
|
||||||
|
* @param c Pointer to the connection struct contining relevant information handling the data from that connection
|
||||||
|
*/
|
||||||
|
|
||||||
|
void TransportProcessor::readStream(Connection *c)
|
||||||
|
{
|
||||||
|
// read bytes from a client
|
||||||
|
int count = c->client->read(buffer, MAX_ETH_BUFFER - 1); // count is the amount of data ready for reading, -1 if there is no data, 0 is the connection has been closed
|
||||||
|
buffer[count] = 0;
|
||||||
|
|
||||||
|
// figure out which protocol
|
||||||
|
|
||||||
|
if (!c->isProtocolDefined)
|
||||||
|
{
|
||||||
|
c->p = setAppProtocol(buffer[0], buffer[1], c);
|
||||||
|
c->isProtocolDefined = true;
|
||||||
|
switch (c->p)
|
||||||
|
{
|
||||||
|
case N_DIAG:
|
||||||
|
case DCCEX:
|
||||||
|
{
|
||||||
|
c->delimiter = '>';
|
||||||
|
c->appProtocolHandler = (appProtocolCallback)jmriProcessor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case WITHROTTLE:
|
||||||
|
{
|
||||||
|
c->delimiter = '\n';
|
||||||
|
c->appProtocolHandler = (appProtocolCallback)withrottleProcessor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case HTTP:
|
||||||
|
{
|
||||||
|
c->appProtocolHandler = (appProtocolCallback)httpProcessor;
|
||||||
|
httpReq.callback = NetworkInterface::getHttpCallback();
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case UNKNOWN_PROTOCOL:
|
||||||
|
{
|
||||||
|
DIAG(F("Requests will not be handeled and packet echoed back\n"));
|
||||||
|
c->appProtocolHandler = (appProtocolCallback)echoProcessor;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
IPAddress remote = c->client->remoteIP();
|
||||||
|
|
||||||
|
buffer[count] = '\0'; // terminate the string properly
|
||||||
|
DIAG(F("\nReceived packet of size:[%d] from [%d.%d.%d.%d]\n"), count, remote[0], remote[1], remote[2], remote[3]);
|
||||||
|
DIAG(F("Client #: [%d]\n"), c->id);
|
||||||
|
DIAG(F("Packet: [%e]\n"), buffer);
|
||||||
|
|
||||||
|
// chop the buffer into CS / WiThrottle commands || assemble command across buffer read boundaries
|
||||||
|
c->appProtocolHandler(c);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sending a reply by using the StringFormatter (this will result in every byte send individually which may/will create an important Network overhead).
|
||||||
|
* Here we hook back into the DCC code for actually processing the command using a DCCParser. Alternatively we could use MemeStream in order to build the entiere reply
|
||||||
|
* before ending it (cf. Scratch pad below)
|
||||||
|
*
|
||||||
|
* @param stream Actually the Client to whom to send the reply. As Clients implement Print this is working
|
||||||
|
* @param command The reply to be send ( echo as in sendReply() )
|
||||||
|
* @param blocking if set to true will instruct the DCC code to not use the async callback functions
|
||||||
|
*/
|
||||||
|
void parse(Print *stream, byte *command, bool blocking)
|
||||||
|
{
|
||||||
|
DIAG(F("DCC parsing: [%e]\n"), command);
|
||||||
|
// echo back (as mock parser )
|
||||||
|
StringFormatter::send(stream, F("reply to: %s"), command);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Sending a reply without going through the StringFormatter. Sends the repy in one go
|
||||||
|
*
|
||||||
|
* @param client Client who send the command to which the reply shall be send
|
||||||
|
* @param command Command initaliy recieved to be echoed back
|
||||||
|
*/
|
||||||
|
void sendReply(Client *client, char *command, uint8_t c)
|
||||||
|
{
|
||||||
|
char *number;
|
||||||
|
char seqNumber[6];
|
||||||
|
int i = 0;
|
||||||
|
|
||||||
|
memset(reply, 0, MAX_ETH_BUFFER); // reset reply
|
||||||
|
|
||||||
|
number = strrchr(command, ':'); // replace the int after the last ':'
|
||||||
|
while( &command[i] != number ) { // copy command into the reply upto the last ':'
|
||||||
|
reply[i] = command[i];
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
|
||||||
|
strcat((char *)reply, ":");
|
||||||
|
itoa(_sseq[c], seqNumber, 10);
|
||||||
|
strcat((char *)reply, seqNumber);
|
||||||
|
strcat((char *)reply, ">");
|
||||||
|
|
||||||
|
DIAG(F("Response: [%e]"), (char *)reply);
|
||||||
|
if (client->connected())
|
||||||
|
{
|
||||||
|
client->write(reply, strlen((char *)reply));
|
||||||
|
_sseq[c]++;
|
||||||
|
DIAG(F(" send\n"));
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
/*
|
||||||
|
// Alternative reply mechanism using MemStream thus allowing to send all in one go using the parser
|
||||||
|
streamer.setBufferContentPosition(0, 0);
|
||||||
|
|
||||||
|
// Parse via MemBuffer to be replaced by DCCEXparser.parse later
|
||||||
|
|
||||||
|
parse(&streamer, buffer, true); // set to true to that the execution in DCC is sync
|
||||||
|
|
||||||
|
if (streamer.available() == 0)
|
||||||
|
{
|
||||||
|
DIAG(F("No response\n"));
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer[streamer.available()] = '\0'; // mark end of buffer, so it can be used as a string later
|
||||||
|
DIAG(F("Response: [%s]\n"), (char *)reply);
|
||||||
|
if (clients[i]->connected())
|
||||||
|
{
|
||||||
|
clients[i]->write(reply, streamer.available());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
/* This should work but creates a segmentation fault ??
|
||||||
|
|
||||||
|
// check if we have one parameter with name 'jmri' then send the payload directly and don't call the callback
|
||||||
|
preq = httpReq.getParsedRequest();
|
||||||
|
DIAG(F("Check parameter count\n"));
|
||||||
|
if (*preq.paramCount == 1)
|
||||||
|
{
|
||||||
|
Params *p;
|
||||||
|
int cmp;
|
||||||
|
p = httpReq.getParam(1);
|
||||||
|
|
||||||
|
DIAG(F("Parameter name[%s]\n"), p->name);
|
||||||
|
DIAG(F("Parameter value[%s]\n"), p->value);
|
||||||
|
|
||||||
|
cmp = strcmp("jmri", p->name);
|
||||||
|
if ( cmp == 0 ) {
|
||||||
|
memset(buffer, 0, MAX_ETH_BUFFER); // reset PacktBuffer
|
||||||
|
strncpy((char *)buffer, p->value, strlen(p->value));
|
||||||
|
jmriHandler(client, c);
|
||||||
|
} else {
|
||||||
|
DIAG(F("Callback 1\n"));
|
||||||
|
httpReq.callback(&preq, client);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DIAG(F("Callback 2\n"));
|
||||||
|
httpReq.callback(&preq, client);
|
||||||
|
}
|
||||||
|
DIAG(F("ResetRequest\n"));
|
||||||
|
httpReq.resetRequest();
|
||||||
|
|
||||||
|
} // else do nothing and wait for the next packet
|
||||||
|
}
|
||||||
|
*/
|
66
TransportProcessor.h
Normal file
66
TransportProcessor.h
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
|
||||||
|
/*
|
||||||
|
* © 2020, Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 TransportProcessor_h
|
||||||
|
#define TransportProcessor_h
|
||||||
|
|
||||||
|
#include <Arduino.h>
|
||||||
|
#include <Ethernet.h>
|
||||||
|
#include <WiFiEspAT.h>
|
||||||
|
|
||||||
|
|
||||||
|
#define MAX_ETH_BUFFER 64 // maximum length we read in one go from a TCP packet. Anything longer in one go send to the Arduino may result in unpredictable behaviour.
|
||||||
|
// idealy the windowsize should be set accordingly so that the sender knows to produce only max 250 size packets.
|
||||||
|
#define MAX_OVERFLOW MAX_ETH_BUFFER/2 // length of the overflow buffer to be used for a given connection.
|
||||||
|
#define MAX_JMRI_CMD 32 // MAX Length of a JMRI Command
|
||||||
|
typedef enum {
|
||||||
|
DCCEX, // if char[0] = < opening bracket the client should be a JMRI / DCC EX client_h
|
||||||
|
WITHROTTLE, //
|
||||||
|
HTTP, // If char[0] = G || P || D; if P then char [1] = U || O || A
|
||||||
|
N_DIAG, // '#' send form a telnet client as FIRST message will then reroute all DIAG messages to that client whilst being able to send jmri type commands
|
||||||
|
UNKNOWN_PROTOCOL
|
||||||
|
} appProtocol;
|
||||||
|
|
||||||
|
struct Connection;
|
||||||
|
using appProtocolCallback = void(*)(Connection* c);
|
||||||
|
|
||||||
|
struct Connection {
|
||||||
|
uint8_t id;
|
||||||
|
Client* client;
|
||||||
|
char overflow[MAX_OVERFLOW];
|
||||||
|
appProtocol p;
|
||||||
|
char delimiter = '\0';
|
||||||
|
bool isProtocolDefined = false;
|
||||||
|
appProtocolCallback appProtocolHandler;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
class TransportProcessor
|
||||||
|
{
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
void readStream(Connection *c); // reads incomming packets and hands over to the commandHandle for taking the stream apart for commands
|
||||||
|
|
||||||
|
TransportProcessor(){};
|
||||||
|
~TransportProcessor(){};
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // !Transport_h
|
110
WifiSetup.cpp
Normal file
110
WifiSetup.cpp
Normal file
|
@ -0,0 +1,110 @@
|
||||||
|
/*
|
||||||
|
* © 2020 Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 <Arduino.h>
|
||||||
|
|
||||||
|
#include "DIAG.h"
|
||||||
|
#include "WifiSetup.h"
|
||||||
|
|
||||||
|
bool WifiSetup::setup() {
|
||||||
|
Serial1.begin(AT_BAUD_RATE);
|
||||||
|
WiFi.init(Serial1);
|
||||||
|
|
||||||
|
maxConnections = MAX_WIFI_SOCK;
|
||||||
|
|
||||||
|
if (WiFi.status() == WL_NO_MODULE)
|
||||||
|
{
|
||||||
|
DIAG(F("Communication with WiFi module failed!\n"));
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
DIAG(F("Waiting for connection to WiFi "));
|
||||||
|
while (WiFi.status() != WL_CONNECTED)
|
||||||
|
{
|
||||||
|
delay(1000);
|
||||||
|
DIAG(F("."));
|
||||||
|
}
|
||||||
|
// Setup the protocol handler
|
||||||
|
DIAG(F("\n\nNetwork Protocol: [%s]"), protocol ? "UDP" : "TCP");
|
||||||
|
|
||||||
|
switch (protocol)
|
||||||
|
{
|
||||||
|
case UDP:
|
||||||
|
{
|
||||||
|
DIAG(F("\nUDP over Wifi is not yet supported\n"));
|
||||||
|
connected = false;
|
||||||
|
/*
|
||||||
|
udp = new WiFiUDP();
|
||||||
|
|
||||||
|
maxConnections = 1;
|
||||||
|
// DIAG(F("Wifi/UDP: [%x:%d]"), udp, port);
|
||||||
|
if (udp->begin(port)) // no need to call begin for the WiFiEspAT library but doesn't run properly in the context of the application
|
||||||
|
{
|
||||||
|
connected = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
DIAG(F("\nUDP client failed to start"));
|
||||||
|
connected = false;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case TCP:
|
||||||
|
{
|
||||||
|
server = new WiFiServer(port);
|
||||||
|
server->begin(MAX_WIFI_SOCK, 240);
|
||||||
|
if(server->status()) {
|
||||||
|
connected = true;
|
||||||
|
|
||||||
|
} else {
|
||||||
|
DIAG(F("\nWiFi server failed to start"));
|
||||||
|
connected = false;
|
||||||
|
} // Connection pool not used for WiFi
|
||||||
|
break;
|
||||||
|
};
|
||||||
|
case MQTT: {
|
||||||
|
// do the MQTT setup stuff here
|
||||||
|
};
|
||||||
|
default:
|
||||||
|
{
|
||||||
|
DIAG(F("Unkown Ethernet protocol; Setup failed"));
|
||||||
|
connected = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (connected)
|
||||||
|
{
|
||||||
|
ip = WiFi.localIP();
|
||||||
|
DIAG(F("\nLocal IP address: [%d.%d.%d.%d]"), ip[0], ip[1], ip[2], ip[3]);
|
||||||
|
DIAG(F("\nListening on port: [%d]"), port);
|
||||||
|
dnsip = WiFi.dnsServer1();
|
||||||
|
DIAG(F("\nDNS server IP address: [%d.%d.%d.%d] "), dnsip[0], dnsip[1], dnsip[2], dnsip[3]);
|
||||||
|
DIAG(F("\nNumber of connections: [%d]"), maxConnections);
|
||||||
|
if( protocol == UDP ) return 0; // no server here as we use UDP
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// something went wrong
|
||||||
|
return false;
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
WifiSetup::WifiSetup() {}
|
||||||
|
WifiSetup::WifiSetup(uint16_t p, protocolType pt ) { port = p; protocol = pt; }
|
||||||
|
WifiSetup::~WifiSetup() {}
|
59
WifiSetup.h
Normal file
59
WifiSetup.h
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
/*
|
||||||
|
* © 2020 Gregor Baues. All rights reserved.
|
||||||
|
*
|
||||||
|
* 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 WifiSetup_h
|
||||||
|
#define WifiSetup_h
|
||||||
|
|
||||||
|
#include "NetworkSetup.h"
|
||||||
|
#include "WiFiEspAT.h"
|
||||||
|
|
||||||
|
// Emulate Serial1 on pins 6/7 if not present
|
||||||
|
#if defined(ARDUINO_ARCH_AVR) && !defined(HAVE_HWSERIAL1)
|
||||||
|
#include <SoftwareSerial.h>
|
||||||
|
SoftwareSerial Serial1(6, 7); // RX, TX
|
||||||
|
#define AT_BAUD_RATE 9600
|
||||||
|
#else
|
||||||
|
#define AT_BAUD_RATE 115200
|
||||||
|
#endif
|
||||||
|
|
||||||
|
|
||||||
|
class WifiSetup: public NetworkSetup {
|
||||||
|
|
||||||
|
private:
|
||||||
|
|
||||||
|
WiFiServer* server;
|
||||||
|
WiFiUDP* udp;
|
||||||
|
|
||||||
|
public:
|
||||||
|
|
||||||
|
// WiFiServer *setup(uint16_t port);
|
||||||
|
bool setup();
|
||||||
|
|
||||||
|
WiFiUDP* getUdp() {
|
||||||
|
return udp;
|
||||||
|
}
|
||||||
|
|
||||||
|
WiFiServer* getServer() {
|
||||||
|
return server;
|
||||||
|
}
|
||||||
|
|
||||||
|
WifiSetup();
|
||||||
|
WifiSetup(uint16_t port, protocolType protocol);
|
||||||
|
~WifiSetup();
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
|
@ -11,7 +11,7 @@
|
||||||
[platformio]
|
[platformio]
|
||||||
default_envs =
|
default_envs =
|
||||||
mega2560
|
mega2560
|
||||||
uno
|
;uno
|
||||||
src_dir = .
|
src_dir = .
|
||||||
|
|
||||||
[env]
|
[env]
|
||||||
|
@ -37,6 +37,7 @@ lib_deps =
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
mathertel/LiquidCrystal_PCF8574
|
mathertel/LiquidCrystal_PCF8574
|
||||||
|
jandrassy/WiFiEspAT@^1.3.0
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_flags = --echo
|
monitor_flags = --echo
|
||||||
|
|
||||||
|
@ -50,6 +51,7 @@ lib_deps =
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
mathertel/LiquidCrystal_PCF8574
|
mathertel/LiquidCrystal_PCF8574
|
||||||
|
jandrassy/WiFiEspAT@^1.3.0
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_flags = --echo
|
monitor_flags = --echo
|
||||||
|
|
||||||
|
@ -63,6 +65,7 @@ lib_deps =
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
mathertel/LiquidCrystal_PCF8574
|
mathertel/LiquidCrystal_PCF8574
|
||||||
|
jandrassy/WiFiEspAT@^1.3.0
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_flags = --echo
|
monitor_flags = --echo
|
||||||
|
|
||||||
|
@ -76,5 +79,6 @@ lib_deps =
|
||||||
arduino-libraries/Ethernet
|
arduino-libraries/Ethernet
|
||||||
SPI
|
SPI
|
||||||
mathertel/LiquidCrystal_PCF8574
|
mathertel/LiquidCrystal_PCF8574
|
||||||
|
jandrassy/WiFiEspAT@^1.3.0
|
||||||
monitor_speed = 115200
|
monitor_speed = 115200
|
||||||
monitor_flags = --echo
|
monitor_flags = --echo
|
||||||
|
|
Loading…
Reference in New Issue
Block a user