mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2024-11-23 16:16:13 +01:00
commit
6b4199be27
|
@ -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
|
||||||
|
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -16,252 +16,63 @@
|
||||||
* 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
|
|
||||||
*
|
|
||||||
* @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
|
* @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()
|
void EthernetInterface::setup()
|
||||||
{
|
{
|
||||||
setup(TCP, LISTEN_PORT);
|
singleton=new EthernetInterface();
|
||||||
|
if (!singleton->connected) singleton=NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Aquire IP Address from DHCP and start server
|
||||||
|
*
|
||||||
|
* @return true
|
||||||
|
* @return false
|
||||||
|
*/
|
||||||
|
EthernetInterface::EthernetInterface()
|
||||||
|
{
|
||||||
|
byte mac[]=MAC_ADDRESS;
|
||||||
|
|
||||||
|
DIAG(F("\n+++++ Ethernet Setup "));
|
||||||
|
connected=false;
|
||||||
|
|
||||||
|
if (Ethernet.begin(mac) == 0)
|
||||||
|
{
|
||||||
|
DIAG(F("begin FAILED\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
DIAG(F("begin OK."));
|
||||||
|
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
|
||||||
|
DIAG(F("shield not found\n"));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (Ethernet.linkStatus() == LinkOFF) {
|
||||||
|
DIAG(F("cable not connected\n"));
|
||||||
|
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -270,35 +81,93 @@ void EthernetInterface::setup()
|
||||||
*/
|
*/
|
||||||
void EthernetInterface::loop()
|
void EthernetInterface::loop()
|
||||||
{
|
{
|
||||||
|
if (!singleton) return;
|
||||||
|
|
||||||
switch (Ethernet.maintain())
|
switch (Ethernet.maintain())
|
||||||
{
|
{
|
||||||
case 1:
|
case 1:
|
||||||
//renewed fail
|
//renewed fail
|
||||||
DIAG(F("\nError: renewed fail"));
|
DIAG(F("\nEthernet Error: renewed fail\n"));
|
||||||
break;
|
singleton=NULL;
|
||||||
|
return;
|
||||||
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:
|
case 3:
|
||||||
//rebind fail
|
//rebind fail
|
||||||
DIAG(F("Error: rebind fail"));
|
DIAG(F("Ethernet Error: rebind fail\n"));
|
||||||
break;
|
singleton=NULL;
|
||||||
|
return;
|
||||||
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:
|
default:
|
||||||
//nothing happened
|
//nothing happened
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
protocolHandler();
|
|
||||||
|
singleton->loop2();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void EthernetInterface::loop2()
|
||||||
|
{
|
||||||
|
// get client from the server
|
||||||
|
EthernetClient client = server->accept();
|
||||||
|
|
||||||
|
// check for new client
|
||||||
|
if (client)
|
||||||
|
{
|
||||||
|
if (Diag::ETHERNET) DIAG(F("\nEthernet: New client "));
|
||||||
|
byte socket;
|
||||||
|
for (socket = 0; socket < MAX_SOCK_NUM; socket++)
|
||||||
|
{
|
||||||
|
if (!clients[socket])
|
||||||
|
{
|
||||||
|
// On accept() the EthernetServer doesn't track the client anymore
|
||||||
|
// so we store it in our client array
|
||||||
|
if (Diag::ETHERNET) DIAG(F("%d\n"),socket);
|
||||||
|
clients[socket] = client;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (socket==MAX_SOCK_NUM) DIAG(F("new Ethernet OVERFLOW\n"));
|
||||||
|
}
|
||||||
|
|
||||||
|
// check for incoming data from all possible clients
|
||||||
|
for (byte socket = 0; socket < MAX_SOCK_NUM; socket++)
|
||||||
|
{
|
||||||
|
if (clients[socket]) {
|
||||||
|
|
||||||
|
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.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop any clients which disconnect
|
||||||
|
for (int socket = 0; socket<MAX_SOCK_NUM; socket++) {
|
||||||
|
if (clients[socket] && !clients[socket].connected()) {
|
||||||
|
clients[socket].stop();
|
||||||
|
if (Diag::ETHERNET) DIAG(F("\nEthernet: disconnect %d \n"), socket);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// handle at most 1 outbound transmission
|
||||||
|
int socketOut=outboundRing->read();
|
||||||
|
if (socketOut>=0) {
|
||||||
|
int count=outboundRing->count();
|
||||||
|
if (Diag::ETHERNET) DIAG(F("Ethernet reply socket=%d, count=:%d\n"), socketOut,count);
|
||||||
|
for(;count>0;count--) clients[socketOut].write(outboundRing->read());
|
||||||
|
clients[socketOut].flush(); //maybe
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
#define ReadPin digitalRead2
|
#define ReadPin digitalRead2
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte brake_pin,
|
MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin,
|
||||||
byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin) {
|
byte current_pin, float sense_factor, unsigned int trip_milliamps, byte fault_pin) {
|
||||||
powerPin=power_pin;
|
powerPin=power_pin;
|
||||||
signalPin=signal_pin;
|
signalPin=signal_pin;
|
||||||
|
@ -46,7 +46,8 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte
|
||||||
tripMilliamps=trip_milliamps;
|
tripMilliamps=trip_milliamps;
|
||||||
rawCurrentTripValue=(int)(trip_milliamps / sense_factor);
|
rawCurrentTripValue=(int)(trip_milliamps / sense_factor);
|
||||||
pinMode(powerPin, OUTPUT);
|
pinMode(powerPin, OUTPUT);
|
||||||
pinMode(brakePin, OUTPUT);
|
pinMode(brakePin < 0 ? -brakePin : brakePin, OUTPUT);
|
||||||
|
setBrake(false);
|
||||||
pinMode(signalPin, OUTPUT);
|
pinMode(signalPin, OUTPUT);
|
||||||
if (signalPin2 != UNUSED_PIN) pinMode(signalPin2, OUTPUT);
|
if (signalPin2 != UNUSED_PIN) pinMode(signalPin2, OUTPUT);
|
||||||
pinMode(currentPin, INPUT);
|
pinMode(currentPin, INPUT);
|
||||||
|
@ -54,10 +55,32 @@ MotorDriver::MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotorDriver::setPower(bool on) {
|
void MotorDriver::setPower(bool on) {
|
||||||
|
if (brakePin == -4 && on) {
|
||||||
|
// toggle brake before turning power on - resets overcurrent error
|
||||||
|
// on the Pololu board if brake is wired to ^D2.
|
||||||
|
setBrake(true);
|
||||||
|
setBrake(false);
|
||||||
|
}
|
||||||
WritePin(powerPin, on ? HIGH : LOW);
|
WritePin(powerPin, on ? HIGH : LOW);
|
||||||
}
|
}
|
||||||
void MotorDriver::setBrake( bool on) {
|
|
||||||
WritePin(brakePin, on ? HIGH : LOW);
|
// setBrake applies brake if on == true. So to get
|
||||||
|
// voltage from the motor bride one needs to do a
|
||||||
|
// setBrake(false).
|
||||||
|
// If the brakePin is negative that means the sense
|
||||||
|
// of the brake pin on the motor bridge is inverted
|
||||||
|
// (HIGH == release brake) and setBrake does
|
||||||
|
// compensate for that.
|
||||||
|
//
|
||||||
|
void MotorDriver::setBrake(bool on) {
|
||||||
|
bool state = on;
|
||||||
|
byte pin = brakePin;
|
||||||
|
if (brakePin < 0) {
|
||||||
|
pin=-pin;
|
||||||
|
state=!state;
|
||||||
|
}
|
||||||
|
WritePin(pin, state ? HIGH : LOW);
|
||||||
|
//DIAG(F("BrakePin: %d is %d\n"), pin, ReadPin(pin));
|
||||||
}
|
}
|
||||||
|
|
||||||
void MotorDriver::setSignal( bool high) {
|
void MotorDriver::setSignal( bool high) {
|
||||||
|
|
|
@ -20,9 +20,13 @@
|
||||||
#define MotorDriver_h
|
#define MotorDriver_h
|
||||||
// Virtualised Motor shield 1-track hardware Interface
|
// Virtualised Motor shield 1-track hardware Interface
|
||||||
|
|
||||||
|
#ifndef UNUSED_PIN // sync define with the one in MotorDrivers.h
|
||||||
|
#define UNUSED_PIN 127 // inside int8_t
|
||||||
|
#endif
|
||||||
|
|
||||||
class MotorDriver {
|
class MotorDriver {
|
||||||
public:
|
public:
|
||||||
MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte brake_pin, byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin);
|
MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin, float senseFactor, unsigned int tripMilliamps, byte faultPin);
|
||||||
virtual void setPower( bool on);
|
virtual void setPower( bool on);
|
||||||
virtual void setSignal( bool high);
|
virtual void setSignal( bool high);
|
||||||
virtual void setBrake( bool on);
|
virtual void setBrake( bool on);
|
||||||
|
@ -34,11 +38,10 @@ class MotorDriver {
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
byte powerPin, signalPin, signalPin2, brakePin,currentPin,faultPin;
|
byte powerPin, signalPin, signalPin2, currentPin, faultPin;
|
||||||
|
int8_t brakePin; // negative means pin is inverted
|
||||||
float senseFactor;
|
float senseFactor;
|
||||||
unsigned int tripMilliamps;
|
unsigned int tripMilliamps;
|
||||||
int rawCurrentTripValue;
|
int rawCurrentTripValue;
|
||||||
const byte UNUSED_PIN = 255;
|
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
|
@ -13,10 +13,16 @@
|
||||||
// similar to those defined here, WITHOUT editing this file. You can put your
|
// similar to those defined here, WITHOUT editing this file. You can put your
|
||||||
// custom defines in config.h.
|
// custom defines in config.h.
|
||||||
|
|
||||||
const byte UNUSED_PIN = 255;
|
#ifndef UNUSED_PIN // sync define with the one in MotorDriver.h
|
||||||
|
#define UNUSED_PIN 127 // inside int8_t
|
||||||
|
#endif
|
||||||
|
|
||||||
// MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, byte brake_pin, byte current_pin,
|
// MotorDriver(byte power_pin, byte signal_pin, byte signal_pin2, int8_t brake_pin, byte current_pin,
|
||||||
// float senseFactor, unsigned int tripMilliamps, byte faultPin);
|
// float senseFactor, unsigned int tripMilliamps, byte faultPin);
|
||||||
|
//
|
||||||
|
// If the brakePin is negative that means the sense
|
||||||
|
// of the brake pin on the motor bridge is inverted
|
||||||
|
// (HIGH == release brake)
|
||||||
|
|
||||||
// Arduino standard Motor Shield
|
// Arduino standard Motor Shield
|
||||||
#define STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), \
|
#define STANDARD_MOTOR_SHIELD F("STANDARD_MOTOR_SHIELD"), \
|
||||||
|
@ -25,8 +31,18 @@ const byte UNUSED_PIN = 255;
|
||||||
|
|
||||||
// Pololu Motor Shield
|
// Pololu Motor Shield
|
||||||
#define POLOLU_MOTOR_SHIELD F("POLOLU_MOTOR_SHIELD"), \
|
#define POLOLU_MOTOR_SHIELD F("POLOLU_MOTOR_SHIELD"), \
|
||||||
new MotorDriver(4, 7, UNUSED_PIN, 9, A0, 18, 3000, 12), \
|
new MotorDriver( 9, 7, UNUSED_PIN, -4, A0, 18, 3000, 12), \
|
||||||
new MotorDriver(2, 8, UNUSED_PIN, 10, A1, 18, 3000, UNUSED_PIN)
|
new MotorDriver(10, 8, UNUSED_PIN, UNUSED_PIN, A1, 18, 3000, UNUSED_PIN)
|
||||||
|
//
|
||||||
|
// Actually, on the Pololu MC33926 shield the enable lines are tied together on pin 4 and the
|
||||||
|
// pins 9 and 10 work as "inverted brake" but as we turn on and off the tracks individually
|
||||||
|
// via the power pins we above use 9 and 10 as power pins and 4 as "inverted brake" which in this
|
||||||
|
// version of the code always will be high. That means this config is not usable for generating
|
||||||
|
// a railcom cuotout in the future. For that one must wire the second ^D2 to pin 2 and define
|
||||||
|
// the motor driver like this:
|
||||||
|
// new MotorDriver(4, 7, UNUSED_PIN, -9, A0, 18, 3000, 12)
|
||||||
|
// new MotorDriver(2, 8, UNUSED_PIN, -10, A1, 18, 3000, UNUSED_PIN)
|
||||||
|
// See Pololu dial_mc33926_shield_schematic.pdf and truth table on page 17 of the MC33926 data sheet.
|
||||||
|
|
||||||
// Firebox Mk1
|
// Firebox Mk1
|
||||||
#define FIREBOX_MK1 F("FIREBOX_MK1"), \
|
#define FIREBOX_MK1 F("FIREBOX_MK1"), \
|
||||||
|
|
|
@ -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...) {
|
||||||
|
|
|
@ -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
|
||||||
|
|
|
@ -36,8 +36,7 @@ class WifiInboundHandler {
|
||||||
IPD_IGNORE_DATA, // got +IPD,c,ll,: ignoring the data that won't fit inblound Ring
|
IPD_IGNORE_DATA, // got +IPD,c,ll,: ignoring the data that won't fit inblound Ring
|
||||||
|
|
||||||
GOT_CLIENT_ID, // clientid prefix to CONNECTED / CLOSED
|
GOT_CLIENT_ID, // clientid prefix to CONNECTED / CLOSED
|
||||||
GOT_CLIENT_ID2, // clientid prefix to CONNECTED / CLOSED
|
GOT_CLIENT_ID2 // clientid prefix to CONNECTED / CLOSED
|
||||||
GOT_CLIENT_ID3 // clientid prefix to CONNECTED / CLOSED
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
||||||
////////////////////////////////////////////////////////////////////////////////
|
////////////////////////////////////////////////////////////////////////////////
|
||||||
|
|
Loading…
Reference in New Issue
Block a user