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

Compare commits

...

25 Commits

Author SHA1 Message Date
Asbelos
dc481a2f0c EthernetInterface 2024-09-23 09:34:26 +01:00
Asbelos
692f97e480 Merge branch 'devel_fozzie2' into devel 2024-09-23 09:16:39 +01:00
Asbelos
7fb7751f19 Merge branch 'devel_fozzie2' of https://github.com/DCC-EX/CommandStation-EX into devel_fozzie2 2024-09-23 08:50:13 +01:00
Asbelos
546ddd8139 STRCHR_P for sensorcam 2024-09-23 08:42:49 +01:00
Harald Barth
ec42c09e06 Merge branch 'devel_fozzie2' of https://github.com/DCC-EX/CommandStation-EX into devel_fozzie2 2024-09-11 11:25:21 +02:00
Asbelos
4174c2a4ab Merge branch 'devel_fozzie2' of https://github.com/DCC-EX/CommandStation-EX into devel_fozzie2 2024-08-30 09:34:25 +01:00
pmantoine
30236f9b36 STM32 Ethernet fixed 2024-08-30 11:52:27 +08:00
Asbelos
8245208b2b stm32 compiles 2024-08-25 17:26:33 +01:00
Asbelos
4ed2ee9adc mDNS restored on mega 2024-08-25 16:50:49 +01:00
Asbelos
06a353cfa0 stm32 merge (mdns disabled} 2024-08-25 16:29:59 +01:00
Harald Barth
dfe9e6b69f platformio eth debug target 2024-08-25 17:01:58 +02:00
Asbelos
4d84eccac3 LCD and link warning 2024-08-20 19:51:45 +01:00
Harald Barth
edb02a00ce allow static IP (again) 2024-08-19 08:59:32 +02:00
Asbelos
5db19a0fb8 Ethgernet simplification 2024-08-18 20:32:05 +01:00
Harald Barth
b62661c337 tag 2024-08-17 23:09:09 +02:00
Harald Barth
048ba3fd1e replace socket.connected() with check for return value of socket.read() 2024-08-17 20:18:59 +02:00
Harald Barth
c8c3697fa0 write buffer 2024-08-09 15:02:11 +02:00
Harald Barth
8c3c5dfe33 do not flush 2024-08-09 14:34:30 +02:00
Harald Barth
92288603bf do not available, just try to read 2024-08-09 12:01:53 +02:00
Harald Barth
80c8b3ef62 better name 2024-08-09 11:57:54 +02:00
Harald Barth
127f3acce5 send whole outbuffer 2024-08-09 11:46:33 +02:00
Harald Barth
690c629e6d looptimer more diag in EthernetInterface 2024-08-09 09:16:56 +02:00
Harald Barth
e328ea5c5d Merge branch 'devel' into devel-fozzie 2024-08-08 10:52:50 +02:00
Harald Barth
923b031d06 Gittag 2024-08-07 21:14:07 +02:00
Harald Barth
7e29011d63 looptimer test 1 2024-08-07 21:13:44 +02:00
6 changed files with 257 additions and 176 deletions

View File

@ -141,6 +141,23 @@ 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()
@ -156,6 +173,7 @@ 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,77 +29,93 @@
#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
{ {
if (singleton!=NULL) { DIAG(F("Ethernet begin"
DIAG(F("Prog Error!")); #ifdef DO_MDNS
return; " with mDNS"
} #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
Ethernet.begin(mac, myIP); static IPAddress myIP(IP_ADDRESS);
#else Ethernet.begin(mac,myIP);
if (Ethernet.begin(mac) == 0) #else
{ if (Ethernet.begin(mac)==0)
DIAG(F("Ethernet.begin FAILED")); {
return; LCD(4,F("IP: No DHCP"));
} return;
#endif }
if (Ethernet.hardwareStatus() == EthernetNoHardware) { #endif
DIAG(F("Ethernet shield not found or W5100"));
}
unsigned long startmilli = millis(); auto ip = Ethernet.localIP(); // look what IP was obtained (dynamic or static)
while ((millis() - startmilli) < 5500) { // Loop to give time to check for cable connection if (!ip) {
if (Ethernet.linkStatus() == LinkON) LCD(4,F("IP: None"));
break; return;
DIAG(F("Ethernet waiting for link (1sec) ")); }
delay(1000); server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT
} server->begin();
// now we either do have link of we have a W5100
// where we do not know if we have link. That's // Arrange display of IP address and port
// the reason to now run checkLink. #ifdef LCD_DRIVER
// CheckLinks sets up outboundRing if it does const byte lcdData[]={LCD_DRIVER};
// not exist yet as well. const bool wideDisplay=lcdData[1]>=24; // data[1] is cols.
checkLink(); #else
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
@ -107,134 +123,132 @@ EthernetInterface::~EthernetInterface() {
*/ */
void EthernetInterface::loop() void EthernetInterface::loop()
{ {
if (!singleton || (!singleton->checkLink())) if (!connected) return;
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"));
singleton=NULL; connected=false;
return; return;
case 3: case 3:
//rebind fail //rebind fail
DIAG(F("Ethernet Error: rebind fail")); DIAG(F("Ethernet Error: rebind fail"));
singleton=NULL; connected=false;
return; return;
default: default:
//nothing happened //nothing happened
//DIAG(F("maintained"));
break; break;
} }
singleton->loop2();
}
/** // get client from the server
* @brief Checks ethernet link cable status and detects when it connects / disconnects #if defined (STM32_ETHERNET)
* // STM32Ethernet doesn't use accept(), just available()
* @return true when cable is connected, false otherwise auto client = server->available();
*/ if (client) {
bool EthernetInterface::checkLink() { // check for new client
if (Ethernet.linkStatus() != LinkOFF) { // check for not linkOFF instead of linkON as the W5100 does return LinkUnknown byte socket;
//if we are not connected yet, setup a new server bool sockfound = false;
if(!connected) { for (socket = 0; socket < MAX_SOCK_NUM; socket++)
DIAG(F("Ethernet cable connected")); {
connected=true; if (client == clients[socket])
#ifdef IP_ADDRESS {
Ethernet.setLocalIP(myIP); // for static IP, set it again sockfound = true;
#endif break;
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 }
server->begin(); if (!sockfound)
LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); { // new client
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])
{ {
// On accept() the EthernetServer doesn't track the client anymore clients[socket] = client;
// so we store it in our client array sockfound=true;
if (Diag::ETHERNET) DIAG(F("Socket %d"),socket); if (Diag::ETHERNET)
clients[socket] = client; DIAG(F("Ethernet: New client socket %d"), socket);
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]) { if (!clients[socket]) continue; // socket is not in use
int available=clients[socket].available(); // read any bytes from this client
if (available > 0) { auto count = clients[socket].read(buffer, MAX_ETH_BUFFER);
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.
}
}
}
// stop any clients which disconnect if (count<0) continue; // -1 indicates nothing to read
for (int socket = 0; socket<MAX_SOCK_NUM; socket++) {
if (clients[socket] && !clients[socket].connected()) { if (count > 0) { // we have incoming data
clients[socket].stop(); buffer[count] = '\0'; // terminate the string properly
CommandDistributor::forget(socket); if (Diag::ETHERNET) DIAG(F("Ethernet s=%d, c=%d b=:%e"), socket, count, buffer);
if (Diag::ETHERNET) DIAG(F("Ethernet: disconnect %d "), socket); // execute with data going directly back
} 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
int socketOut=outboundRing->read(); auto socketOut=outboundRing->read();
if (socketOut<0) return; // no outbound pending
if (socketOut >= MAX_SOCK_NUM) { if (socketOut >= MAX_SOCK_NUM) {
DIAG(F("Ethernet outboundRing socket=%d error"), socketOut); // This is a catastrophic code failure and unrecoverable.
} else if (socketOut >= 0) { DIAG(F("Ethernet outboundRing s=%d error"), socketOut);
int count=outboundRing->count(); connected=false;
if (Diag::ETHERNET) DIAG(F("Ethernet reply socket=%d, count=:%d"), socketOut,count); return;
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,6 +35,14 @@
#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
@ -45,7 +53,7 @@
* *
*/ */
#define MAX_ETH_BUFFER 512 #define MAX_ETH_BUFFER 128
#define OUTBOUND_RING_SIZE 2048 #define OUTBOUND_RING_SIZE 2048
class EthernetInterface { class EthernetInterface {
@ -56,16 +64,11 @@ class EthernetInterface {
static void loop(); static void loop();
private: private:
static EthernetInterface * singleton; static bool connected;
bool connected; static EthernetServer * server;
EthernetInterface(); 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
void loop2(); static RingStream * outboundRing;
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,6 +52,7 @@ 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
@ -98,5 +99,6 @@ 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,10 +104,35 @@ 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
@ -246,6 +271,24 @@ 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,7 +3,8 @@
#include "StringFormatter.h" #include "StringFormatter.h"
#define VERSION "5.2.79" #define VERSION "5.2.80"
// 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.