1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-02-21 08:16:04 +01:00

Compare commits

..

No commits in common. "dc481a2f0cc1b73d74d2440cd07dc68d2566f99f" and "4aa353edbcf7b574b036766fd668317edd937f32" have entirely different histories.

6 changed files with 178 additions and 259 deletions

View File

@ -141,23 +141,6 @@ void setup()
CommandDistributor::broadcastPower(); CommandDistributor::broadcastPower();
} }
/**************** for future reference
void looptimer(unsigned long timeout, const FSH* message)
{
static unsigned long lasttimestamp = 0;
unsigned long now = micros();
if (timeout != 0) {
unsigned long diff = now - lasttimestamp;
if (diff > timeout) {
DIAG(message);
DIAG(F("DeltaT=%L"), diff);
lasttimestamp = micros();
return;
}
}
lasttimestamp = now;
}
*********************************************/
void loop() void loop()
{ {
// The main sketch has responsibilities during loop() // The main sketch has responsibilities during loop()
@ -173,7 +156,6 @@ void loop()
#ifndef ARDUINO_ARCH_ESP32 #ifndef ARDUINO_ARCH_ESP32
#if WIFI_ON #if WIFI_ON
WifiInterface::loop(); WifiInterface::loop();
#endif //WIFI_ON #endif //WIFI_ON
#else //ARDUINO_ARCH_ESP32 #else //ARDUINO_ARCH_ESP32
#ifndef WIFI_TASK_ON_CORE0 #ifndef WIFI_TASK_ON_CORE0

View File

@ -29,93 +29,77 @@
#include "CommandDistributor.h" #include "CommandDistributor.h"
#include "WiThrottle.h" #include "WiThrottle.h"
#include "DCCTimer.h" #include "DCCTimer.h"
#if __has_include ( "MDNS_Generic.h")
#include "MDNS_Generic.h"
#define DO_MDNS
EthernetUDP udp;
MDNS mdns(udp);
#endif
bool EthernetInterface::connected=false;
EthernetServer * EthernetInterface::server= nullptr;
EthernetClient EthernetInterface::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 EthernetInterface::buffer[MAX_ETH_BUFFER+1]; // buffer used by TCP for the recv
RingStream * EthernetInterface::outboundRing = nullptr;
EthernetInterface * EthernetInterface::singleton=NULL;
/** /**
* @brief Setup Ethernet Connection * @brief Setup Ethernet Connection
* *
*/ */
void EthernetInterface::setup()
void EthernetInterface::setup() // STM32 VERSION
{ {
DIAG(F("Ethernet begin" if (singleton!=NULL) {
#ifdef DO_MDNS DIAG(F("Prog Error!"));
" with mDNS" return;
#endif }
)); if ((singleton=new EthernetInterface()))
return;
DIAG(F("Ethernet not initialized"));
};
#ifdef STM32_ETHERNET
// Set a HOSTNAME for the DHCP request - a nice to have, but hard it seems on LWIP for STM32
// The default is "lwip", which is **always** set in STM32Ethernet/src/utility/ethernetif.cpp
// for some reason. One can edit it to instead read:
// #if LWIP_NETIF_HOSTNAME
// /* Initialize interface hostname */
// if (netif->hostname == NULL)
// netif->hostname = "lwip";
// #endif /* LWIP_NETIF_HOSTNAME */
// Which seems more useful! We should propose the patch... so the following line actually works!
netif_set_hostname(&gnetif, WIFI_HOSTNAME); // Should probably be passed in the contructor...
#endif
#ifdef IP_ADDRESS
static IPAddress myIP(IP_ADDRESS);
#endif
/**
* @brief Aquire IP Address from DHCP and start server
*
* @return true
* @return false
*/
EthernetInterface::EthernetInterface()
{
byte mac[6]; byte mac[6];
DCCTimer::getSimulatedMacAddress(mac); DCCTimer::getSimulatedMacAddress(mac);
connected=false;
#ifdef IP_ADDRESS #ifdef IP_ADDRESS
static IPAddress myIP(IP_ADDRESS); Ethernet.begin(mac, myIP);
Ethernet.begin(mac,myIP); #else
#else if (Ethernet.begin(mac) == 0)
if (Ethernet.begin(mac)==0) {
{ DIAG(F("Ethernet.begin FAILED"));
LCD(4,F("IP: No DHCP")); return;
return; }
} #endif
#endif if (Ethernet.hardwareStatus() == EthernetNoHardware) {
DIAG(F("Ethernet shield not found or W5100"));
}
auto ip = Ethernet.localIP(); // look what IP was obtained (dynamic or static) unsigned long startmilli = millis();
if (!ip) { while ((millis() - startmilli) < 5500) { // Loop to give time to check for cable connection
LCD(4,F("IP: None")); if (Ethernet.linkStatus() == LinkON)
return; break;
} DIAG(F("Ethernet waiting for link (1sec) "));
server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT delay(1000);
server->begin(); }
// now we either do have link of we have a W5100
// Arrange display of IP address and port // where we do not know if we have link. That's
#ifdef LCD_DRIVER // the reason to now run checkLink.
const byte lcdData[]={LCD_DRIVER}; // CheckLinks sets up outboundRing if it does
const bool wideDisplay=lcdData[1]>=24; // data[1] is cols. // not exist yet as well.
#else checkLink();
const bool wideDisplay=true;
#endif
if (wideDisplay) {
// OLEDS or just usb diag is ok on one line.
LCD(4,F("IP %d.%d.%d.%d:%d"), ip[0], ip[1], ip[2], ip[3], IP_PORT);
}
else { // LCDs generally too narrow, so take 2 lines
LCD(4,F("IP %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
LCD(5,F("Port %d"), IP_PORT);
}
outboundRing=new RingStream(OUTBOUND_RING_SIZE);
#ifdef DO_MDNS
mdns.begin(Ethernet.localIP(), WIFI_HOSTNAME); // hostname
mdns.addServiceRecord(WIFI_HOSTNAME "._withrottle", IP_PORT, MDNSServiceTCP);
// Not sure if we need to run it once, but just in case!
mdns.run();
#endif
connected=true;
} }
/**
* @brief Cleanup any resources
*
* @return none
*/
EthernetInterface::~EthernetInterface() {
delete server;
delete outboundRing;
}
/** /**
* @brief Main loop for the EthernetInterface * @brief Main loop for the EthernetInterface
@ -123,132 +107,134 @@ void EthernetInterface::setup() // STM32 VERSION
*/ */
void EthernetInterface::loop() void EthernetInterface::loop()
{ {
if (!connected) return; if (!singleton || (!singleton->checkLink()))
return;
static bool warnedAboutLink=false;
if (Ethernet.linkStatus() == LinkOFF){
if (warnedAboutLink) return;
DIAG(F("Ethernet link OFF"));
warnedAboutLink=true;
return;
}
// link status must be ok here
if (warnedAboutLink) {
DIAG(F("Ethernet link RESTORED"));
warnedAboutLink=false;
}
#ifdef DO_MDNS
// Always do this because we don't want traffic to intefere with being found!
mdns.run();
#endif
//
switch (Ethernet.maintain()) { switch (Ethernet.maintain()) {
case 1: case 1:
//renewed fail //renewed fail
DIAG(F("Ethernet Error: renewed fail")); DIAG(F("Ethernet Error: renewed fail"));
connected=false; singleton=NULL;
return; return;
case 3: case 3:
//rebind fail //rebind fail
DIAG(F("Ethernet Error: rebind fail")); DIAG(F("Ethernet Error: rebind fail"));
connected=false; singleton=NULL;
return; return;
default: default:
//nothing happened //nothing happened
//DIAG(F("maintained"));
break; break;
} }
singleton->loop2();
}
// get client from the server /**
#if defined (STM32_ETHERNET) * @brief Checks ethernet link cable status and detects when it connects / disconnects
// STM32Ethernet doesn't use accept(), just available() *
auto client = server->available(); * @return true when cable is connected, false otherwise
if (client) { */
// check for new client bool EthernetInterface::checkLink() {
byte socket; if (Ethernet.linkStatus() != LinkOFF) { // check for not linkOFF instead of linkON as the W5100 does return LinkUnknown
bool sockfound = false; //if we are not connected yet, setup a new server
for (socket = 0; socket < MAX_SOCK_NUM; socket++) if(!connected) {
{ DIAG(F("Ethernet cable connected"));
if (client == clients[socket]) connected=true;
{ #ifdef IP_ADDRESS
sockfound = true; Ethernet.setLocalIP(myIP); // for static IP, set it again
break; #endif
} IPAddress ip = Ethernet.localIP(); // look what IP was obtained (dynamic or static)
} server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT
if (!sockfound) server->begin();
{ // new client LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
LCD(5,F("Port:%d"), IP_PORT);
// only create a outboundRing it none exists, this may happen if the cable
// gets disconnected and connected again
if(!outboundRing)
outboundRing=new RingStream(OUTBOUND_RING_SIZE);
}
return true;
} else { // connected
DIAG(F("Ethernet cable disconnected"));
connected=false;
//clean up any client
for (byte socket = 0; socket < MAX_SOCK_NUM; socket++) {
if(clients[socket].connected())
clients[socket].stop();
}
// tear down server
delete server;
server = nullptr;
LCD(4,F("IP: None"));
}
return false;
}
void EthernetInterface::loop2() {
if (!outboundRing) { // no idea to call loop2() if we can't handle outgoing data in it
if (Diag::ETHERNET) DIAG(F("No outboundRing"));
return;
}
// get client from the server
EthernetClient client = server->accept();
// check for new client
if (client)
{
if (Diag::ETHERNET) DIAG(F("Ethernet: New client "));
byte socket;
for (socket = 0; socket < MAX_SOCK_NUM; socket++) for (socket = 0; socket < MAX_SOCK_NUM; socket++)
{ {
if (!clients[socket]) if (!clients[socket])
{ {
clients[socket] = client; // On accept() the EthernetServer doesn't track the client anymore
sockfound=true; // so we store it in our client array
if (Diag::ETHERNET) if (Diag::ETHERNET) DIAG(F("Socket %d"),socket);
DIAG(F("Ethernet: New client socket %d"), socket); clients[socket] = client;
break; break;
} }
} }
} if (socket==MAX_SOCK_NUM) DIAG(F("new Ethernet OVERFLOW"));
if (!sockfound) DIAG(F("new Ethernet OVERFLOW"));
} }
#else
auto client = server->accept();
if (client) clients[client.getSocketNumber()]=client;
#endif
// check for incoming data from all possible clients // check for incoming data from all possible clients
for (byte socket = 0; socket < MAX_SOCK_NUM; socket++) for (byte socket = 0; socket < MAX_SOCK_NUM; socket++)
{ {
if (!clients[socket]) continue; // socket is not in use if (clients[socket]) {
// read any bytes from this client int available=clients[socket].available();
auto count = clients[socket].read(buffer, MAX_ETH_BUFFER); if (available > 0) {
if (Diag::ETHERNET) DIAG(F("Ethernet: available socket=%d,avail=%d"), 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(",count=%d:%e"), socket,buffer);
// execute with data going directly back
CommandDistributor::parse(socket,buffer,outboundRing);
return; // limit the amount of processing that takes place within 1 loop() cycle.
}
}
}
if (count<0) continue; // -1 indicates nothing to read // stop any clients which disconnect
for (int socket = 0; socket<MAX_SOCK_NUM; socket++) {
if (count > 0) { // we have incoming data if (clients[socket] && !clients[socket].connected()) {
buffer[count] = '\0'; // terminate the string properly clients[socket].stop();
if (Diag::ETHERNET) DIAG(F("Ethernet s=%d, c=%d b=:%e"), socket, count, buffer); CommandDistributor::forget(socket);
// execute with data going directly back if (Diag::ETHERNET) DIAG(F("Ethernet: disconnect %d "), socket);
CommandDistributor::parse(socket,buffer,outboundRing); }
return; // limit the amount of processing that takes place within 1 loop() cycle. }
}
// count=0 The client has disconnected
clients[socket].stop();
CommandDistributor::forget(socket);
if (Diag::ETHERNET) DIAG(F("Ethernet: disconnect %d "), socket);
}
WiThrottle::loop(outboundRing); WiThrottle::loop(outboundRing);
// handle at most 1 outbound transmission // handle at most 1 outbound transmission
auto socketOut=outboundRing->read(); int socketOut=outboundRing->read();
if (socketOut<0) return; // no outbound pending
if (socketOut >= MAX_SOCK_NUM) { if (socketOut >= MAX_SOCK_NUM) {
// This is a catastrophic code failure and unrecoverable. DIAG(F("Ethernet outboundRing socket=%d error"), socketOut);
DIAG(F("Ethernet outboundRing s=%d error"), socketOut); } else if (socketOut >= 0) {
connected=false; int count=outboundRing->count();
return; if (Diag::ETHERNET) DIAG(F("Ethernet reply socket=%d, count=:%d"), socketOut,count);
for(;count>0;count--) clients[socketOut].write(outboundRing->read());
clients[socketOut].flush(); //maybe
} }
auto count=outboundRing->count();
{
char tmpbuf[count+1]; // one extra for '\0'
for(int i=0;i<count;i++) {
tmpbuf[i] = outboundRing->read();
}
tmpbuf[count]=0;
if (Diag::ETHERNET) DIAG(F("Ethernet reply s=%d, c=%d, b:%e"),
socketOut,count,tmpbuf);
clients[socketOut].write(tmpbuf,count);
}
} }
#endif #endif

View File

@ -35,14 +35,6 @@
#if defined (ARDUINO_TEENSY41) #if defined (ARDUINO_TEENSY41)
#include <NativeEthernet.h> //TEENSY Ethernet Treiber #include <NativeEthernet.h> //TEENSY Ethernet Treiber
#include <NativeEthernetUdp.h> #include <NativeEthernetUdp.h>
#elif defined (ARDUINO_NUCLEO_F429ZI) || defined (ARDUINO_NUCLEO_F439ZI) || defined (ARDUINO_NUCLEO_F4X9ZI)
#include <LwIP.h>
// #include "STM32lwipopts.h"
#include <STM32Ethernet.h>
#include <lwip/netif.h>
extern "C" struct netif gnetif;
#define STM32_ETHERNET
#define MAX_SOCK_NUM 8
#else #else
#include "Ethernet.h" #include "Ethernet.h"
#endif #endif
@ -53,7 +45,7 @@
* *
*/ */
#define MAX_ETH_BUFFER 128 #define MAX_ETH_BUFFER 512
#define OUTBOUND_RING_SIZE 2048 #define OUTBOUND_RING_SIZE 2048
class EthernetInterface { class EthernetInterface {
@ -64,11 +56,16 @@ class EthernetInterface {
static void loop(); static void loop();
private: private:
static bool connected; static EthernetInterface * singleton;
static EthernetServer * server; bool connected;
static 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 EthernetInterface();
static uint8_t buffer[MAX_ETH_BUFFER+1]; // buffer used by TCP for the recv ~EthernetInterface();
static RingStream * outboundRing; void loop2();
bool checkLink();
EthernetServer * server = NULL;
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 = NULL;
}; };
#endif #endif

2
FSH.h
View File

@ -52,7 +52,6 @@ typedef __FlashStringHelper FSH;
#define STRNCPY_P strncpy_P #define STRNCPY_P strncpy_P
#define STRNCMP_P strncmp_P #define STRNCMP_P strncmp_P
#define STRLEN_P strlen_P #define STRLEN_P strlen_P
#define STRCHR_P strchr_P
#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) #if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560)
// AVR_MEGA memory deliberately placed at end of link may need _far functions // AVR_MEGA memory deliberately placed at end of link may need _far functions
@ -99,6 +98,5 @@ typedef char FSH;
#define STRNCPY_P strncpy #define STRNCPY_P strncpy
#define STRNCMP_P strncmp #define STRNCMP_P strncmp
#define STRLEN_P strlen #define STRLEN_P strlen
#define STRCHR_P strchr
#endif #endif
#endif #endif

View File

@ -104,35 +104,10 @@ lib_deps =
${env.lib_deps} ${env.lib_deps}
arduino-libraries/Ethernet arduino-libraries/Ethernet
SPI SPI
MDNS_Generic
lib_ignore = WiFi101
WiFi101_Generic
WiFiEspAT
WiFiMulti_Generic
WiFiNINA_Generic
monitor_speed = 115200 monitor_speed = 115200
monitor_echo = yes monitor_echo = yes
build_flags = build_flags =
[env:mega2560-eth]
platform = atmelavr
board = megaatmega2560
framework = arduino
lib_deps =
${env.lib_deps}
arduino-libraries/Ethernet
MDNS_Generic
SPI
lib_ignore = WiFi101
WiFi101_Generic
WiFiEspAT
WiFiMulti_Generic
WiFiNINA_Generic
monitor_speed = 115200
monitor_echo = yes
[env:mega328] [env:mega328]
platform = atmelavr platform = atmelavr
board = uno board = uno
@ -271,24 +246,6 @@ monitor_echo = yes
; Experimental - Ethernet work still in progress ; Experimental - Ethernet work still in progress
; ;
[env:Nucleo-F429ZI]
platform = ststm32
board = nucleo_f429zi
framework = arduino
lib_deps = ${env.lib_deps}
stm32duino/STM32Ethernet @ ^1.3.0
stm32duino/STM32duino LwIP @ ^2.1.2
MDNS_Generic
lib_ignore = WiFi101
WiFi101_Generic
WiFiEspAT
WiFiMulti_Generic
WiFiNINA_Generic
build_flags = -std=c++17 -Os -g2 -Wunused-variable
monitor_speed = 115200
monitor_echo = yes
upload_protocol = stlink
; [env:Nucleo-F429ZI] ; [env:Nucleo-F429ZI]
; platform = ststm32 ; platform = ststm32
; board = nucleo_f429zi ; board = nucleo_f429zi

View File

@ -3,8 +3,7 @@
#include "StringFormatter.h" #include "StringFormatter.h"
#define VERSION "5.2.80" #define VERSION "5.2.79"
// 5.2.80 - EthernetInterface upgrade
// 5.2.79 - serial manager loop that handles quoted strings // 5.2.79 - serial manager loop that handles quoted strings
// - WiFiESP32 reconfig // - WiFiESP32 reconfig
// 5.2.78 - NeoPixel support. // 5.2.78 - NeoPixel support.