Skip to content

Commit

Permalink
Merge branch 'devel-stm32EC-Ash' into devel-temp-Ash
Browse files Browse the repository at this point in the history
  • Loading branch information
Ash-4 authored May 24, 2024
2 parents 0c96d4f + e878f9a commit 33a4cdb
Show file tree
Hide file tree
Showing 18 changed files with 1,158 additions and 101 deletions.
6 changes: 3 additions & 3 deletions DCCTimerSTM32.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -70,9 +70,9 @@ HardwareSerial Serial5(PD2, PC12); // Rx=PD2, Tx=PC12 -- UART5 - F446RE
defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F439ZI) || defined(ARDUINO_NUCLEO_F4X9ZI)
// Nucleo-144 boards don't have Serial1 defined by default
HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6
HardwareSerial Serial5(PD2, PC12); // Rx=PD2, Tx=PC12 -- UART5
#if !defined(ARDUINO_NUCLEO_F412ZG)
HardwareSerial Serial2(PD6, PD5); // Rx=PD6, Tx=PD5 -- UART5
HardwareSerial Serial2(PD6, PD5); // Rx=PD6, Tx=PD5 -- UART2
#if !defined(ARDUINO_NUCLEO_F412ZG) // F412ZG does not have UART5
HardwareSerial Serial5(PD2, PC12); // Rx=PD2, Tx=PC12 -- UART5
#endif
// Serial3 is defined to use USART3 by default, but is in fact used as the diag console
// via the debugger on the Nucleo-144. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc.
Expand Down
211 changes: 147 additions & 64 deletions EthernetInterface.cpp
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
/*
* © 2024 Morten "Doc" Nielsen
* © 2023-2024 Paul M. Antoine
* © 2022 Bruno Sanches
* © 2021 Fred Decker
* © 2020-2022 Harald Barth
Expand Down Expand Up @@ -29,6 +31,10 @@
#include "CommandDistributor.h"
#include "WiThrottle.h"
#include "DCCTimer.h"
#include "MDNS_Generic.h"

EthernetUDP udp;
MDNS mdns(udp);

EthernetInterface * EthernetInterface::singleton=NULL;
/**
Expand All @@ -41,8 +47,11 @@ void EthernetInterface::setup()
DIAG(F("Prog Error!"));
return;
}
if ((singleton=new EthernetInterface()))
DIAG(F("Ethernet starting... please be patient, especially if no cable is connected!"));
if ((singleton=new EthernetInterface())) {
// DIAG(F("Ethernet Class initialized"));
return;
}
DIAG(F("Ethernet not initialized"));
};

Expand All @@ -59,36 +68,62 @@ static IPAddress myIP(IP_ADDRESS);
*/
EthernetInterface::EthernetInterface()
{
byte mac[6];
DCCTimer::getSimulatedMacAddress(mac);
connected=false;

#ifdef IP_ADDRESS
#if defined(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...
#ifdef IP_ADDRESS
Ethernet.begin(myIP);
#else
Ethernet.begin();
#endif // IP_ADDRESS
#else // All other architectures
byte mac[6]= { 0xDE, 0xAD, 0xBE, 0xEF, 0xFE, 0xED };
DIAG(F("Ethernet attempting to get MAC address"));
DCCTimer::getSimulatedMacAddress(mac);
DIAG(F("Ethernet got MAC address"));
#ifdef IP_ADDRESS
Ethernet.begin(mac, myIP);
#else
#else
if (Ethernet.begin(mac) == 0)
{
DIAG(F("Ethernet.begin FAILED"));
return;
}
#endif
}
#endif // IP_ADDRESS
if (Ethernet.hardwareStatus() == EthernetNoHardware) {
DIAG(F("Ethernet shield not found or W5100"));
}

unsigned long startmilli = millis();
#endif // STM32_ETHERNET

uint32_t startmilli = millis();
while ((millis() - startmilli) < 5500) { // Loop to give time to check for cable connection
if (Ethernet.linkStatus() == LinkON)
break;
DIAG(F("Ethernet waiting for link (1sec) "));
DIAG(F("Ethernet cable connected? Waiting for link (1sec) "));
delay(1000);
}
// now we either do have link of we have a W5100
// where we do not know if we have link. That's
// the reason to now run checkLink.
// CheckLinks sets up outboundRing if it does
// not exist yet as well.
checkLink();
// Now we either do have link or we have a W5100 where we do not know if we have link.
// So now run checkLink() which also sets up outboundRing if it does not exist.
// A false returned means we know we booted without an Ethernet cable, or perhaps there
// is no W5100 and we should say so
if (!checkLink())
{
#if defined(STM32_ETHERNET)
DIAG(F("Ethernet cable disconnected!"));
#else
DIAG(F("Ethernet cable disconnected, or W5100 hardware not present"));
#endif
LCD(4,F("Ethernet DOWN"));
}
}

/**
Expand Down Expand Up @@ -140,31 +175,63 @@ bool EthernetInterface::checkLink() {
DIAG(F("Ethernet cable connected"));
connected=true;
#ifdef IP_ADDRESS
#ifndef STM32_ETHERNET
Ethernet.setLocalIP(myIP); // for static IP, set it again
#endif
IPAddress ip = Ethernet.localIP(); // look what IP was obtained (dynamic or static)
#endif
#if defined (STM32_ETHERNET)
netif_set_hostname(&gnetif, WIFI_HOSTNAME); // Should probably be passed in the contructor...
#ifdef IP_ADDRESS
Ethernet.begin(myIP);
#else
Ethernet.begin();
#endif // IP_ADDRESS
#endif // STM32_ETHERNET

server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT
server->begin();
LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
LCD(5,F("Port:%d"), IP_PORT);
#ifndef IP_ADDRESS
LCD(4,F("Awaiting DHCP..."));
IPAddress ip = Ethernet.localIP(); // look what IP was obtained (dynamic or static)
if (ip[0] == 0) {
DIAG(F("Awaiting DHCP... ip was %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
}
while (ip[0] == 0) { // wait until we are given an IP address from the DHCP server
DIAG(F("Awaiting DHCP... ip was %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
ip = Ethernet.localIP(); // look what IP was obtained (dynamic or static)
}
#else
IPAddress ip = Ethernet.localIP(); // look what IP was obtained (dynamic or static)
#endif
if (MAX_MSG_SIZE < 20) {
LCD(4,F("%d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]);
LCD(5,F("Port:%d Eth"), IP_PORT);
} else {
LCD(4,F("%d.%d.%d.%d:%d"), ip[0], ip[1], ip[2], ip[3], IP_PORT);
}
mdns.begin(Ethernet.localIP(), WIFI_HOSTNAME); // hostname
mdns.addServiceRecord(WIFI_HOSTNAME "._withrottle", IP_PORT, MDNSServiceTCP);
// 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);
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();
} else { // LinkOFF
if (connected) { // Were connected, but no longer without a LINK!
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();
}
mdns.removeServiceRecord(IP_PORT, MDNSServiceTCP);
// tear down server
delete server;
server = nullptr;
LCD(4,F("Ethernet DOWN"));
}
// tear down server
delete server;
server = nullptr;
LCD(4,F("IP: None"));
}
return false;
}
Expand All @@ -175,54 +242,70 @@ void EthernetInterface::loop2() {
return;
}
// get client from the server
#if defined (STM32_ETHERNET)
// STM32Ethernet doesn't use accept(), just available()
EthernetClient client = server->available();
#else
EthernetClient client = server->accept();

#endif
// check for new client
if (client)
{
if (Diag::ETHERNET) DIAG(F("Ethernet: 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("Socket %d"),socket);
clients[socket] = client;
break;
bool sockfound = false;
for (socket = 0; socket < MAX_SOCK_NUM; socket++) {
if (clients[socket] && (clients[socket] == client)) {
sockfound = true;
if (Diag::ETHERNET) DIAG(F("Ethernet: Old client socket %d"),socket);
break;
}
}
if (!sockfound) { // new client
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
clients[socket] = client;
if (Diag::ETHERNET) DIAG(F("Ethernet: New client socket %d"),socket);
break;
}
}
}
if (socket==MAX_SOCK_NUM) DIAG(F("new Ethernet OVERFLOW"));
}

// check for incoming data from all possible clients
for (byte socket = 0; socket < MAX_SOCK_NUM; socket++)
{
if (clients[socket]) {
if (clients[socket]) {
if (!clients[socket].connected()) { // stop any clients which disconnect
CommandDistributor::forget(socket);
clients[socket].stop();
#if defined(ARDUINO_ARCH_AVR)
clients[socket]=NULL;
#else
clients[socket]=(EthernetClient)nullptr;
#endif
//if (Diag::ETHERNET)
DIAG(F("Ethernet: disconnect %d "), socket);
return; // Trick: So that we do not continue in this loop with client that is NULL
}

int available=clients[socket].available();
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.
}
}
int available=clients[socket].available();
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.
}
}
}

// stop any clients which disconnect
for (int socket = 0; socket<MAX_SOCK_NUM; socket++) {
if (clients[socket] && !clients[socket].connected()) {
clients[socket].stop();
CommandDistributor::forget(socket);
if (Diag::ETHERNET) DIAG(F("Ethernet: disconnect %d "), socket);
}
}
mdns.run();

WiThrottle::loop(outboundRing);

Expand Down
10 changes: 10 additions & 0 deletions EthernetInterface.h
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,18 @@
#if defined (ARDUINO_TEENSY41)
#include <NativeEthernet.h> //TEENSY Ethernet Treiber
#include <NativeEthernetUdp.h>
#define MAX_SOCK_NUM 4
#elif defined (ARDUINO_NUCLEO_F429ZI) || defined (ARDUINO_NUCLEO_F439ZI)
#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 10
#else
#include "Ethernet.h"
// #define MAX_SOCK_NUM 4
#endif
#include "RingStream.h"

Expand Down
2 changes: 1 addition & 1 deletion GITHUB_SHA.h
Original file line number Diff line number Diff line change
@@ -1 +1 @@
#define GITHUB_SHA "devel-202405232026Z"
#define GITHUB_SHA "devel-stm32ECa-202405232319Z"
5 changes: 3 additions & 2 deletions I2CManager.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,9 @@ static const FSH * guessI2CDeviceType(uint8_t address) {
return F("Real-time clock");
else if (address >= 0x70 && address <= 0x77)
return F("I2C Mux");
else
return F("?");
else if (address >= 0x90 && address <= 0xAE)
return F("UART");
return F("?");
}

// If not already initialised, initialise I2C
Expand Down
4 changes: 2 additions & 2 deletions I2CManager_STM32.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@
#if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_ARCH_STM32)
#if defined(ARDUINO_NUCLEO_F401RE) || defined(ARDUINO_NUCLEO_F411RE) || defined(ARDUINO_NUCLEO_F446RE) \
|| defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) \
|| defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)
|| defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F439ZI) || defined(ARDUINO_NUCLEO_F446ZE)
// Assume I2C1 for now - default I2C bus on Nucleo-F411RE and likely all Nucleo-64
// and Nucleo-144 variants
I2C_TypeDef *s = I2C1;
Expand Down Expand Up @@ -184,7 +184,7 @@ void I2CManagerClass::I2C_init()
GPIOB->OTYPER |= (1<<8) | (1<<9); // PB8 and PB9 set to open drain output capability
GPIOB->OSPEEDR |= (3<<(8*2)) | (3<<(9*2)); // PB8 and PB9 set to High Speed mode
GPIOB->PUPDR &= ~((3<<(8*2)) | (3<<(9*2))); // Clear all PUPDR bits for PB8 and PB9
GPIOB->PUPDR |= (1<<(8*2)) | (1<<(9*2)); // PB8 and PB9 set to pull-up capability
// GPIOB->PUPDR |= (1<<(8*2)) | (1<<(9*2)); // PB8 and PB9 set to pull-up capability
// Alt Function High register routing pins PB8 and PB9 for I2C1:
// Bits (3:2:1:0) = 0:1:0:0 --> AF4 for pin PB8
// Bits (7:6:5:4) = 0:1:0:0 --> AF4 for pin PB9
Expand Down
3 changes: 2 additions & 1 deletion IODevice.h
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,8 @@
#define iodevice_h

// Define symbol DIAG_IO to enable diagnostic output
//#define DIAG_IO Y
//#define DIAG_IO


// Define symbol DIAG_LOOPTIMES to enable CS loop execution time to be reported
//#define DIAG_LOOPTIMES
Expand Down
Loading

0 comments on commit 33a4cdb

Please sign in to comment.