1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-11-23 08:06:13 +01:00

experimental

This commit is contained in:
Asbelos 2020-10-30 13:00:02 +00:00
parent 4dca656fd2
commit a85131ee17
7 changed files with 133 additions and 289 deletions

View File

@ -38,6 +38,10 @@ void setup()
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT); WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT);
#endif // WIFI_ON #endif // WIFI_ON
#if ETHERNET_ON
EthernetInterface::setup();
#endif // ETHERNET_ON
// Responsibility 3: Start the DCC engine. // Responsibility 3: Start the DCC engine.
// Note: this provides DCC with two motor drivers, main and prog, which handle the motor shield(s) // Note: this provides DCC with two motor drivers, main and prog, which handle the motor shield(s)
// Standard supported devices have pre-configured macros but custome hardware installations require // Standard supported devices have pre-configured macros but custome hardware installations require
@ -67,6 +71,9 @@ void loop()
#if WIFI_ON #if WIFI_ON
WifiInterface::loop(); WifiInterface::loop();
#endif #endif
#if ETHERNET_ON
EthernetInterface::loop();
#endif
LCDDisplay::loop(); // ignored if LCD not in use LCDDisplay::loop(); // ignored if LCD not in use

View File

@ -48,6 +48,7 @@ const int HASH_KEYWORD_SLOW = -17209;
const int HASH_KEYWORD_PROGBOOST = -6353; const int HASH_KEYWORD_PROGBOOST = -6353;
const int HASH_KEYWORD_EEPROM = -7168; const int HASH_KEYWORD_EEPROM = -7168;
const int HASH_KEYWORD_LIMIT = 27413; const int HASH_KEYWORD_LIMIT = 27413;
const int HASH_KEYWORD_ETHERNET = -30767;
int DCCEXParser::stashP[MAX_PARAMS]; int DCCEXParser::stashP[MAX_PARAMS];
bool DCCEXParser::stashBusy; bool DCCEXParser::stashBusy;
@ -609,6 +610,10 @@ bool DCCEXParser::parseD(Print *stream, int params, int p[])
Diag::WIFI = onOff; Diag::WIFI = onOff;
return true; return true;
case HASH_KEYWORD_ETHERNET: // <D ETHERNET ON/OFF>
Diag::ETHERNET = onOff;
return true;
case HASH_KEYWORD_WIT: // <D WIT ON/OFF> case HASH_KEYWORD_WIT: // <D WIT ON/OFF>
Diag::WITHROTTLE = onOff; Diag::WITHROTTLE = onOff;
return true; return true;

View File

@ -16,121 +16,94 @@
* You should have received a copy of the GNU General Public License * You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>. * along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
* *
* Ethernet Interface added by Gregor Baues
*/ */
#include "EthernetInterface.h" #include "EthernetInterface.h"
#include "DIAG.h" #include "DIAG.h"
#include "StringFormatter.h" #include "CommandDistributor.h"
//#include <SPI.h> EthernetInterface * EthernetInterface::singleton=NULL;
#include <Ethernet.h>
#include <EthernetUdp.h>
// Support Functions
/** /**
* @brief Aquire IP Address from DHCP; if that fails try a statically configured address * @brief Setup Ethernet Connection
*
*/
void EthernetInterface::setup()
{
singleton=new EthernetInterface();
if (!singleton->connected) singleton=NULL;
};
/**
* @brief Aquire IP Address from DHCP and start server
* *
* @return true * @return true
* @return false * @return false
*/ */
bool EthernetInterface::setupConnection() EthernetInterface::EthernetInterface()
{ {
byte mac[]=MAC_ADDRESS;
singleton=this; DIAG(F("\n+++++ Ethernet Setup "));
DIAG(F("\nInitialize Ethernet with DHCP:")); if (Ethernet.begin(mac) == 0)
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")); DIAG(F("FAILED "));
Ethernet.begin(EthernetInterface::mac, EthernetInterface::ip); // default ip address if (Ethernet.hardwareStatus() == EthernetNoHardware) DIAG(F("shield not found"));
else if (Ethernet.linkStatus() == LinkOFF) DIAG(F("cable not connected"));
if (Ethernet.hardwareStatus() == EthernetNoHardware) DIAG(F(" ++++++\n"));
{ connected=false;
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; return;
} }
connected=true;
IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address
server = new EthernetServer(LISTEN_PORT); // Ethernet Server listening on default port LISTEN_PORT
server->begin();
LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
LCD(5,F("Port:%d"), LISTEN_PORT);
outboundRing=new RingStream(OUTBOUND_RING_SIZE);
} }
/** /**
* @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 * @brief Main loop for the EthernetInterface
* *
*/ */
void EthernetInterface::tcpHandler() void EthernetInterface::loop()
{ {
singleton->tcpHandler2(); if (!singleton) return;
switch (Ethernet.maintain())
{
case 1:
//renewed fail
DIAG(F("\nEthernet Error: renewed fail\n"));
singleton=NULL;
return;
case 3:
//rebind fail
DIAG(F("Ethernet Error: rebind fail\n"));
singleton=NULL;
return;
default:
//nothing happened
break;
}
singleton->loop2();
} }
void EthernetInterface::tcpHandler2()
void EthernetInterface::loop2()
{ {
// get client from the server // get client from the server
EthernetClient client = getServer().accept(); EthernetClient client = server->accept();
// check for new client // check for new client
if (client) if (client)
@ -148,157 +121,41 @@ bool EthernetInterface::setupConnection()
} }
// check for incoming data from all possible clients // check for incoming data from all possible clients
for (byte i = 0; i < MAX_SOCK_NUM; i++) for (byte socket = 0; socket < MAX_SOCK_NUM; socket++)
{ {
if (clients[i] && clients[i].available() > 0) if (clients[socket]) {
{
// 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 // stop any clients which disconnect
for (byte i = 0; i < MAX_SOCK_NUM; i++) if (!clients[socket].connected())
{ {
if (clients[i] && !clients[i].connected()) if (Diag::ETHERNET) DIAG(F("\nEthernet: disconnect %d \n"), socket);
{ clients[socket].stop();
DIAG(F("Disconnect client #%d \n"), i); clients[socket]=NULL;//????
clients[i].stop(); }
int available=clients[socket].available();
if (available > 0) {
if (Diag::ETHERNET) DIAG(F("\nEthernet: available socket=%d,avail=%d,count="), socket, available);
// read bytes from a client
int count = clients[socket].read(buffer, MAX_ETH_BUFFER);
buffer[count] = '\0'; // terminate the string properly
if (Diag::ETHERNET) DIAG(F("%d:%e\n"), socket,buffer);
// execute with data going directly back
outboundRing->mark(socket);
CommandDistributor::parse(socket,buffer,outboundRing);
outboundRing->commit();
return; // limit the amount of processing that takes place within 1 loop() cycle.
} }
} }
} }
}
// Class Functions // handle at most 1 outbound transmission
/** int socketOut=outboundRing->read();
* @brief Setup Ethernet Connection if (socketOut>=0) {
* int count=outboundRing->count();
* @param pt Protocol used if (Diag::ETHERNET) DIAG(F("Ethernet reply socket=%d, count=:%d\n"), socketOut,count);
* @param localPort Port number for the connection for(;count>0;count--) clients[socketOut].write(outboundRing->read());
*/ clients[socketOut].flush(); //maybe
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();
}

View File

@ -28,6 +28,7 @@
#include <Arduino.h> #include <Arduino.h>
#include <avr/pgmspace.h> #include <avr/pgmspace.h>
#include <Ethernet.h> #include <Ethernet.h>
#include "RingStream.h"
/* some generated mac addresses as EthernetShields don't have one by default in HW. /* 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 * Sometimes they come on a sticker on the EthernetShield then use this address otherwise
@ -46,62 +47,28 @@
* *
*/ */
#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 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 // this one is not used elsewhere and corresponds to your network layout
#define LISTEN_PORT 3366 // default listen port for the server #define LISTEN_PORT 2560 // default listen port for the server
#define MAX_ETH_BUFFER 250 #define MAX_ETH_BUFFER 512
#define OUTBOUND_RING_SIZE 2048
typedef void (*HTTP_CALLBACK)(Print * stream, byte * cmd);
enum protocolType {
TCP,
UDP
};
typedef void (*protocolCallback)();
class EthernetInterface { class EthernetInterface {
private:
EthernetServer server;
public: 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 static void setup();
void setup(protocolType pt); // uses default port number static void loop();
void setup(); // all defaults (protocol/port)
protocolCallback protocolHandler;
void loop();
private: private:
static EthernetInterface * singleton; static EthernetInterface * singleton;
bool connected;
char packetBuffer[UDP_TX_PACKET_MAX_SIZE]; // buffer to hold incoming UDP packet, EthernetInterface();
uint8_t buffer[MAX_ETH_BUFFER]; // buffer provided to the streamer to be filled with the reply (used by TCP also for the recv) void loop2();
MemStream * streamer; // streamer who writes the results to the buffer EthernetServer * server;
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 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
uint8_t buffer[MAX_ETH_BUFFER+1]; // buffer used by TCP for the recv
RingStream * outboundRing;
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 #endif

View File

@ -37,6 +37,7 @@ bool Diag::ACK=false;
bool Diag::CMD=false; bool Diag::CMD=false;
bool Diag::WIFI=false; bool Diag::WIFI=false;
bool Diag::WITHROTTLE=false; bool Diag::WITHROTTLE=false;
bool Diag::ETHERNET=false;
void StringFormatter::diag( const __FlashStringHelper* input...) { void StringFormatter::diag( const __FlashStringHelper* input...) {

View File

@ -34,6 +34,8 @@ class Diag {
static bool CMD; static bool CMD;
static bool WIFI; static bool WIFI;
static bool WITHROTTLE; static bool WITHROTTLE;
static bool ETHERNET;
}; };
class StringFormatter class StringFormatter

View File

@ -27,7 +27,12 @@
#define WIFI_ON true #define WIFI_ON true
#else #else
#define WIFI_ON false #define WIFI_ON false
#endif
#if ENABLE_ETHERNET && (defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) || defined(ARDUINO_SAMD_ZERO))
#define ETHERNET_ON true
#else
#define ETHERNET_ON false
#endif #endif
//////////////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////