From e131a9cce8b65fe08771ed0699ffc9db73fd6861 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 2 Nov 2024 21:25:21 +0100 Subject: [PATCH 01/28] Ethernet: Reject additonal connection instead of looping on OVERFLOW --- EthernetInterface.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index f8306cb..1e49a21 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -144,7 +144,9 @@ void EthernetInterface::acceptClient() { // STM32 version return; } } - DIAG(F("Ethernet OVERFLOW")); + // reached here only if more than MAX_SOCK_NUM clients want to connect + DIAG(F("Ethernet more than %d clients, not accepting new connection"), MAX_SOCK_NUM); + client.stop(); } #else void EthernetInterface::acceptClient() { // non-STM32 version From 657fb7009c7c32ff32fcba6a8bc6f0bb7275fd41 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 4 Nov 2024 17:27:34 +0100 Subject: [PATCH 02/28] tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index f1dae0d..46f45ee 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202409300806Z" +#define GITHUB_SHA "devel-202411041626Z" From 701f4e852f385c0004d4f6ad9618b4bf4e5b2d3c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 4 Nov 2024 17:28:51 +0100 Subject: [PATCH 03/28] Variable number of TCP clients --- CommandDistributor.cpp | 3 +-- CommandDistributor.h | 4 ++-- EthernetInterface.h | 2 +- config.example.h | 7 +++++++ defines.h | 15 +++++++++++++++ 5 files changed, 26 insertions(+), 5 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index e889f62..924e0af 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -56,8 +56,7 @@ template void CommandDistributor::broadcastReply(clientType t #ifdef CD_HANDLE_RING // wifi or ethernet ring streams with multiple client types RingStream * CommandDistributor::ring=0; - CommandDistributor::clientType CommandDistributor::clients[8]={ - NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE,NONE_TYPE}; +CommandDistributor::clientType CommandDistributor::clients[MAX_NUM_TCP_CLIENTS]={ NONE_TYPE }; // 0 is and must be NONE_TYPE // Parse is called by Withrottle or Ethernet interface to determine which // protocol the client is using and call the appropriate part of dcc++Ex diff --git a/CommandDistributor.h b/CommandDistributor.h index d86b87f..c1bb7f3 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -36,13 +36,13 @@ class CommandDistributor { public: - enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE}; + enum clientType: byte {NONE_TYPE=0,COMMAND_TYPE,WITHROTTLE_TYPE}; private: static void broadcastToClients(clientType type); static StringBuffer * broadcastBufferWriter; #ifdef CD_HANDLE_RING static RingStream * ring; - static clientType clients[8]; + static clientType clients[MAX_NUM_TCP_CLIENTS]; #endif public : static void parse(byte clientId,byte* buffer, RingStream * ring); diff --git a/EthernetInterface.h b/EthernetInterface.h index 16156fa..6cea6e8 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -45,7 +45,7 @@ #include extern "C" struct netif gnetif; #define STM32_ETHERNET - #define MAX_SOCK_NUM 8 + #define MAX_SOCK_NUM MAX_NUM_TCP_CLIENTS #else #include "Ethernet.h" #endif diff --git a/config.example.h b/config.example.h index d7dab5e..ff4652f 100644 --- a/config.example.h +++ b/config.example.h @@ -137,6 +137,13 @@ The configuration file for DCC-EX Command Station // //#define ENABLE_ETHERNET true +///////////////////////////////////////////////////////////////////////////////////// +// +// MAX_NUM_TCP_CLIENTS: If you on STM32 Ethernet (and only there) want more than +// 10 TCP clients, enable this here **AND** follow the instructions in STM32lwiopts.h +// +//#define MAX_NUM_TCP_CLIENTS 20 + ///////////////////////////////////////////////////////////////////////////////////// // diff --git a/defines.h b/defines.h index 2c3ee55..0cd891f 100644 --- a/defines.h +++ b/defines.h @@ -239,4 +239,19 @@ #endif #endif +#if defined(ARDUINO_ARCH_STM32) +// Allow override of MAX_NUM_TCP_CLIENTS but default is 10 + #ifndef MAX_NUM_TCP_CLIENTS + #define MAX_NUM_TCP_CLIENTS 10 + #endif +#else + #if defined(ARDUINO_ARCH_ESP32) +// Espressif LWIP stack + #define MAX_NUM_TCP_CLIENTS 10 + #else +// Wifi shields etc + #define MAX_NUM_TCP_CLIENTS 8 + #endif #endif + +#endif //DEFINES_H From bcdf9cb1c5eebaeaed752672ac59832e422f4bf2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 4 Nov 2024 17:35:34 +0100 Subject: [PATCH 04/28] Add STM32lwipopts.h --- STM32lwipopts.h | 261 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 261 insertions(+) create mode 100644 STM32lwipopts.h diff --git a/STM32lwipopts.h b/STM32lwipopts.h new file mode 100644 index 0000000..68cb992 --- /dev/null +++ b/STM32lwipopts.h @@ -0,0 +1,261 @@ +/** + ****************************************************************************** + * @file STM32lwipopts_default.h + * @author MCD Application Team + * @brief lwIP Options Configuration. + ****************************************************************************** + * @attention + * + *

© Copyright (c) 2017 STMicroelectronics International N.V. + * All rights reserved.

+ * + * This software component is licensed by ST under Ultimate Liberty license + * SLA0044, the "License"; You may not use this file except in compliance with + * the License. You may obtain a copy of the License at: + * www.st.com/SLA0044 + * + ****************************************************************************** + */ +#ifndef __STM32LWIPOPTS_H__ +#define __STM32LWIPOPTS_H__ + +// we can not include that here so we +// need to duplicate that.define +// #include "defines.h" +#define MAX_NUM_TCP_CLIENTS 20 + +// increase ARP cache +#define MEMP_NUM_ARP_QUEUE MAX_NUM_TCP_CLIENTS+3 // one for each client (all on different HW) and a few extra + +// Example for debug +//#define LWIP_DEBUG 1 +//#define TCP_DEBUG LWIP_DBG_ON + +/** + * NO_SYS==1: Provides VERY minimal functionality. Otherwise, + * use lwIP facilities. + */ +#define NO_SYS 1 + +/** + * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain + * critical regions during buffer allocation, deallocation and memory + * allocation and deallocation. + */ +#define SYS_LIGHTWEIGHT_PROT 0 + +#define LWIP_NOASSERT + +/* ---------- Memory options ---------- */ +/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which + lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 + byte alignment -> define MEM_ALIGNMENT to 2. */ +#define MEM_ALIGNMENT 4 + +/* MEM_SIZE: the size of the heap memory. If the application will send +a lot of data that needs to be copied, this should be set high. */ +#define MEM_SIZE (10*1024) + +// Could be better or worse, needs more tests +//#define MEM_LIBC_MALLOC 1 +//#define MEMP_MEM_MALLOC 1 + +/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application + sends a lot of data out of ROM (or other static memory), this + should be set high. */ +#define MEMP_NUM_PBUF 10 +/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One + per active UDP "connection". */ +#define MEMP_NUM_UDP_PCB 6 +/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP + connections. */ +#define MEMP_NUM_TCP_PCB MAX_NUM_TCP_CLIENTS+1 // one extra so we can reject number N+1 from our code +/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP + connections. */ +#define MEMP_NUM_TCP_PCB_LISTEN 6 +/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP + segments. */ +#define MEMP_NUM_TCP_SEG MAX_NUM_TCP_CLIENTS +/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active + timeouts. */ +#define MEMP_NUM_SYS_TIMEOUT MAX_NUM_TCP_CLIENTS+2 + + +/* ---------- Pbuf options ---------- */ +/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ +#define PBUF_POOL_SIZE MAX_NUM_TCP_CLIENTS + +/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ +#define PBUF_POOL_BUFSIZE 1524 + + +/* ---------- TCP options ---------- */ +#define LWIP_TCP 1 +#define TCP_TTL 255 +#define LWIP_SO_RCVTIMEO 1 +#define LWIP_SO_RCVRCVTIMEO_NONSTANDARD 1 /* Pass an integer number of ms instead of a timeval struct. */ +#define LWIP_SO_SNDTIMEO 1 +#define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 1 /* Pass an integer number of ms instead of a timeval struct. */ + +/* Controls if TCP should queue segments that arrive out of + order. Define to 0 if your device is low on memory and you are not scared by TCP congestion and latencies. */ +#define TCP_QUEUE_OOSEQ 0 + +/* TCP Maximum segment size. */ +#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */ + +/* TCP sender buffer space (bytes). */ +#define TCP_SND_BUF (4*TCP_MSS) + +/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least + as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ + +#define TCP_SND_QUEUELEN (2* TCP_SND_BUF/TCP_MSS) + +/* TCP receive window. */ +#define TCP_WND (3*TCP_MSS) + +#define LWIP_TCP_KEEPALIVE 1 /* Keep the TCP link active. Important for MQTT/TLS */ +#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 1 /* Prevent the same port to be used after reset. + Otherwise, the remote host may be confused if the port was not explicitly closed before the reset. */ + + +/* ---------- ICMP options ---------- */ +#define LWIP_ICMP 1 +#define LWIP_RAW 1 /* PING changed to 1 */ +#define DEFAULT_RAW_RECVMBOX_SIZE 3 /* for ICMP PING */ + + +/* ---------- DHCP options ---------- */ +/* Define LWIP_DHCP to 1 if you want DHCP configuration of + interfaces. DHCP is not implemented in lwIP 0.5.1, however, so + turning this on does currently not work. */ +#define LWIP_DHCP 1 + + +/* ---------- UDP options ---------- */ +#define LWIP_UDP 1 +#define UDP_TTL 255 + + +/* ---------- Statistics options ---------- */ +#define LWIP_STATS 0 +#define LWIP_PROVIDE_ERRNO + +/* ---------- link callback options ---------- */ +/* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface + * whenever the link changes (i.e., link down) + */ +// need for building net_ip.c +#define LWIP_NETIF_HOSTNAME 1 +#define LWIP_NETIF_STATUS_CALLBACK 1 +#define LWIP_NETIF_LINK_CALLBACK 1 +#define LWIP_DHCP_CHECK_LINK_UP 1 + +/* + -------------------------------------- + ---------- Checksum options ---------- + -------------------------------------- +*/ + +/* +The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware: + - To use this feature let the following define uncommented. + - To disable it and process by CPU comment the the checksum. +*/ +#define CHECKSUM_BY_HARDWARE + + +#ifdef CHECKSUM_BY_HARDWARE + /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/ + #define CHECKSUM_GEN_IP 0 + /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/ + #define CHECKSUM_GEN_UDP 0 + /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/ + #define CHECKSUM_GEN_TCP 0 + /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/ + #define CHECKSUM_CHECK_IP 0 + /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/ + #define CHECKSUM_CHECK_UDP 0 + /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/ + #define CHECKSUM_CHECK_TCP 0 + /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/ + #define CHECKSUM_GEN_ICMP 0 +#else + /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/ + #define CHECKSUM_GEN_IP 1 + /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/ + #define CHECKSUM_GEN_UDP 1 + /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/ + #define CHECKSUM_GEN_TCP 1 + /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/ + #define CHECKSUM_CHECK_IP 1 + /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/ + #define CHECKSUM_CHECK_UDP 1 + /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/ + #define CHECKSUM_CHECK_TCP 1 + /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/ + #define CHECKSUM_GEN_ICMP 1 +#endif + + +/* + ---------------------------------------------- + ---------- Sequential layer options ---------- + ---------------------------------------------- +*/ +/** + * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) + */ +#define LWIP_NETCONN 0 + +/* + ------------------------------------ + ---------- Socket options ---------- + ------------------------------------ +*/ +/** + * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) + */ +#define LWIP_SOCKET 0 +#define LWIP_DNS 1 + +/* + ------------------------------------ + ---------- httpd options ---------- + ------------------------------------ +*/ + +/** Set this to 1 to support CGI */ +#define LWIP_HTTPD_CGI 1 + +/** Set this to 1 to support SSI (Server-Side-Includes) */ +#define LWIP_HTTPD_SSI 1 + +/** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the + * file system (to prevent changing the file included in CVS) */ +#define HTTPD_USE_CUSTOM_FSDATA 1 + +/* + ------------------------------------ + ---------- Custom options ---------- + ------------------------------------ +*/ + +/** Enables the Ethernet peripheral in RMII mode. If not defined, MII mode will + be enabled. Pin mapping must be configured for the selected mode + (see PinMap_Ethernet in PeripheralPins.c). */ +#define ETHERNET_RMII_MODE_CONFIGURATION 1 + +/** Uncomment this line to use the ethernet input in interrupt mode. + * NOTE: LwIP stack documentation recommends to use the polling mode without + * an operating system. */ +//#define ETH_INPUT_USE_IT 1 + +#define LWIP_MDNS_RESPONDER 1 +#define LWIP_IGMP 1 +#warning testing this + +#endif /* __STM32LWIPOPTS_H__ */ + +/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ From 19f486940121871297ca618fd88192aa284c5e45 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 9 Nov 2024 12:57:49 +0100 Subject: [PATCH 05/28] multicast receive tests --- EthernetInterface.cpp | 13 ++++++++++++- STM32lwipopts.h | 6 +++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index 1e49a21..2acb92f 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -36,9 +36,17 @@ #define DO_MDNS EthernetUDP udp; MDNS mdns(udp); +void serviceFound(const char* type, MDNSServiceProtocol /*proto*/, const char* name, IPAddress ip, + unsigned short port, const char* txtContent) +{ + if (name == NULL) { + DIAG("End service discovery of %s", type); + return; + } + DIAG("Got %s of type %s", name, type); +} #endif - //extern void looptimer(unsigned long timeout, const FSH* message); #define looptimer(a,b) @@ -117,7 +125,10 @@ void EthernetInterface::setup() outboundRing=new RingStream(OUTBOUND_RING_SIZE); #ifdef DO_MDNS mdns.begin(Ethernet.localIP(), WIFI_HOSTNAME); // hostname + udp.begin(IPAddress(224, 0, 0, 251), 5353, true); mdns.addServiceRecord(WIFI_HOSTNAME "._withrottle", IP_PORT, MDNSServiceTCP); +// mdns.setServiceFoundCallback(serviceFound); +// mdns.startDiscoveringService("_withrottle", MDNSServiceTCP, 0); // Not sure if we need to run it once, but just in case! mdns.run(); #endif diff --git a/STM32lwipopts.h b/STM32lwipopts.h index 68cb992..d4053e8 100644 --- a/STM32lwipopts.h +++ b/STM32lwipopts.h @@ -252,8 +252,12 @@ The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums * an operating system. */ //#define ETH_INPUT_USE_IT 1 -#define LWIP_MDNS_RESPONDER 1 +// We don't need this as we do not need to respond - we only announce +//#define LWIP_MDNS_RESPONDER 1 +//#define LWIP_NUM_NETIF_CLIENT_DATA 1 // MDNS needs at least one #define LWIP_IGMP 1 +#define SO_REUSE 1 +#define SO_REUSE_RXTOALL 1 #warning testing this #endif /* __STM32LWIPOPTS_H__ */ From 64a34b3a32a7cda963fa3eb5752430dca0142264 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 10 Nov 2024 11:31:14 +0100 Subject: [PATCH 06/28] proto works --- EthernetInterface.cpp | 16 ++++++++++------ STM32lwipopts.h | 4 ++-- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index 2acb92f..a2e292d 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -31,7 +31,13 @@ #include "CommandDistributor.h" #include "WiThrottle.h" #include "DCCTimer.h" -#if __has_include ( "MDNS_Generic.h") + +#include "EXmDNS.h" +#define DO_MDNS +EthernetUDP udp; +MDNS mdns(udp); + +#if 0 //#if __has_include ( "MDNS_Generic.h") #include "MDNS_Generic.h" #define DO_MDNS EthernetUDP udp; @@ -124,11 +130,9 @@ void EthernetInterface::setup() outboundRing=new RingStream(OUTBOUND_RING_SIZE); #ifdef DO_MDNS - mdns.begin(Ethernet.localIP(), WIFI_HOSTNAME); // hostname - udp.begin(IPAddress(224, 0, 0, 251), 5353, true); - mdns.addServiceRecord(WIFI_HOSTNAME "._withrottle", IP_PORT, MDNSServiceTCP); -// mdns.setServiceFoundCallback(serviceFound); -// mdns.startDiscoveringService("_withrottle", MDNSServiceTCP, 0); + if (!mdns.begin(Ethernet.localIP(), WIFI_HOSTNAME)) + DIAG("mdns.begin fail"); // hostname + mdns.addServiceRecord(WIFI_HOSTNAME, IP_PORT, MDNSServiceTCP); // Not sure if we need to run it once, but just in case! mdns.run(); #endif diff --git a/STM32lwipopts.h b/STM32lwipopts.h index d4053e8..24a3fae 100644 --- a/STM32lwipopts.h +++ b/STM32lwipopts.h @@ -57,8 +57,8 @@ a lot of data that needs to be copied, this should be set high. */ #define MEM_SIZE (10*1024) // Could be better or worse, needs more tests -//#define MEM_LIBC_MALLOC 1 -//#define MEMP_MEM_MALLOC 1 +#define MEM_LIBC_MALLOC 1 // critical, fixes heap trashing +#define MEMP_MEM_MALLOC 1 // uses malloc which means no pools which means slower but not mean 32KB up front /* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application sends a lot of data out of ROM (or other static memory), this From 9a6e1707e7f7b51c7f0c09ba977d0ab4ef6f6b0c Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 10 Nov 2024 11:31:49 +0100 Subject: [PATCH 07/28] add own mDNS files --- EXmDNS.cpp | 139 +++++++++++++++++++++++++++++++++++++++++++++++++++++ EXmDNS.h | 20 ++++++++ 2 files changed, 159 insertions(+) create mode 100644 EXmDNS.cpp create mode 100644 EXmDNS.h diff --git a/EXmDNS.cpp b/EXmDNS.cpp new file mode 100644 index 0000000..559f09c --- /dev/null +++ b/EXmDNS.cpp @@ -0,0 +1,139 @@ + +#include +#include "EthernetInterface.h" +#include "EXmDNS.h" +#include "DIAG.h" +static IPAddress mdnsMulticastIPAddr = IPAddress(224, 0, 0, 251); +#define MDNS_SERVER_PORT 5353 + +MDNS::MDNS(EthernetUDP& udp) { + _udp = &udp; +} +MDNS::~MDNS() { + _udp->stop(); +} +int MDNS::begin(const IPAddress& ip, char* name) { + // if we were called very soon after the board was booted, we need to give the + // EthernetShield (WIZnet) some time to come up. Hence, we delay until millis() is at + // least 3000. This is necessary, so that if we need to add a service record directly + // after begin, the announce packet does not get lost in the bowels of the WIZnet chip. + //while (millis() < 3000) + // delay(100); + + _ipAddress = ip; + _name = name; + return _udp->beginMulticast(mdnsMulticastIPAddr, MDNS_SERVER_PORT); +} +int MDNS::addServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_t proto) { + // we ignore proto, assume TCP + _serviceName = (char *)malloc(strlen(name +2)); + DIAG("name %d %s", strlen(name), name); + byte n; + for(n = 0; n 10000)) { + return; + } + lastrun = now; + DNSHeader_t dnsHeader = { 0 }; + + _udp->beginPacket(mdnsMulticastIPAddr, MDNS_SERVER_PORT); + + // dns header + dnsHeader.flags = lwip_htons(0x8400); // Response, authorative + dnsHeader.answerCount = lwip_htons(4 /*5*/); + _udp->write((uint8_t*)&dnsHeader, sizeof(DNSHeader_t)); + // rr #1 + _udp->write((uint8_t*)dns_rr_services, sizeof(dns_rr_services)); + byte buf[10]; + buf[0] = 0x00; + buf[1] = 0x0c; //PTR + buf[2] = 0x00; + buf[3] = 0x01; //IN + *((uint32_t*)(buf+4)) = lwip_htonl(120); //TTL in sec + *((uint16_t*)(buf+8)) = lwip_htons(sizeof(dns_rr_withrottle)); + _udp->write(buf, 10); + _udp->write(dns_rr_withrottle, sizeof(dns_rr_withrottle)); + // rr #2 + _udp->write(dns_rr_withrottle, sizeof(dns_rr_withrottle)); + *((uint16_t*)(buf+8)) = lwip_htons(strlen(_serviceName) + sizeof(dns_rr_withrottle)); // recycle most of buf + _udp->write(buf, 10); + + _udp->write(_serviceName, _serviceName[0]+1); + _udp->write(dns_rr_withrottle, sizeof(dns_rr_withrottle)); + // rr #3 + _udp->write(_serviceName, _serviceName[0]+1); + _udp->write(dns_rr_withrottle, sizeof(dns_rr_withrottle)); + + buf[1] = 0x21; // recycle most of buf but here SRV + buf[2] = 0x80; // cache flush + *((uint16_t*)(buf+8)) = lwip_htons(strlen(_serviceName) + strlen(dns_rr_local) + 1 + 6); + _udp->write(buf, 10); + + byte srv[6]; + // priority and weight + srv[0] = srv[1] = srv[2] = srv[3] = 0; + // port + *((uint16_t*)(srv+4)) = lwip_htons(_servicePort); + _udp->write(srv, 6); + // target + _udp->write(_serviceName, _serviceName[0]+1); + _udp->write(dns_rr_local, strlen(dns_rr_local)+1); + + // rr #4 + _udp->write(_serviceName, _serviceName[0]+1); + _udp->write(dns_rr_local, strlen(dns_rr_local)+1); + + buf[1] = 0x01; // recycle most of buf but here A + *((uint16_t*)(buf+8)) = lwip_htons(4); + _udp->write(buf, 10); + byte ip[4]; + ip[0] = _ipAddress[0]; + ip[1] = _ipAddress[1]; + ip[2] = _ipAddress[2]; + ip[3] = _ipAddress[3]; + _udp->write(ip, 4); + + _udp->endPacket(); + _udp->flush(); + // +} diff --git a/EXmDNS.h b/EXmDNS.h new file mode 100644 index 0000000..769a055 --- /dev/null +++ b/EXmDNS.h @@ -0,0 +1,20 @@ +typedef enum _MDNSServiceProtocol_t +{ + MDNSServiceTCP, + MDNSServiceUDP +} MDNSServiceProtocol_t; + +class MDNS { +public: + MDNS(EthernetUDP& udp); + ~MDNS(); + int begin(const IPAddress& ip, char* name); + int addServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_t proto); + void run(); +private: + EthernetUDP *_udp; + IPAddress _ipAddress; + char* _name; + char* _serviceName; + int _servicePort; +}; From 0a96320fd0e491fcdcc449f0df748b39f417bc48 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 10 Nov 2024 19:48:52 +0100 Subject: [PATCH 08/28] tidy up values into name, serviceName, serviceProto and packet generation --- EXmDNS.cpp | 119 +++++++++++++++++++++++++++--------------- EXmDNS.h | 4 ++ EthernetInterface.cpp | 5 +- 3 files changed, 84 insertions(+), 44 deletions(-) diff --git a/EXmDNS.cpp b/EXmDNS.cpp index 559f09c..b4fc200 100644 --- a/EXmDNS.cpp +++ b/EXmDNS.cpp @@ -2,15 +2,49 @@ #include #include "EthernetInterface.h" #include "EXmDNS.h" -#include "DIAG.h" + +// fixed values for mDNS static IPAddress mdnsMulticastIPAddr = IPAddress(224, 0, 0, 251); #define MDNS_SERVER_PORT 5353 +// dotToLen() +// converts stings of form ".foo.barbar.x" to a string with the +// dots replaced with lenght. So string above would result in +// "\x03foo\x06barbar\x01x" in C notation. If not NULL, *substr +// will point to the beginning of the last component, in this +// example that would be "\x01x". +// +static void dotToLen(char *str, char **substr) { + char *dotplace = NULL; + char *s; + byte charcount = 0; + for (s = str;/*see break*/ ; s++) { + if (*s == '.' || *s == '\0') { + // take care of accumulated + if (dotplace != NULL && charcount != 0) { + *dotplace = charcount; + } + if (*s == '\0') + break; + if (substr && *s == '.') + *substr = s; + // set new values + dotplace = s; + charcount = 0; + } else { + charcount++; + } + } +} + MDNS::MDNS(EthernetUDP& udp) { _udp = &udp; } MDNS::~MDNS() { _udp->stop(); + if (_name) free(_name); + if (_serviceName) free(_serviceName); + if (_serviceProto) free(_serviceProto); } int MDNS::begin(const IPAddress& ip, char* name) { // if we were called very soon after the board was booted, we need to give the @@ -21,56 +55,53 @@ int MDNS::begin(const IPAddress& ip, char* name) { // delay(100); _ipAddress = ip; - _name = name; + _name = (char *)malloc(strlen(name +2)); + byte n; + for(n = 0; nbeginMulticast(mdnsMulticastIPAddr, MDNS_SERVER_PORT); } + int MDNS::addServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_t proto) { // we ignore proto, assume TCP _serviceName = (char *)malloc(strlen(name +2)); - DIAG("name %d %s", strlen(name), name); byte n; for(n = 0; n 10000)) { + if (!(now - lastrun > BROADCASTTIME * 1000UL)) { return; } lastrun = now; @@ -80,33 +111,39 @@ void MDNS::run() { // dns header dnsHeader.flags = lwip_htons(0x8400); // Response, authorative - dnsHeader.answerCount = lwip_htons(4 /*5*/); + dnsHeader.answerCount = lwip_htons(4 /*5 if TXT but we do not do that */); _udp->write((uint8_t*)&dnsHeader, sizeof(DNSHeader_t)); - // rr #1 + + // rr #1, the PTR record from generic _services.x.local to service.x.local _udp->write((uint8_t*)dns_rr_services, sizeof(dns_rr_services)); + byte buf[10]; buf[0] = 0x00; buf[1] = 0x0c; //PTR buf[2] = 0x00; buf[3] = 0x01; //IN *((uint32_t*)(buf+4)) = lwip_htonl(120); //TTL in sec - *((uint16_t*)(buf+8)) = lwip_htons(sizeof(dns_rr_withrottle)); - _udp->write(buf, 10); - _udp->write(dns_rr_withrottle, sizeof(dns_rr_withrottle)); - // rr #2 - _udp->write(dns_rr_withrottle, sizeof(dns_rr_withrottle)); - *((uint16_t*)(buf+8)) = lwip_htons(strlen(_serviceName) + sizeof(dns_rr_withrottle)); // recycle most of buf + *((uint16_t*)(buf+8)) = lwip_htons( _serviceProto[0] + 1 + strlen(dns_rr_tcplocal) + 1); _udp->write(buf, 10); - _udp->write(_serviceName, _serviceName[0]+1); - _udp->write(dns_rr_withrottle, sizeof(dns_rr_withrottle)); - // rr #3 - _udp->write(_serviceName, _serviceName[0]+1); - _udp->write(dns_rr_withrottle, sizeof(dns_rr_withrottle)); + _udp->write(_serviceProto,_serviceProto[0]+1); + _udp->write(dns_rr_tcplocal, strlen(dns_rr_tcplocal)+1); + + // rr #2, the PTR record from proto.x to name.proto.x + _udp->write(_serviceProto,_serviceProto[0]+1); + _udp->write(dns_rr_tcplocal, strlen(dns_rr_tcplocal)+1); + *((uint16_t*)(buf+8)) = lwip_htons(strlen(_serviceName) + strlen(dns_rr_tcplocal) + 1); // recycle most of buf + _udp->write(buf, 10); + + _udp->write(_serviceName, strlen(_serviceName)); + _udp->write(dns_rr_tcplocal, strlen(dns_rr_tcplocal)+1); + // rr #3, the SRV record for the service that points to local name + _udp->write(_serviceName, strlen(_serviceName)); + _udp->write(dns_rr_tcplocal, strlen(dns_rr_tcplocal)+1); buf[1] = 0x21; // recycle most of buf but here SRV buf[2] = 0x80; // cache flush - *((uint16_t*)(buf+8)) = lwip_htons(strlen(_serviceName) + strlen(dns_rr_local) + 1 + 6); + *((uint16_t*)(buf+8)) = lwip_htons(strlen(_name) + strlen(dns_rr_local) + 1 + 6); _udp->write(buf, 10); byte srv[6]; @@ -116,11 +153,11 @@ void MDNS::run() { *((uint16_t*)(srv+4)) = lwip_htons(_servicePort); _udp->write(srv, 6); // target - _udp->write(_serviceName, _serviceName[0]+1); + _udp->write(_name, _name[0]+1); _udp->write(dns_rr_local, strlen(dns_rr_local)+1); - // rr #4 - _udp->write(_serviceName, _serviceName[0]+1); + // rr #4, the A record for the name.local + _udp->write(_name, _name[0]+1); _udp->write(dns_rr_local, strlen(dns_rr_local)+1); buf[1] = 0x01; // recycle most of buf but here A diff --git a/EXmDNS.h b/EXmDNS.h index 769a055..fd50a2a 100644 --- a/EXmDNS.h +++ b/EXmDNS.h @@ -1,3 +1,6 @@ + +#define BROADCASTTIME 15 //seconds + typedef enum _MDNSServiceProtocol_t { MDNSServiceTCP, @@ -16,5 +19,6 @@ private: IPAddress _ipAddress; char* _name; char* _serviceName; + char* _serviceProto; int _servicePort; }; diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index a2e292d..26be378 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -132,9 +132,8 @@ void EthernetInterface::setup() #ifdef DO_MDNS if (!mdns.begin(Ethernet.localIP(), WIFI_HOSTNAME)) DIAG("mdns.begin fail"); // hostname - mdns.addServiceRecord(WIFI_HOSTNAME, IP_PORT, MDNSServiceTCP); - // Not sure if we need to run it once, but just in case! - mdns.run(); + mdns.addServiceRecord(WIFI_HOSTNAME "._withrottle", IP_PORT, MDNSServiceTCP); + mdns.run(); // run it right away to get out info ASAP #endif connected=true; } From b9ce166028cfc9ff0db81f127e9a575ef4ea921e Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 10 Nov 2024 20:13:40 +0100 Subject: [PATCH 09/28] remove everything that are not our changes --- STM32lwipopts.h | 253 ++++++------------------------------------------ 1 file changed, 28 insertions(+), 225 deletions(-) diff --git a/STM32lwipopts.h b/STM32lwipopts.h index 24a3fae..9f6e0c5 100644 --- a/STM32lwipopts.h +++ b/STM32lwipopts.h @@ -1,265 +1,68 @@ -/** - ****************************************************************************** - * @file STM32lwipopts_default.h - * @author MCD Application Team - * @brief lwIP Options Configuration. - ****************************************************************************** - * @attention - * - *

© Copyright (c) 2017 STMicroelectronics International N.V. - * All rights reserved.

- * - * This software component is licensed by ST under Ultimate Liberty license - * SLA0044, the "License"; You may not use this file except in compliance with - * the License. You may obtain a copy of the License at: - * www.st.com/SLA0044 - * - ****************************************************************************** - */ + + #ifndef __STM32LWIPOPTS_H__ #define __STM32LWIPOPTS_H__ -// we can not include that here so we -// need to duplicate that.define -// #include "defines.h" +// include this here and then override things we do differnet +#include "lwipopts_default.h" + +// we can not include our "defines.h" here +// so we need to duplicate that define #define MAX_NUM_TCP_CLIENTS 20 // increase ARP cache +#undef MEMP_NUM_APR_QUEUE #define MEMP_NUM_ARP_QUEUE MAX_NUM_TCP_CLIENTS+3 // one for each client (all on different HW) and a few extra // Example for debug //#define LWIP_DEBUG 1 //#define TCP_DEBUG LWIP_DBG_ON -/** - * NO_SYS==1: Provides VERY minimal functionality. Otherwise, - * use lwIP facilities. - */ -#define NO_SYS 1 - -/** - * SYS_LIGHTWEIGHT_PROT==1: if you want inter-task protection for certain - * critical regions during buffer allocation, deallocation and memory - * allocation and deallocation. - */ -#define SYS_LIGHTWEIGHT_PROT 0 - -#define LWIP_NOASSERT - -/* ---------- Memory options ---------- */ -/* MEM_ALIGNMENT: should be set to the alignment of the CPU for which - lwIP is compiled. 4 byte alignment -> define MEM_ALIGNMENT to 4, 2 - byte alignment -> define MEM_ALIGNMENT to 2. */ -#define MEM_ALIGNMENT 4 - -/* MEM_SIZE: the size of the heap memory. If the application will send -a lot of data that needs to be copied, this should be set high. */ -#define MEM_SIZE (10*1024) - -// Could be better or worse, needs more tests +// IMPORTANT CHANGE THE FIRST ONE +#undef MEM_LIBC_MALLOC #define MEM_LIBC_MALLOC 1 // critical, fixes heap trashing +#undef MEMP_MEM_MALLOC #define MEMP_MEM_MALLOC 1 // uses malloc which means no pools which means slower but not mean 32KB up front -/* MEMP_NUM_PBUF: the number of memp struct pbufs. If the application - sends a lot of data out of ROM (or other static memory), this - should be set high. */ -#define MEMP_NUM_PBUF 10 -/* MEMP_NUM_UDP_PCB: the number of UDP protocol control blocks. One - per active UDP "connection". */ -#define MEMP_NUM_UDP_PCB 6 -/* MEMP_NUM_TCP_PCB: the number of simulatenously active TCP - connections. */ +#undef MEMP_NUM_TCP_PCB #define MEMP_NUM_TCP_PCB MAX_NUM_TCP_CLIENTS+1 // one extra so we can reject number N+1 from our code -/* MEMP_NUM_TCP_PCB_LISTEN: the number of listening TCP - connections. */ #define MEMP_NUM_TCP_PCB_LISTEN 6 -/* MEMP_NUM_TCP_SEG: the number of simultaneously queued TCP - segments. */ + +#undef MEMP_NUM_TCP_SEG #define MEMP_NUM_TCP_SEG MAX_NUM_TCP_CLIENTS -/* MEMP_NUM_SYS_TIMEOUT: the number of simulateously active - timeouts. */ + +#undef MEMP_NUM_SYS_TIMEOUT #define MEMP_NUM_SYS_TIMEOUT MAX_NUM_TCP_CLIENTS+2 - -/* ---------- Pbuf options ---------- */ -/* PBUF_POOL_SIZE: the number of buffers in the pbuf pool. */ +#undef PBUF_POOL_SIZE #define PBUF_POOL_SIZE MAX_NUM_TCP_CLIENTS -/* PBUF_POOL_BUFSIZE: the size of each pbuf in the pbuf pool. */ -#define PBUF_POOL_BUFSIZE 1524 - - -/* ---------- TCP options ---------- */ -#define LWIP_TCP 1 -#define TCP_TTL 255 -#define LWIP_SO_RCVTIMEO 1 -#define LWIP_SO_RCVRCVTIMEO_NONSTANDARD 1 /* Pass an integer number of ms instead of a timeval struct. */ -#define LWIP_SO_SNDTIMEO 1 -#define LWIP_SO_SNDRCVTIMEO_NONSTANDARD 1 /* Pass an integer number of ms instead of a timeval struct. */ - -/* Controls if TCP should queue segments that arrive out of - order. Define to 0 if your device is low on memory and you are not scared by TCP congestion and latencies. */ -#define TCP_QUEUE_OOSEQ 0 - -/* TCP Maximum segment size. */ -#define TCP_MSS (1500 - 40) /* TCP_MSS = (Ethernet MTU - IP header size - TCP header size) */ - -/* TCP sender buffer space (bytes). */ -#define TCP_SND_BUF (4*TCP_MSS) - -/* TCP_SND_QUEUELEN: TCP sender buffer space (pbufs). This must be at least - as much as (2 * TCP_SND_BUF/TCP_MSS) for things to work. */ - -#define TCP_SND_QUEUELEN (2* TCP_SND_BUF/TCP_MSS) - -/* TCP receive window. */ -#define TCP_WND (3*TCP_MSS) - -#define LWIP_TCP_KEEPALIVE 1 /* Keep the TCP link active. Important for MQTT/TLS */ -#define LWIP_RANDOMIZE_INITIAL_LOCAL_PORTS 1 /* Prevent the same port to be used after reset. - Otherwise, the remote host may be confused if the port was not explicitly closed before the reset. */ - - -/* ---------- ICMP options ---------- */ +#undef LWIO_ICMP #define LWIP_ICMP 1 +#undef LWIP_RAW #define LWIP_RAW 1 /* PING changed to 1 */ +#undef DEFAULT_RAW_RECVMBOX_SIZE #define DEFAULT_RAW_RECVMBOX_SIZE 3 /* for ICMP PING */ - -/* ---------- DHCP options ---------- */ -/* Define LWIP_DHCP to 1 if you want DHCP configuration of - interfaces. DHCP is not implemented in lwIP 0.5.1, however, so - turning this on does currently not work. */ +#undef LWIP_DHCP #define LWIP_DHCP 1 - - -/* ---------- UDP options ---------- */ +#undef LWIP_UDP #define LWIP_UDP 1 -#define UDP_TTL 255 - - -/* ---------- Statistics options ---------- */ -#define LWIP_STATS 0 -#define LWIP_PROVIDE_ERRNO - -/* ---------- link callback options ---------- */ -/* LWIP_NETIF_LINK_CALLBACK==1: Support a callback function from an interface - * whenever the link changes (i.e., link down) - */ -// need for building net_ip.c -#define LWIP_NETIF_HOSTNAME 1 -#define LWIP_NETIF_STATUS_CALLBACK 1 -#define LWIP_NETIF_LINK_CALLBACK 1 -#define LWIP_DHCP_CHECK_LINK_UP 1 - -/* - -------------------------------------- - ---------- Checksum options ---------- - -------------------------------------- -*/ /* The STM32F4x7 allows computing and verifying the IP, UDP, TCP and ICMP checksums by hardware: - To use this feature let the following define uncommented. - To disable it and process by CPU comment the the checksum. */ -#define CHECKSUM_BY_HARDWARE - -#ifdef CHECKSUM_BY_HARDWARE - /* CHECKSUM_GEN_IP==0: Generate checksums by hardware for outgoing IP packets.*/ - #define CHECKSUM_GEN_IP 0 - /* CHECKSUM_GEN_UDP==0: Generate checksums by hardware for outgoing UDP packets.*/ - #define CHECKSUM_GEN_UDP 0 - /* CHECKSUM_GEN_TCP==0: Generate checksums by hardware for outgoing TCP packets.*/ - #define CHECKSUM_GEN_TCP 0 - /* CHECKSUM_CHECK_IP==0: Check checksums by hardware for incoming IP packets.*/ - #define CHECKSUM_CHECK_IP 0 - /* CHECKSUM_CHECK_UDP==0: Check checksums by hardware for incoming UDP packets.*/ - #define CHECKSUM_CHECK_UDP 0 - /* CHECKSUM_CHECK_TCP==0: Check checksums by hardware for incoming TCP packets.*/ - #define CHECKSUM_CHECK_TCP 0 - /* CHECKSUM_CHECK_ICMP==0: Check checksums by hardware for incoming ICMP packets.*/ - #define CHECKSUM_GEN_ICMP 0 -#else - /* CHECKSUM_GEN_IP==1: Generate checksums in software for outgoing IP packets.*/ - #define CHECKSUM_GEN_IP 1 - /* CHECKSUM_GEN_UDP==1: Generate checksums in software for outgoing UDP packets.*/ - #define CHECKSUM_GEN_UDP 1 - /* CHECKSUM_GEN_TCP==1: Generate checksums in software for outgoing TCP packets.*/ - #define CHECKSUM_GEN_TCP 1 - /* CHECKSUM_CHECK_IP==1: Check checksums in software for incoming IP packets.*/ - #define CHECKSUM_CHECK_IP 1 - /* CHECKSUM_CHECK_UDP==1: Check checksums in software for incoming UDP packets.*/ - #define CHECKSUM_CHECK_UDP 1 - /* CHECKSUM_CHECK_TCP==1: Check checksums in software for incoming TCP packets.*/ - #define CHECKSUM_CHECK_TCP 1 - /* CHECKSUM_CHECK_ICMP==1: Check checksums by hardware for incoming ICMP packets.*/ - #define CHECKSUM_GEN_ICMP 1 +#if CHECKSUM_GEN_TCP == 1 +#error On STM32 TCP checksum should be in HW #endif +#undef LWIP_IGMP +#define LWIP_IGMP 1 -/* - ---------------------------------------------- - ---------- Sequential layer options ---------- - ---------------------------------------------- -*/ -/** - * LWIP_NETCONN==1: Enable Netconn API (require to use api_lib.c) - */ -#define LWIP_NETCONN 0 - -/* - ------------------------------------ - ---------- Socket options ---------- - ------------------------------------ -*/ -/** - * LWIP_SOCKET==1: Enable Socket API (require to use sockets.c) - */ -#define LWIP_SOCKET 0 -#define LWIP_DNS 1 - -/* - ------------------------------------ - ---------- httpd options ---------- - ------------------------------------ -*/ - -/** Set this to 1 to support CGI */ -#define LWIP_HTTPD_CGI 1 - -/** Set this to 1 to support SSI (Server-Side-Includes) */ -#define LWIP_HTTPD_SSI 1 - -/** Set this to 1 to include "fsdata_custom.c" instead of "fsdata.c" for the - * file system (to prevent changing the file included in CVS) */ -#define HTTPD_USE_CUSTOM_FSDATA 1 - -/* - ------------------------------------ - ---------- Custom options ---------- - ------------------------------------ -*/ - -/** Enables the Ethernet peripheral in RMII mode. If not defined, MII mode will - be enabled. Pin mapping must be configured for the selected mode - (see PinMap_Ethernet in PeripheralPins.c). */ -#define ETHERNET_RMII_MODE_CONFIGURATION 1 - -/** Uncomment this line to use the ethernet input in interrupt mode. - * NOTE: LwIP stack documentation recommends to use the polling mode without - * an operating system. */ -//#define ETH_INPUT_USE_IT 1 - -// We don't need this as we do not need to respond - we only announce -//#define LWIP_MDNS_RESPONDER 1 -//#define LWIP_NUM_NETIF_CLIENT_DATA 1 // MDNS needs at least one -#define LWIP_IGMP 1 -#define SO_REUSE 1 -#define SO_REUSE_RXTOALL 1 -#warning testing this +//#define SO_REUSE 1 +//#define SO_REUSE_RXTOALL 1 #endif /* __STM32LWIPOPTS_H__ */ - -/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ From afff10df28009ee0f622c8002ca098914ba7addc Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 10 Nov 2024 20:15:45 +0100 Subject: [PATCH 10/28] add copyright notices --- EXmDNS.cpp | 19 +++++++++++++++++++ EXmDNS.h | 19 +++++++++++++++++++ STM32lwipopts.h | 25 +++++++++++++++++++++++-- 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/EXmDNS.cpp b/EXmDNS.cpp index b4fc200..6cc1090 100644 --- a/EXmDNS.cpp +++ b/EXmDNS.cpp @@ -1,3 +1,22 @@ +/* + * © 2024 Harald Barth + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ #include #include "EthernetInterface.h" diff --git a/EXmDNS.h b/EXmDNS.h index fd50a2a..d8b24d2 100644 --- a/EXmDNS.h +++ b/EXmDNS.h @@ -1,3 +1,22 @@ +/* + * © 2024 Harald Barth + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ #define BROADCASTTIME 15 //seconds diff --git a/STM32lwipopts.h b/STM32lwipopts.h index 9f6e0c5..8efc641 100644 --- a/STM32lwipopts.h +++ b/STM32lwipopts.h @@ -1,5 +1,26 @@ - - +/* + * © 2024 Harald Barth + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ +// +// Rewrite of the STM32lwipopts.h file from STM +// To be copied into where lwipopts_default.h resides +// #ifndef __STM32LWIPOPTS_H__ #define __STM32LWIPOPTS_H__ From 6a35daab6b5ed3188bb1072a48a75bbfc65e6ff5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 10 Nov 2024 20:20:25 +0100 Subject: [PATCH 11/28] remove dead code --- EthernetInterface.cpp | 16 ---------------- 1 file changed, 16 deletions(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index 26be378..ecdfefb 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -37,22 +37,6 @@ EthernetUDP udp; MDNS mdns(udp); -#if 0 //#if __has_include ( "MDNS_Generic.h") - #include "MDNS_Generic.h" - #define DO_MDNS - EthernetUDP udp; - MDNS mdns(udp); -void serviceFound(const char* type, MDNSServiceProtocol /*proto*/, const char* name, IPAddress ip, - unsigned short port, const char* txtContent) -{ - if (name == NULL) { - DIAG("End service discovery of %s", type); - return; - } - DIAG("Got %s of type %s", name, type); -} -#endif - //extern void looptimer(unsigned long timeout, const FSH* message); #define looptimer(a,b) From 9786ea9b3a412df08d97cdaa23da491f2e6176cd Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 10 Nov 2024 20:25:20 +0100 Subject: [PATCH 12/28] fix compiler warnings --- EXmDNS.cpp | 2 +- EthernetInterface.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/EXmDNS.cpp b/EXmDNS.cpp index 6cc1090..62511a2 100644 --- a/EXmDNS.cpp +++ b/EXmDNS.cpp @@ -99,7 +99,7 @@ int MDNS::addServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_ } static char dns_rr_services[] = "\x09_services\x07_dns-sd\x04_udp\x05local"; -static char *dns_rr_tcplocal = "\x04_tcp\x05local"; +static char dns_rr_tcplocal[] = "\x04_tcp\x05local"; static char *dns_rr_local = dns_rr_tcplocal + dns_rr_tcplocal[0] + 1; typedef struct _DNSHeader_t diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index ecdfefb..cd89c0e 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -114,7 +114,7 @@ void EthernetInterface::setup() outboundRing=new RingStream(OUTBOUND_RING_SIZE); #ifdef DO_MDNS - if (!mdns.begin(Ethernet.localIP(), WIFI_HOSTNAME)) + if (!mdns.begin(Ethernet.localIP(), (char *)WIFI_HOSTNAME)) DIAG("mdns.begin fail"); // hostname mdns.addServiceRecord(WIFI_HOSTNAME "._withrottle", IP_PORT, MDNSServiceTCP); mdns.run(); // run it right away to get out info ASAP From 001c4664c12eae48adf555106cb48c830cf9f6ab Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 10 Nov 2024 20:32:06 +0100 Subject: [PATCH 13/28] as MDNS_Generic does not work and we now have our own, remove it --- platformio.ini | 4 ---- 1 file changed, 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index 6317372..1bc6e60 100644 --- a/platformio.ini +++ b/platformio.ini @@ -104,7 +104,6 @@ lib_deps = ${env.lib_deps} arduino-libraries/Ethernet SPI - MDNS_Generic lib_ignore = WiFi101 WiFi101_Generic @@ -123,7 +122,6 @@ framework = arduino lib_deps = ${env.lib_deps} arduino-libraries/Ethernet - MDNS_Generic SPI lib_ignore = WiFi101 WiFi101_Generic @@ -278,7 +276,6 @@ framework = arduino lib_deps = ${env.lib_deps} stm32duino/STM32Ethernet @ ^1.4.0 stm32duino/STM32duino LwIP @ ^2.1.3 - MDNS_Generic lib_ignore = WiFi101 WiFi101_Generic WiFiEspAT @@ -302,7 +299,6 @@ framework = arduino lib_deps = ${env.lib_deps} stm32duino/STM32Ethernet @ ^1.4.0 stm32duino/STM32duino LwIP @ ^2.1.3 - MDNS_Generic lib_ignore = WiFi101 WiFi101_Generic WiFiEspAT From 1235c288dc795cedd433bca3abd2842556a6aefe Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 10 Nov 2024 21:11:40 +0100 Subject: [PATCH 14/28] make it compile on all default platforms --- EXmDNS.cpp | 2 ++ EXmDNS.h | 3 ++- EthernetInterface.cpp | 3 ++- EthernetInterface.h | 6 +++++- 4 files changed, 11 insertions(+), 3 deletions(-) diff --git a/EXmDNS.cpp b/EXmDNS.cpp index 62511a2..09a3391 100644 --- a/EXmDNS.cpp +++ b/EXmDNS.cpp @@ -20,6 +20,7 @@ #include #include "EthernetInterface.h" +#ifdef DO_MDNS #include "EXmDNS.h" // fixed values for mDNS @@ -193,3 +194,4 @@ void MDNS::run() { _udp->flush(); // } +#endif //DO_MDNS diff --git a/EXmDNS.h b/EXmDNS.h index d8b24d2..676dbe3 100644 --- a/EXmDNS.h +++ b/EXmDNS.h @@ -17,7 +17,7 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ - +#ifdef DO_MDNS #define BROADCASTTIME 15 //seconds typedef enum _MDNSServiceProtocol_t @@ -41,3 +41,4 @@ private: char* _serviceProto; int _servicePort; }; +#endif //DO_MDNS diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index cd89c0e..f8a43f2 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -32,10 +32,11 @@ #include "WiThrottle.h" #include "DCCTimer.h" +#ifdef DO_MDNS #include "EXmDNS.h" -#define DO_MDNS EthernetUDP udp; MDNS mdns(udp); +#endif //extern void looptimer(unsigned long timeout, const FSH* message); #define looptimer(a,b) diff --git a/EthernetInterface.h b/EthernetInterface.h index 6cea6e8..13a8b10 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -31,13 +31,16 @@ #define EthernetInterface_h #include "defines.h" +#if ETHERNET_ON == true #include "DCCEXParser.h" #include //#include #if defined (ARDUINO_TEENSY41) #include //TEENSY Ethernet Treiber #include + #ifndef MAX_SOCK_NUM #define MAX_SOCK_NUM 4 + #endif #elif defined (ARDUINO_NUCLEO_F429ZI) || defined (ARDUINO_NUCLEO_F439ZI) || defined (ARDUINO_NUCLEO_F4X9ZI) #include // #include "STM32lwipopts.h" @@ -46,6 +49,7 @@ extern "C" struct netif gnetif; #define STM32_ETHERNET #define MAX_SOCK_NUM MAX_NUM_TCP_CLIENTS + #define DO_MDNS #else #include "Ethernet.h" #endif @@ -77,5 +81,5 @@ class EthernetInterface { static void dropClient(byte socketnum); }; - +#endif // ETHERNET_ON #endif From 9602c32ea72b92c71f74a8b92943ed088c940297 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 16 Nov 2024 20:50:25 +0100 Subject: [PATCH 15/28] mDNS malloc error fix --- EXmDNS.cpp | 4 ++-- STM32lwipopts.h | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/EXmDNS.cpp b/EXmDNS.cpp index 09a3391..14e824b 100644 --- a/EXmDNS.cpp +++ b/EXmDNS.cpp @@ -75,7 +75,7 @@ int MDNS::begin(const IPAddress& ip, char* name) { // delay(100); _ipAddress = ip; - _name = (char *)malloc(strlen(name +2)); + _name = (char *)malloc(strlen(name)+2); byte n; for(n = 0; n Date: Wed, 27 Nov 2024 19:59:15 +0800 Subject: [PATCH 16/28] Fixes for Mega, HTONS/L macro --- EXmDNS.cpp | 23 +++++++++++++---------- EXmDNS.h | 6 ++++++ EthernetInterface.cpp | 2 +- EthernetInterface.h | 8 ++++++-- 4 files changed, 26 insertions(+), 13 deletions(-) diff --git a/EXmDNS.cpp b/EXmDNS.cpp index 14e824b..b5250df 100644 --- a/EXmDNS.cpp +++ b/EXmDNS.cpp @@ -1,5 +1,6 @@ /* * © 2024 Harald Barth + * © 2024 Paul M. Antoine * All rights reserved. * * This file is part of CommandStation-EX @@ -87,7 +88,8 @@ int MDNS::begin(const IPAddress& ip, char* name) { int MDNS::addServiceRecord(const char* name, uint16_t port, MDNSServiceProtocol_t proto) { // we ignore proto, assume TCP - _serviceName = (char *)malloc(strlen(name)+2); + (void)proto; + _serviceName = (char *)malloc(strlen(name) + 2); byte n; for(n = 0; nbeginPacket(mdnsMulticastIPAddr, MDNS_SERVER_PORT); // dns header - dnsHeader.flags = lwip_htons(0x8400); // Response, authorative - dnsHeader.answerCount = lwip_htons(4 /*5 if TXT but we do not do that */); + dnsHeader.flags = HTONS((uint16_t)0x8400); // Response, authorative + dnsHeader.answerCount = HTONS(4 /*5 if TXT but we do not do that */); _udp->write((uint8_t*)&dnsHeader, sizeof(DNSHeader_t)); // rr #1, the PTR record from generic _services.x.local to service.x.local @@ -142,8 +145,8 @@ void MDNS::run() { buf[1] = 0x0c; //PTR buf[2] = 0x00; buf[3] = 0x01; //IN - *((uint32_t*)(buf+4)) = lwip_htonl(120); //TTL in sec - *((uint16_t*)(buf+8)) = lwip_htons( _serviceProto[0] + 1 + strlen(dns_rr_tcplocal) + 1); + *((uint32_t*)(buf+4)) = HTONL(120); //TTL in sec + *((uint16_t*)(buf+8)) = HTONS( _serviceProto[0] + 1 + strlen(dns_rr_tcplocal) + 1); _udp->write(buf, 10); _udp->write(_serviceProto,_serviceProto[0]+1); @@ -152,7 +155,7 @@ void MDNS::run() { // rr #2, the PTR record from proto.x to name.proto.x _udp->write(_serviceProto,_serviceProto[0]+1); _udp->write(dns_rr_tcplocal, strlen(dns_rr_tcplocal)+1); - *((uint16_t*)(buf+8)) = lwip_htons(strlen(_serviceName) + strlen(dns_rr_tcplocal) + 1); // recycle most of buf + *((uint16_t*)(buf+8)) = HTONS(strlen(_serviceName) + strlen(dns_rr_tcplocal) + 1); // recycle most of buf _udp->write(buf, 10); _udp->write(_serviceName, strlen(_serviceName)); @@ -163,14 +166,14 @@ void MDNS::run() { buf[1] = 0x21; // recycle most of buf but here SRV buf[2] = 0x80; // cache flush - *((uint16_t*)(buf+8)) = lwip_htons(strlen(_name) + strlen(dns_rr_local) + 1 + 6); + *((uint16_t*)(buf+8)) = HTONS(strlen(_name) + strlen(dns_rr_local) + 1 + 6); _udp->write(buf, 10); byte srv[6]; // priority and weight srv[0] = srv[1] = srv[2] = srv[3] = 0; // port - *((uint16_t*)(srv+4)) = lwip_htons(_servicePort); + *((uint16_t*)(srv+4)) = HTONS(_servicePort); _udp->write(srv, 6); // target _udp->write(_name, _name[0]+1); @@ -181,7 +184,7 @@ void MDNS::run() { _udp->write(dns_rr_local, strlen(dns_rr_local)+1); buf[1] = 0x01; // recycle most of buf but here A - *((uint16_t*)(buf+8)) = lwip_htons(4); + *((uint16_t*)(buf+8)) = HTONS(4); _udp->write(buf, 10); byte ip[4]; ip[0] = _ipAddress[0]; diff --git a/EXmDNS.h b/EXmDNS.h index 676dbe3..0ec1e5e 100644 --- a/EXmDNS.h +++ b/EXmDNS.h @@ -1,5 +1,6 @@ /* * © 2024 Harald Barth + * © 2024 Paul M. Antoine * All rights reserved. * * This file is part of CommandStation-EX @@ -20,6 +21,11 @@ #ifdef DO_MDNS #define BROADCASTTIME 15 //seconds +// We do this ourselves because every library is different and/or broken... +#define HTONS(x) ((uint16_t)(((x) << 8) | (((x) >> 8) & 0xFF))) +#define HTONL(x) ( ((uint32_t)(x) << 24) | (((uint32_t)(x) << 8) & 0xFF0000) | \ + (((uint32_t)(x) >> 8) & 0xFF00) | ((uint32_t)(x) >> 24) ) + typedef enum _MDNSServiceProtocol_t { MDNSServiceTCP, diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index f8a43f2..266afd8 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -116,7 +116,7 @@ void EthernetInterface::setup() outboundRing=new RingStream(OUTBOUND_RING_SIZE); #ifdef DO_MDNS if (!mdns.begin(Ethernet.localIP(), (char *)WIFI_HOSTNAME)) - DIAG("mdns.begin fail"); // hostname + DIAG(F("mdns.begin fail")); // hostname mdns.addServiceRecord(WIFI_HOSTNAME "._withrottle", IP_PORT, MDNSServiceTCP); mdns.run(); // run it right away to get out info ASAP #endif diff --git a/EthernetInterface.h b/EthernetInterface.h index 13a8b10..e5458d9 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -3,7 +3,7 @@ * © 2021 Neil McKechnie * © 2021 Mike S * © 2021 Fred Decker - * © 2020-2022 Harald Barth + * © 2020-2024 Harald Barth * © 2020-2024 Chris Harlow * © 2020 Gregor Baues * All rights reserved. @@ -41,9 +41,10 @@ #ifndef MAX_SOCK_NUM #define MAX_SOCK_NUM 4 #endif + // can't use our MDNS because of a namespace clash with Teensy's NativeEthernet library! + // #define DO_MDNS #elif defined (ARDUINO_NUCLEO_F429ZI) || defined (ARDUINO_NUCLEO_F439ZI) || defined (ARDUINO_NUCLEO_F4X9ZI) #include -// #include "STM32lwipopts.h" #include #include extern "C" struct netif gnetif; @@ -52,7 +53,10 @@ #define DO_MDNS #else #include "Ethernet.h" + #define DO_MDNS #endif + + #include "RingStream.h" /** From e0aa16ff2cbdd243aa80c831951e14e824b9b9cc Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 5 Dec 2024 22:03:55 +0100 Subject: [PATCH 17/28] adjust max default TCP clients to 9 because of bug in LwIP --- STM32lwipopts.h | 10 +++++++++- config.example.h | 5 ++++- defines.h | 10 ++++++++-- 3 files changed, 21 insertions(+), 4 deletions(-) diff --git a/STM32lwipopts.h b/STM32lwipopts.h index 8347b66..d5e23c5 100644 --- a/STM32lwipopts.h +++ b/STM32lwipopts.h @@ -29,7 +29,15 @@ // we can not include our "defines.h" here // so we need to duplicate that define -#define MAX_NUM_TCP_CLIENTS 20 +#define MAX_NUM_TCP_CLIENTS_HERE 9 + +#ifdef MAX_NUM_TCP_CLIENTS + #if MAX_NUM_TCP_CLIENTS != MAX_NUM_TCP_CLIENTS_HERE + #error MAX_NUM_TCP_CLIENTS and MAX_NUM_TCP_CLIENTS_HERE must be same + #endif +#else + #define MAX_NUM_TCP_CLIENTS MAX_NUM_TCP_CLIENTS_HERE +#endif // increase ARP cache #undef MEMP_NUM_APR_QUEUE diff --git a/config.example.h b/config.example.h index ff4652f..0108721 100644 --- a/config.example.h +++ b/config.example.h @@ -140,7 +140,10 @@ The configuration file for DCC-EX Command Station ///////////////////////////////////////////////////////////////////////////////////// // // MAX_NUM_TCP_CLIENTS: If you on STM32 Ethernet (and only there) want more than -// 10 TCP clients, enable this here **AND** follow the instructions in STM32lwiopts.h +// 9 (*) TCP clients, change this number to for example 20 here **AND** in +// STM32lwiopts.h and follow the instructions in STM32lwiopts.h +// +// (*) It would be 10 if there would not be a bug in LwIP by STM32duino. // //#define MAX_NUM_TCP_CLIENTS 20 diff --git a/defines.h b/defines.h index 0cd891f..561ed64 100644 --- a/defines.h +++ b/defines.h @@ -240,9 +240,15 @@ #endif #if defined(ARDUINO_ARCH_STM32) -// Allow override of MAX_NUM_TCP_CLIENTS but default is 10 +// The LwIP library for the STM32 wired ethernet has by default 10 TCP +// clients defined but because of a bug in the library #11 is not +// rejected but kicks out any old connection. By restricting our limit +// to 9 the #10 will be rejected by our code so that the number can +// never get to 11 which would kick an existing connection. +// If you want to change this value, do that in +// config.h AND in STM32lwipopts.h. #ifndef MAX_NUM_TCP_CLIENTS - #define MAX_NUM_TCP_CLIENTS 10 + #define MAX_NUM_TCP_CLIENTS 9 #endif #else #if defined(ARDUINO_ARCH_ESP32) From ccbff56355b2808af893a2be76a13ace055434ff Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 5 Dec 2024 22:04:44 +0100 Subject: [PATCH 18/28] rename to STM32lwipopts.h.copyme because of #include hell --- STM32lwipopts.h => STM32lwipopts.h.copyme | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename STM32lwipopts.h => STM32lwipopts.h.copyme (100%) diff --git a/STM32lwipopts.h b/STM32lwipopts.h.copyme similarity index 100% rename from STM32lwipopts.h rename to STM32lwipopts.h.copyme From aa4306123d4d84d859853e5719d68e47f1a480e5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 5 Dec 2024 22:10:29 +0100 Subject: [PATCH 19/28] more explaining --- STM32lwipopts.h.copyme | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/STM32lwipopts.h.copyme b/STM32lwipopts.h.copyme index d5e23c5..eb9d624 100644 --- a/STM32lwipopts.h.copyme +++ b/STM32lwipopts.h.copyme @@ -20,6 +20,10 @@ // // Rewrite of the STM32lwipopts.h file from STM // To be copied into where lwipopts_default.h resides +// typically into STM32Ethernet/src/STM32lwipopts.h +// or STM32Ethernet\src\STM32lwipopts.h +// search for `lwipopts_default.h` and copy this file into the +// same directory but name it STM32lwipopts.h // #ifndef __STM32LWIPOPTS_H__ #define __STM32LWIPOPTS_H__ From 519cabffb6192cc2a748ffcbaa63f69d1e39d082 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 5 Dec 2024 22:10:55 +0100 Subject: [PATCH 20/28] version tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 46f45ee..b3f9e46 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202411041626Z" +#define GITHUB_SHA "devel-more-ether-202412052105Z" From c93dd75323daf543a5ca27240f62ea27cfedf2da Mon Sep 17 00:00:00 2001 From: pmantoine Date: Thu, 12 Dec 2024 15:18:37 +0800 Subject: [PATCH 21/28] Upgrade ststm32 to v18.0.0 for PeripheralPins issue --- platformio.ini | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/platformio.ini b/platformio.ini index 1bc6e60..f31e790 100644 --- a/platformio.ini +++ b/platformio.ini @@ -200,7 +200,7 @@ monitor_speed = 115200 monitor_echo = yes [env:Nucleo-F411RE] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ ^18.0.0 board = nucleo_f411re framework = arduino lib_deps = ${env.lib_deps} @@ -209,7 +209,7 @@ monitor_speed = 115200 monitor_echo = yes [env:Nucleo-F446RE] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ ^18.0.0 board = nucleo_f446re framework = arduino lib_deps = ${env.lib_deps} @@ -221,7 +221,7 @@ monitor_echo = yes ; tested as yet ; [env:Nucleo-F401RE] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ ^18.0.0 board = nucleo_f401re framework = arduino lib_deps = ${env.lib_deps} @@ -234,7 +234,7 @@ monitor_echo = yes ; installed before you can let PlatformIO see this ; ; [env:Nucleo-F413ZH] -; platform = ststm32 @ 17.6.0 +; platform = ststm32 @ ^18.0.0 ; board = nucleo_f413zh ; framework = arduino ; lib_deps = ${env.lib_deps} @@ -246,7 +246,7 @@ monitor_echo = yes ; installed before you can let PlatformIO see this ; [env:Nucleo-F446ZE] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ ^18.0.0 board = nucleo_f446ze framework = arduino lib_deps = ${env.lib_deps} @@ -258,7 +258,7 @@ monitor_echo = yes ; installed before you can let PlatformIO see this ; ; [env:Nucleo-F412ZG] -; platform = ststm32 @ 17.6.0 +; platform = ststm32 @ ^18.0.0 ; board = nucleo_f412zg ; framework = arduino ; lib_deps = ${env.lib_deps} @@ -270,7 +270,7 @@ monitor_echo = yes ; Experimental - Ethernet work still in progress ; [env:Nucleo-F429ZI] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ ^18.0.0 board = nucleo_f429zi framework = arduino lib_deps = ${env.lib_deps} @@ -289,7 +289,7 @@ upload_protocol = stlink ; Experimental - Ethernet work still in progress ; [env:Nucleo-F439ZI] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ ^18.0.0 ; board = nucleo_f439zi ; Temporarily treat it as an F429ZI (they are code compatible) until ; the PR to PlatformIO to update the F439ZI JSON file is available From f2a7577313d8a0435cedcce8e17834ee81624ae3 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Sat, 8 Feb 2025 16:46:59 +0800 Subject: [PATCH 22/28] Update STM32 core to v19.0.0 for PeripheralPins --- platformio.ini | 16 ++++++++-------- version.h | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/platformio.ini b/platformio.ini index 9797748..7828029 100644 --- a/platformio.ini +++ b/platformio.ini @@ -194,7 +194,7 @@ monitor_speed = 115200 monitor_echo = yes [env:Nucleo-F411RE] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ 19.0.0 board = nucleo_f411re framework = arduino lib_deps = ${env.lib_deps} @@ -203,7 +203,7 @@ monitor_speed = 115200 monitor_echo = yes [env:Nucleo-F446RE] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ 19.0.0 board = nucleo_f446re framework = arduino lib_deps = ${env.lib_deps} @@ -215,7 +215,7 @@ monitor_echo = yes ; tested as yet ; [env:Nucleo-F401RE] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ 19.0.0 board = nucleo_f401re framework = arduino lib_deps = ${env.lib_deps} @@ -228,7 +228,7 @@ monitor_echo = yes ; installed before you can let PlatformIO see this ; ; [env:Nucleo-F413ZH] -; platform = ststm32 @ 17.6.0 +; platform = ststm32 @ 19.0.0 ; board = nucleo_f413zh ; framework = arduino ; lib_deps = ${env.lib_deps} @@ -240,7 +240,7 @@ monitor_echo = yes ; installed before you can let PlatformIO see this ; [env:Nucleo-F446ZE] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ 19.0.0 board = nucleo_f446ze framework = arduino lib_deps = ${env.lib_deps} @@ -252,7 +252,7 @@ monitor_echo = yes ; installed before you can let PlatformIO see this ; ; [env:Nucleo-F412ZG] -; platform = ststm32 @ 17.6.0 +; platform = ststm32 @ 19.0.0 ; board = nucleo_f412zg ; framework = arduino ; lib_deps = ${env.lib_deps} @@ -264,7 +264,7 @@ monitor_echo = yes ; Experimental - Ethernet work still in progress ; [env:Nucleo-F429ZI] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ 19.0.0 board = nucleo_f429zi framework = arduino lib_deps = ${env.lib_deps} @@ -284,7 +284,7 @@ upload_protocol = stlink ; Experimental - Ethernet work still in progress ; [env:Nucleo-F439ZI] -platform = ststm32 @ 17.6.0 +platform = ststm32 @ 19.0.0 ; board = nucleo_f439zi ; Temporarily treat it as an F429ZI (they are code compatible) until ; the PR to PlatformIO to update the F439ZI JSON file is available diff --git a/version.h b/version.h index ea1326d..372491e 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.5.12" +#define VERSION "5.5.13" +// 5.5.13 - Update STM32duino core to v19.0.0. for updated PeripheralPins.c in preparation for F429/439ZI Ethernet support // 5.5.12 - Websocket support (wifi only) // 5.5.11 - (5.4.2) accessory command reverse // 5.5.10 - CamParser fix From ed69a51e97f20fd36cb694a54805772ea505c972 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Tue, 11 Feb 2025 11:46:48 +1100 Subject: [PATCH 23/28] STM32 platformio.ini update for PeripheralPins --- platformio.ini | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/platformio.ini b/platformio.ini index 7828029..0e4e6c5 100644 --- a/platformio.ini +++ b/platformio.ini @@ -276,7 +276,7 @@ lib_ignore = WiFi101 WiFiEspAT WiFiMulti_Generic WiFiNINA_Generic -build_flags = -std=c++17 -Os -g2 -Wunused-variable +build_flags = -std=c++17 -Os -g2 -Wunused-variable -DCUSTOM_PERIPHERAL_PINS monitor_speed = 115200 monitor_echo = yes upload_protocol = stlink @@ -300,7 +300,7 @@ lib_ignore = WiFi101 WiFiEspAT WiFiMulti_Generic WiFiNINA_Generic -build_flags = -std=c++17 -Os -g2 -Wunused-variable +build_flags = -std=c++17 -Os -g2 -Wunused-variable -DCUSTOM_PERIPHERAL_PINS monitor_speed = 115200 monitor_echo = yes upload_protocol = stlink From c99eac6ada20484982382bb4fcc22b6de140e58c Mon Sep 17 00:00:00 2001 From: pmantoine Date: Tue, 11 Feb 2025 11:47:58 +1100 Subject: [PATCH 24/28] STM32 platformio.ini PeripheralPins update --- platformio.ini | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/platformio.ini b/platformio.ini index f31e790..9d4cb46 100644 --- a/platformio.ini +++ b/platformio.ini @@ -270,7 +270,7 @@ monitor_echo = yes ; Experimental - Ethernet work still in progress ; [env:Nucleo-F429ZI] -platform = ststm32 @ ^18.0.0 +platform = ststm32 @ 19.0.0 board = nucleo_f429zi framework = arduino lib_deps = ${env.lib_deps} @@ -281,7 +281,7 @@ lib_ignore = WiFi101 WiFiEspAT WiFiMulti_Generic WiFiNINA_Generic -build_flags = -std=c++17 -Os -g2 -Wunused-variable +build_flags = -std=c++17 -Os -g2 -Wunused-variable -DCUSTOM_PERIPHERAL_PINS monitor_speed = 115200 monitor_echo = yes upload_protocol = stlink @@ -289,7 +289,7 @@ upload_protocol = stlink ; Experimental - Ethernet work still in progress ; [env:Nucleo-F439ZI] -platform = ststm32 @ ^18.0.0 +platform = ststm32 @ 19.0.0 ; board = nucleo_f439zi ; Temporarily treat it as an F429ZI (they are code compatible) until ; the PR to PlatformIO to update the F439ZI JSON file is available @@ -304,7 +304,7 @@ lib_ignore = WiFi101 WiFiEspAT WiFiMulti_Generic WiFiNINA_Generic -build_flags = -std=c++17 -Os -g2 -Wunused-variable +build_flags = -std=c++17 -Os -g2 -Wunused-variable -DCUSTOM_PERIPHERAL_PINS monitor_speed = 115200 monitor_echo = yes upload_protocol = stlink From dcfb3f061d9cdc0a8502b540f5916539be8ad646 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 16 Feb 2025 14:40:57 +0000 Subject: [PATCH 25/28] Squashed commit of the following: commit 87d0a94443a830510923829fe87e89442c7f3bbe Author: Asbelos Date: Sun Feb 16 14:40:09 2025 +0000 Update version.h commit 8c051710c6ef1f8bd4210b66c7a6b7b7a9b6339a Author: Asbelos Date: Sun Feb 16 10:57:54 2025 +0000 Now working commit 6095bf2b3b768442dc10f598e7ef1a514deaa5d1 Author: Asbelos Date: Sat Feb 15 01:06:55 2025 +0000 Untested dcc q implementation commit 52ecab10a79a027b0b289e536577671805f2f026 Author: Asbelos Date: Sat Feb 15 00:22:50 2025 +0000 Initial dcc queue manager --- DCC.cpp | 49 ++++++++------ DCCQueue.cpp | 185 +++++++++++++++++++++++++++++++++++++++++++++++++++ DCCQueue.h | 84 +++++++++++++++++++++++ version.h | 3 +- 4 files changed, 299 insertions(+), 22 deletions(-) create mode 100644 DCCQueue.cpp create mode 100644 DCCQueue.h diff --git a/DCC.cpp b/DCC.cpp index aec1662..b4b6fc1 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -38,6 +38,7 @@ #include "TrackManager.h" #include "DCCTimer.h" #include "Railcom.h" +#include "DCCQueue.h" // This module is responsible for converting API calls into // messages to be sent to the waveform generator. @@ -157,8 +158,8 @@ void DCC::setThrottle2( uint16_t cab, byte speedCode) { b[nB++] = speedCode; // for encoding see setThrottle } - - DCCWaveform::mainTrack.schedulePacket(b, nB, 0); + if ((speedCode & 0x7F) == 1) DCCQueue::scheduleEstopPacket(b, nB, 4, cab); // highest priority + else DCCQueue::scheduleDCCSpeedPacket( b, nB, 4, cab); } void DCC::setFunctionInternal(int cab, byte byte1, byte byte2, byte count) { @@ -172,7 +173,7 @@ void DCC::setFunctionInternal(int cab, byte byte1, byte byte2, byte count) { if (byte1!=0) b[nB++] = byte1; b[nB++] = byte2; - DCCWaveform::mainTrack.schedulePacket(b, nB, count); + DCCQueue::scheduleDCCPacket(b, nB, count); } // returns speed steps 0 to 127 (1 == emergency stop) @@ -238,7 +239,7 @@ bool DCC::setFn( int cab, int16_t functionNumber, bool on) { b[nB++] = (functionNumber & 0x7F) | (on ? 0x80 : 0); // low order bits and state flag b[nB++] = functionNumber >>7 ; // high order bits } - DCCWaveform::mainTrack.schedulePacket(b, nB, 4); + DCCQueue::scheduleDCCPacket(b, nB, 4); } // We use the reminder table up to 28 for normal functions. // We use 29 to 31 for DC frequency as well so up to 28 @@ -339,16 +340,17 @@ void DCC::setAccessory(int address, byte port, bool gate, byte onoff /*= 2*/) { // second byte is of the form 1AAACPPG, where C is 1 for on, PP the ports 0 to 3 and G the gate (coil). b[0] = address % 64 + 128; b[1] = ((((address / 64) % 8) << 4) + (port % 4 << 1) + gate % 2) ^ 0xF8; - if (onoff != 0) { - DCCWaveform::mainTrack.schedulePacket(b, 2, 3); // Repeat on packet three times -#if defined(EXRAIL_ACTIVE) - RMFT2::activateEvent(address<<2|port,gate); -#endif - } - if (onoff != 1) { + if (onoff==0) { // off packet only b[1] &= ~0x08; // set C to 0 - DCCWaveform::mainTrack.schedulePacket(b, 2, 3); // Repeat off packet three times - } + DCCQueue::scheduleDCCPacket(b, 2, 3); + } else if (onoff==1) { // on packet only + DCCQueue::scheduleDCCPacket(b, 2, 3); + } else { // auto timed on then off + DCCQueue::scheduleAccOnOffPacket(b, 2, 3, 100); // On then off after 100mS + } +#if defined(EXRAIL_ACTIVE) + if (onoff !=0) RMFT2::activateEvent(address<<2|port,gate); +#endif } bool DCC::setExtendedAccessory(int16_t address, int16_t value, byte repeats) { @@ -398,7 +400,7 @@ whole range of the 11 bits sent to track. | (((~(address>>8)) & 0x07)<<4) // shift out 8, invert, mask 3 bits, shift up 4 | ((address & 0x03)<<1); // mask 2 bits, shift up 1 b[2]=value; - DCCWaveform::mainTrack.schedulePacket(b, sizeof(b), repeats); + DCCQueue::scheduleDCCPacket(b, sizeof(b), repeats); return true; } @@ -417,7 +419,7 @@ void DCC::writeCVByteMain(int cab, int cv, byte bValue) { b[nB++] = cv2(cv); b[nB++] = bValue; - DCCWaveform::mainTrack.schedulePacket(b, nB, 4); + DCCQueue::scheduleDCCPacket(b, nB, 4); } // @@ -435,7 +437,7 @@ void DCC::readCVByteMain(int cab, int cv, ACK_CALLBACK callback) { b[nB++] = cv2(cv); b[nB++] = 0; - DCCWaveform::mainTrack.schedulePacket(b, nB, 4); + DCCQueue::scheduleDCCPacket(b, nB, 4); Railcom::anticipate(cab,cv,callback); } @@ -457,7 +459,7 @@ void DCC::writeCVBitMain(int cab, int cv, byte bNum, bool bValue) { b[nB++] = cv2(cv); b[nB++] = WRITE_BIT | (bValue ? BIT_ON : BIT_OFF) | bNum; - DCCWaveform::mainTrack.schedulePacket(b, nB, 4); + DCCQueue::scheduleDCCPacket(b, nB, 4); } bool DCC::setTime(uint16_t minutes,uint8_t speed, bool suddenChange) { @@ -494,7 +496,7 @@ b[1]=0b11000001; // 1100-0001 (model time) b[2]=minutes % 60 ; // MM b[3]= 0b11100000 | (minutes/60); // 111H-HHHH weekday not supported b[4]= (suddenChange ? 0b10000000 : 0) | speed; -DCCWaveform::mainTrack.schedulePacket(b, sizeof(b), 2); +DCCQueue::scheduleDCCPacket(b, sizeof(b), 2); return true; } @@ -844,12 +846,17 @@ byte DCC::loopStatus=0; void DCC::loop() { TrackManager::loop(); // power overload checks - issueReminders(); + if (DCCWaveform::mainTrack.isReminderWindowOpen()) { + // Now is a good time to choose a packet to be sent + // Either highest priority from the queues or a reminder + if (!DCCQueue::scheduleNext()) { + issueReminders(); + DCCQueue::scheduleNext(); // push through any just created reminder + } + } } void DCC::issueReminders() { - // if the main track transmitter still has a pending packet, skip this time around. - if (!DCCWaveform::mainTrack.isReminderWindowOpen()) return; // Move to next loco slot. If occupied, send a reminder. auto slot = nextLocoReminder; if (slot >= &speedTable[MAX_LOCOS]) slot=&speedTable[0]; // Go to start of table diff --git a/DCCQueue.cpp b/DCCQueue.cpp new file mode 100644 index 0000000..8330e74 --- /dev/null +++ b/DCCQueue.cpp @@ -0,0 +1,185 @@ +/* + * © 2025 Chris Harlow + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + + +#include "Arduino.h" +#include "defines.h" +#include "DCCQueue.h" +#include "DCCWaveform.h" +#include "DIAG.h" + +// create statics +DCCQueue* DCCQueue::lowPriorityQueue=new DCCQueue(); +DCCQueue* DCCQueue::highPriorityQueue=new DCCQueue(); +PendingSlot* DCCQueue::recycleList=nullptr; + + DCCQueue::DCCQueue() { + head=nullptr; + tail=nullptr; + } + + void DCCQueue::addQueue(PendingSlot* p) { + if (tail) tail->next=p; + else head=p; + tail=p; + p->next=nullptr; + } + + void DCCQueue::jumpQueue(PendingSlot* p) { + p->next=head; + head=p; + if (!tail) tail=p; + } + + + void DCCQueue::recycle(PendingSlot* p) { + p->next=recycleList; + recycleList=p; + } + + // Packet joins end of low priority queue. + void DCCQueue::scheduleDCCPacket(byte* packet, byte length, byte repeats) { + lowPriorityQueue->addQueue(getSlot(NORMAL_PACKET,packet,length,repeats,0)); + } + + // Packet replaces existing loco speed packet or joins end of high priority queue. + + void DCCQueue::scheduleDCCSpeedPacket(byte* packet, byte length, byte repeats, uint16_t loco) { + for (auto p=highPriorityQueue->head;p;p=p->next) { + if (p->locoId==loco) { + // replace existing packet + memcpy(p->packet,packet,length); + p->packetLength=length; + p->packetRepeat=repeats; + return; + } + } + highPriorityQueue->addQueue(getSlot(NORMAL_PACKET,packet,length,repeats,loco)); + } + + + // ESTOP - + // any outstanding throttle packet for this loco (all if loco=0) discarded + // Packet joins start of queue, + + + void DCCQueue::scheduleEstopPacket(byte* packet, byte length, byte repeats,uint16_t loco) { + + // DIAG(F("DCC ESTOP loco=%d"),loco); + + // kill any existing throttle packets for this loco + PendingSlot * previous=nullptr; + auto p=highPriorityQueue->head; + while(p) { + if (loco==0 || p->locoId==loco) { + // drop this packet from the highPriority queue + if (previous) previous->next=p->next; + else highPriorityQueue->head=p->next; + + recycle(p); // recycle this slot + + // address next packet + p=previous?previous->next : highPriorityQueue->head; + } + else { + previous=p; + p=p->next; + } + } + // add the estop packet to the start of the queue + highPriorityQueue->jumpQueue(getSlot(NORMAL_PACKET,packet,length,repeats,0)); + } + + // Accessory gate-On Packet joins end of queue as normal. + // When dequeued, packet is retained at start of queue + // but modified to gate-off and given the delayed start. + // getNext will ignore this packet until the requested start time. + void DCCQueue::scheduleAccOnOffPacket(byte* packet, byte length, byte repeats,int16_t delayms) { + auto p=getSlot(ACC_ON_PACKET,packet,length,repeats,0); + p->delayOff=delayms; + lowPriorityQueue->addQueue(p); + }; + + + // Obtain packet (fills packet, length and repeats) + // returns 0 length if nothing in queue. + + bool DCCQueue::scheduleNext() { + // check high priority queue first + if (!DCCWaveform::mainTrack.isReminderWindowOpen()) return false; + PendingSlot* previous=nullptr; + for (auto p=highPriorityQueue->head;p;p=p->next) { + // skip over pending ACC_OFF packets which are still delayed + if (p->type == ACC_OFF_PACKET && millis()startTime) continue; + // use this slot + DCCWaveform::mainTrack.schedulePacket(p->packet,p->packetLength,p->packetRepeat); + // remove this slot from the queue + if (previous) previous->next=p->next; + else highPriorityQueue->head=p->next; + if (!highPriorityQueue->head) highPriorityQueue->tail=nullptr; + + // and recycle it. + recycle(p); + return true; + } + + // No high priopity packets found, check low priority queue + auto p=lowPriorityQueue->head; + if (!p) return false; // nothing in queues + + // schedule first packet in queue + DCCWaveform::mainTrack.schedulePacket(p->packet,p->packetLength,p->packetRepeat); + + // remove from queue + lowPriorityQueue->head=p->next; + if (!lowPriorityQueue->head) lowPriorityQueue->tail=nullptr; + + if (p->type == ACC_ON_PACKET) { + // convert to a delayed off packet and jump the high priority queue + p->type= ACC_OFF_PACKET; + p->packet[1] &= ~0x08; // set C to 0 (gate off) + p->startTime=millis()+p->delayOff; + highPriorityQueue->jumpQueue(p); + } + else recycle(p); // recycle this slot + return true; + } + + // obtain and initialise slot for a PendingSlot. + PendingSlot* DCCQueue::getSlot(PendingType type, byte* packet, byte length, byte repeats,uint16_t loco) { + PendingSlot * p; + if (recycleList) { + p=recycleList; + recycleList=p->next; + } + else { + DIAG(F("New DCC queue slot")); + p=new PendingSlot; // need a queue entry + } + p->next=nullptr; + p->type=type; + p->packetLength=length; + p->packetRepeat=repeats; + memcpy((void*)p->packet,packet,length); + p->locoId=loco; + return p; + } + + diff --git a/DCCQueue.h b/DCCQueue.h new file mode 100644 index 0000000..4c1eacb --- /dev/null +++ b/DCCQueue.h @@ -0,0 +1,84 @@ +/* + * © 2025 Chris Harlow + * All rights reserved. + * + * This file is part of CommandStation-EX + * + * This is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * It is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with CommandStation. If not, see . + */ + +#ifndef DCCQueue_h +#define DCCQueue_h +#include "Arduino.h" +#include "DCCWaveform.h" + +enum PendingType:byte {NORMAL_PACKET,ACC_ON_PACKET,ACC_OFF_PACKET,DEAD_PACKET}; + struct PendingSlot { + PendingSlot* next; + PendingType type; + byte packetLength; + byte packetRepeat; + byte packet[MAX_PACKET_SIZE]; + + union { // use depends on packet type + uint16_t locoId; // NORMAL_PACKET .. only set >0 for speed change packets + // so they can be easily discarded if an estop jumps the queue. + uint16_t delayOff; // ACC_ON_PACKET delay to apply between on/off + uint32_t startTime; // ACC_OFF_PACKET time (mS) to transmit + }; + }; + +class DCCQueue { + public: + + + // Non-speed packets are queued in the main queue + static void scheduleDCCPacket(byte* packet, byte length, byte repeats); + + // Speed packets are queued in the high priority queue + static void scheduleDCCSpeedPacket(byte* packet, byte length, byte repeats, uint16_t loco); + + // ESTOP packets jump the high priority queue and discard any outstanding throttle packets for this loco + static void scheduleEstopPacket(byte* packet, byte length, byte repeats,uint16_t loco); + + // Accessory gate-On Packet joins end of main queue as normal. + // When dequeued, packet is modified to gate-off and given the delayed start in the high priority queue. + // getNext will ignore this packet until the requested start time. + static void scheduleAccOnOffPacket(byte* packet, byte length, byte repeats,int16_t delayms); + + + // Schedules a main track packet from the queues if none pending. + // returns true if a packet was scheduled. + static bool scheduleNext(); + + private: + + // statics to manage high and low priority queues and recycleing of PENDINGs + static PendingSlot* recycleList; + static DCCQueue* highPriorityQueue; + static DCCQueue* lowPriorityQueue; + + DCCQueue(); + + PendingSlot* head; + PendingSlot * tail; + + // obtain and initialise slot for a PendingSlot. + static PendingSlot* getSlot(PendingType type, byte* packet, byte length, byte repeats, uint16_t loco); + static void recycle(PendingSlot* p); + void addQueue(PendingSlot * p); + void jumpQueue(PendingSlot * p); + +}; +#endif \ No newline at end of file diff --git a/version.h b/version.h index 372491e..6334f14 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.5.13" +#define VERSION "5.5.14" +// 5.5.14 - DCC Non-blocking packet queue with priority // 5.5.13 - Update STM32duino core to v19.0.0. for updated PeripheralPins.c in preparation for F429/439ZI Ethernet support // 5.5.12 - Websocket support (wifi only) // 5.5.11 - (5.4.2) accessory command reverse From 2a007f99dd8ab7dff0ad3627e0290e539d0ae351 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 2 Mar 2025 22:44:01 +0100 Subject: [PATCH 26/28] version 5.5.15 --- GITHUB_SHA.h | 2 +- version.h | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index e7b6ef2..0d3c4b6 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-more-ether-202503022012Z" +#define GITHUB_SHA "devel-202503022043Z" diff --git a/version.h b/version.h index 6334f14..0ebeda3 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.5.14" +#define VERSION "5.5.15" +// 5.5.15 - Support for F429ZI/F329ZI +// - Own mDNS support for (wired) Ethernet // 5.5.14 - DCC Non-blocking packet queue with priority // 5.5.13 - Update STM32duino core to v19.0.0. for updated PeripheralPins.c in preparation for F429/439ZI Ethernet support // 5.5.12 - Websocket support (wifi only) From 029440921418ccbf409534ce9352a8badedffba0 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Mon, 3 Mar 2025 11:18:31 +1100 Subject: [PATCH 27/28] STM32 update platformio.ini for F439ZI --- platformio.ini | 10 +++------- 1 file changed, 3 insertions(+), 7 deletions(-) diff --git a/platformio.ini b/platformio.ini index d1634ce..a6108f2 100644 --- a/platformio.ini +++ b/platformio.ini @@ -259,7 +259,7 @@ monitor_echo = yes ; monitor_echo = yes ; upload_protocol = stlink -; Experimental - Ethernet work still in progress +; Experimental - Ethernet beta test ; [env:Nucleo-F429ZI] platform = ststm32 @ 19.0.0 @@ -278,15 +278,11 @@ monitor_speed = 115200 monitor_echo = yes upload_protocol = stlink -; Experimental - Ethernet work still in progress +; Experimental - Ethernet beta test ; [env:Nucleo-F439ZI] platform = ststm32 @ 19.0.0 -; board = nucleo_f439zi -; Temporarily treat it as an F429ZI (they are code compatible) until -; the PR to PlatformIO to update the F439ZI JSON file is available -; PMA - 28-Sep-2024 -board = nucleo_f429zi +board = nucleo_f439zi framework = arduino lib_deps = ${env.lib_deps} stm32duino/STM32Ethernet @ ^1.4.0 From 84918cbf3681ab54a210b576628c1bf7c1e9f906 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 5 Mar 2025 01:32:57 +0000 Subject: [PATCH 28/28] DOXYGEN in Exrail --- EXRAIL2MacroReset.h | 1129 ++++++++++++++++++++++++++++++++++++++++--- version.h | 3 +- 2 files changed, 1062 insertions(+), 70 deletions(-) diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 9405a19..df4e413 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -17,13 +17,15 @@ * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License - * along with CommandStation. If not, see . + * along with CommandStation. If not, see https://www.gnu.org/licenses/. */ // This file cleans and resets the RMFT2 Macros. // It is used between passes to reduce complexity in RMFT2Macros.h // DO NOT add an include guard to this file. +// Doxygen comments in this file are intended for the EXRAIL end user. + // Undefine all RMFT macros #undef ACTIVATE #undef ACTIVATEL @@ -135,9 +137,7 @@ #undef PICKUP_STASH #undef PIN_TURNOUT #undef PRINT -#ifndef DISABLE_PROG #undef POM -#endif #undef POWEROFF #undef POWERON #undef READ_LOCO @@ -204,180 +204,1171 @@ #undef XFWD #ifndef RMFT2_UNDEF_ONLY +/** + * @def ACTIVATE(addr,subaddr) + * @brief Send DCC Accessory Activate packet (gate on then off) + * @param addr DCC short address of accessory + * @param subaddr DCC sub address + */ #define ACTIVATE(addr,subaddr) -#define ACTIVATEL(addr) -#define AFTER(sensor_id,timer...) +/** + * @def ACTIVATEL(linearaddr) + * @brief Send DCC Accessory Activate packet (gate on then off) + * @param linearaddr DCC linear address of accessory + */ +#define ACTIVATEL(linearaddr) +/** + * @def AFTER(vpin,timer...) + * @brief Wait for sensor activated, then decativated for given time + * @param vpin Virtual Pin number of sensor + * @param timer... optional wait in mS, default 500 + */ +#define AFTER(vpin,timer...) +/** + * @def AFTEROVERLOAD(track_id) + * @brief Wait for overload to be resolved + * @param track_id A..H + */ #define AFTEROVERLOAD(track_id) +/** + * @def ALIAS(name,value...) + * @brief defines a named numeric value. + * @param name c++ variable name that can be used throighout the script + * @param value... if omitted, a large negative value is created automatically + */ #define ALIAS(name,value...) +/** + * @def AMBER(signal_id) + * @brief Sets a signal to amber state + * @see ONAMBER + * @param signal_id + */ #define AMBER(signal_id) +/** + * @def ANOUT(vpin,value,param1,param2) + * @brief Writes to the HAL analog output interface of a device driver. + * Values and meanings of extra parameters depend on driver. + * @param vpin Virtual pin number of device + * @param value basic analog value + * @param param1 device dependent + * @param param2 device dependent + */ #define ANOUT(vpin,value,param1,param2) -#define AT(sensor_id) +/** + * @def AT(vpin) + * @brief wait intil a sensor becomes active + * @param vpin Virtual pin of sensor. Use negative value for sensors that are HIGH when activated + */ +#define AT(vpin) +/** + * @def ASPECT(address,value) + * @brief Sends a DCC aspect value to an accessory address. + * May also change status of a signal defined using this aspect. + * @param address Linear DCC address of device + * @param value Aspect value (Device dependent) + */ #define ASPECT(address,value) -#define ATGTE(sensor_id,value) -#define ATLT(sensor_id,value) -#define ATTIMEOUT(sensor_id,timeout_ms) -#define AUTOMATION(id,description) +/** + * @def ATGTE(vpin,value) + * @brief Wait for analog sensor to be greater than given value + * @param vpin Analog pin number + * @param value integer value to compare against + */ +#define ATGTE(vpin,value) +/** + * @def ATLT(vpin,value) + * @brief Wait for analog sensor value to be less than given value + * @param vpin Analog pin number + * @param value integer value to compare against + */ +#define ATLT(vpin,value) +/** + * @def ATTIMEOUT(vpin,timeout_ms) + * @brief Wait for sensor active, with timeout. Use IFTIMEOUT to determine whether the AT was satisfied. + * @see IFTIMEOUT + * @param vpin Sensor pin number + * @param timeout_ms Millseconds to wait before timeout + */ +#define ATTIMEOUT(vpin,timeout_ms) +/** + * @def AUTOMATION(sequence_id,description) + * @brief Defines starting point of a sequence that will be shown as an Automation by + * the throttles. Automations are started by the throttle handing over a loco id to be driven. + * @param sequence_id Unique sequence id value + * @param description (Quoted text) will be shown on throttle button + */ +#define AUTOMATION(sequence_id,description) +/** + * @def AUTOSTART + * @brief A new task will be created starting from this point at Command Station startup + */ #define AUTOSTART +/** + * @def BLINK(vpin,onDuty,offDuty) + * @brief Starts a blinking process for a vpin (typically a LED) + * Stop blink with SET or RESET. + * @param vpin Pin to blink + * @param onDuty Milliseconds with LED ON + * @param offDuty Milliseconds with LED off + */ #define BLINK(vpin,onDuty,offDuty) +/** + * @def BROADCAST(msg) + * @brief Send raw message text to all throttles using the DCC-EX protocol + * @see WITHROTTLE + * @param msg Quoted message + */ #define BROADCAST(msg) -#define CALL(route) -#define CLEAR_STASH(id) -#define CLEAR_ALL_STASH(id) -#define CLOSE(id) +/** + * @def CALL(sequence_id) + * @brief transfer control to another sequence with expectation to return + * @see RETURN + * @param sequence_id SEQUENCE to jump processing to, must terminate or RETURN + * + */ +#define CALL(sequence_id) +/** + * @def CLEAR_STASH(stash_id) + * @brief Clears loco value stored in stash + * @param stash_id which stash to clear. + */ +#define CLEAR_STASH(stash_id) +/** + * @def CLEAR_ALL_STASH + * @brief Clears all stashed loco values + */ +#define CLEAR_ALL_STASH +/** + * @def CLOSE(turnout_id) + * @brief Close turnout by id + * @see THROW + * @param turnout_id + */ +#define CLOSE(turnout_id) +/** + * @def CONFIGURE_SERVO(vpin,pos1,pos2,profile) + * @brief setup servo movement parameters for non-turnout + * @param vpin must refer to a servo capable pin + * @param pos1 SET position of servo + * @param pos2 RESET position of servo + * @param profile Movement profile (Instant, Fast, Medium, Slow, Bounce) + */ #define CONFIGURE_SERVO(vpin,pos1,pos2,profile) -#define DCC_SIGNAL(id,add,subaddr) -#define DCCX_SIGNAL(id,redAspect,amberAspect,greenAspect) -#define DCC_TURNTABLE(id,home,description...) +/** + * @def DCC_SIGNAL(signal_id,addr,subaddr) + * @brief Define a DCC accessory signal with short address + * @param signal_id Id used for all signal manipulation commands + * @param addr DCC address + * @param subaddr DCC subaddress + */ +#define DCC_SIGNAL(signal_id,addr,subaddr) +/** + * @def DCCX_SIGNAL(signal_id,redAspect,amberAspect,greenAspect) + * @brief Dfeine advanced DCC accessory signal with aspects + * @param signal_id DCC Linear address AND Id used for all signal manipulation commands + * @param redAspect + * @param amberAspect + * @param greenAspect + */ +#define DCCX_SIGNAL(signal_id,redAspect,amberAspect,greenAspect) +/** + * @def DCC_TURNTABLE(turntable_id,homeAngle,description...) + * @brief defines a Turntable device + * @param turntable_id + * @param homeAngle the angle of the home position, valid angles are 0 - 3600 + * @param description... Quoted text description of turntable + */ +#define DCC_TURNTABLE(turntable_id,home,description...) +/** + * @def DEACTIVATE(addr,subaddr) + * @brief Sends DCC Deactivate packet (gate on, gate off) + * @param addr DCC accessory address + * @param subaddr DCC accessory subaddress + */ #define DEACTIVATE(addr,subaddr) +/** + * @def DEACTIVATEL(addr) + * @brief Sends DCC Deactivate packet (gate on, gate off) + * @param addr DCC Linear accessory address + */ #define DEACTIVATEL(addr) -#define DELAY(mindelay) -#define DELAYMINS(mindelay) +/** + * @def DELAY(delay_mS) + * @brief Waits for given milliseconds delay (This is not blocking) + * @param delay_mS Delay time in milliseconds + */ +#define DELAY(delay_ms) +/** + * @def DELAYMINS(delay_minutes) + * @brief Waits for given minutes delay (This is not blocking) + * @param delay_minutes + */ +#define DELAYMINS(delay_minutes) +/** + * @def DELAYRANDOM(mindelay,maxdelay) + * @brief Waits for random delay between min and max milliseconds (This is not blocking) + * @param mindelay minumum delay in mS + * @param maxdelay maximum delay in mS + */ #define DELAYRANDOM(mindelay,maxdelay) +/** + * @def DONE + * @brief Stops task loco (if any) and terminates current task + */ #define DONE +/** + * @def DRIVE(analogpin) + * @brief RESERVED do not use + * @param analogpin + */ #define DRIVE(analogpin) +/** + * @def ELSE + * @brief introduces alternate processing path after any kind of IF + * @see IF + */ #define ELSE -#define ENDEXRAIL +/** + * @def ENDIF + * @brief determines end of IF(any type) block. + * @see IF + * IF something ENDIF, or + * IF something ELSE something ENDIF + */ #define ENDIF +/** + * @def ENDTASK + * @brief same as DONE + * @see DONE + */ #define ENDTASK -#define ESTOP +/** + * @def ESTOP + * @brief Performs emergency stop on current task loco + */ +#define ESTOP +/** + * @def ESTOPALL + * @brief Performs emergency stop on all locos + */ #define ESTOPALL -#define EXRAIL +/** + * @def EXTT_TURNTABLE(turntable_id,vpin,homeAngle,description...) + * @brief This statement will create the EX‑Turntable turntable/traverser object only, so you will need a separate HAL() statement for an EX‑Turntable device driver. + * @param turntable_id + * @param vpin + * @param homeAngle the angle of the home position, valid angles are 0 - 3600 + * @param description... + */ #define EXTT_TURNTABLE(id,vpin,home,description...) -#define FADE(pin,value,ms) +/** + * @def FADE(vpin,value,ms) + * @brief Modifies analog value slowly taking a given time + * @param vpin Servo virtual pin number + * @param value new target value + * @param ms time to reach value + */ +#define FADE(vpin,value,ms) +/** + * @def FOFF(func) + * @brief Turns off current loco function + * @see FON + * @param func + */ #define FOFF(func) -#define FOLLOW(route) +/** + * @def FOLLOW(sequence_id) + * @brief Task processing follows given route or sequence (Effectively a GoTo) + * @param sequence_id + */ +#define FOLLOW(sequence_id) +/** + * @def FON(func) + * @brief Turn on current loco function + * @see FOFF + * @param func + */ #define FON(func) +/** + * @def FORGET + * @brief Removes current loco from task and DCC reminders table. + */ #define FORGET -#define FREE(blockid) +/** + * @def FREE(token_id) + * @brief Frees logical token + * @see RESERVE + * @param token_id 0..255 + */ +#define FREE(token_id) +/** + * @def FTOGGLE(func) + * @brief Toggles function for current loco + * @param func + */ #define FTOGGLE(func) +/** + * @def FWD(speed) + * @brief Instructs current loco to set DCC speed + * @param speed 0..127 (1=ESTOP) + */ #define FWD(speed) +/** + * @def GREEN(signal_id) + * @brief Sets signal to green state + * @param signal_id + */ #define GREEN(signal_id) +/** + * @def HAL(haltype,params...) + * @brief Defines VPIN mapping for specific hardware drivers + * @param haltype driver name, normally device type + * @param params... depend on driver. + */ #define HAL(haltype,params...) +/** + * @def HAL_IGNORE_DEFAULTS + * @brief System will ignore default HAL device mappings + */ #define HAL_IGNORE_DEFAULTS -#define IF(sensor_id) +/** + * @def IF(vpin) + * @brief Checks sensor state, If false jumps to matching nested ELSE or ENDIF + * @param vpin VPIN of sensor. Negative VPIN will invert sensor state. + */ +#define IF(vpin) +/** + * @def IFAMBER(signal_id) + * @brief Checks if signal is in AMBER state. + * @see IF + * @param signal_id + */ #define IFAMBER(signal_id) +/** + * @def IFCLOSED(turnout_id) + * @brief Checks if given turnout is in close state + * @see IF + * @param turnout_id + */ #define IFCLOSED(turnout_id) +/** + * @def IFGREEN(signal_id) + * @brief Checks if given signal is in GREEN state + * @see IF + * @param signal_id + */ #define IFGREEN(signal_id) -#define IFGTE(sensor_id,value) +/** + * @def IFGTE(vpin,value) + * @brief Checks if analog vpin sensor >= value + * @see IF + * @param vpin + * @param value + */ +#define IFGTE(vpin,value) +/** + * @def IFLOCO(loco_id) + * @brief Checks if current task loco = loco_id + * @see IF + * @param loco_id + */ #define IFLOCO(loco_id) -#define IFLT(sensor_id,value) -#define IFNOT(sensor_id) +/** + * @def IFLT(sensor_id,value) + * @brief Checks if analog sensor < value + * @see IF + * @param vpin Analog vpin of sensor + * @param value + */ +#define IFLT(vpin,value) +/** + * @def IFNOT(vpin) + * @brief Inverse of IF + * @see IF + * @param vpin + */ +#define IFNOT(vpin) +/** + * @def IFRANDOM(percent) + * @brief randomly satisfield IF at given percent probability + * @see IF + * @param percent + */ #define IFRANDOM(percent) +/** + * @def IFRED(signal_id) + * @brief Checks if given signal is in RED state + * @see IF + * @param signal_id + */ #define IFRED(signal_id) +/** + * @def IFTHROWN(turnout_id) + * @brief Checks if given turnout is in THROWN state + * @see IF + * @param turnout_id + */ #define IFTHROWN(turnout_id) -#define IFRESERVE(block) +/** + * @def IFRESERVE(token_id) + * @brief Attempts to reserve token and if satisfiled the token remains reserved. + * @see IF RESERVE FREE + * @param token_id + */ +#define IFRESERVE(token_id) +/** + * @def IFTIMEOUT + * @brief Checks TIMEOUT state after an AT/AFTER request with timeout value. + * @see IF AT AFTER + */ #define IFTIMEOUT +/** + * @def IFTTPOSITION(turntable_id,position) + * @brief Checks if Turntable is in given position + * @see IF + * @param turntable_id + * @param position + */ #define IFTTPOSITION(turntable_id,position) -#define IFRE(sensor_id,value) +/** + * @def IFRE(vpin,value) + * @brief Checks external rotary encoder value + * @param vpin of device driver for rotary encoder + * @param value + */ +#define IFRE(vpin,value) +/** + * @def INVERT_DIRECTION + * @brief Marks current task so that FWD and REV commands are inverted. + */ #define INVERT_DIRECTION +/** + * @def JMRI_SENSOR(vpin,count...) + * @brief Defines multiple JMRI type sensor feedback definitions each with id matching vpin + * @param vpin first vpin number + * @param count... Number of consecutine VPINS for which to create JMRI sensor feedbacks. Default 1. + */ #define JMRI_SENSOR(vpin,count...) +/** + * @def JOIN + * @brief Switches PROG track to receive MAIN track DCC packets. (Drive on PROG track) + */ #define JOIN +/** + * @def KILLALL + * @brief Tertminates all running EXRAIL tasks + */ #define KILLALL -#define LATCH(sensor_id) +/** + * @def LATCH(vpin) + * @brief Make all AT/AFTER/IF see vpin as HIGH without checking hardware + * @param vpin Must only be for VPINS 0..255 + */ +#define LATCH(vpin) +/** + * @def LCC(eventid) + * @brief Issue event to LCC + * @param eventid + */ #define LCC(eventid) +/** + * @def LCCX(senderid,eventid) + * @brief Issue LCC event while impersonating another sender + * @param senderid + * @param eventid + */ #define LCCX(senderid,eventid) +/** + * @def LCD(row,msg) + * @brief Write message on row of default configured LCD/OLED + * @see SCREEN + * @param row + * @param msg Quoted text + */ #define LCD(row,msg) +/** + * @def MOMENTUM(accel,decel...) + * @brief sets momentum in mS per DCC 127 step for curent loco. + * @param accel Acceleration momentum + * @param decel... Braking momantum. (=Acceleration of not given) + */ #define MOMENTUM(accel,decel...) +/** + * @def SCREEN(display,row,msg) + * @brief Send message to external display hadlers + * @param display number, 0=local display, others are handled by external + * displays which may have different display numbers on different devices. + * @param row + * @param msg Quoted text + */ #define SCREEN(display,row,msg) +/** + * @def LCN(msg) + * @brief Reserved for LCN communication. Refer to their documentation. + * @param msg + */ #define LCN(msg) +/** + * @def MESSAGE(msg) + * @brief Send a human readable message to all throttle users + * @param msg Quoted text + */ #define MESSAGE(msg) -#define MOVETT(id,steps,activity) -#define NEOPIXEL(id,r,g,b,count...) -#define NEOPIXEL_SIGNAL(sigid,redcolour,ambercolour,greencolour) +/** + * @def MOVETT(turntable_id,steps,activity) + * @brief Move Turntable to specific position + * @see ROTATE + * @param turntable_id + * @param steps position to move to + * @param activity see ROTATE + */ +#define MOVETT(turntable_id,steps,activity) +/** + * @def NEOPIXEL(vpin,r,g,b,count...) + * @brief Set a NEOPIXEL vpin to a given red/green/blue colour + * @param vpin VPIN of a pixel + * @param r red component 0-255 + * @param g green component 0-255 + * @param b blue component 0-255 + * @param count... Number of consecutive pixels to set, Default 1. + */ +#define NEOPIXEL(vpin,r,g,b,count...) +/** + * @def NEOPIXEL_SIGNAL(signal_id,redcolour,ambercolour,greencolour) + * @brief Define a signal that uses a single multi colour pixel + * @see NEORGB + * @param vpin unique signal_id + * @param redcolour RGB colour use NEORGB(red,green,blue) to create values. + * @param ambercolour + * @param greencolour + */ +#define NEOPIXEL_SIGNAL(vpin,redcolour,ambercolour,greencolour) +/** + * @def ACON(eventid) + * @brief Send MERG CBUS ACON to Adapter + * @param eventid + */ #define ACON(eventid) +/** + * @def ACOF(eventid) + * @brief Send MERG CBUS ACOF to Adapter + * @param eventid + */ #define ACOF(eventid) +/** + * @def ONACON(eventid) + * @brief Start task here when ACON for event receied from MERG CBUS + * @param eventid + */ #define ONACON(eventid) +/** + * @def ONACOF(eventid) + * @brief Start task here when ACOF for event receied from MERG CBUS + * @param eventid + */ #define ONACOF(eventid) +/** + * @def ONACTIVATE(addr,subaddr) + * @brief Start task here when DCC Activate sent for short address + * @param addr + * @param subaddr + */ #define ONACTIVATE(addr,subaddr) +/** + * @def ONACTIVATEL(linear) + * @brief Start task here when DCC Activate sent for linear address + * @param linear + */ #define ONACTIVATEL(linear) +/** + * @def ONAMBER(signal_id) + * @brief Start task here when signal set to AMBER state + * @param signal_id + */ #define ONAMBER(signal_id) -#define ONBLOCKENTER(blockid) -#define ONBLOCKEXIT(blockid) -#define ONTIME(value) +/** + * @def OBLOCKENTER(block_id) + * @brief Start task here when a loco enters a railcom block + * @param block_id vpin associated to block by HAL(I2CRailcom..) + */ +#define ONBLOCKENTER(block_id) +/** + * @def OBLOCKEXIT(vpin) + * @brief Start task here when a loco leaves a railcom block + * @param block_id vpin associated to block by HAL(I2CRailcom..) + */ +#define ONBLOCKEXIT(block_id) +/** + * @def ONTIME(minute_in_day) + * @brief Start task here when fastclock matches + * @param minute_in_day (0..1439) + */ +#define ONTIME(minute_in_day) +/** + * @def ONCLOCKTIME(hours,mins) + * @brief Start task here when fastclock matches time + * @param hours + * @param mins + */ #define ONCLOCKTIME(hours,mins) +/** + * @def ONCLOCKMINS(mins) + * @brief Start task here hourly when fastclock minutes matches + * @param mins + */ #define ONCLOCKMINS(mins) +/** + * @def ONOVERLOAD(track_id) + * @brief Start task here when given track goes into overload + * @param track_id A..H + */ #define ONOVERLOAD(track_id) +/** + * @def ONDEACTIVATE(addr,subaddr) + * @brief Start task here when DCC deactivate packet sent + * @param addr + * @param subaddr + */ #define ONDEACTIVATE(addr,subaddr) +/** + * @def ONDEACTIVATEL(linear) + * @brief Start task here when DCC deactivate sent to linear address + * @param linear + */ #define ONDEACTIVATEL(linear) +/** + * @def ONCLOSE(turnout_id) + * @brief Start task here when turnout closed + * @param turnout_id + */ #define ONCLOSE(turnout_id) +/** + * @def ONLCC(sender,event) + * @brief Start task here when LCC event arrives from sender + * @param sender + * @param event + */ #define ONLCC(sender,event) +/** + * @def ONGREEN(signal_id) + * @brief Start task here when signal set to GREEN state + * @param signal_id + */ #define ONGREEN(signal_id) +/** + * @def ONRED(signal_id) + * @brief Start task here when signal set to RED state + * @param signal_id + */ #define ONRED(signal_id) +/** + * @def ONROTATE(turntable_id) + * @brief Start task here when turntable is rotated + * @param turntable_id + */ #define ONROTATE(turntable_id) +/** + * @def ONTHROW(turnout_id) + * @brief Start task here when turnout is Thrown + * @param turnout_id + */ #define ONTHROW(turnout_id) -#define ONCHANGE(sensor_id) -#define ONSENSOR(sensor_id) -#define ONBUTTON(sensor_id) +/** + * @def ONCHANGE(vpin) + * @brief Toratry encoder change starts task here (This is obscurely different from ONSENSOR which will be merged in a later release.) + * @param vpin + */ +#define ONCHANGE(vpin) +/** + * @def ONSENSOR(vpin) + * @brief Start task here when sensor changes state (debounced) + * @param vpin + */ +#define ONSENSOR(vpin) +/** + * @def ONBUTTON(vpin) + * @brief Start task here when sensor changes HIGH to LOW. + * @param vpin + */ +#define ONBUTTON(vpin) +/** + * @def PAUSE + * @brief Pauses all EXRAIL tasks except the curremnt one. + * Other tasks ESTOP their locos until RESUME issued + */ #define PAUSE -#define PIN_TURNOUT(id,pin,description...) +/** + * @def PIN_TURNOUT(turnout_id,vpin,description...) + * @brief Defines a turnout which operates on a signle pin + * @param turnout_id + * @param vpin + * @param description... Quoted text (shown to throttles) or HIDDEN + + */ +#define PIN_TURNOUT(id,vpin,description...) +/** + * @def PRINT(msg) + * @brief prints diagnostic message on USB serial + * @param msg Quoted text + */ #define PRINT(msg) +/** + * @def PARSE(msg) + * @brief Executes <> command as if entered from serial + * @param msg Quoted text, preferably including <> + */ #define PARSE(msg) -#define PICKUP_STASH(id) -#ifndef DISABLE_PROG +/** + * @def PICKUP_STASH(stash_id) + * @see STASH + * @brief Loads stashed value into current task loco + * @param stash_id position in stash where a loco id was previously saved. + */ +#define PICKUP_STASH(stash_id) +/** + * @def POM(cv,value) + * @brief Write value to cv on current tasks loco (Program on Main) + * @param cv + * @param value + */ #define POM(cv,value) -#endif +/** + * @def POWEROFF + * @brief Powers off all tracks + */ #define POWEROFF +/** + * @def POWERON + * @brief Powers ON all tracks + */ #define POWERON +/** + * @def READ_LOCO + * @brief Reads loco Id from prog track and sets currenmt task loco id. + */ #define READ_LOCO +/** + * @def RED(signal_id) + * @brief sets signal to RED state + * @param signal_id + */ #define RED(signal_id) -#define RESERVE(blockid) -#define RESET(pin,count...) +/** + * @def RESERVE(token_id) + * @brief Waits for token for block. If not available immediately, current task loco is stopped. + * @param token_id + */ +#define RESERVE(token_id) +/** + * @def RESET(vpin,count...) + * @brief Sets output pin LOW + * @see SET + * @param vpin + * @param count... Number of consecutive pins, default 1 + */ +#define RESET(vpin,count...) +/** + * @def RESUME + * @brief Resumes PAUSEd tasks + * @see PAUSE + */ #define RESUME +/** + * @def RETURN + * @brief Returns to CALL + * @see CALL + */ #define RETURN +/** + * @def REV(speed) + * @brief Issues DCC speed packet for current loco in reverse. + * @see FWD + * @param speed (0..127, 1=ESTOP) + */ #define REV(speed) +/** + * @def ROTATE(turntable_id,position_id,activity) + * @brief Rotates an EX-Turntable to a given position + * @param turntable_id + * @param position_id + * @param activity + * - **Turn**: Rotate turntable, maintain phase + * - **Turn_PInvert**: Rotate turntable, invert phase + * - **Home**: Initiate homing + * - **Calibrate**: Initiate calibration sequence + * - **LED_On**: Turn LED on + * - **LED_Slow**: Set LED to a slow blink + * - **LED_Fast**: Set LED to a fast blink + * - **LED_Off**: Turn LED off + * - **Acc_On**: Turn accessory pin on + * - **Acc_Off**: Turn accessory pin off + */ #define ROTATE(turntable_id,position,activity) -#define ROTATE_DCC(turntable_id,position) +/** + * @def ROTATE_DCC(turntable_id,position_id) + * @brief Rotates turntable to a given position using DCC commands + * @param turntable_id + * @param position_id + */ +#define ROTATE_DCC(turntable_id,position_id) +/** + * @def ROSTER(cab,name,funcmap...) + * @brief Describes a loco roster entry visible to throttles + * @param cab loco DCC address or 0 for default entry + * @param name Quoted text + * @param funcmap... Quoted text, optional list of function names separated by / character with momentary function names prefixed with an *. + * + */ #define ROSTER(cab,name,funcmap...) -#define ROUTE(id,description) -#define ROUTE_ACTIVE(id) -#define ROUTE_INACTIVE(id) -#define ROUTE_HIDDEN(id) -#define ROUTE_DISABLED(id) -#define ROUTE_CAPTION(id,caption) -#define SENDLOCO(cab,route) -#define SEQUENCE(id) +/** + * @def ROUTE(sequence_id,description) + * @brief DEfines starting point of a sequence that will appear as a route on throttle buttons. + * @param sequence_id + * @param description Quoted text, throttle button capotion. + */ +#define ROUTE(sequence_id,description) +/** + * @def ROUTE_ACTIVE(sequence_id) + * @brief Tells throttle to display the route button as active + * @param sequence_id of ROUTE/AUTOMATION + */ +#define ROUTE_ACTIVE(sequence_id) +/** + * @def ROUTE_INACTIVE(sequence_id) + * @brief Tells throttle to display the route button as inactive + * @param sequence_id of ROUTE/AUTOMATION + */ +#define ROUTE_INACTIVE(sequence_id) +/** + * @def ROUTE_HIDDEN(sequence_id) + * @brief Tells throttle to hide the route button + * @param sequence_id of ROUTE/AUTOMATION + */ +#define ROUTE_HIDDEN(sequence_id) +/** + * @def ROUTE_DISABLED(sequence_id) + * @brief Tells throttle to display the route button as disabled + * @param sequence_id of ROUTE/AUTOMATION + */ +#define ROUTE_DISABLED(sequence_id) +/** + * @def ROUTE_CAPTION(sequence_id,caption) + * @brief Tells throttle to change thr route button caption + * @param sequence_id of ROUTE/AUTOMATION + * @param caption + */ +#define ROUTE_CAPTION(sequence_id,caption) +/** + * @def SENDLOCO(cab,sequence_id) + * @brief Start a new task to drive the loco + * @param cab loco to be driven + * @param route sequence_id of route, automation or sequence to drive + */ +#define SENDLOCO(cab,sequence_id) +/** + * @def SEQUENCE(sequence_id) + * @brief Provides a unique label than can be used to call, follow or start. + * @see CALL + * @see FOLLOW + * @see START + * @param sequence_id + */ +#define SEQUENCE(sequence_id) +/** + * @def SERIAL(msg) + * @brief Write direct to Serial output + * @param msg Quoted text + */ #define SERIAL(msg) +/** + * @def SERIAL1(msg) + * @brief Write direct to Serial1 output + * @param msg Quoted text + */ #define SERIAL1(msg) +/** + * @def SERIAL2(msg) + * @brief Write direct to Serial2 output + * @param msg Quoted text + */ #define SERIAL2(msg) +/** + * @def SERIAL3(msg) + * @brief Write direct to Serial3 output + * @param msg Quoted text +*/ #define SERIAL3(msg) +/** + * @def SERIAL4(msg) + * @brief Write direct to Serial4 output + * @param msg Quoted text + */ #define SERIAL4(msg) +/** + * @def SERIAL5(msg) + * @brief Write direct to Serial5 output + * @param msg Quoted text + */ #define SERIAL5(msg) +/** + * @def SERIAL6(msg) + * @brief Write direct to Serial6 output + * @param msg Quoted text + */ #define SERIAL6(msg) -#define SERVO(id,position,profile) -#define SERVO2(id,position,duration) +/** + * @def SERVO(vpin,position,profile) + * @brief Move servo to given position + * @param vpin of servo + * @param position servo position (values are hardware dependent) + * @param profile movement profile (Instant, Fast, Medium, Slow, Bounce) + */ +#define SERVO(vpin,position,profile) +/** + * @def SERVO2(id,position,duration) + * @brief Move servo to given position taking time + * @param vpin of servo + * @param position servo position (values are hardware dependent) + * @param duration mS + */ +#define SERVO2(vpin,position,duration) +/** + * @def SERVO_SIGNAL(vpin,redpos,amberpos,greenpos) + * @brief Dedfine a servo based signal with 3 servo positions + * @param vpin of servo, acts as signal_id + * @param redpos servo position (values are hardware dependent) + * @param amberpos servo position (values are hardware dependent) + * @param greenpos servo position (values are hardware dependent) + */ #define SERVO_SIGNAL(vpin,redpos,amberpos,greenpos) -#define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) -#define SET(pin,count...) +/** + * @def SERVO_TURNOUT(turnout_id,vpin,activeAngle,inactiveAngle,profile,description...) + * @brief Define a servo driven turnout + * @param turnout_id used by THROW/CLOSE + * @param vpin for servo + * @param activeAngle servo position (values are hardware dependent) + * @param inactiveAngle servo position (values are hardware dependent) + * @param profile movement profile (Instant, Fast, Medium, Slow, Bounce) + * @param description... Quoted text shown to throttles or HIDDEN keyword to hide turnout button + */ +#define SERVO_TURNOUT(turnout_id,vpin,activeAngle,inactiveAngle,profile,description...) +/** + * @def SET(vpin,count...) + * @brief Set pin HIGH + * @see RESET + * @param vpin + * @param count... Number of sequential vpins to set. Default 1. + */ +#define SET(vpin,count...) +/** + * @def SET_TRACK(track,mode) + * @brief Set output track type + * @param track A..H + * @param mode NONE, MAIN, PROG, DC, EXT, BOOST, BOOST_INV, BOOST_AUTO, MAIN_INV, MAIN_AUTO, DC_INV, DCX + */ #define SET_TRACK(track,mode) +/** + * @def SET_POWER(track,onoff) + * @brief Set track power mode + * @param track A..H + * @param onoff ON or OFF + */ #define SET_POWER(track,onoff) +/** + * @def SETLOCO(loco) + * @brief Sets the loco being handled by the current task + * @param loco + */ #define SETLOCO(loco) +/** + * @def SETFREQ(freq) + * @brief Sets the DC track PWM frequency + * @param freq Frequency is default 0, or 1..3 + */ #define SETFREQ(freq) +/** + * @def SIGNAL(redpin,amberpin,greenpin) + * @brief Define a Signal with LOW=on leds + * @see SIGNALH + * @param redpin vpin for RED state, also acts as signal_id + * @param amberpin + * @param greenpin + */ #define SIGNAL(redpin,amberpin,greenpin) +/** + * @def SIGNALH(redpin,amberpin,greenpin) + * @brief define a signal with HIGH=ON leds + * @param redpin vpin for RED state, also acts as signal_id + * @param amberpin + * @param greenpin + */ #define SIGNALH(redpin,amberpin,greenpin) +/** + * @def SPEED(speed) + * @brief Changes current tasks loco speed without changing direction + * @param speed 0..127 (1=ESTOP) + */ #define SPEED(speed) -#define START(route) -#define STASH(id) +/** + * @def START(sequence_id) + * @brief Starts a new task at the given route/animation/sequence + * @param sequence_id + */ +#define START(sequence_id) +/** + * @def STASH(stash_id) + * @brief saves cuttent tasks loco id in the stash array + * @param stash_id position in stash array to save loco id + */ +#define STASH(stash_id) +/** + * @def STEALTH(code...) + * @brief Allows for embedding raw C++ code in context of current task. + * @param code... c++ code to be executed. This requires intimate understanding of the product acrhitecture. + */ #define STEALTH(code...) +/** + * @def STEALTH_GLOBAL(code...) + * @brief Allows for embedding raw c++ code out of context. + * @param code... c++ code to be defined. This requires intimate understanding of the product acrhitecture. + */ #define STEALTH_GLOBAL(code...) +/** + * @def STOP + * @brief Same as SPEED(0) + * @see SPEED + */ #define STOP -#define THROW(id) -#define TOGGLE_TURNOUT(id) -#define TT_ADDPOSITION(turntable_id,position,value,angle,description...) -#define TURNOUT(id,addr,subaddr,description...) -#define TURNOUTL(id,addr,description...) +/** + * @def THROW(turnout_id) + * @brief Throws given turnout + * @see CLOSE + * @param turnout_id + */ +#define THROW(turnout_id) +/** + * @def TOGGLE_TURNOUT(turnout_id) + * @brief Toggles given turnout + * @param tuirnout_id + */ +#define TOGGLE_TURNOUT(turnout_id) +/** + * @def TT_ADDPOSITION(turntable_id,position_id,value,angle,description...) + * @brief Defines a turntable track position + * @param turntable_id + * @param position_id each position is given an id + * @param address DCC accessory address + * @param angle Used only for throttles that may draw a visual representation of the turntable + * @param description... quoted text or HIDDEN + */ +#define TT_ADDPOSITION(turntable_id,position_id,value,angle,description...) +/** + * @def TURNOUT(turnout_id,addr,subaddr,description...) + * @brief Defines a DCC accessory turnout with legacy address + * @param turnout_id to be used in THROW/CLOSE etc + * @param addr DCC accessory address + * @param subaddr DCC accessory subaddress + * @param description... Quoted text or HIDDEN, appears on throttle buttons + */ +#define TURNOUT(turnout_id,addr,subaddr,description...) +/** + * @def TURNOUTL(turnout_id,addr,description...) + * @brief Defines a DCC accessory turnout with inear address + * @see TURNOUT + * @param turnout_id to be used in THROW/CLOSE etc + * @param addr DCC accessory linear address + * @param description... Quoted text or HIDDEN, appears on throttle buttons + */ +#define TURNOUTL(tirnout_id,addr,description...) +/** + * @def UNJOIN + * @brief Disconnects PROG track from MAIN + * @see JOIN + */ #define UNJOIN -#define UNLATCH(sensor_id) -#define VIRTUAL_SIGNAL(id) +/** + * @def UNLATCH(vpin) + * @brief removes latched on flag + * @see LATCH + * @param vpin (limited to 0..255) + */ +#define UNLATCH(vpin) +/** + * @def VIRTUAL_SIGNAL(signal_id) + * @brief Defines a virtual (no hardware) signal, use ONhandlers to simulate hardware + * @see SIGNAL ONRED ONAMBER ONGREEN + * @param signal_id + */ +#define VIRTUAL_SIGNAL(signal_id) +/** + * @def VIRTUAL_TURNOUT(turnout_id,description...) + * @brief Defines a virtual (no hardware) turnout, use ONhandlers to simulate hardware + * @see TURNOUT ONCLOSE ONTHROW + * @param turnout_id + * @param description... quoted text or HIDDEN + */ #define VIRTUAL_TURNOUT(id,description...) +/** + * @def WAITFOR(vpin) + * @brief WAits for completion of servo movement + * @param vpin + */ #define WAITFOR(pin) #ifndef IO_NO_HAL +/** + * @def WAITFORTT(turntable_id) + * @brief waits for completion of turntable movement + * @param turntable_id + */ #define WAITFORTT(turntable_id) #endif +/** + * @def WITHROTTLE(msg) + * @brief Broadcasts a string in Withrottle protocol format to all throttles using this protocol. + * @param msg quoted string + */ #define WITHROTTLE(msg) +/** + * @def XFOFF(cab,func) + * @brief Turns function off for given loco + * @param cab + * @param func function number + */ #define XFOFF(cab,func) +/** + * @def XFON(cab,func) + * @brief Turns function ON for given loco + * @param cab + * @param func + */ #define XFON(cab,func) +/** + * @def XFTOGGLE(cab,func) + * @brief Toggles function state for given loco + * @param cab + * @param func + */ #define XFTOGGLE(cab,func) +/** + * @def XFWD(cab,speed) + * @brief Sends DCC speed to loco in forward direction + * @param cab + * @param speed (0..127, 1=ESTOP) + */ #define XFWD(cab,speed) +/** + * @def XREV(cab,speed) + * @brief Sends DCC speed to loco in reverse direction + * @param cab + * @param speed (0..127, 1=ESTOP) + */ #define XREV(cab,speed) +/** + * @def XPOM(cab,cv,value) + * @brief Sends DCC speed to loco in reverse direction + * @param cab loco id + * @param cv to be updated + * @param value to be written to cv + */ #define XPOM(cab,cv,value) #endif diff --git a/version.h b/version.h index 0ebeda3..f32c980 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.5.15" +#define VERSION "5.5.16" +// 5.5.16 - DOXYGEN comments in EXRAIL2MacroReset.h // 5.5.15 - Support for F429ZI/F329ZI // - Own mDNS support for (wired) Ethernet // 5.5.14 - DCC Non-blocking packet queue with priority