From b1f5e9f48c0262f758ab17b3d58f4d0cf1b4f958 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Jun 2022 15:04:45 +0200 Subject: [PATCH 001/218] Initial version --- log2lrc.py | 62 ++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 62 insertions(+) create mode 100755 log2lrc.py diff --git a/log2lrc.py b/log2lrc.py new file mode 100755 index 0000000..1f24338 --- /dev/null +++ b/log2lrc.py @@ -0,0 +1,62 @@ +#!/usr/bin/env python3 +# +# Copyright 2022 Harald Barth +# +# This converts serial logs with timestamps from the +# Arduino IDE to LRC format. +# +# 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 . + +# Usage: log2lrc.py HH:MM:SS HH:MM:SS filename +# logtime videotime +# The resulting timestamps will be adjusted by logtime - videotime. +# logtime > videotime + +import sys +import datetime +import fileinput +import re + +timediff = datetime.datetime.strptime('00:00:00','%H:%M:%S') + +def convert(timestr): + times=timestr.split('.',1) # remove fractions of second + timestamp_obj=datetime.datetime.strptime(times[0],'%H:%M:%S') + timestamp_obj=timestamp_obj-timediff # calculate offset + timestr='{0:%H:%M:%S}'.format(timestamp_obj) + timestr='%s.%s' % (timestr, times[1][0:2]) # add fractions of second, 2 digits + return timestr + +def main(argv): + global timediff + fromtime_str=sys.argv[1] + fromtime_obj=datetime.datetime.strptime(fromtime_str,'%H:%M:%S') + totime_str=sys.argv[2] + totime_obj=datetime.datetime.strptime(totime_str,'%H:%M:%S') + sys.argv=sys.argv[2:] + + + timediff = fromtime_obj - totime_obj + + for line in fileinput.input(): + line = re.split('\s+', line, 1) + if (len(line) > 1 and len(line[1]) > 1): + l = line[1].replace('\n','') + l = l.replace('\r','') + l = re.sub('^-> ','',l) + print("[%s]%s" % (convert(line[0]),l)) + +if __name__ == "__main__": + main(sys.argv) + From a80b16acba66194475b2923479f8f43fe1170692 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Jun 2022 19:46:59 +0200 Subject: [PATCH 002/218] HH not supported --- log2lrc.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/log2lrc.py b/log2lrc.py index 1f24338..046cb8f 100755 --- a/log2lrc.py +++ b/log2lrc.py @@ -34,7 +34,7 @@ def convert(timestr): times=timestr.split('.',1) # remove fractions of second timestamp_obj=datetime.datetime.strptime(times[0],'%H:%M:%S') timestamp_obj=timestamp_obj-timediff # calculate offset - timestr='{0:%H:%M:%S}'.format(timestamp_obj) + timestr='{0:%M:%S}'.format(timestamp_obj) timestr='%s.%s' % (timestr, times[1][0:2]) # add fractions of second, 2 digits return timestr From f2eb64fd2156e7ae742bed96fbdc15d6587eb186 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 31 Jul 2022 23:07:19 +0200 Subject: [PATCH 003/218] make service start to be outside the DONT_TOUCH_WIFI_CONF area --- WifiInterface.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 0120074..7d8b8bb 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -276,6 +276,7 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password, checkForOK(2000, true); } } +#endif //DONT_TOUCH_WIFI_CONF StringFormatter::send(wifiStream, F("AT+CIPSERVER=0\r\n")); // turn off tcp server (to clean connections before CIPMUX=1) checkForOK(1000, true); // ignore result in case it already was off @@ -291,7 +292,6 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password, StringFormatter::send(wifiStream, F("AT+CIPSERVER=1,%d\r\n"), port); // turn on server on port if (!checkForOK(1000, true)) return WIFI_DISCONNECTED; -#endif //DONT_TOUCH_WIFI_CONF StringFormatter::send(wifiStream, F("AT+CIFSR\r\n")); // Display ip addresses to the DIAG if (!checkForOK(1000, F("IP,\"") , true, false)) return WIFI_DISCONNECTED; From 34c3d1076795e53941b6b06f0770910c8ab62bf3 Mon Sep 17 00:00:00 2001 From: Bruno Crivelari Sanches Date: Sat, 3 Sep 2022 17:16:33 -0300 Subject: [PATCH 004/218] Keep Ethernet singleton "alive" until connection is established. --- EthernetInterface.cpp | 70 +++++++++++++++++++++---------------------- EthernetInterface.h | 3 ++ 2 files changed, 38 insertions(+), 35 deletions(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index f691f45..9d04423 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -36,7 +36,15 @@ EthernetInterface * EthernetInterface::singleton=NULL; void EthernetInterface::setup() { singleton=new EthernetInterface(); - if (!singleton->connected) singleton=NULL; + + DIAG(F("Ethernet begin OK.")); + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + DIAG(F("Ethernet shield not found")); + + singleton=NULL; + + return; + } }; @@ -60,38 +68,7 @@ EthernetInterface::EthernetInterface() DIAG(F("Ethernet.begin FAILED")); return; } - #endif - DIAG(F("begin OK.")); - if (Ethernet.hardwareStatus() == EthernetNoHardware) { - DIAG(F("Ethernet shield not found")); - return; - } - - unsigned long startmilli = millis(); - while ((millis() - startmilli) < 5500) // Loop to give time to check for cable connection - { - if (Ethernet.linkStatus() == LinkON) - break; - DIAG(F("Ethernet waiting for link (1sec) ")); - delay(1000); - } - - if (Ethernet.linkStatus() == LinkOFF) { - DIAG(F("Ethernet cable not connected")); - return; - } - - connected=true; - - IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address - - server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT - server->begin(); - - LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); - LCD(5,F("Port:%d"), IP_PORT); - - outboundRing=new RingStream(OUTBOUND_RING_SIZE); + #endif } /** @@ -99,8 +76,9 @@ EthernetInterface::EthernetInterface() * */ void EthernetInterface::loop() -{ - if (!singleton) return; +{ + if(!singleton || ((!singleton->connected) && (!singleton->checkLink()))) + return; switch (Ethernet.maintain()) { @@ -125,6 +103,28 @@ void EthernetInterface::loop() } +bool EthernetInterface::checkLink() +{ + if (Ethernet.linkStatus() != LinkON) + return false; + + DIAG(F("Ethernet cable connected")); + + connected=true; + + IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address + + server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT + server->begin(); + + LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); + LCD(5,F("Port:%d"), IP_PORT); + + outboundRing=new RingStream(OUTBOUND_RING_SIZE); + + return true; +} + void EthernetInterface::loop2() { // get client from the server diff --git a/EthernetInterface.h b/EthernetInterface.h index 2a94ac6..ff673c1 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -60,6 +60,9 @@ class EthernetInterface { bool connected; EthernetInterface(); void loop2(); + + bool checkLink(); + EthernetServer * server; EthernetClient clients[MAX_SOCK_NUM]; // accept up to MAX_SOCK_NUM client connections at the same time; This depends on the chipset used on the Shield uint8_t buffer[MAX_ETH_BUFFER+1]; // buffer used by TCP for the recv From 64b1de08be6e1e78d8858c43dddfbed08d0a3097 Mon Sep 17 00:00:00 2001 From: Bruno Crivelari Sanches Date: Mon, 5 Sep 2022 14:23:54 -0300 Subject: [PATCH 005/218] Detects when ethernet cable is connected and is disconnected, also correctly handles EthernetServer tead down on such situations --- EthernetInterface.cpp | 74 ++++++++++++++++++++++++++++++++++--------- EthernetInterface.h | 5 +-- 2 files changed, 62 insertions(+), 17 deletions(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index 9d04423..ddd762c 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -41,6 +41,7 @@ void EthernetInterface::setup() if (Ethernet.hardwareStatus() == EthernetNoHardware) { DIAG(F("Ethernet shield not found")); + delete singleton; singleton=NULL; return; @@ -71,13 +72,24 @@ EthernetInterface::EthernetInterface() #endif } +/** + * @brief Cleanup any resources + * + * @return none + */ +EthernetInterface::~EthernetInterface() +{ + delete server; + delete outboundRing; +} + /** * @brief Main loop for the EthernetInterface * */ void EthernetInterface::loop() { - if(!singleton || ((!singleton->connected) && (!singleton->checkLink()))) + if(!singleton || (!singleton->checkLink())) return; switch (Ethernet.maintain()) @@ -103,26 +115,58 @@ void EthernetInterface::loop() } +/** + * @brief Checks ethernet link cable status and detects when it connects / disconnects + * + * @return true when cable is connected, false otherwise + */ bool EthernetInterface::checkLink() { - if (Ethernet.linkStatus() != LinkON) - return false; - - DIAG(F("Ethernet cable connected")); + if (Ethernet.linkStatus() == LinkON) + { + //if we are not connected yet, setup a new server + if(!connected) + { + DIAG(F("Ethernet cable connected")); - connected=true; - - IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address + connected=true; + + IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address - server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT - server->begin(); - - LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); - LCD(5,F("Port:%d"), IP_PORT); + server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT + server->begin(); + + LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); + LCD(5,F("Port:%d"), IP_PORT); - outboundRing=new RingStream(OUTBOUND_RING_SIZE); + // + //only create a outboundRing it none exists, this may happen if the cable gets disconnected and connected again + if(!outboundRing) + outboundRing=new RingStream(OUTBOUND_RING_SIZE); + } - return true; + return true; + } + else if(connected) + { + DIAG(F("Ethernet cable disconnected")); + connected=false; + + //clean up any client + for (byte socket = 0; socket < MAX_SOCK_NUM; socket++) + { + if(clients[socket].connected()) + clients[socket].stop(); + } + + /* tear down server */ + delete server; + server = nullptr; + + LCD(4,F("IP: None")); + } + + return false; } void EthernetInterface::loop2() diff --git a/EthernetInterface.h b/EthernetInterface.h index ff673c1..ce5bbd9 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -59,14 +59,15 @@ class EthernetInterface { static EthernetInterface * singleton; bool connected; EthernetInterface(); + ~EthernetInterface(); void loop2(); bool checkLink(); - EthernetServer * server; + EthernetServer * server = nullptr; EthernetClient clients[MAX_SOCK_NUM]; // accept up to MAX_SOCK_NUM client connections at the same time; This depends on the chipset used on the Shield uint8_t buffer[MAX_ETH_BUFFER+1]; // buffer used by TCP for the recv - RingStream * outboundRing; + RingStream * outboundRing = nullptr; }; From 23d015880443db9dc776d1172ee461bcc2850814 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 5 Sep 2022 22:19:18 +0200 Subject: [PATCH 006/218] simplify EthernetInterface::setup, make code shorter and format according to our overall style --- EthernetInterface.cpp | 149 +++++++++++++++++------------------------- EthernetInterface.h | 34 +++++----- 2 files changed, 77 insertions(+), 106 deletions(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index ddd762c..7d8fed8 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -1,6 +1,7 @@ /* + * © 2022 Bruno Sanches * © 2021 Fred Decker - * © 2020-2021 Harald Barth + * © 2020-2022 Harald Barth * © 2020-2021 Chris Harlow * © 2020 Gregor Baues * All rights reserved. @@ -35,17 +36,13 @@ EthernetInterface * EthernetInterface::singleton=NULL; */ void EthernetInterface::setup() { + if (Ethernet.hardwareStatus() == EthernetNoHardware) { + if (singleton!=NULL) + DIAG(F("Prog Error!")); + } else { singleton=new EthernetInterface(); - - DIAG(F("Ethernet begin OK.")); - if (Ethernet.hardwareStatus() == EthernetNoHardware) { - DIAG(F("Ethernet shield not found")); - - delete singleton; - singleton=NULL; - - return; - } + } + DIAG(F("Ethernet shield %sfound"), singleton==NULL ? "not " : ""); }; @@ -77,42 +74,35 @@ EthernetInterface::EthernetInterface() * * @return none */ -EthernetInterface::~EthernetInterface() -{ - delete server; - delete outboundRing; +EthernetInterface::~EthernetInterface() { + delete server; + delete outboundRing; } /** * @brief Main loop for the EthernetInterface * */ -void EthernetInterface::loop() -{ - if(!singleton || (!singleton->checkLink())) - return; - - switch (Ethernet.maintain()) - { - case 1: - //renewed fail - DIAG(F("Ethernet Error: renewed fail")); - singleton=NULL; - return; - - case 3: - //rebind fail - DIAG(F("Ethernet Error: rebind fail")); - singleton=NULL; - return; - - default: - //nothing happened - break; - } - - singleton->loop2(); +void EthernetInterface::loop() { + if(!singleton || (!singleton->checkLink())) + return; + switch (Ethernet.maintain()) { + case 1: + //renewed fail + DIAG(F("Ethernet Error: renewed fail")); + singleton=NULL; + return; + case 3: + //rebind fail + DIAG(F("Ethernet Error: rebind fail")); + singleton=NULL; + return; + default: + //nothing happened + break; + } + singleton->loop2(); } /** @@ -120,57 +110,40 @@ void EthernetInterface::loop() * * @return true when cable is connected, false otherwise */ -bool EthernetInterface::checkLink() -{ - if (Ethernet.linkStatus() == LinkON) - { - //if we are not connected yet, setup a new server - if(!connected) - { - DIAG(F("Ethernet cable connected")); - - connected=true; - - IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address - - server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT - server->begin(); - - LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); - LCD(5,F("Port:%d"), IP_PORT); - - // - //only create a outboundRing it none exists, this may happen if the cable gets disconnected and connected again - if(!outboundRing) - outboundRing=new RingStream(OUTBOUND_RING_SIZE); - } - - return true; - } - else if(connected) - { - DIAG(F("Ethernet cable disconnected")); - connected=false; - - //clean up any client - for (byte socket = 0; socket < MAX_SOCK_NUM; socket++) - { - if(clients[socket].connected()) - clients[socket].stop(); - } - - /* tear down server */ - delete server; - server = nullptr; - - LCD(4,F("IP: None")); +bool EthernetInterface::checkLink() { + if (Ethernet.linkStatus() == LinkON) { + //if we are not connected yet, setup a new server + if(!connected) { + DIAG(F("Ethernet cable connected")); + connected=true; + IPAddress ip = Ethernet.localIP(); // reassign the obtained ip address + server = new EthernetServer(IP_PORT); // Ethernet Server listening on default port IP_PORT + server->begin(); + LCD(4,F("IP: %d.%d.%d.%d"), ip[0], ip[1], ip[2], ip[3]); + LCD(5,F("Port:%d"), IP_PORT); + // only create a outboundRing it none exists, this may happen if the cable + // gets disconnected and connected again + if(!outboundRing) + outboundRing=new RingStream(OUTBOUND_RING_SIZE); } - - return false; + return true; + } else { // connected + DIAG(F("Ethernet cable disconnected")); + connected=false; + //clean up any client + for (byte socket = 0; socket < MAX_SOCK_NUM; socket++) { + if(clients[socket].connected()) + clients[socket].stop(); + } + // tear down server + delete server; + server = nullptr; + LCD(4,F("IP: None")); + } + return false; } - void EthernetInterface::loop2() -{ +void EthernetInterface::loop2() { // get client from the server EthernetClient client = server->accept(); diff --git a/EthernetInterface.h b/EthernetInterface.h index ce5bbd9..a4544c6 100644 --- a/EthernetInterface.h +++ b/EthernetInterface.h @@ -50,25 +50,23 @@ class EthernetInterface { - public: - - static void setup(); - static void loop(); - - private: - static EthernetInterface * singleton; - bool connected; - EthernetInterface(); - ~EthernetInterface(); - void loop2(); - - bool checkLink(); - - EthernetServer * server = nullptr; - EthernetClient clients[MAX_SOCK_NUM]; // accept up to MAX_SOCK_NUM client connections at the same time; This depends on the chipset used on the Shield - uint8_t buffer[MAX_ETH_BUFFER+1]; // buffer used by TCP for the recv - RingStream * outboundRing = nullptr; +public: + static void setup(); + static void loop(); +private: + static EthernetInterface * singleton; + bool connected; + EthernetInterface(); + ~EthernetInterface(); + void loop2(); + bool checkLink(); + + EthernetServer *server = nullptr; + EthernetClient clients[MAX_SOCK_NUM]; // accept up to MAX_SOCK_NUM client connections at the same time + // This depends on the chipset used on the Shield + uint8_t buffer[MAX_ETH_BUFFER+1]; // buffer used by TCP for the recv + RingStream * outboundRing = nullptr; }; #endif From cd15eed0056ae115397fe54dc415e7b2f301fc64 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 13 Sep 2022 22:42:38 +0200 Subject: [PATCH 007/218] EX-RAIL bugfix: Could not read long loco addrs --- EXRAIL2.cpp | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 541e2fb..a6afa2d 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -551,7 +551,15 @@ bool RMFT2::skipIfBlock() { /* static */ void RMFT2::readLocoCallback(int16_t cv) { - progtrackLocoId=cv; + if (cv & LONG_ADDR_MARKER) { // maker bit indicates long addr + progtrackLocoId = cv ^ LONG_ADDR_MARKER; // remove marker bit to get real long addr + if (progtrackLocoId <= HIGHEST_SHORT_ADDR ) { // out of range for long addr + DIAG(F("Long addr %d <= %d unsupported\n"), progtrackLocoId, HIGHEST_SHORT_ADDR); + progtrackLocoId = -1; + } + } else { + progtrackLocoId=cv; + } } void RMFT2::loop() { From 06bd80438e92089364f3c5b3c0cbd69cf7c917eb Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 13 Sep 2022 22:46:43 +0200 Subject: [PATCH 008/218] new version --- version.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/version.h b/version.h index 846ad3a..fc73a4b 100644 --- a/version.h +++ b/version.h @@ -3,11 +3,11 @@ #include "StringFormatter.h" - -#define VERSION "4.1.1 rc3" +#define VERSION "4.1.1 rc4" // 4.1.1 Bugfix: preserve turnout format // Bugfix: parse multiple commands in one buffer string correct // Bugfix: command signal status in Exrail +// Bugfix: EX-RAIL read long loco addr // 4.1.0 ... // // 4.0.2 EXRAIL additions: From 2da28ad2db24f6896a92974803b27c0bfefde189 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 18 Sep 2022 22:23:18 +0200 Subject: [PATCH 009/218] version --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index fc73a4b..4a65046 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,7 @@ #include "StringFormatter.h" -#define VERSION "4.1.1 rc4" +#define VERSION "4.1.1" // 4.1.1 Bugfix: preserve turnout format // Bugfix: parse multiple commands in one buffer string correct // Bugfix: command signal status in Exrail From 7388d14bab7f3eb05a6035cd818162008c53ddc5 Mon Sep 17 00:00:00 2001 From: Fred Date: Thu, 20 Oct 2022 16:08:40 -0400 Subject: [PATCH 010/218] Update release_notes.md --- release_notes.md | 35 ++++++++++++++++++++++++++++++++++- 1 file changed, 34 insertions(+), 1 deletion(-) diff --git a/release_notes.md b/release_notes.md index e5b06ed..98cd5d1 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,4 +1,4 @@ -The DCC-EX Team is pleased to release CommandStation-EX-v4.0.0 as a Production Release. Release v4.0.0 is a Major release that adds significant new product design, plus Automation features and bug fixes. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code so as to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v3.2.0 to v3.2.0 rc13. +The DCC-EX Team is pleased to release CommandStation-EX-v4.1.1 as a Production Release. Release v4.0.0 is a Minor release though it does add significant new automation features to EX-RAIL in addition to some small changes and bug fixes. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code so as to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.1 rc13. **Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** @@ -8,6 +8,39 @@ The DCC-EX Team is pleased to release CommandStation-EX-v4.0.0 as a Production R [CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v0.0.0-Prod/CommandStation-EX.tar.gz) +**EX-RAIL New Features** + + - ACK defaults set to 50mA LIMIT, 2000uS MIN, 20000uS MAX for more compatibility with non NMRA compliant decoders + - Automatically detect and run a myFilter add-on (no need to call setFilter) + - Add commands + - Add "if" signal detection with IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) + - New command to obtain current throttle settings + - New JA, JR, JT commands to obtain route, roster and turnout descriptions + - Add ability for HIDDEN turnouts (hide a REAL turnout and create a VIRTUAL turnout to handle actions that happen BEFORE a turnout is thrown) + - Add VIRTUAL_TURNOUT definition + - New PARSE <> commands in EXRAIL allows sending of command station commands from EX-RAIL + - New and KILLALL command to stop all tasks + - Add FORGET command. Forgets the current loco in DCC reminder tables saving memory and wasted packets sent to the track + - Add Servo signals (SERVO_SIGNAL) + - Add High-On signal pins (SIGNALH) (Arduino normally handles active LOW signals. This allows for active HIGH) + - Add Wait for analog value (ATGTE, ATLT)... (At greater than or Equal and At less than a certain value) + +**EX-RAIL Updates** + + - EXRAIL BROADCAST("msg") sends any message to all throttles/JMRI via serial and WiFi + - EXRAIL POWERON turns on power to both tracks from EX-RAIL (the equivalent of sending the <1> command) + + +**4.1.1 Bug Fixes** + +- Preserve the turnout format +- Parse multiple commands in one buffer string currectly +- Fix command signal status in EX-RAIL +- Read long loco addresses in EX-RAIL +- FIX negative route IDs in WIthrottle + +See the version.h file for notes about which of the 4.1.1 features were added/changed by point release. + **Known Issues** - **Wi-Fi** - Requires sending `` commands from a serial monitor if you want to switch between AP mode and STA station mode after initial setup From 1901d9547eb8cea2438b486415c833c25c0297bb Mon Sep 17 00:00:00 2001 From: Fred Date: Thu, 20 Oct 2022 16:23:13 -0400 Subject: [PATCH 011/218] Update release_notes.md --- release_notes.md | 16 +++++++++++++--- 1 file changed, 13 insertions(+), 3 deletions(-) diff --git a/release_notes.md b/release_notes.md index 98cd5d1..7943f42 100644 --- a/release_notes.md +++ b/release_notes.md @@ -252,7 +252,7 @@ See the version.h file for notes about which of the 4.1.1 features were added/ch - Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) -**CommandStation-EX Developers** +**EX-CommandStation Developers** - Chris Harlow - Bournemouth, UK (UKBloke) - Harald Barth - Stockholm, Sweden (Haba) @@ -267,7 +267,7 @@ See the version.h file for notes about which of the 4.1.1 features were added/ch - M Steve Todd -**exInstaller Software** +**EX-Installer Software** - Anthony W - Dayton, Ohio, USA (Dex, Dex++) @@ -280,12 +280,22 @@ See the version.h file for notes about which of the 4.1.1 features were added/ch - Keith Ledbetter - Chicago, Illinois, USA (Keith Ledbetter) - Kevin Smith - Rochester Hills, Michigan USA (KC Smith) - Colin Grabham - Central NSW, Australia (Kebbin) +- Peter Cole - Brisbane, QLD, Australia (peteGSX) +- Peter Akers - Brisbane, QLD, Australia (flash62au) -**WebThrotle-EX** +**EX-WebThrottle** - Fred Decker - Holly Springs, NC (FlightRisk/FrightRisk) - Mani Kumar - Bangalor, India (Mani /Mani Kumar) - Matt H - Somewhere in Europe + +**Hardware / Electronics** + +- Harald Barth - Stockholm, Sweden (Haba) +- Paul Antoine, Western Australia +- Neil McKechnie - Worcestershire, UK +- Fred Decker - Holly Springs NC, USA +- Herb Morton - Kingwood Texas, USA (Ash++) **Beta Testing / Release Management / Support** From dcab5a0e7268818f39d6330cac001c13606a3b6d Mon Sep 17 00:00:00 2001 From: Fred Date: Thu, 20 Oct 2022 16:25:10 -0400 Subject: [PATCH 012/218] Create release_notes_v4.1.1.md --- Release_Notes/release_notes_v4.1.1.md | 325 ++++++++++++++++++++++++++ 1 file changed, 325 insertions(+) create mode 100644 Release_Notes/release_notes_v4.1.1.md diff --git a/Release_Notes/release_notes_v4.1.1.md b/Release_Notes/release_notes_v4.1.1.md new file mode 100644 index 0000000..7943f42 --- /dev/null +++ b/Release_Notes/release_notes_v4.1.1.md @@ -0,0 +1,325 @@ +The DCC-EX Team is pleased to release CommandStation-EX-v4.1.1 as a Production Release. Release v4.0.0 is a Minor release though it does add significant new automation features to EX-RAIL in addition to some small changes and bug fixes. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code so as to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.1 rc13. + +**Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** + +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip) + + + +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v0.0.0-Prod/CommandStation-EX.tar.gz) + +**EX-RAIL New Features** + + - ACK defaults set to 50mA LIMIT, 2000uS MIN, 20000uS MAX for more compatibility with non NMRA compliant decoders + - Automatically detect and run a myFilter add-on (no need to call setFilter) + - Add commands + - Add "if" signal detection with IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) + - New command to obtain current throttle settings + - New JA, JR, JT commands to obtain route, roster and turnout descriptions + - Add ability for HIDDEN turnouts (hide a REAL turnout and create a VIRTUAL turnout to handle actions that happen BEFORE a turnout is thrown) + - Add VIRTUAL_TURNOUT definition + - New PARSE <> commands in EXRAIL allows sending of command station commands from EX-RAIL + - New and KILLALL command to stop all tasks + - Add FORGET command. Forgets the current loco in DCC reminder tables saving memory and wasted packets sent to the track + - Add Servo signals (SERVO_SIGNAL) + - Add High-On signal pins (SIGNALH) (Arduino normally handles active LOW signals. This allows for active HIGH) + - Add Wait for analog value (ATGTE, ATLT)... (At greater than or Equal and At less than a certain value) + +**EX-RAIL Updates** + + - EXRAIL BROADCAST("msg") sends any message to all throttles/JMRI via serial and WiFi + - EXRAIL POWERON turns on power to both tracks from EX-RAIL (the equivalent of sending the <1> command) + + +**4.1.1 Bug Fixes** + +- Preserve the turnout format +- Parse multiple commands in one buffer string currectly +- Fix command signal status in EX-RAIL +- Read long loco addresses in EX-RAIL +- FIX negative route IDs in WIthrottle + +See the version.h file for notes about which of the 4.1.1 features were added/changed by point release. + +**Known Issues** + +- **Wi-Fi** - Requires sending `` commands from a serial monitor if you want to switch between AP mode and STA station mode after initial setup +- **Pololu Motor Shield** - is supported with this release, but the user may have to adjust timings to enable programming mode due to limitations in its current sensing circuitry + +**All New Major DCC++EX 4.0.0 features** + +- **New HAL Hardware Abstraction Layer API** that automatically detects and greatly simplifies interfacing to many predefined accessory boards for servos, signals & sensors and added I/O (digital and analog inputs and outputs, servos etc). +- HAL Support for; + - MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules. + - PCA9685 PWM (servo & signal) control modules. + - Analogue inputs on Arduino pins and on ADS111x I2C modules. + - MP3 sound playback via DFPlayer module. + - HC-SR04 Ultrasonic range sensor module. + - VL53L0X Laser range sensor module (Time-Of-Flight). + - A new `` command to list the HAL devices attached to the command station + +**New Command Station Broadcast throttle logic** + +- Synchronizes multiple WiThrottles and PC based JMRI Throttles for direction, speed and F-key updates + +**New ‘Discovered Servers’ on WiFi Throttles** + +- Our New multicast Dynamic Network Server (mDNS) enhancement allows us to display the available WiFi server connections to a DCC++EX Command Station. Selecting it allows your WiThrottle App to connect to and load Server Rosters and function keys to your throttle from the new DCC++EX Command Station Server Roster. + +**New DCC++EX 4.0.0 with EX-RAIL Extended Railroad Automation Instruction Language** + +- Use to control your entire layout or as a separate accessory/animation controller +- Awesome, cleverly powerful yet simple user friendly scripting language for user built Automation & Routing scripts. +- You can control Engines, Sensors, Turnouts, Signals, Outputs and Accessories that are entered into your new myAutomation.h file, then uploaded into the DCC++EX Command Station. +- EX-RAIL scripts are automatically displayed as Automation {Handoff} and Route {Set} buttons on supported WiFi Throttle Apps. + +**New EX-RAIL ‘Roster’ Feature** + +- List and store user defined engine roster & function keys inside the command station, and automatically load them in WiFi Throttle Apps. +- When choosing “DCC++EX” from discovered servers an Engine Driver or WiThrottle is directly connected to the Command Station. +- The EX-RAIL ’ROSTER’ command allows all the engine numbers, names and function keys you’ve listed in your myAutomation.h file to automatically upload the Command Station's ‘Server Roster’ into your Engine Driver and WiThrottle Apps. + +**New JMRI 4.99.2 and above specific DCC++EX 4.0 features** + +- Enhanced JMRI DCC++ Configure Base Station pane for building and maintaining Sensor, Turnout and Output devices, or these can automatically be populated from the DCC++EX Command Station's mySetup.h file into JMRI. + +- JMRI now supports multiple serial connected DCC++EX Command Stations, to display and track separate "Send DCC++ Command" and "DCC++ Traffic" Monitors for each Command Station at the same time. + For example: Use an Uno DCC++EX DecoderPro Programming Station {DCC++Prg} on a desktop programming track and a second Mega DCC++EX EX-RAIL Command Station for Operations {DCC++Ops} on the layout with an additional `` programming spur or siding track for acquiring an engine and ‘Drive Away’ onto the mainline (see the DriveAway feature for more information). + +**DCC++EX 4.0.0 additional product enhancements** + +- Additional Motor Shields and Motor Board {boosters) supported +- Additional Accessory boards supported for GPIO expansion, Sensors, Servos & Signals +- Additional diagnostic commands like ‘D ACK RETRY’ and ‘D EXRAIL ON’ events, ‘D HAL SHOW’ devices and ‘D SERVO’ positions, and ‘D RESET’ the command station while maintaining the serial connection with JMRI +- Automatic retry on failed ACK detection to give decoders another chance +- New EX-RAIL ’/’ slash command allows JMRI to directly communicate with many EX-RAIL scripts +- Turnout class revised to expand turnout capabilities and allow turnout names/descriptors to display in WiThrottle Apps. +- Build turnouts through either or both mySetup.h and myAutomation.h files, and have them automatically passed to, and populate, JMRI Turnout Tables +- Turnout user names display in Engine Driver & WiThrottles +- Output class now allows ID > 255. +- Configuration options to globally flip polarity of DCC Accessory states when driven from `` command and `` command. +- Increased use of display for showing loco decoder programming information. +- Can disable EEPROM memory code to allow room for DCC++EX 4.0 to fit on a Uno Command Station +- Can define border between long and short addresses +- Native non-blocking I2C drivers for AVR and Nano architectures (fallback to blocking Wire library for other platforms). +- EEPROM layout change - deletes EEPROM contents on first start following upgrade. + +**4.0.0 Bug Fixes** + +- Compiles on Nano Every +- Diagnostic display of ack pulses >32ms +- Current read from wrong ADC during interrupt +- AT(+) Command Pass Through +- CiDAP WiFi Drop out and the WiThrottle F-key looping error corrected +- One-off error in CIPSEND drop +- Common Fault Pin Error +- Uno Memory Utilization optimized + +#### Summary of Release 3.1.0 key features and/or bug fixes by Point Release + +**Summary of the key new features added to CommandStation-EX V3.0.16** + +- Ignore CV1 bit 7 read if rejected by a non NMRA compliant decoder when identifying loco id + +**Summary of the key new features added to CommandStation-EX V3.0.15** + +- Send function commands just once instead of repeating them 4 times + +**Summary of the key new features added to CommandStation-EX V3.0.14** + +- Add feature to tolerate decoders that incorrectly have gaps in their ACK pulse +- Provide proper track power management when joining and unjoining tracks with <1 JOIN> + +**Summary of the key new features added to CommandStation-EX V3.0.13** + +- Fix for CAB Functions greater than 127 + +**Summary of the key new features added to CommandStation-EX V3.0.12** + +- Fixed clear screen issue for nanoEvery and nanoWifi + +**Summary of the key new features added to CommandStation-EX V3.0.11** + +- Reorganized files for support of 128 speed steps + +**Summary of the key new features added to CommandStation-EX V3.0.10** + +- Added Support for the Teensy 3.2, 3.5, 3.6, 4.0 and 4.1 MCUs +- No functional change just changes to avoid complier warnings for Teensy/nanoEvery + +**Summary of the key new features added to CommandStation-EX V3.0.9** + +- Rearranges serial newlines for the benefit of JMRI +- Major update for efficiencies in displays (LCD, OLED) +- Add I2C Support functions + +**Summary of the key new features added to CommandStation-EX V3.0.8** + +- Wraps <* *> around DIAGS for the benefit of JMRI + +**Summary of the key new features added to CommandStation-EX V3.0.7** + +- Implemented support for older 28 apeed step decoders - Option to turn on 28 step speed decoders in addition to 128. If set, all locos will use 28 steps. +- Improved overload messages with raw values (relative to offset) + +**Summary of the key new features added to CommandStation-EX V3.0.6** + +- Prevent compiler warning about deprecated B constants +- Fix Bug that did not let us transmit 5 byte sized packets - 5 Byte commands like PoM (programming on main) were not being sent correctly +- Support for Huge function numbers (DCC BinaryStateControl) - Support Functions beyond F28 +- ESTOP all - New command to emergency stop all locos on the main track +- <- [cab]> estop and forget cab/all cabs - Stop and remove loco from the CS. Stops the repeating throttle messages +- `` command to reboot Arduino +- Automatic sensor offset detect +- Improved startup msgs from Motor Drivers (accuracy and auto sense factors) +- Drop post-write verify - No need to double check CV writes. Writes are now even faster. +- Allow current sense pin set to UNUSED_PIN - No need to ground an unused analog current pin. Produce startup warning and callback -2 for prog track cmds. + +**Summary of the key new features added to CommandStation-EX V3.0.5** + +- Fix Fn Key startup with loco ID and fix state change for F16-28 +- Removed ethernet mac config and made it automatic +- Show wifi ip and port on lcd +- Auto load config.example.h with warning +- Dropped example .ino files +- Corrected .ino comments +- Add Pololu fault pin handling +- Waveform speed/simplicity improvements +- Improved pin speed in waveform +- Portability to nanoEvery and UnoWifiRev2 CPUs +- Analog read speed improvements +- Drop need for DIO2 library +- Improved current check code +- Linear command +- Removed need for ArduinoTimers files +- Removed option to choose different timer +- Added EX-RAIL hooks for automation in future version +- Fixed Turnout list +- Allow command keywords in mixed case +- Dropped unused memstream +- PWM pin accuracy if requirements met + +**Summary of the key new features added to CommandStation-EX V3.0.4** + +- "Drive-Away" Feature - added so that throttles like Engine Driver can allow a loco to be programmed on a usable, electrically isolated programming track and then drive off onto the main track +- WiFi Startup Fixes + +**Summary of the key new features added to CommandStation-EX V3.0.3** + +- Command to write loco address and clear consist +- Command will allow for consist address +- Startup commands implemented + +**Summary of the key new features added to CommandStation-EX V3.0.2:** + +- Create new output for current in mA for `` command - New current response outputs current in mA, overlimit current, and maximum board capable current +- Simultaneously update JMRI to handle new current meter + +**Summary of the key new features added to CommandStation-EX V3.0.1:** + +- Add back fix for jitter +- Add Turnouts, Outputs and Sensors to `` command output + +**CommandStation-EX V3.0.0:** + +**Release v3.0.0 was a major rewrite if earlier versions of DCC++. The code base was re-architeced and core changes were made to the Waveform generator to reduce overhead and make better use of Arduino.** **Summary of the key new features added in Release v3.0.0 include:** + +- **New USB Browser Based Throttle** - WebThrottle-EX is a full front-end to controller to control the CS to run trains. +- **WiFi Support** - AP and station modes supported. Auto-detection of an ESP8266 WiFi module with AT firmware on a Mega's serial port. Connection to JMRI and WiThrottle clients. +- **Withrottle Integrations** - Act as a host for up to four WiThrottle clients concurrently. +- **Add LCD/OLED support** - OLED supported on Mega only +- **Improved CV programming routines** - checks for length of CV pulse, and breaks out of the wait state once it has received an ACK, now reading one CV per second. +- **Improved current sensing** - rewrote current sensing routines for safer operation. Current thresholds based on milliamps, not magic numbers +- **Individual track power control** - Ability to toggle power on either or both tracks, and to "JOIN" the tracks and make them output the same waveform for multiple power districts. +- **Single or Dual-Pin PWM output** - Allows control of H-bridges with PH/EN or dual PWM inputs +- **New, simpler function command** - `` command allows setting functions based on their number, not based on a code as in `` +- **Function reminders** - Function reminders are sent in addition to speed reminders +- **Functions to F28** - All NMRA functions are now supported +- **Filters and user functions** - Ability to filter commands in the parser and execute custom code based on them. (ex: Redirect Turnout commands via NRF24) +- **Diagnostic `` commands** - See documentation for a full list of new diagnostic commands +- **Rewrote DCC++ Parser** - more efficient operation, accepts multi-char input and uses less RAM +- **Rewritten waveform generator** - capable of using any pin for DCC waveform out, eliminating the need for jumpers +- **Rewritten packet generator** - Simplify and make smaller, remove idea of "registers" from original code +- **Add free RAM messages** - Free RAM messages are now printed whenever there is a decerase in available RAM +- **Fix EEPROM bugs** +- **Number of locos discovery command** - `<#>` command +- **Support for more locomotives** - 20 locomotives on an UNO and 50 an a Mega. +- **Automatic slot management** - slot variable in throttle/function commands are ignored and slot management is taken care of automatically. `<->` and `<- CAB>` commands added to release locos from memory and stop packets to the track. + +**Key Contributors** + +**Project Lead** + +- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) + +**EX-CommandStation Developers** + +- Chris Harlow - Bournemouth, UK (UKBloke) +- Harald Barth - Stockholm, Sweden (Haba) +- Neil McKechnie - Worcestershire, UK (NeilMck) +- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) +- Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting) +- M Steve Todd - Oregon, USA (MSteveTodd) +- Scott Catalano - Pennsylvania +- Gregor Baues - Île-de-France, France (grbba) + +**Engine Driver and JMRI Interface** + +- M Steve Todd + +**EX-Installer Software** + +- Anthony W - Dayton, Ohio, USA (Dex, Dex++) + +**Website and Documentation** + +- Mani Kumar - Bangalor, India (Mani / Mani Kumar) +- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) +- Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting) +- Roger Beschizza - Dorset, UK (Roger Beschizza) +- Keith Ledbetter - Chicago, Illinois, USA (Keith Ledbetter) +- Kevin Smith - Rochester Hills, Michigan USA (KC Smith) +- Colin Grabham - Central NSW, Australia (Kebbin) +- Peter Cole - Brisbane, QLD, Australia (peteGSX) +- Peter Akers - Brisbane, QLD, Australia (flash62au) + +**EX-WebThrottle** + +- Fred Decker - Holly Springs, NC (FlightRisk/FrightRisk) +- Mani Kumar - Bangalor, India (Mani /Mani Kumar) +- Matt H - Somewhere in Europe + +**Hardware / Electronics** + +- Harald Barth - Stockholm, Sweden (Haba) +- Paul Antoine, Western Australia +- Neil McKechnie - Worcestershire, UK +- Fred Decker - Holly Springs NC, USA +- Herb Morton - Kingwood Texas, USA (Ash++) + +**Beta Testing / Release Management / Support** + +- Larry Dribin - Release Management +- Kevin Smith - Rochester Hills, Michigan USA (KC Smith) +- Herb Morton - Kingwood Texas, USA (Ash++) +- Keith Ledbetter +- Brad Van der Elst +- Andrew Pye +- Mike Bowers +- Randy McKenzie +- Roberto Bravin +- Sam Brigden +- Alan Lautenslager +- Martin Bafver +- Mário André Silva +- Anthony Kochevar +- Gajanatha Kobbekaduwe +- Sumner Patterson +- Paul - Virginia, USA + +**Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** + +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip) + + +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.tar.gz) From e618b91900cfeed72ef1857d3c546577e068f1fd Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 21 Oct 2022 11:57:55 -0400 Subject: [PATCH 013/218] Update version.h --- version.h | 55 ++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 40 insertions(+), 15 deletions(-) diff --git a/version.h b/version.h index 4a65046..c2bb135 100644 --- a/version.h +++ b/version.h @@ -5,30 +5,55 @@ #define VERSION "4.1.1" // 4.1.1 Bugfix: preserve turnout format -// Bugfix: parse multiple commands in one buffer string correct +// Bugfix: parse multiple commands in one buffer string correctly (ex: ) // Bugfix: command signal status in Exrail // Bugfix: EX-RAIL read long loco addr +// Bugfix: Add space character after version string 4.1.1 for JMRI parsing. +// Improved display and loop time for signalsmake service start to be outside the DONT_TOUCH_WIFI_CONF area +// Improve WiFi startup by making service start to be outside the DONT_TOUCH_WIFI_CONF area // 4.1.0 ... // // 4.0.2 EXRAIL additions: -// ACK defaults set to 50mA LIMIT, 2000uS MIN, 20000uS MAX +// Code: struct TurnoutData to enable EEPROM in v 4.0 format +// Code: Fix weak reference to myFilter +// Updated CV read command . Equivalent to . Uses the verify callback. +// Allow WRITE CV on PROG commands -// command to obtain current throttle settings -// JA, JR, JT commands to obtain route, roster and turnout descriptions -// HIDDEN turnouts -// PARSE <> commands in EXRAIL -// VIRTUAL_TURNOUT -// and KILLALL command to stop all tasks. +// FIX negative route ids in WIthrottle problem by allowing them +// Add IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) +// Add commands +// Add command to obtain current throttle settings +// Add JA, JR, JT commands to obtain route, roster and turnout descriptions +// Add HIDDEN turnouts +// Add PARSE <> commands in EXRAIL +// Add VIRTUAL_TURNOUT +// Add and KILLALL command to stop all tasks +// Add Diagnostic messages when KILL is used // FORGET forgets the current loco in DCC reminder tables. -// Servo signals (SERVO_SIGNAL) -// High-On signal pins (SIGNALH) -// Wait for analog value (ATGTE, ATLT) +// Add Servo signals (SERVO_SIGNAL) +// Add High-On signal pins (SIGNALH) +// Add Wait for analog value (ATGTE, ATLT) +// Allow underscore in keywords ex: MY_KEYWORD +// Automatically assign a name with ALIAS(name) without having to define it first +// README.md: removed misleading "folder/subforlders" (#218) +// README.md: fix dead link to rewrite (#217) in notes/rewrite.md +// Incoming LCN turnout throw +// Broadcast jopin after DriveAway +// Corrections to I2C code: +// 1) I2CManager_Mega4809.h: Correct bitwise 'and' to logical 'and' - no impact. +// 2) I2CManager_Wire.h: Ensure that error codes from Wire subsystem are passed back to caller in queueRequest(). +// Save memory on the Uno // 4.0.1 Small EXRAIL updates // EXRAIL BROADCAST("msg") -// EXRAIL POWERON +// EXRAIL POWERON (only turns on MAIN) +// Remove EXRAIL/ENDEXRAIL from myAutomation.example.h (#215) +// Use "startup sequence" to describe the initial instructions +// Add description of display scroll modes +// restructure GetLocoCallback() for better readability and put broadcastPower() at right place // 4.0.0 Major functional and non-functional changes. // Engine Driver "DriveAway" feature enhancement // 'Discovered Server' multicast Dynamic Network Server (mDNS) displays available WiFi connections to a DCC++EX Command Station From 941e74beaf1a974f01e0b2176ddec9919fd11234 Mon Sep 17 00:00:00 2001 From: Kcsmith0708 Date: Fri, 21 Oct 2022 13:45:54 -0400 Subject: [PATCH 014/218] Realese Document Edit & Enhancements (#262) * Realese Document Edit & Enhancements Edited Intro and Rearranged EXRAIL Enhancements * Update release_notes_v4.1.1.md edited indents * Update release_notes_v4.1.1.md Fomating --- Release_Notes/release_notes_v4.1.1.md | 46 ++++++++++++++++----------- 1 file changed, 28 insertions(+), 18 deletions(-) diff --git a/Release_Notes/release_notes_v4.1.1.md b/Release_Notes/release_notes_v4.1.1.md index 7943f42..875243a 100644 --- a/Release_Notes/release_notes_v4.1.1.md +++ b/Release_Notes/release_notes_v4.1.1.md @@ -1,4 +1,6 @@ -The DCC-EX Team is pleased to release CommandStation-EX-v4.1.1 as a Production Release. Release v4.0.0 is a Minor release though it does add significant new automation features to EX-RAIL in addition to some small changes and bug fixes. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code so as to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.1 rc13. +The DCC-EX Team is pleased to release CommandStation-EX v4.1.1 as a Production Release for the general public. +This release is a Minor release with many significant EX-RAIL enhancements and new automation features in addition to some bug fixes. +The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.1 rc13. **Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** @@ -8,28 +10,36 @@ The DCC-EX Team is pleased to release CommandStation-EX-v4.1.1 as a Production R [CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v0.0.0-Prod/CommandStation-EX.tar.gz) -**EX-RAIL New Features** - - - ACK defaults set to 50mA LIMIT, 2000uS MIN, 20000uS MAX for more compatibility with non NMRA compliant decoders +**New Command Station & EX-RAIL Features** + - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 2000uS for more compatibility with non NMRA compliant decoders - Automatically detect and run a myFilter add-on (no need to call setFilter) - - Add commands - - Add "if" signal detection with IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) - - New command to obtain current throttle settings - - New JA, JR, JT commands to obtain route, roster and turnout descriptions - - Add ability for HIDDEN turnouts (hide a REAL turnout and create a VIRTUAL turnout to handle actions that happen BEFORE a turnout is thrown) - - Add VIRTUAL_TURNOUT definition - - New PARSE <> commands in EXRAIL allows sending of command station commands from EX-RAIL - - New and KILLALL command to stop all tasks - - Add FORGET command. Forgets the current loco in DCC reminder tables saving memory and wasted packets sent to the track - - Add Servo signals (SERVO_SIGNAL) - - Add High-On signal pins (SIGNALH) (Arduino normally handles active LOW signals. This allows for active HIGH) - - Add Wait for analog value (ATGTE, ATLT)... (At greater than or Equal and At less than a certain value) - -**EX-RAIL Updates** + - New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor + - commands + - and KILLALL command to stop all tasks + - command to obtain current throttle settings + + - New JA, JR, JT commands availabe for Throttle Developers to obtain Route, Roster and Turnout descriptions for communications + + - New EX-RAIL Functions to use in Automation(n), ROUTE(N) & SEQUENCE(N) Scripts + - ATGTE & ATLT wait for analog value, (At Greater Than or Equal and At Less Than a certain value) + - FADE command now works for LEDs connected on PCA9685 Servo/Signal board Output vpins + - FORGET Forgets the current loco in DCC reminder tables saving memory and wasted packets sent to the track + - "IF" signal detection with IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) + - PARSE <> commands in EXRAIL allows sending of DCC-EX commands from EX-RAIL + - SERVO_SIGNAL Servo signals assigned to a specific servo turnout + - SIGNALH High-On signal pins (Arduino normally handles active LOW signals. This allows for active HIGH) + - HIDDEN turnouts (hide a REAL turnout and create a VIRTUAL turnout to handle actions that happen BEFORE a turnout is thrown) + - VIRTUAL_TURNOUT definition + +**EX-RAIL Updates** - EXRAIL BROADCAST("msg") sends any message to all throttles/JMRI via serial and WiFi - EXRAIL POWERON turns on power to both tracks from EX-RAIL (the equivalent of sending the <1> command) +** Other Enhancements** + - UNO Progmem is optimize to allow for small EXRAIL Automation scipts to run within the limited space for testing purposes. + - PCA9685 Servo Signal board supports 'Nopoweroffleds', servo pins stay powered on after position reached, otherwise the new FADE would always turn off. + - Position servo can use spare servo pin as a GPIO pin. **4.1.1 Bug Fixes** From d72474cd8f7b8132fd31347a4dcd527f2954d50e Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 21 Oct 2022 20:03:00 -0400 Subject: [PATCH 015/218] Update release_notes_v4.1.1.md --- Release_Notes/release_notes_v4.1.1.md | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Release_Notes/release_notes_v4.1.1.md b/Release_Notes/release_notes_v4.1.1.md index 875243a..03a9a45 100644 --- a/Release_Notes/release_notes_v4.1.1.md +++ b/Release_Notes/release_notes_v4.1.1.md @@ -4,11 +4,11 @@ The team continues improving the architecture of DCC++EX to make it more flexibl **Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** -[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip) +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.zip) -[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v0.0.0-Prod/CommandStation-EX.tar.gz) +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.tar.gz) **New Command Station & EX-RAIL Features** - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 2000uS for more compatibility with non NMRA compliant decoders @@ -329,7 +329,7 @@ See the version.h file for notes about which of the 4.1.1 features were added/ch **Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** -[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip) +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.zip) -[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.tar.gz) +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.tar.gz) From 39a85903cec643814254d7b52f5d486490f87e46 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 21 Oct 2022 20:04:05 -0400 Subject: [PATCH 016/218] Update release_notes.md --- release_notes.md | 54 ++++++++++++++++++++++++++++-------------------- 1 file changed, 32 insertions(+), 22 deletions(-) diff --git a/release_notes.md b/release_notes.md index 7943f42..03a9a45 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,35 +1,45 @@ -The DCC-EX Team is pleased to release CommandStation-EX-v4.1.1 as a Production Release. Release v4.0.0 is a Minor release though it does add significant new automation features to EX-RAIL in addition to some small changes and bug fixes. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code so as to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.1 rc13. +The DCC-EX Team is pleased to release CommandStation-EX v4.1.1 as a Production Release for the general public. +This release is a Minor release with many significant EX-RAIL enhancements and new automation features in addition to some bug fixes. +The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.1 rc13. **Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** -[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip) +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.zip) -[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v0.0.0-Prod/CommandStation-EX.tar.gz) +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.tar.gz) -**EX-RAIL New Features** - - - ACK defaults set to 50mA LIMIT, 2000uS MIN, 20000uS MAX for more compatibility with non NMRA compliant decoders +**New Command Station & EX-RAIL Features** + - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 2000uS for more compatibility with non NMRA compliant decoders - Automatically detect and run a myFilter add-on (no need to call setFilter) - - Add commands - - Add "if" signal detection with IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) - - New command to obtain current throttle settings - - New JA, JR, JT commands to obtain route, roster and turnout descriptions - - Add ability for HIDDEN turnouts (hide a REAL turnout and create a VIRTUAL turnout to handle actions that happen BEFORE a turnout is thrown) - - Add VIRTUAL_TURNOUT definition - - New PARSE <> commands in EXRAIL allows sending of command station commands from EX-RAIL - - New and KILLALL command to stop all tasks - - Add FORGET command. Forgets the current loco in DCC reminder tables saving memory and wasted packets sent to the track - - Add Servo signals (SERVO_SIGNAL) - - Add High-On signal pins (SIGNALH) (Arduino normally handles active LOW signals. This allows for active HIGH) - - Add Wait for analog value (ATGTE, ATLT)... (At greater than or Equal and At less than a certain value) - -**EX-RAIL Updates** + - New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor + - commands + - and KILLALL command to stop all tasks + - command to obtain current throttle settings + + - New JA, JR, JT commands availabe for Throttle Developers to obtain Route, Roster and Turnout descriptions for communications + + - New EX-RAIL Functions to use in Automation(n), ROUTE(N) & SEQUENCE(N) Scripts + - ATGTE & ATLT wait for analog value, (At Greater Than or Equal and At Less Than a certain value) + - FADE command now works for LEDs connected on PCA9685 Servo/Signal board Output vpins + - FORGET Forgets the current loco in DCC reminder tables saving memory and wasted packets sent to the track + - "IF" signal detection with IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) + - PARSE <> commands in EXRAIL allows sending of DCC-EX commands from EX-RAIL + - SERVO_SIGNAL Servo signals assigned to a specific servo turnout + - SIGNALH High-On signal pins (Arduino normally handles active LOW signals. This allows for active HIGH) + - HIDDEN turnouts (hide a REAL turnout and create a VIRTUAL turnout to handle actions that happen BEFORE a turnout is thrown) + - VIRTUAL_TURNOUT definition + +**EX-RAIL Updates** - EXRAIL BROADCAST("msg") sends any message to all throttles/JMRI via serial and WiFi - EXRAIL POWERON turns on power to both tracks from EX-RAIL (the equivalent of sending the <1> command) +** Other Enhancements** + - UNO Progmem is optimize to allow for small EXRAIL Automation scipts to run within the limited space for testing purposes. + - PCA9685 Servo Signal board supports 'Nopoweroffleds', servo pins stay powered on after position reached, otherwise the new FADE would always turn off. + - Position servo can use spare servo pin as a GPIO pin. **4.1.1 Bug Fixes** @@ -319,7 +329,7 @@ See the version.h file for notes about which of the 4.1.1 features were added/ch **Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** -[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.zip) +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.zip) -[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.0.0-Prod/CommandStation-EX.tar.gz) +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.tar.gz) From d1518b8af02a6306fc2c46be581b3158ee6f515d Mon Sep 17 00:00:00 2001 From: Kcsmith0708 Date: Sat, 22 Oct 2022 18:00:21 -0400 Subject: [PATCH 017/218] Update Release_notes_v4.1.1.md (#264) * Update release_notes_v4.1.1.md Edited etc., commands and added KILLALL function to EXRAIL list * Update release_notes_v4.1.1.md added back in * Update release_notes_v4.1.1.md fixed < t cab> so it would display --- Release_Notes/release_notes_v4.1.1.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/Release_Notes/release_notes_v4.1.1.md b/Release_Notes/release_notes_v4.1.1.md index 03a9a45..cd9531b 100644 --- a/Release_Notes/release_notes_v4.1.1.md +++ b/Release_Notes/release_notes_v4.1.1.md @@ -14,11 +14,18 @@ The team continues improving the architecture of DCC++EX to make it more flexibl - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 2000uS for more compatibility with non NMRA compliant decoders - Automatically detect and run a myFilter add-on (no need to call setFilter) - - New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor - - commands - - and KILLALL command to stop all tasks - - command to obtain current throttle settings + - New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor + - to turn a individual LED Signal On & Off + - " + - " + - command to stop all tasks, and Diagnostic messages when KILL is used + - < t cab> command to obtain current throttle setting + - Allow WRITE CV on PROG + - Updated CV read command . Equivalent to . Uses the verify callback. + - Allow WRITE CV on PROG commands in EXRAIL allows sending of DCC-EX commands from EX-RAIL - SERVO_SIGNAL Servo signals assigned to a specific servo turnout - SIGNALH High-On signal pins (Arduino normally handles active LOW signals. This allows for active HIGH) From b7a010f904d6cf66e57eaebe0efd9bdd237d67f3 Mon Sep 17 00:00:00 2001 From: Kcsmith0708 Date: Sat, 22 Oct 2022 18:01:37 -0400 Subject: [PATCH 018/218] Verion.h 4.1.1 (#263) Edited & Reformatted verify then ready for release --- version.h | 69 +++++++++++++++++++++++++++++-------------------------- 1 file changed, 36 insertions(+), 33 deletions(-) diff --git a/version.h b/version.h index c2bb135..e9b61bf 100644 --- a/version.h +++ b/version.h @@ -6,55 +6,58 @@ #define VERSION "4.1.1" // 4.1.1 Bugfix: preserve turnout format // Bugfix: parse multiple commands in one buffer string correctly (ex: ) -// Bugfix: command signal status in Exrail +// Bugfix: command signal status of EX-RAIL tasks or threads // Bugfix: EX-RAIL read long loco addr // Bugfix: Add space character after version string 4.1.1 for JMRI parsing. -// Improved display and loop time for signalsmake service start to be outside the DONT_TOUCH_WIFI_CONF area +// Improved display and loop time for signals make service start to be outside the DONT_TOUCH_WIFI_CONF area // Improve WiFi startup by making service start to be outside the DONT_TOUCH_WIFI_CONF area // 4.1.0 ... -// -// 4.0.2 EXRAIL additions: -// Code: struct TurnoutData to enable EEPROM in v 4.0 format -// Code: Fix weak reference to myFilter +// UNO Progmem optimized to allow for small EXRAIL Automation scipts +// 4.0.2 Command Station and EX-RAIL Ehancements & Additions: +// New JA, JR, JT commands availabe for Throttle Developers to obtain Route, Roster and Turnout descriptions for communications +// Change ACK defaults now set to LIMIT 50mA, MIN 2000uS, MAX 2000uS for more compatibility with non NMRA compliant decoders +// New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor +// to turn a individual LED Signal On & Off +// " +// " +// command to stop all tasks, and Diagnostic messages when KILL is used +// command to obtain current throttle settings +// Allow WRITE CV on PROG // Updated CV read command . Equivalent to . Uses the verify callback. -// Allow WRITE CV on PROG commands -// Add command to obtain current throttle settings -// Add JA, JR, JT commands to obtain route, roster and turnout descriptions -// Add HIDDEN turnouts -// Add PARSE <> commands in EXRAIL -// Add VIRTUAL_TURNOUT -// Add and KILLALL command to stop all tasks -// Add Diagnostic messages when KILL is used -// FORGET forgets the current loco in DCC reminder tables. -// Add Servo signals (SERVO_SIGNAL) -// Add High-On signal pins (SIGNALH) -// Add Wait for analog value (ATGTE, ATLT) -// Allow underscore in keywords ex: MY_KEYWORD -// Automatically assign a name with ALIAS(name) without having to define it first -// README.md: removed misleading "folder/subforlders" (#218) -// README.md: fix dead link to rewrite (#217) in notes/rewrite.md +// Improved SIGNALs startup and diagnostics // Incoming LCN turnout throw +// Allow turnout ID of "0" +// Code: struct TurnoutData to enable EEPROM in v 4.0 format // Broadcast jopin after DriveAway // Corrections to I2C code: // 1) I2CManager_Mega4809.h: Correct bitwise 'and' to logical 'and' - no impact. // 2) I2CManager_Wire.h: Ensure that error codes from Wire subsystem are passed back to caller in queueRequest(). -// Save memory on the Uno -// 4.0.1 Small EXRAIL updates +// New EX-RAIL Functions to use in Automation(n), ROUTE(N) & SEQUENCE(N) scripts +// Automatically assign a name with ALIAS(name) without having to define it first +// ALIAS now has the aility to use Low underscore in keywords e.g., MY_KEYWORD +// ATGTE & ATLT wait for analog value, (At Greater Than or Equal and At Less Than a certain value) +// FADE command now works for LEDs connected on PCA9685 Servo/Signal board Output vpins +// FORGET Forgets the current loco in DCC reminder tables saving memory and wasted packets sent to the track +// "IF" signal detection with IFRED(signal_id), IFAMBER(signal_id), IFGREEN(signal_id) +// KILLALL command to stop all tasks, and Diagnostic messages when KILL is used +// PARSE <> commands in EXRAIL allows sending of DCC-EX commands from EX-RAIL +// SERVO_SIGNAL Servo signals assigned to a specific servo turnout +// SIGNALH High-On signal pins (Arduino normally handles active LOW signals. This allows for active HIGH) +// HIDDEN turnouts (hide a REAL turnout and create a VIRTUAL turnout to handle actions that happen BEFORE a turnout is thrown) +// VIRTUAL_TURNOUT definition +// README.md: removed misleading "folder/subforlders" (#218) +// README.md: fix dead link to rewrite (#217) in notes/rewrite.md +// 4.0.1 Additional EXRAIL updates // EXRAIL BROADCAST("msg") // EXRAIL POWERON (only turns on MAIN) -// Remove EXRAIL/ENDEXRAIL from myAutomation.example.h (#215) +// Remove optional EXRAIL/ENDEXRAIL from myAutomation.example.h (#215) // Use "startup sequence" to describe the initial instructions // Add description of display scroll modes -// restructure GetLocoCallback() for better readability and put broadcastPower() at right place -// 4.0.0 Major functional and non-functional changes. +// GetLocoCallback() restructured for better readability and put broadcastPower() at right place +// 4.0.0 Major Production Release with New Functional and non-functional changes. // Engine Driver "DriveAway" feature enhancement // 'Discovered Server' multicast Dynamic Network Server (mDNS) displays available WiFi connections to a DCC++EX Command Station // New EX-RAIL "Extended Railroad Automation Instruction Language" automation capability. From daf6799ac1ef1fbb07fe5ca836c12acd37ffed28 Mon Sep 17 00:00:00 2001 From: Fred Date: Sat, 22 Oct 2022 18:10:42 -0400 Subject: [PATCH 019/218] Update release_notes.md --- release_notes.md | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/release_notes.md b/release_notes.md index 03a9a45..cd9531b 100644 --- a/release_notes.md +++ b/release_notes.md @@ -14,11 +14,18 @@ The team continues improving the architecture of DCC++EX to make it more flexibl - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 2000uS for more compatibility with non NMRA compliant decoders - Automatically detect and run a myFilter add-on (no need to call setFilter) - - New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor - - commands - - and KILLALL command to stop all tasks - - command to obtain current throttle settings + - New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor + - to turn a individual LED Signal On & Off + - " + - " + - command to stop all tasks, and Diagnostic messages when KILL is used + - < t cab> command to obtain current throttle setting + - Allow WRITE CV on PROG + - Updated CV read command . Equivalent to . Uses the verify callback. + - Allow WRITE CV on PROG commands in EXRAIL allows sending of DCC-EX commands from EX-RAIL - SERVO_SIGNAL Servo signals assigned to a specific servo turnout - SIGNALH High-On signal pins (Arduino normally handles active LOW signals. This allows for active HIGH) From f0d1909d9f7e487c30114e050f3f7f2ab7695189 Mon Sep 17 00:00:00 2001 From: Fred Date: Sun, 23 Oct 2022 08:21:36 -0400 Subject: [PATCH 020/218] Update release_notes.md --- release_notes.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release_notes.md b/release_notes.md index cd9531b..1842565 100644 --- a/release_notes.md +++ b/release_notes.md @@ -11,7 +11,7 @@ The team continues improving the architecture of DCC++EX to make it more flexibl [CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.tar.gz) **New Command Station & EX-RAIL Features** - - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 2000uS for more compatibility with non NMRA compliant decoders + - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 20000uS for more compatibility with non NMRA compliant decoders - Automatically detect and run a myFilter add-on (no need to call setFilter) - New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor From 4a3f3d0f3483fa116cb873764358034b26a1f9b0 Mon Sep 17 00:00:00 2001 From: Fred Date: Sun, 23 Oct 2022 08:22:00 -0400 Subject: [PATCH 021/218] Update release_notes_v4.1.1.md --- Release_Notes/release_notes_v4.1.1.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release_Notes/release_notes_v4.1.1.md b/Release_Notes/release_notes_v4.1.1.md index cd9531b..1842565 100644 --- a/Release_Notes/release_notes_v4.1.1.md +++ b/Release_Notes/release_notes_v4.1.1.md @@ -11,7 +11,7 @@ The team continues improving the architecture of DCC++EX to make it more flexibl [CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.tar.gz) **New Command Station & EX-RAIL Features** - - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 2000uS for more compatibility with non NMRA compliant decoders + - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 20000uS for more compatibility with non NMRA compliant decoders - Automatically detect and run a myFilter add-on (no need to call setFilter) - New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor From 4bfd4b1a124aaa35ec4c18999497e761a0f20025 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 27 Oct 2022 09:34:13 +1000 Subject: [PATCH 022/218] Add templates and project workflow (#258) * Add templates and project workflow * Fixed template typos --- .github/ISSUE_TEMPLATE/bug_report.yml | 80 ++++++++++ .github/ISSUE_TEMPLATE/config.yml | 12 ++ .../ISSUE_TEMPLATE/documentation_update.yml | 31 ++++ .github/ISSUE_TEMPLATE/feature_request.yml | 37 +++++ .github/ISSUE_TEMPLATE/support_request.yml | 39 +++++ .github/ISSUE_TEMPLATE/to_do.yml | 24 +++ .github/workflows/new-items.yml | 143 ++++++++++++++++++ 7 files changed, 366 insertions(+) create mode 100644 .github/ISSUE_TEMPLATE/bug_report.yml create mode 100644 .github/ISSUE_TEMPLATE/config.yml create mode 100644 .github/ISSUE_TEMPLATE/documentation_update.yml create mode 100644 .github/ISSUE_TEMPLATE/feature_request.yml create mode 100644 .github/ISSUE_TEMPLATE/support_request.yml create mode 100644 .github/ISSUE_TEMPLATE/to_do.yml create mode 100644 .github/workflows/new-items.yml diff --git a/.github/ISSUE_TEMPLATE/bug_report.yml b/.github/ISSUE_TEMPLATE/bug_report.yml new file mode 100644 index 0000000..457a075 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.yml @@ -0,0 +1,80 @@ +# Bug report GitHub issue form +# +# This file needs to reside in the ".github/ISSUE_TEMPLATE/" folder. + +name: Bug Report +description: Submit a bug report +labels: + - Bug +title: "Bug Report: " +body: + - type: markdown + attributes: + value: | + Thanks for taking the time to submit a bug report to the DCC-EX team! + + In order to help us to validate the bug and ascertain what's causing it, please provide as much information as possible in this form. + + - type: input + id: version + attributes: + label: Version + description: Please provide the version of EX-CommandStation in use. + validations: + required: true + + - type: textarea + id: description + attributes: + label: Bug description + description: Please provide a clear and concise description of what the symptoms of the bug are. + placeholder: | + When attempting to drive a locomotive on the main track, it runs forwards, backwards, spins around, jumps up and down, blows the horn, and then stops. + validations: + required: true + + - type: textarea + id: reproduction + attributes: + label: Steps to reproduce the bug + description: Please provide the steps to reproduce the behaviour. + placeholder: | + 1. Turn on the CommandStation and track power. + 2. Connect Engine Driver to the CommandStation. + 3. Select locomotive with address 123. + 4. Throttle up to half speed. + validations: + required: true + + - type: textarea + id: expectation + attributes: + label: Expected behaviour + description: Please provide a clear and concise description of what you expected to happen. + placeholder: | + The locomotive should accelerate smoothly to half speed in a forward direction. + validations: + required: true + + - type: textarea + id: screenshots + attributes: + label: Screenshots + description: If applicable, upload any screenshots here. + + - type: textarea + id: hardware + attributes: + label: Hardware in use + description: Please provide details of hardware in use including microcontroller, motor shield, and any other relevant information. + placeholder: | + Elegoo Mega2560 + Arduino R3 motor shield + validations: + required: true + + - type: textarea + id: extra-context + attributes: + label: Additional context + description: Please provide any other relevant information that could help us resolve this issue, for example a customised config.h file. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..21c2243 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,12 @@ +# Configuration file for the template chooser +# +# This file needs to exist in the https://github.com/DCC-EX/.github repository in the ".github/ISSUE_TEMPLATE/" folder. + +blank_issues_enabled: false +contact_links: + - name: DCC-EX Discord server + url: https://discord.gg/y2sB4Fp + about: For the best support experience, join our Discord server + - name: DCC-EX Contact and Support page + url: https://dcc-ex.com/support/index.html + about: For other support options, refer to our Contact & Support page \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/documentation_update.yml b/.github/ISSUE_TEMPLATE/documentation_update.yml new file mode 100644 index 0000000..2fc9405 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/documentation_update.yml @@ -0,0 +1,31 @@ +# Documentation update GitHub issue form +# +# This file needs to reside in the ".github/ISSUE_TEMPLATE/" folder. + +name: Documentation Update +description: Submit a request for documentation updates, or to report broken links or inaccuracies +title: "[Documentation Update]: " +labels: + - Needs Documentation +body: + - type: markdown + attributes: + value: | + Use this template to submit a request for updates to our documentation. + + This can be used for general documentation requests if information is missing or lacking, or to correct issues with our existing documentation such as broken links, or inaccurate information. + + - type: textarea + id: details + attributes: + label: Documentation details + description: Provide the details of what needs to be documented or corrected. + validations: + required: true + + - type: input + id: page + attributes: + label: Page with issues + description: If reporting broken links or inaccuracies, please provide the link to the page here. + placeholder: https://dcc-ex.com/index.html \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/feature_request.yml b/.github/ISSUE_TEMPLATE/feature_request.yml new file mode 100644 index 0000000..c1fae16 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.yml @@ -0,0 +1,37 @@ +# Feature Request GitHub issue form +# +# This file needs to reside in the ".github/ISSUE_TEMPLATE/" folder. + +name: Feature Request +description: Suggest a new feature +title: "[Feature Request]: " +labels: + - Enhancement +body: + - type: markdown + attributes: + value: | + Use this template to suggest a new feature for EX-CommandStation. + + - type: textarea + id: description + attributes: + label: Problem/idea statement + description: Please provide the problem you're trying to solve, or share the idea you have. + placeholder: A clear and concise description of the problem you're trying to solve, or the idea you have. For example, I'm always frustrated when... + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives or workarounds + description: Please provide any alternatives or workarounds you currently use. + validations: + required: true + + - type: textarea + id: context + attributes: + label: Additional context + description: Add any other context, screenshots, or files related to the feature request here. \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/support_request.yml b/.github/ISSUE_TEMPLATE/support_request.yml new file mode 100644 index 0000000..c53d2b7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/support_request.yml @@ -0,0 +1,39 @@ +# Support Request GitHub issue form +# +# This file needs to reside in the ".github/ISSUE_TEMPLATE/" folder. + +name: Support Request +description: Request support or assistance +title: "[Support Request]: " +labels: + - Support Request +body: + - type: markdown + attributes: + value: | + Use this template to request support or assistance with EX-CommandStation. + + - type: input + id: version + attributes: + label: Version + description: Please provide the version of the software in use. + validations: + required: true + + - type: textarea + id: description + attributes: + label: Issue description + description: Please describe the issue being encountered as accurately and detailed as possible. + validations: + required: true + + - type: textarea + id: hardware + attributes: + label: Hardware + description: If appropriate, please provide details of the hardware in use. + placeholder: | + Elegoo Mega2560 + Arduino Motor Shield R3 \ No newline at end of file diff --git a/.github/ISSUE_TEMPLATE/to_do.yml b/.github/ISSUE_TEMPLATE/to_do.yml new file mode 100644 index 0000000..2905b8c --- /dev/null +++ b/.github/ISSUE_TEMPLATE/to_do.yml @@ -0,0 +1,24 @@ +# General To Do item GitHub issue form +# +# This file needs to reside in the ".github/ISSUE_TEMPLATE/" folder. + +name: To Do +description: Create a general To Do item +title: "[To Do]: " +labels: + - To Do +body: + - type: markdown + attributes: + value: | + Use this template to create an issue for a general task that needs to be done. + + This is handy for capturing ad-hoc items that don't necessarily require code to be written or updated. + + - type: textarea + id: description + attributes: + label: Task description + description: Provide the details of what needs to be done. + validations: + required: true \ No newline at end of file diff --git a/.github/workflows/new-items.yml b/.github/workflows/new-items.yml new file mode 100644 index 0000000..b5f0a48 --- /dev/null +++ b/.github/workflows/new-items.yml @@ -0,0 +1,143 @@ +# This workflow is to be used for all repositories to integrate with the DCC++ EX Beta Project. +# It will add all issues and pull requests for a repository to the project, and put in the correct status. +# +# Ensure "REPO_LABEL" is updated with the correct label for the repo stream of work. +name: Add Issue or Pull Request to Project + +env: + REPO_LABEL: ${{ secrets.PROJECT_STREAM_LABEL }} + PROJECT_NUMBER: 7 + ORG: DCC-EX + +on: + issues: + types: + - opened + pull_request: + types: + - ready_for_review + - opened + - review_requested + +jobs: + add_to_project: + runs-on: ubuntu-latest + steps: + - name: Add labels + uses: andymckay/labeler@master + with: + add-labels: ${{ env.REPO_LABEL }} + + - name: Generate token + id: generate_token + uses: tibdex/github-app-token@36464acb844fc53b9b8b2401da68844f6b05ebb0 + with: + app_id: ${{ secrets.PROJECT_APP_ID }} + private_key: ${{ secrets. PROJECT_APP_KEY }} + + - name: Get project data + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + run: | + gh api graphql -f query=' + query($org: String!, $number: Int!) { + organization(login: $org){ + projectNext(number: $number) { + id + fields(first:20) { + nodes { + id + name + settings + } + } + } + } + }' -f org=$ORG -F number=$PROJECT_NUMBER > project_data.json + + echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV + echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV + echo 'BACKLOG_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Backlog") |.id' project_data.json) >> $GITHUB_ENV + echo 'TO_DO_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="To Do") |.id' project_data.json) >> $GITHUB_ENV + echo 'NEEDS_REVIEW_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Needs Review") |.id' project_data.json) >> $GITHUB_ENV + echo 'IN_PROGRESS_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="In Progress") |.id' project_data.json) >> $GITHUB_ENV + + - name: Add issue to project + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + ITEM_ID: ${{ github.event.issue.node_id }} + if: github.event_name == 'issues' + run: | + project_item_id="$( gh api graphql -f query=' + mutation($project:ID!, $item:ID!) { + addProjectNextItem(input: {projectId: $project, contentId: $item}) { + projectNextItem { + id + } + } + }' -f project=$PROJECT_ID -f item=$ITEM_ID --jq '.data.addProjectNextItem.projectNextItem.id')" + echo 'PROJECT_ITEM_ID='$project_item_id >> $GITHUB_ENV + + - name: Add PR to project + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + ITEM_ID: ${{ github.event.pull_request.node_id }} + if: github.event_name == 'pull_request' + run: | + project_item_id="$( gh api graphql -f query=' + mutation($project:ID!, $item:ID!) { + addProjectNextItem(input: {projectId: $project, contentId: $item}) { + projectNextItem { + id + } + } + }' -f project=$PROJECT_ID -f item=$ITEM_ID --jq '.data.addProjectNextItem.projectNextItem.id')" + echo 'PROJECT_ITEM_ID='$project_item_id >> $GITHUB_ENV + + - name: Set status - To Do + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + if: github.event_name == 'issues' && (contains(github.event.*.labels.*.name, 'Bug') || contains(github.event.*.labels.*.name, 'Support Request')) + run: | + gh api graphql -f query=' + mutation( + $project: ID! + $item: ID! + $status_field: ID! + $status_value: String! + ){ + set_status: updateProjectNextItemField(input: { + projectId: $project + itemId: $item + fieldId: $status_field + value: $status_value + }) { + projectNextItem { + id + } + } + }' -f project=$PROJECT_ID -f item=$PROJECT_ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.TO_DO_OPTION_ID }} --silent + + - name: Set status - Review + env: + GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} + if: github.event_name == 'issues' && (contains(github.event.*.labels.*.name, 'Unit Tested') || contains(github.event.*.labels.*.name, 'Regression Tested') || contains(github.event.*.labels.*.name, 'Needs Review')) || github.event_name == 'pull_request' + run: | + gh api graphql -f query=' + mutation( + $project: ID! + $item: ID! + $status_field: ID! + $status_value: String! + ){ + set_status: updateProjectNextItemField(input: { + projectId: $project + itemId: $item + fieldId: $status_field + value: $status_value + }) { + projectNextItem { + id + } + } + }' -f project=$PROJECT_ID -f item=$PROJECT_ITEM_ID -f status_field=$STATUS_FIELD_ID -f status_value=${{ env.NEEDS_REVIEW_OPTION_ID }} --silent From df6c511d1deb1b3f805fe8073064f7ff61339fe6 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 28 Oct 2022 13:24:12 +0200 Subject: [PATCH 023/218] Fix for W5100 ethernet shield which does not report as the W5200 or W5500 --- EthernetInterface.cpp | 16 ++++++++-------- version.h | 3 ++- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index 7d8fed8..ddc71be 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -36,13 +36,13 @@ EthernetInterface * EthernetInterface::singleton=NULL; */ void EthernetInterface::setup() { - if (Ethernet.hardwareStatus() == EthernetNoHardware) { - if (singleton!=NULL) - DIAG(F("Prog Error!")); - } else { - singleton=new EthernetInterface(); - } - DIAG(F("Ethernet shield %sfound"), singleton==NULL ? "not " : ""); + if (Ethernet.hardwareStatus() == EthernetNoHardware) + DIAG(F("Ethernet shield not detected or is a W5100")); + if (singleton!=NULL) + DIAG(F("Prog Error!")); + if (singleton=new EthernetInterface()) + return; + DIAG(F("Ethernet not initialized")); }; @@ -111,7 +111,7 @@ void EthernetInterface::loop() { * @return true when cable is connected, false otherwise */ bool EthernetInterface::checkLink() { - if (Ethernet.linkStatus() == LinkON) { + if (Ethernet.linkStatus() != LinkOFF) { // check for not linkOFF instead of linkON as the W5100 does return LinkUnknown //if we are not connected yet, setup a new server if(!connected) { DIAG(F("Ethernet cable connected")); diff --git a/version.h b/version.h index e9b61bf..591084d 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "4.1.1" +#define VERSION "4.1.2" +// 4.2.1 Bugfix: Ethernet shield W5100 does not report HW or link level // 4.1.1 Bugfix: preserve turnout format // Bugfix: parse multiple commands in one buffer string correctly (ex: ) // Bugfix: command signal status of EX-RAIL tasks or threads From 5e50731a78a5fba7bd7c25a0f232f49f548102eb Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 28 Oct 2022 10:28:54 -0400 Subject: [PATCH 024/218] Update version.h Fix version number in notes from 4.2.1 to 4.1.2 --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index 591084d..d821db0 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,7 @@ #include "StringFormatter.h" #define VERSION "4.1.2" -// 4.2.1 Bugfix: Ethernet shield W5100 does not report HW or link level +// 4.1.2 Bugfix: Ethernet shield W5100 does not report HW or link level // 4.1.1 Bugfix: preserve turnout format // Bugfix: parse multiple commands in one buffer string correctly (ex: ) // Bugfix: command signal status of EX-RAIL tasks or threads From 7b9f3ae08d1cee90c96ec1e1f5a24bda23344f0d Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 28 Oct 2022 10:39:13 -0400 Subject: [PATCH 025/218] Update release_notes.md --- release_notes.md | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/release_notes.md b/release_notes.md index 1842565..025cda8 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,14 +1,14 @@ -The DCC-EX Team is pleased to release CommandStation-EX v4.1.1 as a Production Release for the general public. +The DCC-EX Team is pleased to release CommandStation-EX v4.1.2 as a Production Release for the general public. This release is a Minor release with many significant EX-RAIL enhancements and new automation features in addition to some bug fixes. -The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.1 rc13. +The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.2. **Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** -[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.zip) +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.2-Prod/CommandStation-EX.zip) -[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.tar.gz) +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.2-Prod/CommandStation-EX.tar.gz) **New Command Station & EX-RAIL Features** - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 20000uS for more compatibility with non NMRA compliant decoders @@ -47,7 +47,10 @@ The team continues improving the architecture of DCC++EX to make it more flexibl ** Other Enhancements** - UNO Progmem is optimize to allow for small EXRAIL Automation scipts to run within the limited space for testing purposes. - PCA9685 Servo Signal board supports 'Nopoweroffleds', servo pins stay powered on after position reached, otherwise the new FADE would always turn off. - - Position servo can use spare servo pin as a GPIO pin. + - Position servo can use spare servo pin as a GPIO pin. + +**4.1.2 Bug Fixes** +- Fixed Ethernet shield W5100 support since it does not report HW or link level like the W5200 and W5500 chips. **4.1.1 Bug Fixes** @@ -57,7 +60,7 @@ The team continues improving the architecture of DCC++EX to make it more flexibl - Read long loco addresses in EX-RAIL - FIX negative route IDs in WIthrottle -See the version.h file for notes about which of the 4.1.1 features were added/changed by point release. +See the version.h file for notes about which of the 4.1.2 features were added/changed by point release. **Known Issues** @@ -337,7 +340,7 @@ See the version.h file for notes about which of the 4.1.1 features were added/ch **Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** -[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.zip) +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.2-Prod/CommandStation-EX.zip) -[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.1-Prod/CommandStation-EX.tar.gz) +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.2-Prod/CommandStation-EX.tar.gz) From 0023ce33562d1c422c7a31d51630c6038dec1b23 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 28 Oct 2022 10:48:40 -0400 Subject: [PATCH 026/218] Create release_notes_v4.1.2.md --- Release_Notes/release_notes_v4.1.2.md | 346 ++++++++++++++++++++++++++ 1 file changed, 346 insertions(+) create mode 100644 Release_Notes/release_notes_v4.1.2.md diff --git a/Release_Notes/release_notes_v4.1.2.md b/Release_Notes/release_notes_v4.1.2.md new file mode 100644 index 0000000..9bff53c --- /dev/null +++ b/Release_Notes/release_notes_v4.1.2.md @@ -0,0 +1,346 @@ +The DCC-EX Team is pleased to release CommandStation-EX v4.1.2 as a Production Release for the general public. +This release is a Bugfix release which fixes support for Ethernet Shields based on the W5100 chip that broke with the release of v4.1.1. This chip does not report HW and link status the way the W5200 and W5500 do, so the check routine needed to be changed. +The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.2. + +**Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** + +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.2-Prod/CommandStation-EX.zip) + + + +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.2-Prod/CommandStation-EX.tar.gz) + +**New Command Station & EX-RAIL Features** + - ACK defaults are now set to LIMIT 50mA, MIN 2000uS, MAX 20000uS for more compatibility with non NMRA compliant decoders + - Automatically detect and run a myFilter add-on (no need to call setFilter) + + - New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor + - to turn a individual LED Signal On & Off + - " + - " + - command to stop all tasks, and Diagnostic messages when KILL is used + - < t cab> command to obtain current throttle setting + + - Allow WRITE CV on PROG + - Updated CV read command . Equivalent to . Uses the verify callback. + - Allow WRITE CV on PROG commands in EXRAIL allows sending of DCC-EX commands from EX-RAIL + - SERVO_SIGNAL Servo signals assigned to a specific servo turnout + - SIGNALH High-On signal pins (Arduino normally handles active LOW signals. This allows for active HIGH) + - HIDDEN turnouts (hide a REAL turnout and create a VIRTUAL turnout to handle actions that happen BEFORE a turnout is thrown) + - VIRTUAL_TURNOUT definition + +**EX-RAIL Updates** + - EXRAIL BROADCAST("msg") sends any message to all throttles/JMRI via serial and WiFi + - EXRAIL POWERON turns on power to both tracks from EX-RAIL (the equivalent of sending the <1> command) + +** Other Enhancements** + - UNO Progmem is optimize to allow for small EXRAIL Automation scipts to run within the limited space for testing purposes. + - PCA9685 Servo Signal board supports 'Nopoweroffleds', servo pins stay powered on after position reached, otherwise the new FADE would always turn off. + - Position servo can use spare servo pin as a GPIO pin. + +**4.1.2 Bug Fixes** +- Fixed Ethernet shield W5100 support since it does not report HW or link level like the W5200 and W5500 chips. + +**4.1.1 Bug Fixes** + +- Preserve the turnout format +- Parse multiple commands in one buffer string currectly +- Fix command signal status in EX-RAIL +- Read long loco addresses in EX-RAIL +- FIX negative route IDs in WIthrottle + +See the version.h file for notes about which of the 4.1.2 features were added/changed by point release. + +**Known Issues** + +- **Wi-Fi** - Requires sending `` commands from a serial monitor if you want to switch between AP mode and STA station mode after initial setup +- **Pololu Motor Shield** - is supported with this release, but the user may have to adjust timings to enable programming mode due to limitations in its current sensing circuitry + +**All New Major DCC++EX 4.0.0 features** + +- **New HAL Hardware Abstraction Layer API** that automatically detects and greatly simplifies interfacing to many predefined accessory boards for servos, signals & sensors and added I/O (digital and analog inputs and outputs, servos etc). +- HAL Support for; + - MCP23008, MCP23017 and PCF9584 I2C GPIO Extender modules. + - PCA9685 PWM (servo & signal) control modules. + - Analogue inputs on Arduino pins and on ADS111x I2C modules. + - MP3 sound playback via DFPlayer module. + - HC-SR04 Ultrasonic range sensor module. + - VL53L0X Laser range sensor module (Time-Of-Flight). + - A new `` command to list the HAL devices attached to the command station + +**New Command Station Broadcast throttle logic** + +- Synchronizes multiple WiThrottles and PC based JMRI Throttles for direction, speed and F-key updates + +**New ‘Discovered Servers’ on WiFi Throttles** + +- Our New multicast Dynamic Network Server (mDNS) enhancement allows us to display the available WiFi server connections to a DCC++EX Command Station. Selecting it allows your WiThrottle App to connect to and load Server Rosters and function keys to your throttle from the new DCC++EX Command Station Server Roster. + +**New DCC++EX 4.0.0 with EX-RAIL Extended Railroad Automation Instruction Language** + +- Use to control your entire layout or as a separate accessory/animation controller +- Awesome, cleverly powerful yet simple user friendly scripting language for user built Automation & Routing scripts. +- You can control Engines, Sensors, Turnouts, Signals, Outputs and Accessories that are entered into your new myAutomation.h file, then uploaded into the DCC++EX Command Station. +- EX-RAIL scripts are automatically displayed as Automation {Handoff} and Route {Set} buttons on supported WiFi Throttle Apps. + +**New EX-RAIL ‘Roster’ Feature** + +- List and store user defined engine roster & function keys inside the command station, and automatically load them in WiFi Throttle Apps. +- When choosing “DCC++EX” from discovered servers an Engine Driver or WiThrottle is directly connected to the Command Station. +- The EX-RAIL ’ROSTER’ command allows all the engine numbers, names and function keys you’ve listed in your myAutomation.h file to automatically upload the Command Station's ‘Server Roster’ into your Engine Driver and WiThrottle Apps. + +**New JMRI 4.99.2 and above specific DCC++EX 4.0 features** + +- Enhanced JMRI DCC++ Configure Base Station pane for building and maintaining Sensor, Turnout and Output devices, or these can automatically be populated from the DCC++EX Command Station's mySetup.h file into JMRI. + +- JMRI now supports multiple serial connected DCC++EX Command Stations, to display and track separate "Send DCC++ Command" and "DCC++ Traffic" Monitors for each Command Station at the same time. + For example: Use an Uno DCC++EX DecoderPro Programming Station {DCC++Prg} on a desktop programming track and a second Mega DCC++EX EX-RAIL Command Station for Operations {DCC++Ops} on the layout with an additional `` programming spur or siding track for acquiring an engine and ‘Drive Away’ onto the mainline (see the DriveAway feature for more information). + +**DCC++EX 4.0.0 additional product enhancements** + +- Additional Motor Shields and Motor Board {boosters) supported +- Additional Accessory boards supported for GPIO expansion, Sensors, Servos & Signals +- Additional diagnostic commands like ‘D ACK RETRY’ and ‘D EXRAIL ON’ events, ‘D HAL SHOW’ devices and ‘D SERVO’ positions, and ‘D RESET’ the command station while maintaining the serial connection with JMRI +- Automatic retry on failed ACK detection to give decoders another chance +- New EX-RAIL ’/’ slash command allows JMRI to directly communicate with many EX-RAIL scripts +- Turnout class revised to expand turnout capabilities and allow turnout names/descriptors to display in WiThrottle Apps. +- Build turnouts through either or both mySetup.h and myAutomation.h files, and have them automatically passed to, and populate, JMRI Turnout Tables +- Turnout user names display in Engine Driver & WiThrottles +- Output class now allows ID > 255. +- Configuration options to globally flip polarity of DCC Accessory states when driven from `` command and `` command. +- Increased use of display for showing loco decoder programming information. +- Can disable EEPROM memory code to allow room for DCC++EX 4.0 to fit on a Uno Command Station +- Can define border between long and short addresses +- Native non-blocking I2C drivers for AVR and Nano architectures (fallback to blocking Wire library for other platforms). +- EEPROM layout change - deletes EEPROM contents on first start following upgrade. + +**4.0.0 Bug Fixes** + +- Compiles on Nano Every +- Diagnostic display of ack pulses >32ms +- Current read from wrong ADC during interrupt +- AT(+) Command Pass Through +- CiDAP WiFi Drop out and the WiThrottle F-key looping error corrected +- One-off error in CIPSEND drop +- Common Fault Pin Error +- Uno Memory Utilization optimized + +#### Summary of Release 3.1.0 key features and/or bug fixes by Point Release + +**Summary of the key new features added to CommandStation-EX V3.0.16** + +- Ignore CV1 bit 7 read if rejected by a non NMRA compliant decoder when identifying loco id + +**Summary of the key new features added to CommandStation-EX V3.0.15** + +- Send function commands just once instead of repeating them 4 times + +**Summary of the key new features added to CommandStation-EX V3.0.14** + +- Add feature to tolerate decoders that incorrectly have gaps in their ACK pulse +- Provide proper track power management when joining and unjoining tracks with <1 JOIN> + +**Summary of the key new features added to CommandStation-EX V3.0.13** + +- Fix for CAB Functions greater than 127 + +**Summary of the key new features added to CommandStation-EX V3.0.12** + +- Fixed clear screen issue for nanoEvery and nanoWifi + +**Summary of the key new features added to CommandStation-EX V3.0.11** + +- Reorganized files for support of 128 speed steps + +**Summary of the key new features added to CommandStation-EX V3.0.10** + +- Added Support for the Teensy 3.2, 3.5, 3.6, 4.0 and 4.1 MCUs +- No functional change just changes to avoid complier warnings for Teensy/nanoEvery + +**Summary of the key new features added to CommandStation-EX V3.0.9** + +- Rearranges serial newlines for the benefit of JMRI +- Major update for efficiencies in displays (LCD, OLED) +- Add I2C Support functions + +**Summary of the key new features added to CommandStation-EX V3.0.8** + +- Wraps <* *> around DIAGS for the benefit of JMRI + +**Summary of the key new features added to CommandStation-EX V3.0.7** + +- Implemented support for older 28 apeed step decoders - Option to turn on 28 step speed decoders in addition to 128. If set, all locos will use 28 steps. +- Improved overload messages with raw values (relative to offset) + +**Summary of the key new features added to CommandStation-EX V3.0.6** + +- Prevent compiler warning about deprecated B constants +- Fix Bug that did not let us transmit 5 byte sized packets - 5 Byte commands like PoM (programming on main) were not being sent correctly +- Support for Huge function numbers (DCC BinaryStateControl) - Support Functions beyond F28 +- ESTOP all - New command to emergency stop all locos on the main track +- <- [cab]> estop and forget cab/all cabs - Stop and remove loco from the CS. Stops the repeating throttle messages +- `` command to reboot Arduino +- Automatic sensor offset detect +- Improved startup msgs from Motor Drivers (accuracy and auto sense factors) +- Drop post-write verify - No need to double check CV writes. Writes are now even faster. +- Allow current sense pin set to UNUSED_PIN - No need to ground an unused analog current pin. Produce startup warning and callback -2 for prog track cmds. + +**Summary of the key new features added to CommandStation-EX V3.0.5** + +- Fix Fn Key startup with loco ID and fix state change for F16-28 +- Removed ethernet mac config and made it automatic +- Show wifi ip and port on lcd +- Auto load config.example.h with warning +- Dropped example .ino files +- Corrected .ino comments +- Add Pololu fault pin handling +- Waveform speed/simplicity improvements +- Improved pin speed in waveform +- Portability to nanoEvery and UnoWifiRev2 CPUs +- Analog read speed improvements +- Drop need for DIO2 library +- Improved current check code +- Linear command +- Removed need for ArduinoTimers files +- Removed option to choose different timer +- Added EX-RAIL hooks for automation in future version +- Fixed Turnout list +- Allow command keywords in mixed case +- Dropped unused memstream +- PWM pin accuracy if requirements met + +**Summary of the key new features added to CommandStation-EX V3.0.4** + +- "Drive-Away" Feature - added so that throttles like Engine Driver can allow a loco to be programmed on a usable, electrically isolated programming track and then drive off onto the main track +- WiFi Startup Fixes + +**Summary of the key new features added to CommandStation-EX V3.0.3** + +- Command to write loco address and clear consist +- Command will allow for consist address +- Startup commands implemented + +**Summary of the key new features added to CommandStation-EX V3.0.2:** + +- Create new output for current in mA for `` command - New current response outputs current in mA, overlimit current, and maximum board capable current +- Simultaneously update JMRI to handle new current meter + +**Summary of the key new features added to CommandStation-EX V3.0.1:** + +- Add back fix for jitter +- Add Turnouts, Outputs and Sensors to `` command output + +**CommandStation-EX V3.0.0:** + +**Release v3.0.0 was a major rewrite if earlier versions of DCC++. The code base was re-architeced and core changes were made to the Waveform generator to reduce overhead and make better use of Arduino.** **Summary of the key new features added in Release v3.0.0 include:** + +- **New USB Browser Based Throttle** - WebThrottle-EX is a full front-end to controller to control the CS to run trains. +- **WiFi Support** - AP and station modes supported. Auto-detection of an ESP8266 WiFi module with AT firmware on a Mega's serial port. Connection to JMRI and WiThrottle clients. +- **Withrottle Integrations** - Act as a host for up to four WiThrottle clients concurrently. +- **Add LCD/OLED support** - OLED supported on Mega only +- **Improved CV programming routines** - checks for length of CV pulse, and breaks out of the wait state once it has received an ACK, now reading one CV per second. +- **Improved current sensing** - rewrote current sensing routines for safer operation. Current thresholds based on milliamps, not magic numbers +- **Individual track power control** - Ability to toggle power on either or both tracks, and to "JOIN" the tracks and make them output the same waveform for multiple power districts. +- **Single or Dual-Pin PWM output** - Allows control of H-bridges with PH/EN or dual PWM inputs +- **New, simpler function command** - `` command allows setting functions based on their number, not based on a code as in `` +- **Function reminders** - Function reminders are sent in addition to speed reminders +- **Functions to F28** - All NMRA functions are now supported +- **Filters and user functions** - Ability to filter commands in the parser and execute custom code based on them. (ex: Redirect Turnout commands via NRF24) +- **Diagnostic `` commands** - See documentation for a full list of new diagnostic commands +- **Rewrote DCC++ Parser** - more efficient operation, accepts multi-char input and uses less RAM +- **Rewritten waveform generator** - capable of using any pin for DCC waveform out, eliminating the need for jumpers +- **Rewritten packet generator** - Simplify and make smaller, remove idea of "registers" from original code +- **Add free RAM messages** - Free RAM messages are now printed whenever there is a decerase in available RAM +- **Fix EEPROM bugs** +- **Number of locos discovery command** - `<#>` command +- **Support for more locomotives** - 20 locomotives on an UNO and 50 an a Mega. +- **Automatic slot management** - slot variable in throttle/function commands are ignored and slot management is taken care of automatically. `<->` and `<- CAB>` commands added to release locos from memory and stop packets to the track. + +**Key Contributors** + +**Project Lead** + +- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) + +**EX-CommandStation Developers** + +- Chris Harlow - Bournemouth, UK (UKBloke) +- Harald Barth - Stockholm, Sweden (Haba) +- Neil McKechnie - Worcestershire, UK (NeilMck) +- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) +- Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting) +- M Steve Todd - Oregon, USA (MSteveTodd) +- Scott Catalano - Pennsylvania +- Gregor Baues - Île-de-France, France (grbba) + +**Engine Driver and JMRI Interface** + +- M Steve Todd + +**EX-Installer Software** + +- Anthony W - Dayton, Ohio, USA (Dex, Dex++) + +**Website and Documentation** + +- Mani Kumar - Bangalor, India (Mani / Mani Kumar) +- Fred Decker - Holly Springs, North Carolina, USA (FlightRisk) +- Dave Cutting - Logan, Utah, USA (Dave Cutting/ David Cutting) +- Roger Beschizza - Dorset, UK (Roger Beschizza) +- Keith Ledbetter - Chicago, Illinois, USA (Keith Ledbetter) +- Kevin Smith - Rochester Hills, Michigan USA (KC Smith) +- Colin Grabham - Central NSW, Australia (Kebbin) +- Peter Cole - Brisbane, QLD, Australia (peteGSX) +- Peter Akers - Brisbane, QLD, Australia (flash62au) + +**EX-WebThrottle** + +- Fred Decker - Holly Springs, NC (FlightRisk/FrightRisk) +- Mani Kumar - Bangalor, India (Mani /Mani Kumar) +- Matt H - Somewhere in Europe + +**Hardware / Electronics** + +- Harald Barth - Stockholm, Sweden (Haba) +- Paul Antoine, Western Australia +- Neil McKechnie - Worcestershire, UK +- Fred Decker - Holly Springs NC, USA +- Herb Morton - Kingwood Texas, USA (Ash++) + +**Beta Testing / Release Management / Support** + +- Larry Dribin - Release Management +- Kevin Smith - Rochester Hills, Michigan USA (KC Smith) +- Herb Morton - Kingwood Texas, USA (Ash++) +- Keith Ledbetter +- Brad Van der Elst +- Andrew Pye +- Mike Bowers +- Randy McKenzie +- Roberto Bravin +- Sam Brigden +- Alan Lautenslager +- Martin Bafver +- Mário André Silva +- Anthony Kochevar +- Gajanatha Kobbekaduwe +- Sumner Patterson +- Paul - Virginia, USA + +**Downloads (zip and tar.gz) below. These are named without version number in the folder name to make the Arduino IDE happy.** + +[CommandStation-EX.zip](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.2-Prod/CommandStation-EX.zip) + + +[CommandStation-EX.tar.gz](https://github.com/DCC-EX/CommandStation-EX/releases/download/v4.1.2-Prod/CommandStation-EX.tar.gz) From 1827a11f83b1fe9675705a11fc290b5d67dd65c3 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 28 Oct 2022 10:49:32 -0400 Subject: [PATCH 027/218] Update release_notes_v4.1.1.md --- Release_Notes/release_notes_v4.1.1.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Release_Notes/release_notes_v4.1.1.md b/Release_Notes/release_notes_v4.1.1.md index 1842565..654c42d 100644 --- a/Release_Notes/release_notes_v4.1.1.md +++ b/Release_Notes/release_notes_v4.1.1.md @@ -1,3 +1,6 @@ +Version 4.1.1 Release Notes +************************* + The DCC-EX Team is pleased to release CommandStation-EX v4.1.1 as a Production Release for the general public. This release is a Minor release with many significant EX-RAIL enhancements and new automation features in addition to some bug fixes. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.1 rc13. From 6f94cd71ab9540ee78e9e7650ef69ef025b9520b Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 28 Oct 2022 10:50:35 -0400 Subject: [PATCH 028/218] Update release_notes_v4.1.2.md --- Release_Notes/release_notes_v4.1.2.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Release_Notes/release_notes_v4.1.2.md b/Release_Notes/release_notes_v4.1.2.md index 9bff53c..687d0c9 100644 --- a/Release_Notes/release_notes_v4.1.2.md +++ b/Release_Notes/release_notes_v4.1.2.md @@ -1,3 +1,6 @@ +Version 4.1.2 Release Notes +************************* + The DCC-EX Team is pleased to release CommandStation-EX v4.1.2 as a Production Release for the general public. This release is a Bugfix release which fixes support for Ethernet Shields based on the W5100 chip that broke with the release of v4.1.1. This chip does not report HW and link status the way the W5200 and W5500 do, so the check routine needed to be changed. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.2. From aca9c9c941185a57bf1dd7d4a0c68ef199248083 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 28 Oct 2022 10:52:12 -0400 Subject: [PATCH 029/218] Update release_notes.md --- release_notes.md | 3 +++ 1 file changed, 3 insertions(+) diff --git a/release_notes.md b/release_notes.md index 025cda8..f38e22e 100644 --- a/release_notes.md +++ b/release_notes.md @@ -1,3 +1,6 @@ +Version 4.0 Release Notes +************************* + The DCC-EX Team is pleased to release CommandStation-EX v4.1.2 as a Production Release for the general public. This release is a Minor release with many significant EX-RAIL enhancements and new automation features in addition to some bug fixes. The team continues improving the architecture of DCC++EX to make it more flexible and optimizing the code to get more performance from the Arduino (and other) microprocessors. This release includes all of the Point Releases from v4.0.1 to v4.1.2. From 5b7801ca6c8f99e3f51218f038f18e22961ec4c0 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 28 Oct 2022 14:05:35 -0400 Subject: [PATCH 030/218] Update version.h --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index d821db0..537a44b 100644 --- a/version.h +++ b/version.h @@ -16,7 +16,7 @@ // UNO Progmem optimized to allow for small EXRAIL Automation scipts // 4.0.2 Command Station and EX-RAIL Ehancements & Additions: // New JA, JR, JT commands availabe for Throttle Developers to obtain Route, Roster and Turnout descriptions for communications -// Change ACK defaults now set to LIMIT 50mA, MIN 2000uS, MAX 2000uS for more compatibility with non NMRA compliant decoders +// Change ACK defaults now set to LIMIT 50mA, MIN 2000uS, MAX 20000uS for more compatibility with non NMRA compliant decoders // New Commands for the Arduino IDE Serial Monitor and JMRI DCC++ Traffic Monitor // to turn a individual LED Signal On & Off // " From b1d110ecbf262058268d0ddfd3a63dfc026797dd Mon Sep 17 00:00:00 2001 From: peteGSX Date: Thu, 3 Nov 2022 14:06:43 +1000 Subject: [PATCH 031/218] Fix project workflow --- .github/workflows/new-items.yml | 59 ++++++++++++++++++++------------- 1 file changed, 36 insertions(+), 23 deletions(-) diff --git a/.github/workflows/new-items.yml b/.github/workflows/new-items.yml index b5f0a48..492a0f1 100644 --- a/.github/workflows/new-items.yml +++ b/.github/workflows/new-items.yml @@ -42,26 +42,35 @@ jobs: gh api graphql -f query=' query($org: String!, $number: Int!) { organization(login: $org){ - projectNext(number: $number) { + projectV2(number: $number) { id fields(first:20) { nodes { - id - name - settings + ... on ProjectV2Field { + id + name + } + ... on ProjectV2SingleSelectField { + id + name + options { + id + name + } + } } } } } }' -f org=$ORG -F number=$PROJECT_NUMBER > project_data.json - echo 'PROJECT_ID='$(jq '.data.organization.projectNext.id' project_data.json) >> $GITHUB_ENV - echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV - echo 'BACKLOG_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Backlog") |.id' project_data.json) >> $GITHUB_ENV - echo 'TO_DO_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="To Do") |.id' project_data.json) >> $GITHUB_ENV - echo 'NEEDS_REVIEW_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="Needs Review") |.id' project_data.json) >> $GITHUB_ENV - echo 'IN_PROGRESS_OPTION_ID='$(jq '.data.organization.projectNext.fields.nodes[] | select(.name== "Status") |.settings | fromjson.options[] | select(.name=="In Progress") |.id' project_data.json) >> $GITHUB_ENV - + echo 'PROJECT_ID='$(jq '.data.organization.projectV2.id' project_data.json) >> $GITHUB_ENV + echo 'STATUS_FIELD_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") | .id' project_data.json) >> $GITHUB_ENV + echo 'BACKLOG_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") |.options[] | select(.name=="Backlog") |.id' project_data.json) >> $GITHUB_ENV + echo 'TO_DO_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") |.options[] | select(.name=="To Do") |.id' project_data.json) >> $GITHUB_ENV + echo 'NEEDS_REVIEW_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") |.options[] | select(.name=="Needs Review") |.id' project_data.json) >> $GITHUB_ENV + echo 'IN_PROGRESS_OPTION_ID='$(jq '.data.organization.projectV2.fields.nodes[] | select(.name== "Status") |.options[] | select(.name=="In Progress") |.id' project_data.json) >> $GITHUB_ENV + - name: Add issue to project env: GITHUB_TOKEN: ${{ steps.generate_token.outputs.token }} @@ -70,12 +79,12 @@ jobs: run: | project_item_id="$( gh api graphql -f query=' mutation($project:ID!, $item:ID!) { - addProjectNextItem(input: {projectId: $project, contentId: $item}) { - projectNextItem { + addProjectV2ItemById(input: {projectId: $project, contentId: $item}) { + item { id } } - }' -f project=$PROJECT_ID -f item=$ITEM_ID --jq '.data.addProjectNextItem.projectNextItem.id')" + }' -f project=$PROJECT_ID -f item=$ITEM_ID --jq '.data.addProjectV2ItemById.item.id')" echo 'PROJECT_ITEM_ID='$project_item_id >> $GITHUB_ENV - name: Add PR to project @@ -86,12 +95,12 @@ jobs: run: | project_item_id="$( gh api graphql -f query=' mutation($project:ID!, $item:ID!) { - addProjectNextItem(input: {projectId: $project, contentId: $item}) { - projectNextItem { + addProjectV2ItemById(input: {projectId: $project, contentId: $item}) { + item { id } } - }' -f project=$PROJECT_ID -f item=$ITEM_ID --jq '.data.addProjectNextItem.projectNextItem.id')" + }' -f project=$PROJECT_ID -f item=$ITEM_ID --jq '.data.addProjectV2ItemById.item.id')" echo 'PROJECT_ITEM_ID='$project_item_id >> $GITHUB_ENV - name: Set status - To Do @@ -106,13 +115,15 @@ jobs: $status_field: ID! $status_value: String! ){ - set_status: updateProjectNextItemField(input: { + set_status: updateProjectV2ItemFieldValue(input: { projectId: $project itemId: $item fieldId: $status_field - value: $status_value + value: { + singleSelectOptionId: $status_value + } }) { - projectNextItem { + projectV2Item { id } } @@ -130,13 +141,15 @@ jobs: $status_field: ID! $status_value: String! ){ - set_status: updateProjectNextItemField(input: { + set_status: updateProjectV2ItemFieldValue(input: { projectId: $project itemId: $item fieldId: $status_field - value: $status_value + value: { + singleSelectOptionId: $status_value + } }) { - projectNextItem { + projectV2Item { id } } From 5376c9f410c71372a6ce6855aa8f6bfcc3e72b4f Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 4 Nov 2022 06:54:49 +1000 Subject: [PATCH 032/218] Update project workflow for forks --- .github/workflows/new-items.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/new-items.yml b/.github/workflows/new-items.yml index 492a0f1..1718730 100644 --- a/.github/workflows/new-items.yml +++ b/.github/workflows/new-items.yml @@ -13,7 +13,7 @@ on: issues: types: - opened - pull_request: + pull_request_target: types: - ready_for_review - opened From dd309a3705dde6de86b9d1ed46b757c7ea27e0ad Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 4 Nov 2022 15:39:35 +0100 Subject: [PATCH 033/218] Ethernet init order --- EthernetInterface.cpp | 18 ++++++++++++++---- version.h | 3 ++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/EthernetInterface.cpp b/EthernetInterface.cpp index ddc71be..fe780b7 100644 --- a/EthernetInterface.cpp +++ b/EthernetInterface.cpp @@ -36,11 +36,11 @@ EthernetInterface * EthernetInterface::singleton=NULL; */ void EthernetInterface::setup() { - if (Ethernet.hardwareStatus() == EthernetNoHardware) - DIAG(F("Ethernet shield not detected or is a W5100")); - if (singleton!=NULL) + if (singleton!=NULL) { DIAG(F("Prog Error!")); - if (singleton=new EthernetInterface()) + return; + } + if ((singleton=new EthernetInterface())) return; DIAG(F("Ethernet not initialized")); }; @@ -67,6 +67,16 @@ EthernetInterface::EthernetInterface() return; } #endif + if (Ethernet.hardwareStatus() == EthernetNoHardware) + DIAG(F("Ethernet shield not detected or is a W5100")); + + unsigned long startmilli = millis(); + while ((millis() - startmilli) < 5500) { // Loop to give time to check for cable connection + if (Ethernet.linkStatus() == LinkON) + break; + DIAG(F("Ethernet waiting for link (1sec) ")); + delay(1000); + } } /** diff --git a/version.h b/version.h index d821db0..4804870 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "4.1.2" +#define VERSION "4.1.3" +// 4.1.3 Bugfix: Ethernet init order // 4.1.2 Bugfix: Ethernet shield W5100 does not report HW or link level // 4.1.1 Bugfix: preserve turnout format // Bugfix: parse multiple commands in one buffer string correctly (ex: ) From 7e16ec70884e91b72d83ecb6ab190d842a487171 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 5 Nov 2022 05:17:03 +1000 Subject: [PATCH 034/218] Fix support request issue template --- .github/ISSUE_TEMPLATE/support_request.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/ISSUE_TEMPLATE/support_request.yml b/.github/ISSUE_TEMPLATE/support_request.yml index c53d2b7..a8c7649 100644 --- a/.github/ISSUE_TEMPLATE/support_request.yml +++ b/.github/ISSUE_TEMPLATE/support_request.yml @@ -35,5 +35,5 @@ body: label: Hardware description: If appropriate, please provide details of the hardware in use. placeholder: | - Elegoo Mega2560 - Arduino Motor Shield R3 \ No newline at end of file + Elegoo Mega2560 + Arduino Motor Shield R3 \ No newline at end of file From 1f5eafbccac97e9ef5d1991417a1a2881b6bf7f9 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 27 Jan 2023 18:42:26 +0100 Subject: [PATCH 035/218] Bugfix for issue #299 TurnoutDescription NULL --- DCCEXParser.cpp | 19 ++++++++++++------- version.h | 3 ++- 2 files changed, 14 insertions(+), 8 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index b262600..2cfaafd 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -599,14 +599,19 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) else { // Turnout * t=Turnout::get(id); if (!t || t->isHidden()) StringFormatter::send(stream, F(" %d X"),id); - else StringFormatter::send(stream, F(" %d %c \"%S\""), - id,t->isThrown()?'T':'C', + else { + const FSH *tdesc = NULL; #ifdef EXRAIL_ACTIVE - RMFT2::getTurnoutDescription(id) -#else - F("") -#endif - ); + tdesc = RMFT2::getTurnoutDescription(id); +#endif + if (tdesc == NULL) + StringFormatter::send(stream, F(" %d %c"), + id,t->isThrown()?'T':'C'); + else + StringFormatter::send(stream, F(" %d %c \"%S\""), + id,t->isThrown()?'T':'C', + tdesc); + } } StringFormatter::send(stream, F(">\n")); return; diff --git a/version.h b/version.h index 028999c..947b363 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "4.1.3" +#define VERSION "4.1.4" +// 4.1.4 Bugfix for issue #299 TurnoutDescription NULL // 4.1.3 Bugfix: Ethernet init order // 4.1.2 Bugfix: Ethernet shield W5100 does not report HW or link level // 4.1.1 Bugfix: preserve turnout format From 7e4f9eb0e18d0936094f4e5aaf4140913e350e7f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 28 Jan 2023 19:07:59 +0100 Subject: [PATCH 036/218] jT answer should contain empty string --- DCCEXParser.cpp | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 2cfaafd..05b3005 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -605,12 +605,10 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) tdesc = RMFT2::getTurnoutDescription(id); #endif if (tdesc == NULL) - StringFormatter::send(stream, F(" %d %c"), - id,t->isThrown()?'T':'C'); - else - StringFormatter::send(stream, F(" %d %c \"%S\""), - id,t->isThrown()?'T':'C', - tdesc); + tdesc = F(""); + StringFormatter::send(stream, F(" %d %c \"%S\""), + id,t->isThrown()?'T':'C', + tdesc); } } StringFormatter::send(stream, F(">\n")); From 7311f2ce64b66c0703859e786717df57d7cc1eb7 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 12 Feb 2023 20:38:03 +0100 Subject: [PATCH 037/218] LCN bugfix --- LCN.cpp | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/LCN.cpp b/LCN.cpp index efb49ff..d1e1228 100644 --- a/LCN.cpp +++ b/LCN.cpp @@ -43,7 +43,7 @@ void LCN::loop() { while (stream->available()) { int ch = stream->read(); - if (ch >= 0 && ch <= '9') { // accumulate id value + if (ch >= '0' && ch <= '9') { // accumulate id value id = 10 * id + ch - '0'; } else if (ch == 't' || ch == 'T') { // Turnout opcodes diff --git a/version.h b/version.h index 947b363..d340aa0 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "4.1.4" +#define VERSION "4.1.5" +// 4.1.5 Bugfix LCN number parsing // 4.1.4 Bugfix for issue #299 TurnoutDescription NULL // 4.1.3 Bugfix: Ethernet init order // 4.1.2 Bugfix: Ethernet shield W5100 does not report HW or link level From da8faa808bce6abbb51734db289f821126d0edef Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 3 Mar 2023 21:07:58 -0500 Subject: [PATCH 038/218] Update ThrottleAssists.md --- Release_Notes/ThrottleAssists.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index 9d979c9..c4b2b99 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -7,7 +7,7 @@ These commands are new and not overlapped with the existing commands which are p Turnouts: -The conventional turnout definition commands and the `````` responses do not contain information about the turnout description which may have been provided in an EXRAIL script. A turnout description is much more user friendly than T123 and having a list helps the throttle UI build a suitable set of buttons. +The conventional turnout definition commands and the `````` responses do not contain information about the turnout description which may have been provided in an EXRAIL script. A turnout description is much more user friendly than the cryptic "T123", and having a list helps the throttle UI build a suitable set of buttons. `````` command returns a list of turnout ids. The throttle should be uninterested in the turnout technology used but needs to know the ids it can throw/close and monitor the current state. e.g. response `````` From 0f5b8adb6b56b4b4a6162d30b06ec9705820a470 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 3 Mar 2023 21:26:46 -0500 Subject: [PATCH 039/218] Update ThrottleAssists.md --- Release_Notes/ThrottleAssists.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index c4b2b99..5f4a68d 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -5,7 +5,8 @@ Chris Harlow April 2022 There are a number of additional throttle information commands that have been implemented to assist throttle authors to obtain information from the Command Station in order to implement turnout, route/automation and roster features which are already found in the Withrottle implementations. These commands are new and not overlapped with the existing commands which are probabaly due to be obsoleted as they are over complex and unfit for purpose. -Turnouts: +Turnouts +============ The conventional turnout definition commands and the `````` responses do not contain information about the turnout description which may have been provided in an EXRAIL script. A turnout description is much more user friendly than the cryptic "T123", and having a list helps the throttle UI build a suitable set of buttons. @@ -25,6 +26,7 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. Automations/Routes + ==================== A throttle need to know which EXRAIL Automations and Routes it can show the user. @@ -64,6 +66,7 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. COMMANDS TO AVOID + ====================== `````` Use `````` `````` Just drop the slot number From 79eaaa85fabd403ec46f925be8f5ee2fa1dbfa44 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 3 Mar 2023 21:35:13 -0500 Subject: [PATCH 040/218] Update ThrottleAssists.md Fixing formatting --- Release_Notes/ThrottleAssists.md | 64 +++++++++++++++++--------------- 1 file changed, 34 insertions(+), 30 deletions(-) diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index 5f4a68d..e704b42 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -10,40 +10,44 @@ Turnouts The conventional turnout definition commands and the `````` responses do not contain information about the turnout description which may have been provided in an EXRAIL script. A turnout description is much more user friendly than the cryptic "T123", and having a list helps the throttle UI build a suitable set of buttons. -`````` command returns a list of turnout ids. The throttle should be uninterested in the turnout technology used but needs to know the ids it can throw/close and monitor the current state. +`````` command returns a list of turnout ids. The throttle should be uninterested in the turnout technology used but needs to know the ids it can throw/close and monitor the current state. e.g. response `````` -````` requests info on turnout 17. -e.g. response `````` or `````` -(T=thrown, C=closed) -or `````` indicating turnout description not given. -or `````` indicating turnout unknown (or possibly hidden.) - -Note: It is still the throttles responsibility to monitor the status broadcasts. - (TBD I'm thinking that the existing broadcast is messy and needs cleaning up) - However, I'm not keen on dynamically created/deleted turnouts so I have no intention of providing a command that indicates the turnout list has been updated since the throttle started. - Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "description" will NOT show up in these commands. +````` requests info on turnout 17. +e.g. response `````` or ````` +(T=thrown, C=closed) +or `````` indicating turnout description not given. +or `````` indicating turnout unknown (or possibly hidden.) + +Note: It is still the throttles responsibility to monitor the status broadcasts. +(TBD I'm thinking that the existing broadcast is messy and needs cleaning up) +However, I'm not keen on dynamically created/deleted turnouts so I have no intention of providing a command that indicates the turnout list has been updated since the throttle started. +Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "description" will NOT show up in these commands. Automations/Routes ==================== - A throttle need to know which EXRAIL Automations and Routes it can show the user. - - `````` Returns a list of Automations/Routes - e.g. `````` - Indicates route/automation ids. - Information on each route needs to be obtained by - `````` - returns e.g. `````` for a route - or `````` for an automation. - or `````` for id not found - - Whats the difference: - A Route is just a call to an EXRAIL ROUTE, traditionally to set some turnouts or signals but can be used to perform any kind of EXRAIL function... but its not expecting to know the loco. - Thus a route can be triggered by sending in for example ``````. + A throttle need to know which EXRAIL Automations and Routes it can show the user. + + `````` Returns a list of Automations/Routes + e.g. `````` + Indicates route/automation ids. + Information on each route needs to be obtained by + `````` + returns e.g. `````` for a route + or `````` for an automation. + or `````` for id not found + + What's the difference? + ----------------------- + +A *Route* is just a call to an **EXRAIL ROUTE**, traditionally to set some turnouts or signals but can be used to perform any kind of EXRAIL function... but its not expecting to know the loco. + +Thus, a route can be triggered by sending in for example ``````. + + An *Automation* is a handoff of the last accessed loco id to an EXRAIL AUTOMATION which would typically drive the loco away. - An Automation is a handoff of the last accessed loco id to an EXRAIL AUTOMATION which would typically drive the loco away. Thus an Automation expects a start command with a cab id e.g. `````` @@ -68,10 +72,10 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. COMMANDS TO AVOID ====================== - `````` Use `````` - `````` Just drop the slot number - `````` other than `````` - `````` + `````` Use `````` + `````` Just drop the slot number + `````` other than `````` + `````` `````` From d3eceb6d6c61e507304e5f85cb196405b99148f3 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 3 Mar 2023 21:41:22 -0500 Subject: [PATCH 041/218] Update ThrottleAssists.md --- Release_Notes/ThrottleAssists.md | 46 +++++++++++++++++--------------- 1 file changed, 24 insertions(+), 22 deletions(-) diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index e704b42..2055618 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -13,14 +13,14 @@ The conventional turnout definition commands and the `````` responses do not `````` command returns a list of turnout ids. The throttle should be uninterested in the turnout technology used but needs to know the ids it can throw/close and monitor the current state. e.g. response `````` -````` requests info on turnout 17. -e.g. response `````` or ````` -(T=thrown, C=closed) -or `````` indicating turnout description not given. +````` requests info on turnout 17.\ +e.g. response `````` or `````\ +(T=thrown, C=closed)\ +or `````` indicating turnout description not given.\ or `````` indicating turnout unknown (or possibly hidden.) -Note: It is still the throttles responsibility to monitor the status broadcasts. -(TBD I'm thinking that the existing broadcast is messy and needs cleaning up) +Note: It is still the throttles responsibility to monitor the status broadcasts.\ +(TBD I'm thinking that the existing broadcast is messy and needs cleaning up)\ However, I'm not keen on dynamically created/deleted turnouts so I have no intention of providing a command that indicates the turnout list has been updated since the throttle started. Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "description" will NOT show up in these commands. @@ -30,13 +30,13 @@ Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "d A throttle need to know which EXRAIL Automations and Routes it can show the user. - `````` Returns a list of Automations/Routes - e.g. `````` - Indicates route/automation ids. - Information on each route needs to be obtained by - `````` - returns e.g. `````` for a route - or `````` for an automation. + `````` Returns a list of Automations/Routes\ + e.g. ``````\ + Indicates route/automation ids.\ + Information on each route needs to be obtained by\ + ``````\ + returns e.g. `````` for a route\ + or `````` for an automation.\ or `````` for id not found What's the difference? @@ -45,14 +45,15 @@ Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "d A *Route* is just a call to an **EXRAIL ROUTE**, traditionally to set some turnouts or signals but can be used to perform any kind of EXRAIL function... but its not expecting to know the loco. Thus, a route can be triggered by sending in for example ``````. + +An *Automation* is a handoff of the last accessed loco id to an EXRAIL AUTOMATION which would typically drive the loco away. - An *Automation* is a handoff of the last accessed loco id to an EXRAIL AUTOMATION which would typically drive the loco away. - - Thus an Automation expects a start command with a cab id + Thus an Automation expects a start command with a cab id\ e.g. `````` - Roster Information: + Roster Information + ^^^^^^^^^^^^^^^^^^ The `````` command requests a list of cab ids from the roster. e.g. responding `````` or for none. @@ -63,7 +64,8 @@ Thus, a route can be triggered by sending in for example ``````. Refer to EXRAIL ROSTER command for function map format. - Obtaining throttle status. + Obtaining throttle status + ^^^^^^^^^^^^^^^^^^^^^^^^^^ `````` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast. `````` Note that a slot of -1 indicates that the cab is not in the reminders table and this comand will not reserve a slot until such time as the cab is throttled. @@ -72,10 +74,10 @@ Thus, a route can be triggered by sending in for example ``````. COMMANDS TO AVOID ====================== - `````` Use `````` - `````` Just drop the slot number - `````` other than `````` - `````` + `````` Use ``````\ + `````` Just drop the slot number\ + `````` other than ``````\ + ``````\ `````` From 98af5c45eda659ad48b5b29aeba173ec09897db7 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 3 Mar 2023 21:46:07 -0500 Subject: [PATCH 042/218] Update ThrottleAssists.md --- Release_Notes/ThrottleAssists.md | 40 ++++++++++++++++---------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index 2055618..56677ae 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -13,7 +13,7 @@ The conventional turnout definition commands and the `````` responses do not `````` command returns a list of turnout ids. The throttle should be uninterested in the turnout technology used but needs to know the ids it can throw/close and monitor the current state. e.g. response `````` -````` requests info on turnout 17.\ +````` requests info on turnout 17\ e.g. response `````` or `````\ (T=thrown, C=closed)\ or `````` indicating turnout description not given.\ @@ -24,7 +24,6 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. However, I'm not keen on dynamically created/deleted turnouts so I have no intention of providing a command that indicates the turnout list has been updated since the throttle started. Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "description" will NOT show up in these commands. - Automations/Routes ==================== @@ -51,30 +50,31 @@ An *Automation* is a handoff of the last accessed loco id to an EXRAIL AUTOMATIO Thus an Automation expects a start command with a cab id\ e.g. `````` +Roster Information +^^^^^^^^^^^^^^^^^^ - Roster Information - ^^^^^^^^^^^^^^^^^^ - The `````` command requests a list of cab ids from the roster. - e.g. responding `````` - or for none. +The `````` command requests a list of cab ids from the roster. +e.g. responding `````` +or for none. - Each Roster entry had a name and function map obtained by: - `````` reply like ``` +Each Roster entry had a name and function map obtained by: +`````` reply like ``` - Refer to EXRAIL ROSTER command for function map format. +Refer to EXRAIL ROSTER command for function map format. + +Obtaining throttle status +^^^^^^^^^^^^^^^^^^^^^^^^^^ + +`````` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast. +`````` + +Note that a slot of -1 indicates that the cab is not in the reminders table and this comand will not reserve a slot until such time as the cab is throttled. - Obtaining throttle status - ^^^^^^^^^^^^^^^^^^^^^^^^^^ - `````` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast. - `````` - Note that a slot of -1 indicates that the cab is not in the reminders table and this comand will not reserve a slot until such time as the cab is throttled. +COMMANDS TO AVOID +====================== - - COMMANDS TO AVOID - ====================== - - `````` Use ``````\ + `````` - Instead Use ``````\ `````` Just drop the slot number\ `````` other than ``````\ ``````\ From 91d36ae909b21eb7cbd215f4f5b6a85ab1181d83 Mon Sep 17 00:00:00 2001 From: Fred Date: Fri, 3 Mar 2023 21:59:18 -0500 Subject: [PATCH 043/218] Update ThrottleAssists.md --- Release_Notes/ThrottleAssists.md | 35 +++++++++++++------------------- 1 file changed, 14 insertions(+), 21 deletions(-) diff --git a/Release_Notes/ThrottleAssists.md b/Release_Notes/ThrottleAssists.md index 56677ae..19622fd 100644 --- a/Release_Notes/ThrottleAssists.md +++ b/Release_Notes/ThrottleAssists.md @@ -3,18 +3,17 @@ Throttle Assist updates for versiuon 4.? Chris Harlow April 2022 There are a number of additional throttle information commands that have been implemented to assist throttle authors to obtain information from the Command Station in order to implement turnout, route/automation and roster features which are already found in the Withrottle implementations. -These commands are new and not overlapped with the existing commands which are probabaly due to be obsoleted as they are over complex and unfit for purpose. +These commands are new and not overlapped with the existing commands which are probabaly due to be obsoleted as they are over complex and unfit for purpose. -Turnouts -============ +# Turnouts The conventional turnout definition commands and the `````` responses do not contain information about the turnout description which may have been provided in an EXRAIL script. A turnout description is much more user friendly than the cryptic "T123", and having a list helps the throttle UI build a suitable set of buttons. `````` command returns a list of turnout ids. The throttle should be uninterested in the turnout technology used but needs to know the ids it can throw/close and monitor the current state. e.g. response `````` -````` requests info on turnout 17\ -e.g. response `````` or `````\ +`````` requests info on turnout 17\ +e.g. response `````` or ``````\ (T=thrown, C=closed)\ or `````` indicating turnout description not given.\ or `````` indicating turnout unknown (or possibly hidden.) @@ -24,8 +23,7 @@ Note: It is still the throttles responsibility to monitor the status broadcasts. However, I'm not keen on dynamically created/deleted turnouts so I have no intention of providing a command that indicates the turnout list has been updated since the throttle started. Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "description" will NOT show up in these commands. - Automations/Routes - ==================== + # Automations/Routes A throttle need to know which EXRAIL Automations and Routes it can show the user. @@ -38,8 +36,7 @@ Also note that turnouts marked in EXRAIL with the HIDDEN keyword instead of a "d or `````` for an automation.\ or `````` for id not found - What's the difference? - ----------------------- + ## What's the difference? A *Route* is just a call to an **EXRAIL ROUTE**, traditionally to set some turnouts or signals but can be used to perform any kind of EXRAIL function... but its not expecting to know the loco. @@ -50,31 +47,27 @@ An *Automation* is a handoff of the last accessed loco id to an EXRAIL AUTOMATIO Thus an Automation expects a start command with a cab id\ e.g. `````` -Roster Information -^^^^^^^^^^^^^^^^^^ +### Roster Information -The `````` command requests a list of cab ids from the roster. -e.g. responding `````` +The `````` command requests a list of cab ids from the roster\ +e.g. responding ``````\ or for none. -Each Roster entry had a name and function map obtained by: +Each Roster entry had a name and function map obtained by:\ `````` reply like ``` Refer to EXRAIL ROSTER command for function map format. -Obtaining throttle status -^^^^^^^^^^^^^^^^^^^^^^^^^^ +### Obtaining throttle status -`````` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast. +`````` Requests a deliberate update on the cab speed/functions in the same format as the cab broadcast\ `````` Note that a slot of -1 indicates that the cab is not in the reminders table and this comand will not reserve a slot until such time as the cab is throttled. +# COMMANDS TO AVOID -COMMANDS TO AVOID -====================== - - `````` - Instead Use ``````\ + `````` Instead Use ``````\ `````` Just drop the slot number\ `````` other than ``````\ ``````\ From 83325ebf78d9f0e654cb5bf029126d1bfbea26ad Mon Sep 17 00:00:00 2001 From: pmantoine Date: Thu, 23 Mar 2023 08:44:25 +1100 Subject: [PATCH 044/218] Initial I2C native driver --- I2CManager_STM32.h | 322 ++++++++++++++++++++++++++++++++++----------- 1 file changed, 242 insertions(+), 80 deletions(-) diff --git a/I2CManager_STM32.h b/I2CManager_STM32.h index a55fd2e..58a623d 100644 --- a/I2CManager_STM32.h +++ b/I2CManager_STM32.h @@ -38,7 +38,10 @@ * bus on the SAMD architecture ***************************************************************************/ #if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_ARCH_STM32) -void I2C1_IRQHandler() { +extern "C" void I2C1_EV_IRQHandler(void) { + I2CManager.handleInterrupt(); +} +extern "C" void I2C1_ER_IRQHandler(void) { I2CManager.handleInterrupt(); } #endif @@ -91,44 +94,60 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) { // Use 10x the rise time spec to enable integer divide of 62.5ns clock period uint16_t t_rise; uint32_t ccr_freq; - if (i2cClockSpeed < 200000L) { - // i2cClockSpeed = 100000L; - t_rise = 0x11; // (1000ns /62.5ns) + 1; - } - else if (i2cClockSpeed < 800000L) + + while (s->CR1 & I2C_CR1_STOP); // Prevents lockup by guarding further + // writes to CR1 while STOP is being executed! + // Disable the I2C device, as TRISE can only be programmed whilst disabled + s->CR1 &= ~(I2C_CR1_PE); // Disable I2C + // Software reset the I2C peripheral + // s->CR1 |= I2C_CR1_SWRST; // reset the I2C + // delay(1); + // Release reset + // s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation + + if (i2cClockSpeed > 100000L) { - i2cClockSpeed = 400000L; - t_rise = 0x06; // (300ns / 62.5ns) + 1; - // } else if (i2cClockSpeed < 1200000L) { - // i2cClockSpeed = 1000000L; - // t_rise = 120; + if (i2cClockSpeed > 400000L) + i2cClockSpeed = 400000L; + + t_rise = 0x06; // (300ns /62.5ns) + 1; } else { i2cClockSpeed = 100000L; t_rise = 0x11; // (1000ns /62.5ns) + 1; } + // Configure the rise time register + s->TRISE = t_rise; - // Enable the I2C master mode - s->CR1 &= ~(I2C_CR1_PE); // Enable I2C - // Software reset the I2C peripheral - // s->CR1 |= I2C_CR1_SWRST; // reset the I2C - // Release reset - // s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation - - // Calculate baudrate - using a rise time appropriate for the speed + // DIAG(F("Setting I2C clock to: %d"), i2cClockSpeed); + // Calculate baudrate ccr_freq = I2C_BUSFREQ * 1000000 / i2cClockSpeed / 2; // Bit 15: I2C Master mode, 0=standard, 1=Fast Mode // Bit 14: Duty, fast mode duty cycle // Bit 11-0: FREQR = 16MHz => TPCLK1 = 62.5ns, so CCR divisor must be 0x50 (80 * 62.5ns = 5000ns) - s->CCR = (uint16_t)ccr_freq; - - // Configure the rise time register - s->TRISE = t_rise; // 1000 ns / 62.5 ns = 16 + 1 + if (i2cClockSpeed > 100000L) + s->CCR = (uint16_t)ccr_freq | 0x8000; // We need Fast Mode set + else + s->CCR = (uint16_t)ccr_freq; // Enable the I2C master mode s->CR1 |= I2C_CR1_PE; // Enable I2C + // Wait for bus to be clear? + unsigned long startTime = micros(); + bool timeout = false; + while (s->SR2 & I2C_SR2_BUSY) { + if (micros() - startTime >= 500UL) { + timeout = true; + break; + } + } + if (timeout) { + digitalWrite(D13, HIGH); + DIAG(F("I2C: SR2->BUSY timeout")); + // delay(1000); + } } /*************************************************************************** @@ -136,32 +155,46 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) { ***************************************************************************/ void I2CManagerClass::I2C_init() { - //Setting up the clocks - RCC->APB1ENR |= (1<<21); // Enable I2C CLOCK - RCC->AHB1ENR |= (1<<1); // Enable GPIOB CLOCK for PB8/PB9 + // Setting up the clocks + RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;//(1 << 21); // Enable I2C CLOCK + // Reset the I2C1 peripheral to initial state + RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST; + RCC->APB1RSTR &= ~RCC_APB1RSTR_I2C1RST; // Standard I2C pins are SCL on PB8 and SDA on PB9 + RCC->AHB1ENR |= (1<<1); // Enable GPIOB CLOCK for PB8/PB9 // Bits (17:16)= 1:0 --> Alternate Function for Pin PB8; // Bits (19:18)= 1:0 --> Alternate Function for Pin PB9 + GPIOB->MODER &= ~((3<<(8*2)) | (3<<(9*2))); // Clear all MODER bits for PB8 and PB9 GPIOB->MODER |= (2<<(8*2)) | (2<<(9*2)); // PB8 and PB9 set to ALT function GPIOB->OTYPER |= (1<<8) | (1<<9); // PB8 and PB9 set to open drain output capability GPIOB->OSPEEDR |= (3<<(8*2)) | (3<<(9*2)); // PB8 and PB9 set to High Speed mode + GPIOB->PUPDR &= ~((3<<(8*2)) | (3<<(9*2))); // Clear all PUPDR bits for PB8 and PB9 GPIOB->PUPDR |= (1<<(8*2)) | (1<<(9*2)); // PB8 and PB9 set to pull-up capability // Alt Function High register routing pins PB8 and PB9 for I2C1: // Bits (3:2:1:0) = 0:1:0:0 --> AF4 for pin PB8 // Bits (7:6:5:4) = 0:1:0:0 --> AF4 for pin PB9 + GPIOB->AFR[1] &= ~((15<<0) | (15<<4)); // Clear all AFR bits for PB8 on low nibble, PB9 on next nibble up GPIOB->AFR[1] |= (4<<0) | (4<<4); // PB8 on low nibble, PB9 on next nibble up - // Software reset the I2C peripheral + // // Software reset the I2C peripheral s->CR1 |= I2C_CR1_SWRST; // reset the I2C - s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation + asm("nop"); // wait a bit... suggestion from online! + s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation + // Clear all bits in I2C CR2 register except reserved bits + s->CR2 &= 0xE000; // Program the peripheral input clock in CR2 Register in order to generate correct timings s->CR2 |= I2C_BUSFREQ; // PCLK1 FREQUENCY in MHz + // set own address to 00 - not really used in master mode + I2C1->OAR1 |= (1 << 14); // bit 14 should be kept at 1 according to the datasheet + #if defined(I2C_USE_INTERRUPTS) // Setting NVIC - NVIC_SetPriority(I2C_IRQn, 1); // Match default priorities - NVIC_EnableIRQ(I2C_IRQn); + NVIC_SetPriority(I2C1_EV_IRQn, 1); // Match default priorities + NVIC_EnableIRQ(I2C1_EV_IRQn); + NVIC_SetPriority(I2C1_ER_IRQn, 1); // Match default priorities + NVIC_EnableIRQ(I2C1_ER_IRQn); // CR2 Interrupt Settings // Bit 15-13: reserved @@ -172,8 +205,8 @@ void I2CManagerClass::I2C_init() // Bit 8: ITERREN - Error interrupt enable // Bit 7-6: reserved // Bit 5-0: FREQ - Peripheral clock frequency (max 50MHz) - // s->CR2 |= 0x0700; // Enable Buffer, Event and Error interrupts - s->CR2 |= 0x0300; // Enable Event and Error interrupts + s->CR2 |= 0x0700; // Enable Buffer, Event and Error interrupts + // s->CR2 |= 0x0300; // Enable Event and Error interrupts #endif // Calculate baudrate and set default rate for now @@ -181,14 +214,26 @@ void I2CManagerClass::I2C_init() // Bit 15: I2C Master mode, 0=standard, 1=Fast Mode // Bit 14: Duty, fast mode duty cycle // Bit 11-0: FREQR = 16MHz => TPCLK1 = 62.5ns, so CCR divisor must be 0x50 (80 * 62.5ns = 5000ns) - s->CCR = 0x0050; + s->CCR = 0x50; // Configure the rise time register - max allowed in 1000ns s->TRISE = 0x0011; // 1000 ns / 62.5 ns = 16 + 1 // Enable the I2C master mode s->CR1 |= I2C_CR1_PE; // Enable I2C - // Setting bus idle mode and wait for sync + // Wait for bus to be clear? + unsigned long startTime = micros(); + bool timeout = false; + while (s->SR2 & I2C_SR2_BUSY) { + if (micros() - startTime >= 500UL) { + timeout = true; + break; + } + } + if (timeout) { + DIAG(F("I2C: SR2->BUSY timeout")); + // delay(1000); + } } /*************************************************************************** @@ -198,49 +243,56 @@ void I2CManagerClass::I2C_sendStart() { // Set counters here in case this is a retry. rxCount = txCount = 0; - uint8_t temp; - - // On a single-master I2C bus, the start bit won't be sent until the bus - // state goes to IDLE so we can request it without waiting. On a - // multi-master bus, the bus may be BUSY under control of another master, + // On a single-master I2C bus, the start bit won't be sent until the bus + // state goes to IDLE so we can request it without waiting. On a + // multi-master bus, the bus may be BUSY under control of another master, // in which case we can avoid some arbitration failures by waiting until // the bus state is IDLE. We don't do that here. - // If anything to send, initiate write. Otherwise initiate read. - if (operation == OPERATION_READ || ((operation == OPERATION_REQUEST) && !bytesToSend)) - { - // Send start for read operation - s->CR1 |= I2C_CR1_ACK; // Enable the ACK - s->CR1 |= I2C_CR1_START; // Generate START - // Send address with read flag (1) or'd in - s->DR = (deviceAddress << 1) | 1; // send the address - while (!(s->SR1 && I2C_SR1_ADDR)); // wait for ADDR bit to set - // Special case for 1 byte reads! - if (bytesToReceive == 1) - { - s->CR1 &= ~I2C_CR1_ACK; // clear the ACK bit - temp = I2C1->SR1 | I2C1->SR2; // read SR1 and SR2 to clear the ADDR bit.... EV6 condition - s->CR1 |= I2C_CR1_STOP; // Stop I2C + // Send start for read operation + while (s->CR1 & I2C_CR1_STOP); // Prevents lockup by guarding further + // writes to CR1 while STOP is being executed! + // Wait for bus to be clear? + unsigned long startTime = micros(); + bool timeout = false; + while (s->SR2 & I2C_SR2_BUSY) { + if (micros() - startTime >= 500UL) { + timeout = true; + break; } - else - temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit } - else { - // Send start for write operation - s->CR1 |= I2C_CR1_ACK; // Enable the ACK - s->CR1 |= I2C_CR1_START; // Generate START - // Send address with write flag (0) or'd in - s->DR = (deviceAddress << 1) | 0; // send the address - while (!(s->SR1 && I2C_SR1_ADDR)); // wait for ADDR bit to set - temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit + if (timeout) { + DIAG(F("I2C_sendStart: SR2->BUSY timeout")); + // delay(1000); } + s->CR1 |= I2C_CR1_ACK; // Enable the ACK + s->CR1 &= ~(I2C_CR1_POS); // Reset the POS bit - only used for 2-byte reception + s->CR1 |= I2C_CR1_START; // Generate START } /*************************************************************************** * Initiate a stop bit for transmission (does not interrupt) ***************************************************************************/ void I2CManagerClass::I2C_sendStop() { - s->CR1 |= I2C_CR1_STOP; // Stop I2C + uint32_t temp; + + s->CR1 |= I2C_CR1_STOP; // Stop I2C + temp = s->SR1 | s->SR2; // Read the status registers to clear them + while (s->CR1 & I2C_CR1_STOP); // Prevents lockup by guarding further + // writes to CR1 while STOP is being executed! + // Wait for bus to be clear? + unsigned long startTime = micros(); + bool timeout = false; + while (s->SR2 & I2C_SR2_BUSY) { + if (micros() - startTime >= 500UL) { + timeout = true; + break; + } + } + if (timeout) { + DIAG(F("I2C_sendStop: SR2->BUSY timeout")); + // delay(1000); + } } /*************************************************************************** @@ -252,9 +304,11 @@ void I2CManagerClass::I2C_close() { s->CR1 &= ~I2C_CR1_PE; // Disable I2C peripheral // Should never happen, but wait for up to 500us only. unsigned long startTime = micros(); - while ((s->CR1 && I2C_CR1_PE) != 0) { + while ((s->CR1 & I2C_CR1_PE) != 0) { if (micros() - startTime >= 500UL) break; } + NVIC_DisableIRQ(I2C1_EV_IRQn); + NVIC_DisableIRQ(I2C1_ER_IRQn); } /*************************************************************************** @@ -263,50 +317,158 @@ void I2CManagerClass::I2C_close() { * (and therefore, indirectly, from I2CRB::wait() and I2CRB::isBusy()). ***************************************************************************/ void I2CManagerClass::I2C_handleInterrupt() { + volatile uint16_t temp_sr1, temp_sr2, temp; + static bool led_lit = false; - if (s->SR1 && I2C_SR1_ARLO) { + temp_sr1 = s->SR1; + // if (temp_sr1 & I2C_SR1_ADDR) + // temp_sr2 = s->SR2; + + // Check to see if start bit sent - SB interrupt! + if (temp_sr1 & I2C_SR1_SB) + { + // If anything to send, initiate write. Otherwise initiate read. + if (operation == OPERATION_READ || ((operation == OPERATION_REQUEST) && !bytesToSend)) + { + // Send address with read flag (1) or'd in + s->DR = (deviceAddress << 1) | 1; // send the address + // while (!(s->SR1 & I2C_SR1_ADDR)); // wait for ADDR bit to set + // // // Special case for 1 byte reads! + // if (bytesToReceive == 1) + // { + // s->CR1 &= ~I2C_CR1_ACK; // clear the ACK bit + // temp = I2C1->SR1 | I2C1->SR2; // read SR1 and SR2 to clear the ADDR bit.... EV6 condition + // s->CR1 |= I2C_CR1_STOP; // Stop I2C + // } + // else + // temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit + } + else + { + // Send address with write flag (0) or'd in + s->DR = (deviceAddress << 1) | 0; // send the address + // while (!(s->SR1 & I2C_SR1_ADDR)); // wait for ADDR bit to set + // temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit + } + // while (!(s->SR1 & I2C_SR1_ADDR)); // wait for ADDR bit to set + // temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit + } + else if (temp_sr1 & I2C_SR1_ADDR) { + // Receive 1 byte (AN2824 figure 2) + if (bytesToReceive == 1) { + s->CR1 &= ~I2C_CR1_ACK; // Disable ACK final byte + // EV6_1 must be atomic operation (AN2824) + // noInterrupts(); + (void)s->SR2; // read SR2 to complete clearing the ADDR bit + I2C_sendStop(); // send stop + // interrupts(); + } + // Receive 2 bytes (AN2824 figure 2) + else if (bytesToReceive == 2) { + s->CR1 |= I2C_CR1_POS; // Set POS flag (NACK position next) + // EV6_1 must be atomic operation (AN2824) + // noInterrupts(); + (void)s->SR2; // read SR2 to complete clearing the ADDR bit + s->CR1 &= ~I2C_CR1_ACK; // Disable ACK byte + // interrupts(); + } + else + temp = temp_sr1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit + } + else if (temp_sr1 & I2C_SR1_AF) + { + s->SR1 &= ~(I2C_SR1_AF); // Clear AF + s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK + while (s->SR1 & I2C_SR1_AF); // Check AF cleared + I2C_sendStop(); // Clear the bus + completionStatus = I2C_STATUS_NEGATIVE_ACKNOWLEDGE; + state = I2C_STATE_COMPLETED; + } + else if (temp_sr1 & I2C_SR1_ARLO) + { // Arbitration lost, restart - I2C_sendStart(); // Reinitiate request - } else if (s->SR1 && I2C_SR1_BERR) { + s->SR1 &= ~(I2C_SR1_ARLO); // Clear ARLO + s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK + I2C_sendStop(); + I2C_sendStart(); // Reinitiate request + // state = I2C_STATE_COMPLETED; + } + else if (temp_sr1 & I2C_SR1_BERR) + { // Bus error + s->SR1 &= ~(I2C_SR1_BERR); // Clear BERR + s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK + I2C_sendStop(); // Clear the bus completionStatus = I2C_STATUS_BUS_ERROR; state = I2C_STATE_COMPLETED; - } else if (s->SR1 && I2C_SR1_TXE) { + } + else if (temp_sr1 & I2C_SR1_TXE) + { + // temp_sr2 = s->SR2; // Master write completed - if (s->SR1 && (1<<10)) { - // Nacked, send stop. + if (temp_sr1 & I2C_SR1_AF) { + // Nacked + s->SR1 &= ~(I2C_SR1_AF); // Clear AF + s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK + // send stop. I2C_sendStop(); completionStatus = I2C_STATUS_NEGATIVE_ACKNOWLEDGE; state = I2C_STATE_COMPLETED; } else if (bytesToSend) { // Acked, so send next byte + while ((s->SR1 & I2C_SR1_BTF)); // Check BTF before proceeding s->DR = sendBuffer[txCount++]; bytesToSend--; - } else if (bytesToReceive) { - // Last sent byte acked and no more to send. Send repeated start, address and read bit. + // } else if (bytesToReceive) { + // // Last sent byte acked and no more to send. Send repeated start, address and read bit. + // s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK + // I2C_sendStart(); // s->I2CM.ADDR.bit.ADDR = (deviceAddress << 1) | 1; } else { + // No bytes left to send or receive // Check both TxE/BTF == 1 before generating stop - while (!(s->SR1 && I2C_SR1_TXE)); // Check TxE - while (!(s->SR1 && I2C_SR1_BTF)); // Check BTF + // while (!(s->SR1 & I2C_SR1_TXE)); // Check TxE + while ((s->SR1 & I2C_SR1_BTF)); // Check BTF // No more data to send/receive. Initiate a STOP condition and finish + s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK I2C_sendStop(); + // completionStatus = I2C_STATUS_OK; state = I2C_STATE_COMPLETED; } - } else if (s->SR1 && I2C_SR1_RXNE) { + } + else if (temp_sr1 & I2C_SR1_RXNE) + { // Master read completed without errors if (bytesToReceive == 1) { -// s->I2CM.CTRLB.bit.ACKACT = 1; // NAK final byte + s->CR1 &= ~I2C_CR1_ACK; // NAK final byte I2C_sendStop(); // send stop receiveBuffer[rxCount++] = s->DR; // Store received byte bytesToReceive = 0; + // completionStatus = I2C_STATUS_OK; state = I2C_STATE_COMPLETED; - } else if (bytesToReceive) { -// s->I2CM.CTRLB.bit.ACKACT = 0; // ACK all but final byte + } + else if (bytesToReceive == 2) + { + // Also needs to be atomic! + // noInterrupts(); + I2C_sendStop(); receiveBuffer[rxCount++] = s->DR; // Store received byte + // interrupts(); + } + else if (bytesToReceive) + { + s->CR1 &= ~(I2C_CR1_ACK); // ACK all but final byte + receiveBuffer[rxCount++] = s->DR; // Store received byte bytesToReceive--; } } + else + { + // DIAG(F("Unhandled I2C interrupt!")); + led_lit = ~led_lit; + digitalWrite(D13, led_lit); + // delay(1000); + } } #endif /* I2CMANAGER_STM32_H */ From cc2846d932461ace1c546d956812f943f78adaa0 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Mon, 27 Mar 2023 00:20:59 +0100 Subject: [PATCH 045/218] STM32 Native I2C first working version Working for reads and writes, needs more testing and perhaps a polish. --- I2CManager.h | 3 +- I2CManager_STM32.h | 437 +++++++++++++++++++++------------------------ defines.h | 6 +- 3 files changed, 213 insertions(+), 233 deletions(-) diff --git a/I2CManager.h b/I2CManager.h index b1003e6..ede30cc 100644 --- a/I2CManager.h +++ b/I2CManager.h @@ -539,7 +539,8 @@ private: uint8_t deviceAddress; const uint8_t *sendBuffer; uint8_t *receiveBuffer; - + uint8_t transactionState = 0; + volatile uint32_t pendingClockSpeed = 0; void startTransaction(); diff --git a/I2CManager_STM32.h b/I2CManager_STM32.h index 58a623d..5132640 100644 --- a/I2CManager_STM32.h +++ b/I2CManager_STM32.h @@ -49,7 +49,11 @@ extern "C" void I2C1_ER_IRQHandler(void) { // Assume I2C1 for now - default I2C bus on Nucleo-F411RE and likely Nucleo-64 variants I2C_TypeDef *s = I2C1; #define I2C_IRQn I2C1_EV_IRQn -#define I2C_BUSFREQ 16 + +// Peripheral Input Clock speed in MHz. +// For STM32F446RE, the speed is 45MHz. Ideally, this should be determined +// at run-time from the APB1 clock, as it can vary from STM32 family to family. +#define I2C_PERIPH_CLK 45 // I2C SR1 Status Register #1 bit definitions for convenience // #define I2C_SR1_SMBALERT (1<<15) // SMBus alert @@ -83,15 +87,20 @@ I2C_TypeDef *s = I2C1; // #define I2C_CR1_SMBUS (1<<1) // SMBus mode, 1=SMBus, 0=I2C // #define I2C_CR1_PE (1<<0) // I2C Peripheral enable +// States of the STM32 I2C driver state machine +enum {TS_IDLE,TS_START,TS_W_ADDR,TS_W_DATA,TS_W_STOP,TS_R_ADDR,TS_R_DATA,TS_R_STOP}; + + /*************************************************************************** * Set I2C clock speed register. This should only be called outside of * a transmission. The I2CManagerClass::_setClock() function ensures * that it is only called at the beginning of an I2C transaction. ***************************************************************************/ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) { + return; // Calculate a rise time appropriate to the requested bus speed - // Use 10x the rise time spec to enable integer divide of 62.5ns clock period + // Use 10x the rise time spec to enable integer divide of 50ns clock period uint16_t t_rise; uint32_t ccr_freq; @@ -110,44 +119,31 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) { if (i2cClockSpeed > 400000L) i2cClockSpeed = 400000L; - t_rise = 0x06; // (300ns /62.5ns) + 1; + t_rise = 300; // nanoseconds } else { i2cClockSpeed = 100000L; - t_rise = 0x11; // (1000ns /62.5ns) + 1; + t_rise = 1000; // nanoseconds } // Configure the rise time register - s->TRISE = t_rise; - - // DIAG(F("Setting I2C clock to: %d"), i2cClockSpeed); - // Calculate baudrate - ccr_freq = I2C_BUSFREQ * 1000000 / i2cClockSpeed / 2; + s->TRISE = t_rise * I2C_PERIPH_CLK / 1000UL + 1; // Bit 15: I2C Master mode, 0=standard, 1=Fast Mode - // Bit 14: Duty, fast mode duty cycle - // Bit 11-0: FREQR = 16MHz => TPCLK1 = 62.5ns, so CCR divisor must be 0x50 (80 * 62.5ns = 5000ns) - if (i2cClockSpeed > 100000L) + // Bit 14: Duty, fast mode duty cycle (use 2:1) + // Bit 11-0: FREQR = 16MHz => TPCLK1 = 62.5ns + if (i2cClockSpeed > 100000L) { + // In fast mode, I2C period is 3 * CCR * TPCLK1. + ccr_freq = I2C_PERIPH_CLK * 1000000 / 3 / i2cClockSpeed; s->CCR = (uint16_t)ccr_freq | 0x8000; // We need Fast Mode set - else + } else { + // In standard mode, I2C period is 2 * CCR * TPCLK1. + ccr_freq = I2C_PERIPH_CLK * 1000000 / 2 / i2cClockSpeed; s->CCR = (uint16_t)ccr_freq; + } // Enable the I2C master mode s->CR1 |= I2C_CR1_PE; // Enable I2C - // Wait for bus to be clear? - unsigned long startTime = micros(); - bool timeout = false; - while (s->SR2 & I2C_SR2_BUSY) { - if (micros() - startTime >= 500UL) { - timeout = true; - break; - } - } - if (timeout) { - digitalWrite(D13, HIGH); - DIAG(F("I2C: SR2->BUSY timeout")); - // delay(1000); - } } /*************************************************************************** @@ -176,18 +172,19 @@ void I2CManagerClass::I2C_init() GPIOB->AFR[1] &= ~((15<<0) | (15<<4)); // Clear all AFR bits for PB8 on low nibble, PB9 on next nibble up GPIOB->AFR[1] |= (4<<0) | (4<<4); // PB8 on low nibble, PB9 on next nibble up - // // Software reset the I2C peripheral + // Software reset the I2C peripheral s->CR1 |= I2C_CR1_SWRST; // reset the I2C asm("nop"); // wait a bit... suggestion from online! s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation // Clear all bits in I2C CR2 register except reserved bits s->CR2 &= 0xE000; - // Program the peripheral input clock in CR2 Register in order to generate correct timings - s->CR2 |= I2C_BUSFREQ; // PCLK1 FREQUENCY in MHz - // set own address to 00 - not really used in master mode - I2C1->OAR1 |= (1 << 14); // bit 14 should be kept at 1 according to the datasheet + // Set I2C peripheral clock frequency + s->CR2 |= I2C_PERIPH_CLK; + + // set own address to 00 - not used in master mode + I2C1->OAR1 = (1 << 14); // bit 14 should be kept at 1 according to the datasheet #if defined(I2C_USE_INTERRUPTS) // Setting NVIC @@ -205,35 +202,21 @@ void I2CManagerClass::I2C_init() // Bit 8: ITERREN - Error interrupt enable // Bit 7-6: reserved // Bit 5-0: FREQ - Peripheral clock frequency (max 50MHz) - s->CR2 |= 0x0700; // Enable Buffer, Event and Error interrupts - // s->CR2 |= 0x0300; // Enable Event and Error interrupts + s->CR2 |= (I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN | I2C_CR2_ITERREN); // Enable Buffer, Event and Error interrupts #endif // Calculate baudrate and set default rate for now // Configure the Clock Control Register for 100KHz SCL frequency // Bit 15: I2C Master mode, 0=standard, 1=Fast Mode // Bit 14: Duty, fast mode duty cycle - // Bit 11-0: FREQR = 16MHz => TPCLK1 = 62.5ns, so CCR divisor must be 0x50 (80 * 62.5ns = 5000ns) - s->CCR = 0x50; + // Bit 11-0: so CCR divisor would be clk / 2 / 100000 (where clk is in Hz) + s->CCR = I2C_PERIPH_CLK * 5; - // Configure the rise time register - max allowed in 1000ns - s->TRISE = 0x0011; // 1000 ns / 62.5 ns = 16 + 1 + // Configure the rise time register - max allowed is 1000ns, so value = 1000ns * I2C_PERIPH_CLK MHz / 1000 + 1. + s->TRISE = I2C_PERIPH_CLK + 1; // 1000 ns / 50 ns = 20 + 1 = 21 // Enable the I2C master mode s->CR1 |= I2C_CR1_PE; // Enable I2C - // Wait for bus to be clear? - unsigned long startTime = micros(); - bool timeout = false; - while (s->SR2 & I2C_SR2_BUSY) { - if (micros() - startTime >= 500UL) { - timeout = true; - break; - } - } - if (timeout) { - DIAG(F("I2C: SR2->BUSY timeout")); - // delay(1000); - } } /*************************************************************************** @@ -243,56 +226,27 @@ void I2CManagerClass::I2C_sendStart() { // Set counters here in case this is a retry. rxCount = txCount = 0; + // On a single-master I2C bus, the start bit won't be sent until the bus // state goes to IDLE so we can request it without waiting. On a // multi-master bus, the bus may be BUSY under control of another master, // in which case we can avoid some arbitration failures by waiting until // the bus state is IDLE. We don't do that here. - // Send start for read operation - while (s->CR1 & I2C_CR1_STOP); // Prevents lockup by guarding further - // writes to CR1 while STOP is being executed! - // Wait for bus to be clear? - unsigned long startTime = micros(); - bool timeout = false; - while (s->SR2 & I2C_SR2_BUSY) { - if (micros() - startTime >= 500UL) { - timeout = true; - break; - } - } - if (timeout) { - DIAG(F("I2C_sendStart: SR2->BUSY timeout")); - // delay(1000); - } - s->CR1 |= I2C_CR1_ACK; // Enable the ACK - s->CR1 &= ~(I2C_CR1_POS); // Reset the POS bit - only used for 2-byte reception - s->CR1 |= I2C_CR1_START; // Generate START + // Check there's no STOP still in progress. If we OR the START bit into CR1 + // and the STOP bit is already set, we could output multiple STOP conditions. + while (s->CR1 & I2C_CR1_STOP) {} // Wait for STOP bit to reset + + s->CR1 &= ~I2C_CR1_POS; // Clear the POS bit + s->CR1 |= (I2C_CR1_ACK | I2C_CR1_START); // Enable the ACK and generate START + transactionState = TS_START; } /*************************************************************************** * Initiate a stop bit for transmission (does not interrupt) ***************************************************************************/ void I2CManagerClass::I2C_sendStop() { - uint32_t temp; - s->CR1 |= I2C_CR1_STOP; // Stop I2C - temp = s->SR1 | s->SR2; // Read the status registers to clear them - while (s->CR1 & I2C_CR1_STOP); // Prevents lockup by guarding further - // writes to CR1 while STOP is being executed! - // Wait for bus to be clear? - unsigned long startTime = micros(); - bool timeout = false; - while (s->SR2 & I2C_SR2_BUSY) { - if (micros() - startTime >= 500UL) { - timeout = true; - break; - } - } - if (timeout) { - DIAG(F("I2C_sendStop: SR2->BUSY timeout")); - // delay(1000); - } } /*************************************************************************** @@ -317,157 +271,182 @@ void I2CManagerClass::I2C_close() { * (and therefore, indirectly, from I2CRB::wait() and I2CRB::isBusy()). ***************************************************************************/ void I2CManagerClass::I2C_handleInterrupt() { - volatile uint16_t temp_sr1, temp_sr2, temp; - static bool led_lit = false; + volatile uint16_t temp_sr1, temp_sr2; temp_sr1 = s->SR1; - // if (temp_sr1 & I2C_SR1_ADDR) - // temp_sr2 = s->SR2; - // Check to see if start bit sent - SB interrupt! - if (temp_sr1 & I2C_SR1_SB) - { - // If anything to send, initiate write. Otherwise initiate read. - if (operation == OPERATION_READ || ((operation == OPERATION_REQUEST) && !bytesToSend)) + // Check for errors first + if (temp_sr1 & (I2C_SR1_AF | I2C_SR1_ARLO | I2C_SR1_BERR)) { + // Check which error flag is set + if (temp_sr1 & I2C_SR1_AF) { - // Send address with read flag (1) or'd in - s->DR = (deviceAddress << 1) | 1; // send the address - // while (!(s->SR1 & I2C_SR1_ADDR)); // wait for ADDR bit to set - // // // Special case for 1 byte reads! - // if (bytesToReceive == 1) - // { - // s->CR1 &= ~I2C_CR1_ACK; // clear the ACK bit - // temp = I2C1->SR1 | I2C1->SR2; // read SR1 and SR2 to clear the ADDR bit.... EV6 condition - // s->CR1 |= I2C_CR1_STOP; // Stop I2C - // } - // else - // temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit - } - else - { - // Send address with write flag (0) or'd in - s->DR = (deviceAddress << 1) | 0; // send the address - // while (!(s->SR1 & I2C_SR1_ADDR)); // wait for ADDR bit to set - // temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit - } - // while (!(s->SR1 & I2C_SR1_ADDR)); // wait for ADDR bit to set - // temp = s->SR1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit - } - else if (temp_sr1 & I2C_SR1_ADDR) { - // Receive 1 byte (AN2824 figure 2) - if (bytesToReceive == 1) { - s->CR1 &= ~I2C_CR1_ACK; // Disable ACK final byte - // EV6_1 must be atomic operation (AN2824) - // noInterrupts(); - (void)s->SR2; // read SR2 to complete clearing the ADDR bit - I2C_sendStop(); // send stop - // interrupts(); - } - // Receive 2 bytes (AN2824 figure 2) - else if (bytesToReceive == 2) { - s->CR1 |= I2C_CR1_POS; // Set POS flag (NACK position next) - // EV6_1 must be atomic operation (AN2824) - // noInterrupts(); - (void)s->SR2; // read SR2 to complete clearing the ADDR bit - s->CR1 &= ~I2C_CR1_ACK; // Disable ACK byte - // interrupts(); - } - else - temp = temp_sr1 | s->SR2; // read SR1 and SR2 to clear the ADDR bit - } - else if (temp_sr1 & I2C_SR1_AF) - { - s->SR1 &= ~(I2C_SR1_AF); // Clear AF - s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK - while (s->SR1 & I2C_SR1_AF); // Check AF cleared - I2C_sendStop(); // Clear the bus - completionStatus = I2C_STATUS_NEGATIVE_ACKNOWLEDGE; - state = I2C_STATE_COMPLETED; - } - else if (temp_sr1 & I2C_SR1_ARLO) - { - // Arbitration lost, restart - s->SR1 &= ~(I2C_SR1_ARLO); // Clear ARLO - s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK - I2C_sendStop(); - I2C_sendStart(); // Reinitiate request - // state = I2C_STATE_COMPLETED; - } - else if (temp_sr1 & I2C_SR1_BERR) - { - // Bus error - s->SR1 &= ~(I2C_SR1_BERR); // Clear BERR - s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK - I2C_sendStop(); // Clear the bus - completionStatus = I2C_STATUS_BUS_ERROR; - state = I2C_STATE_COMPLETED; - } - else if (temp_sr1 & I2C_SR1_TXE) - { - // temp_sr2 = s->SR2; - // Master write completed - if (temp_sr1 & I2C_SR1_AF) { - // Nacked s->SR1 &= ~(I2C_SR1_AF); // Clear AF - s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK - // send stop. - I2C_sendStop(); + I2C_sendStop(); // Clear the bus + transactionState = TS_IDLE; completionStatus = I2C_STATUS_NEGATIVE_ACKNOWLEDGE; state = I2C_STATE_COMPLETED; - } else if (bytesToSend) { - // Acked, so send next byte - while ((s->SR1 & I2C_SR1_BTF)); // Check BTF before proceeding - s->DR = sendBuffer[txCount++]; - bytesToSend--; - // } else if (bytesToReceive) { - // // Last sent byte acked and no more to send. Send repeated start, address and read bit. - // s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK - // I2C_sendStart(); - // s->I2CM.ADDR.bit.ADDR = (deviceAddress << 1) | 1; - } else { - // No bytes left to send or receive - // Check both TxE/BTF == 1 before generating stop - // while (!(s->SR1 & I2C_SR1_TXE)); // Check TxE - while ((s->SR1 & I2C_SR1_BTF)); // Check BTF - // No more data to send/receive. Initiate a STOP condition and finish - s->CR1 &= ~(I2C_CR1_ACK); // Clear ACK - I2C_sendStop(); - // completionStatus = I2C_STATUS_OK; + } + else if (temp_sr1 & I2C_SR1_ARLO) + { + // Arbitration lost, restart + s->SR1 &= ~(I2C_SR1_ARLO); // Clear ARLO + I2C_sendStart(); // Reinitiate request + transactionState = TS_START; + } + else if (temp_sr1 & I2C_SR1_BERR) + { + // Bus error + s->SR1 &= ~(I2C_SR1_BERR); // Clear BERR + I2C_sendStop(); // Clear the bus + transactionState = TS_IDLE; + completionStatus = I2C_STATUS_BUS_ERROR; state = I2C_STATE_COMPLETED; } - } - else if (temp_sr1 & I2C_SR1_RXNE) - { - // Master read completed without errors - if (bytesToReceive == 1) { - s->CR1 &= ~I2C_CR1_ACK; // NAK final byte - I2C_sendStop(); // send stop - receiveBuffer[rxCount++] = s->DR; // Store received byte - bytesToReceive = 0; - // completionStatus = I2C_STATUS_OK; - state = I2C_STATE_COMPLETED; + } else { + // No error flags, so process event according to current state. + switch (transactionState) { + case TS_START: + if (temp_sr1 & I2C_SR1_SB) { + // Event EV5 + // Start bit has been sent successfully and we have the bus. + // If anything to send, initiate write. Otherwise initiate read. + if (operation == OPERATION_READ || ((operation == OPERATION_REQUEST) && !bytesToSend)) { + // Send address with read flag (1) or'd in + s->DR = (deviceAddress << 1) | 1; // send the address + transactionState = TS_R_ADDR; + } else { + // Send address with write flag (0) or'd in + s->DR = (deviceAddress << 1) | 0; // send the address + transactionState = TS_W_ADDR; + } + } + // SB bit is cleared by writing to DR (already done). + break; + + case TS_W_ADDR: + if (temp_sr1 & I2C_SR1_ADDR) { + // Event EV6 + // Address sent successfully, device has ack'd in response. + if (!bytesToSend) { + I2C_sendStop(); + transactionState = TS_IDLE; + completionStatus = I2C_STATUS_OK; + state = I2C_STATE_COMPLETED; + } else { + transactionState = TS_W_DATA; + } + } + temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit + break; + + case TS_W_DATA: + if (temp_sr1 & I2C_SR1_TXE) { + // Event EV8_1/EV8/EV8_2 + // Transmitter empty, write a byte to it. + if (bytesToSend) { + s->DR = sendBuffer[txCount++]; + bytesToSend--; + } + // See if we're finished sending + if (!bytesToSend) { + // Wait for last byte to be sent. + transactionState = TS_W_STOP; + } + } + break; + + case TS_W_STOP: + if ((temp_sr1 & I2C_SR1_BTF) && (temp_sr1 & I2C_SR1_TXE)) { + // Event EV8_2 + // Write finished. + if (bytesToReceive) { + // Start a read operation by sending (re)start + I2C_sendStart(); + } else { + // Done. + I2C_sendStop(); + transactionState = TS_IDLE; + completionStatus = I2C_STATUS_OK; + state = I2C_STATE_COMPLETED; + } + } + break; + + case TS_R_ADDR: + if (temp_sr1 & I2C_SR1_ADDR) { + // Event EV6 + // Address sent for receive. + // The next bit is different depending on whether there are + // 1 byte, 2 bytes or >2 bytes to be received, in accordance with the + // Programmers Reference RM0390. + if (bytesToReceive == 1) { + // Receive 1 byte + s->CR1 &= ~I2C_CR1_ACK; // Disable ack + temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit + transactionState = TS_R_STOP; + // Next step will occur after a BTF interrupt + } else if (bytesToReceive == 2) { + // Receive 2 bytes + s->CR1 &= ~I2C_CR1_ACK; // Disable ACK for final byte + s->CR1 |= I2C_CR1_POS; // set POS flag to delay effect of ACK flag + temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit + transactionState = TS_R_STOP; + } else { + // >2 bytes, just wait for bytes to come in and ack them for the time being + // (ack flag has already been set). + temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit + transactionState = TS_R_DATA; + } + } + break; + + case TS_R_DATA: + // Event EV7/EV7_1 + if (temp_sr1 & I2C_SR1_BTF) { + // Byte received in receiver - read next byte + if (bytesToReceive == 3) { + // Getting close to the last byte, so a specific sequence is recommended. + s->CR1 &= ~I2C_CR1_ACK; // Reset ack for next byte received. + transactionState = TS_R_STOP; + } + receiveBuffer[rxCount++] = s->DR; // Store received byte + bytesToReceive--; + } + break; + + case TS_R_STOP: + if (temp_sr1 & I2C_SR1_BTF) { + // Event EV7 (last one) + // When we've got here, the receiver has got the last two bytes + // (or one byte, if only one byte is being received), + // and NAK has already been sent, so we need to read from the receiver. + if (bytesToReceive) { + if (bytesToReceive > 1) + I2C_sendStop(); + while(bytesToReceive) { + receiveBuffer[rxCount++] = s->DR; // Store received byte(s) + bytesToReceive--; + } + // Finish. + transactionState = TS_IDLE; + completionStatus = I2C_STATUS_OK; + state = I2C_STATE_COMPLETED; + } + } else if (temp_sr1 & I2C_SR1_RXNE) { + if (bytesToReceive == 1) { + // One byte on a single-byte transfer. Ack has already been set. + I2C_sendStop(); + receiveBuffer[rxCount++] = s->DR; // Store received byte + bytesToReceive--; + // Finish. + transactionState = TS_IDLE; + completionStatus = I2C_STATUS_OK; + state = I2C_STATE_COMPLETED; + } else + s->SR1 &= I2C_SR1_RXNE; // Acknowledge interrupt + } + break; } - else if (bytesToReceive == 2) - { - // Also needs to be atomic! - // noInterrupts(); - I2C_sendStop(); - receiveBuffer[rxCount++] = s->DR; // Store received byte - // interrupts(); - } - else if (bytesToReceive) - { - s->CR1 &= ~(I2C_CR1_ACK); // ACK all but final byte - receiveBuffer[rxCount++] = s->DR; // Store received byte - bytesToReceive--; - } - } - else - { - // DIAG(F("Unhandled I2C interrupt!")); - led_lit = ~led_lit; - digitalWrite(D13, led_lit); - // delay(1000); } } diff --git a/defines.h b/defines.h index 5582e8b..ab9cf45 100644 --- a/defines.h +++ b/defines.h @@ -144,9 +144,9 @@ #define DISABLE_EEPROM #endif // STM32 support for native I2C is awaiting development - #ifndef I2C_USE_WIRE - #define I2C_USE_WIRE - #endif + // #ifndef I2C_USE_WIRE + // #define I2C_USE_WIRE + // #endif /* TODO when ready From 4f56837d28198e2cee8af66ad757f3d3c8040bb7 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Tue, 28 Mar 2023 18:07:52 +0100 Subject: [PATCH 046/218] Fixes to timeout handling (due to STM32 micros() difference). --- I2CManager.cpp | 2 +- I2CManager.h | 4 ++-- I2CManager_NonBlocking.h | 13 ++++++---- I2CManager_STM32.h | 52 ++++++++++++++++++++++++++++++++-------- 4 files changed, 54 insertions(+), 17 deletions(-) diff --git a/I2CManager.cpp b/I2CManager.cpp index d0d8550..1d1387e 100644 --- a/I2CManager.cpp +++ b/I2CManager.cpp @@ -92,7 +92,7 @@ void I2CManagerClass::begin(void) { // Probe and list devices. Use standard mode // (clock speed 100kHz) for best device compatibility. _setClock(100000); - unsigned long originalTimeout = _timeout; + uint32_t originalTimeout = _timeout; setTimeout(1000); // use 1ms timeout for probes #if defined(I2C_EXTENDED_ADDRESS) diff --git a/I2CManager.h b/I2CManager.h index ede30cc..08d81d4 100644 --- a/I2CManager.h +++ b/I2CManager.h @@ -485,7 +485,7 @@ private: // When retries are enabled, the timeout applies to each // try, and failure from timeout does not get retried. // A value of 0 means disable timeout monitoring. - unsigned long _timeout = 100000UL; + uint32_t _timeout = 100000UL; // Finish off request block by waiting for completion and posting status. uint8_t finishRB(I2CRB *rb, uint8_t status); @@ -532,7 +532,7 @@ private: uint8_t bytesToSend = 0; uint8_t bytesToReceive = 0; uint8_t operation = 0; - unsigned long startTime = 0; + uint32_t startTime = 0; uint8_t muxPhase = 0; uint8_t muxAddress = 0; uint8_t muxData[1]; diff --git a/I2CManager_NonBlocking.h b/I2CManager_NonBlocking.h index fb5bae5..59bbcaf 100644 --- a/I2CManager_NonBlocking.h +++ b/I2CManager_NonBlocking.h @@ -172,6 +172,10 @@ void I2CManagerClass::startTransaction() { * Function to queue a request block and initiate operations. ***************************************************************************/ void I2CManagerClass::queueRequest(I2CRB *req) { + + if (((req->operation & OPERATION_MASK) == OPERATION_READ) && req->readLen == 0) + return; // Ignore null read + req->status = I2C_STATUS_PENDING; req->nextRequest = NULL; ATOMIC_BLOCK() { @@ -184,6 +188,7 @@ void I2CManagerClass::queueRequest(I2CRB *req) { } + /*************************************************************************** * Initiate a write to an I2C device (non-blocking operation) ***************************************************************************/ @@ -240,8 +245,8 @@ void I2CManagerClass::checkForTimeout() { I2CRB *t = queueHead; if (state==I2C_STATE_ACTIVE && t!=0 && t==currentRequest && _timeout > 0) { // Check for timeout - unsigned long elapsed = micros() - startTime; - if (elapsed > _timeout) { + int32_t elapsed = micros() - startTime; + if (elapsed > (int32_t)_timeout) { #ifdef DIAG_IO //DIAG(F("I2CManager Timeout on %s"), t->i2cAddress.toString()); #endif @@ -300,12 +305,12 @@ void I2CManagerClass::handleInterrupt() { // Check if current request has completed. If there's a current request // and state isn't active then state contains the completion status of the request. - if (state == I2C_STATE_COMPLETED && currentRequest != NULL) { + if (state == I2C_STATE_COMPLETED && currentRequest != NULL && currentRequest == queueHead) { // Operation has completed. if (completionStatus == I2C_STATUS_OK || ++retryCounter > MAX_I2C_RETRIES || currentRequest->operation & OPERATION_NORETRY) { - // Status is OK, or has failed and retry count exceeded, or retries disabled. + // Status is OK, or has failed and retry count exceeded, or failed and retries disabled. #if defined(I2C_EXTENDED_ADDRESS) if (muxPhase == MuxPhase_PROLOG ) { overallStatus = completionStatus; diff --git a/I2CManager_STM32.h b/I2CManager_STM32.h index 5132640..eac331a 100644 --- a/I2CManager_STM32.h +++ b/I2CManager_STM32.h @@ -232,6 +232,7 @@ void I2CManagerClass::I2C_sendStart() { // multi-master bus, the bus may be BUSY under control of another master, // in which case we can avoid some arbitration failures by waiting until // the bus state is IDLE. We don't do that here. + //while (s->SR2 & I2C_SR2_BUSY) {} // Check there's no STOP still in progress. If we OR the START bit into CR1 // and the STOP bit is already set, we could output multiple STOP conditions. @@ -247,6 +248,7 @@ void I2CManagerClass::I2C_sendStart() { ***************************************************************************/ void I2CManagerClass::I2C_sendStop() { s->CR1 |= I2C_CR1_STOP; // Stop I2C + //while (s->CR1 & I2C_CR1_STOP) {} // Wait for STOP bit to reset } /*************************************************************************** @@ -273,6 +275,9 @@ void I2CManagerClass::I2C_close() { void I2CManagerClass::I2C_handleInterrupt() { volatile uint16_t temp_sr1, temp_sr2; + pinMode(D2, OUTPUT); + digitalWrite(D2, 1); + temp_sr1 = s->SR1; // Check for errors first @@ -302,7 +307,8 @@ void I2CManagerClass::I2C_handleInterrupt() { completionStatus = I2C_STATUS_BUS_ERROR; state = I2C_STATE_COMPLETED; } - } else { + } + else { // No error flags, so process event according to current state. switch (transactionState) { case TS_START: @@ -324,6 +330,7 @@ void I2CManagerClass::I2C_handleInterrupt() { break; case TS_W_ADDR: + temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit if (temp_sr1 & I2C_SR1_ADDR) { // Event EV6 // Address sent successfully, device has ack'd in response. @@ -333,10 +340,25 @@ void I2CManagerClass::I2C_handleInterrupt() { completionStatus = I2C_STATUS_OK; state = I2C_STATE_COMPLETED; } else { - transactionState = TS_W_DATA; + if (bytesToSend <= 2) { + // After this interrupt, we will have no more data to send. + // Next event of interest will be the BTF interrupt, so disable TXE interrupt + s->CR2 &= ~I2C_CR2_ITBUFEN; + transactionState = TS_W_STOP; + } else { + // More data to send, enable TXE interrupt. + s->CR2 |= I2C_CR2_ITBUFEN; + transactionState = TS_W_DATA; + } + // Put one or two bytes into DR to avoid interrupts + s->DR = sendBuffer[txCount++]; + bytesToSend--; + if (bytesToSend) { + s->DR = sendBuffer[txCount++]; + bytesToSend--; + } } } - temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit break; case TS_W_DATA: @@ -344,21 +366,24 @@ void I2CManagerClass::I2C_handleInterrupt() { // Event EV8_1/EV8/EV8_2 // Transmitter empty, write a byte to it. if (bytesToSend) { + if (bytesToSend == 1) { + // We will next need to wait for BTF. + // TXE becomes set one byte before BTF is set, so disable + // TXE interrupt while we're waiting for BTF, to suppress + // repeated interrupts during that period. + s->CR2 &= ~I2C_CR2_ITBUFEN; + transactionState = TS_W_STOP; + } s->DR = sendBuffer[txCount++]; bytesToSend--; } - // See if we're finished sending - if (!bytesToSend) { - // Wait for last byte to be sent. - transactionState = TS_W_STOP; - } } break; case TS_W_STOP: if ((temp_sr1 & I2C_SR1_BTF) && (temp_sr1 & I2C_SR1_TXE)) { // Event EV8_2 - // Write finished. + // All writes finished. if (bytesToReceive) { // Start a read operation by sending (re)start I2C_sendStart(); @@ -383,17 +408,22 @@ void I2CManagerClass::I2C_handleInterrupt() { // Receive 1 byte s->CR1 &= ~I2C_CR1_ACK; // Disable ack temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit + // Next step will occur after a RXNE interrupt, so enable it + s->CR2 |= I2C_CR2_ITBUFEN; transactionState = TS_R_STOP; - // Next step will occur after a BTF interrupt } else if (bytesToReceive == 2) { // Receive 2 bytes s->CR1 &= ~I2C_CR1_ACK; // Disable ACK for final byte s->CR1 |= I2C_CR1_POS; // set POS flag to delay effect of ACK flag + // Next step will occur after a BTF interrupt, so disable RXNE interrupt + s->CR2 &= ~I2C_CR2_ITBUFEN; temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit transactionState = TS_R_STOP; } else { // >2 bytes, just wait for bytes to come in and ack them for the time being // (ack flag has already been set). + // Next step will occur after a BTF interrupt, so disable RXNE interrupt + s->CR2 &= ~I2C_CR2_ITBUFEN; temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit transactionState = TS_R_DATA; } @@ -448,6 +478,8 @@ void I2CManagerClass::I2C_handleInterrupt() { break; } } + delayMicroseconds(1); + digitalWrite(D2, 0); } #endif /* I2CMANAGER_STM32_H */ From 4f43a413b5aeeb709fe14e2cb298077ebbe38081 Mon Sep 17 00:00:00 2001 From: Neil McKechnie Date: Thu, 30 Mar 2023 18:30:38 +0100 Subject: [PATCH 047/218] Update I2CManager_STM32.h Remove debug code (writing to pin D2). Update comments. Restructure. --- I2CManager_STM32.h | 73 +++++++++++++++++++++++++--------------------- 1 file changed, 39 insertions(+), 34 deletions(-) diff --git a/I2CManager_STM32.h b/I2CManager_STM32.h index eac331a..f1d7e08 100644 --- a/I2CManager_STM32.h +++ b/I2CManager_STM32.h @@ -238,6 +238,8 @@ void I2CManagerClass::I2C_sendStart() { // and the STOP bit is already set, we could output multiple STOP conditions. while (s->CR1 & I2C_CR1_STOP) {} // Wait for STOP bit to reset + s->CR2 |= (I2C_CR2_ITEVTEN | I2C_CR2_ITERREN); // Enable interrupts + s->CR2 &= ~I2C_CR2_ITBUFEN; // Don't enable buffer interupts yet. s->CR1 &= ~I2C_CR1_POS; // Clear the POS bit s->CR1 |= (I2C_CR1_ACK | I2C_CR1_START); // Enable the ACK and generate START transactionState = TS_START; @@ -248,7 +250,6 @@ void I2CManagerClass::I2C_sendStart() { ***************************************************************************/ void I2CManagerClass::I2C_sendStop() { s->CR1 |= I2C_CR1_STOP; // Stop I2C - //while (s->CR1 & I2C_CR1_STOP) {} // Wait for STOP bit to reset } /*************************************************************************** @@ -261,7 +262,7 @@ void I2CManagerClass::I2C_close() { // Should never happen, but wait for up to 500us only. unsigned long startTime = micros(); while ((s->CR1 & I2C_CR1_PE) != 0) { - if (micros() - startTime >= 500UL) break; + if ((int32_t)(micros() - startTime) >= 500) break; } NVIC_DisableIRQ(I2C1_EV_IRQn); NVIC_DisableIRQ(I2C1_ER_IRQn); @@ -275,9 +276,6 @@ void I2CManagerClass::I2C_close() { void I2CManagerClass::I2C_handleInterrupt() { volatile uint16_t temp_sr1, temp_sr2; - pinMode(D2, OUTPUT); - digitalWrite(D2, 1); - temp_sr1 = s->SR1; // Check for errors first @@ -330,8 +328,8 @@ void I2CManagerClass::I2C_handleInterrupt() { break; case TS_W_ADDR: - temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit if (temp_sr1 & I2C_SR1_ADDR) { + temp_sr2 = s->SR2; // read SR2 to complete clearing the ADDR bit // Event EV6 // Address sent successfully, device has ack'd in response. if (!bytesToSend) { @@ -340,60 +338,65 @@ void I2CManagerClass::I2C_handleInterrupt() { completionStatus = I2C_STATUS_OK; state = I2C_STATE_COMPLETED; } else { - if (bytesToSend <= 2) { - // After this interrupt, we will have no more data to send. - // Next event of interest will be the BTF interrupt, so disable TXE interrupt - s->CR2 &= ~I2C_CR2_ITBUFEN; - transactionState = TS_W_STOP; - } else { - // More data to send, enable TXE interrupt. - s->CR2 |= I2C_CR2_ITBUFEN; - transactionState = TS_W_DATA; - } - // Put one or two bytes into DR to avoid interrupts + // Put one byte into DR to load shift register. s->DR = sendBuffer[txCount++]; bytesToSend--; if (bytesToSend) { + // Put another byte to load DR s->DR = sendBuffer[txCount++]; bytesToSend--; } + if (!bytesToSend) { + // No more bytes to send. + // The TXE interrupt occurs when the DR is empty, and the BTF interrupt + // occurs when the shift register is also empty (one character later). + // To avoid repeated TXE interrupts during this time, we disable TXE interrupt. + s->CR2 &= ~I2C_CR2_ITBUFEN; // Wait for BTF interrupt, disable TXE interrupt + transactionState = TS_W_STOP; + } else { + // More data remaining to send after this interrupt, enable TXE interrupt. + s->CR2 |= I2C_CR2_ITBUFEN; + transactionState = TS_W_DATA; + } } } break; case TS_W_DATA: if (temp_sr1 & I2C_SR1_TXE) { - // Event EV8_1/EV8/EV8_2 + // Event EV8_1/EV8 // Transmitter empty, write a byte to it. if (bytesToSend) { - if (bytesToSend == 1) { - // We will next need to wait for BTF. - // TXE becomes set one byte before BTF is set, so disable - // TXE interrupt while we're waiting for BTF, to suppress - // repeated interrupts during that period. - s->CR2 &= ~I2C_CR2_ITBUFEN; - transactionState = TS_W_STOP; - } s->DR = sendBuffer[txCount++]; bytesToSend--; + if (!bytesToSend) { + s->CR2 &= ~I2C_CR2_ITBUFEN; // Disable TXE interrupt + transactionState = TS_W_STOP; + } } - } + } break; case TS_W_STOP: - if ((temp_sr1 & I2C_SR1_BTF) && (temp_sr1 & I2C_SR1_TXE)) { + if (temp_sr1 & I2C_SR1_BTF) { // Event EV8_2 - // All writes finished. + // Done, last character sent. Anything to receive? if (bytesToReceive) { - // Start a read operation by sending (re)start - I2C_sendStart(); + I2C_sendStart(); + // NOTE: Three redundant BTF interrupts take place between the + // first BTF interrupt and the START interrupt. I've tried all sorts + // of ways to eliminate them, and the only thing that worked for + // me was to loop until the BTF bit becomes reset. Either way, + // it's a waste of processor time. Anyone got a solution? + //while (s->SR1 && I2C_SR1_BTF) {} + transactionState = TS_START; } else { - // Done. I2C_sendStop(); transactionState = TS_IDLE; completionStatus = I2C_STATUS_OK; state = I2C_STATE_COMPLETED; } + s->SR1 &= I2C_SR1_BTF; // Clear BTF interrupt } break; @@ -477,9 +480,11 @@ void I2CManagerClass::I2C_handleInterrupt() { } break; } + // If we've received an interrupt at any other time, we're not interested so clear it + // to prevent it recurring ad infinitum. + s->SR1 = 0; } - delayMicroseconds(1); - digitalWrite(D2, 0); + } #endif /* I2CMANAGER_STM32_H */ From e51f8e9c0a53e4de08699ac255b6f17cb42f18bd Mon Sep 17 00:00:00 2001 From: pmantoine Date: Tue, 11 Apr 2023 15:48:35 +0800 Subject: [PATCH 048/218] STM32 I2C Clock selection for 100/400KHz --- I2CManager_STM32.h | 75 ++++++++++++++++++++++++++-------------------- 1 file changed, 43 insertions(+), 32 deletions(-) diff --git a/I2CManager_STM32.h b/I2CManager_STM32.h index f1d7e08..cde4f20 100644 --- a/I2CManager_STM32.h +++ b/I2CManager_STM32.h @@ -26,34 +26,42 @@ #include "I2CManager.h" #include "I2CManager_NonBlocking.h" // to satisfy intellisense -//#include -//#include #include +#include "stm32f4xx_hal_rcc.h" -/*************************************************************************** - * Interrupt handler. - * IRQ handler for SERCOM3 which is the default I2C definition for Arduino Zero - * compatible variants such as the Sparkfun SAMD21 Dev Breakout etc. - * Later we may wish to allow use of an alternate I2C bus, or more than one I2C - * bus on the SAMD architecture - ***************************************************************************/ +/***************************************************************************** + * STM32F4xx I2C native driver support + * + * Nucleo-64 and Nucleo-144 boards all use I2C1 as the default I2C peripheral + * Later we may wish to support other STM32 boards, allow use of an alternate + * I2C bus, or more than one I2C bus on the STM32 architecture + *****************************************************************************/ #if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_ARCH_STM32) +#if defined(ARDUINO_NUCLEO_F411RE) || defined(ARDUINO_NUCLEO_F446RE) || defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) +// Assume I2C1 for now - default I2C bus on Nucleo-F411RE and likely all Nucleo-64 +// and Nucleo-144variants +I2C_TypeDef *s = I2C1; + +// In init we will ask the STM32 HAL layer for the configured APB1 clock frequency in Hz +uint32_t APB1clk1; // Peripheral Input Clock speed in Hz. +uint32_t i2c_MHz; // Peripheral Input Clock speed in MHz. + +// IRQ handler for I2C1, replacing the weak definition in the STM32 HAL extern "C" void I2C1_EV_IRQHandler(void) { I2CManager.handleInterrupt(); } extern "C" void I2C1_ER_IRQHandler(void) { I2CManager.handleInterrupt(); } +#else +#warning STM32 board selected is not yet supported - so I2C1 peripheral is not defined +#endif #endif - -// Assume I2C1 for now - default I2C bus on Nucleo-F411RE and likely Nucleo-64 variants -I2C_TypeDef *s = I2C1; -#define I2C_IRQn I2C1_EV_IRQn // Peripheral Input Clock speed in MHz. // For STM32F446RE, the speed is 45MHz. Ideally, this should be determined // at run-time from the APB1 clock, as it can vary from STM32 family to family. -#define I2C_PERIPH_CLK 45 +// #define I2C_PERIPH_CLK 45 // I2C SR1 Status Register #1 bit definitions for convenience // #define I2C_SR1_SMBALERT (1<<15) // SMBus alert @@ -97,8 +105,6 @@ enum {TS_IDLE,TS_START,TS_W_ADDR,TS_W_DATA,TS_W_STOP,TS_R_ADDR,TS_R_DATA,TS_R_ST * that it is only called at the beginning of an I2C transaction. ***************************************************************************/ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) { - return; - // Calculate a rise time appropriate to the requested bus speed // Use 10x the rise time spec to enable integer divide of 50ns clock period uint16_t t_rise; @@ -106,13 +112,9 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) { while (s->CR1 & I2C_CR1_STOP); // Prevents lockup by guarding further // writes to CR1 while STOP is being executed! + // Disable the I2C device, as TRISE can only be programmed whilst disabled s->CR1 &= ~(I2C_CR1_PE); // Disable I2C - // Software reset the I2C peripheral - // s->CR1 |= I2C_CR1_SWRST; // reset the I2C - // delay(1); - // Release reset - // s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation if (i2cClockSpeed > 100000L) { @@ -127,19 +129,20 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) { t_rise = 1000; // nanoseconds } // Configure the rise time register - s->TRISE = t_rise * I2C_PERIPH_CLK / 1000UL + 1; + s->TRISE = (t_rise / (1000 / i2c_MHz)) + 1; // Bit 15: I2C Master mode, 0=standard, 1=Fast Mode // Bit 14: Duty, fast mode duty cycle (use 2:1) - // Bit 11-0: FREQR = 16MHz => TPCLK1 = 62.5ns + // Bit 11-0: FREQR if (i2cClockSpeed > 100000L) { // In fast mode, I2C period is 3 * CCR * TPCLK1. - ccr_freq = I2C_PERIPH_CLK * 1000000 / 3 / i2cClockSpeed; - s->CCR = (uint16_t)ccr_freq | 0x8000; // We need Fast Mode set + //APB1clk1 / 3 / i2cClockSpeed = 38, but that results in 306KHz not 400! + ccr_freq = 30; // So 30 gives 396KHz or so! + s->CCR = (uint16_t)(ccr_freq | 0x8000); // We need Fast Mode set } else { - // In standard mode, I2C period is 2 * CCR * TPCLK1. - ccr_freq = I2C_PERIPH_CLK * 1000000 / 2 / i2cClockSpeed; - s->CCR = (uint16_t)ccr_freq; + // In standard mode, I2C period is 2 * CCR * TPCLK1 + ccr_freq = (APB1clk1 / 2 / i2cClockSpeed); // Should be 225 for 45Mhz APB1 clock + s->CCR |= (uint16_t)ccr_freq; } // Enable the I2C master mode @@ -151,7 +154,10 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) { ***************************************************************************/ void I2CManagerClass::I2C_init() { - // Setting up the clocks + // Query the clockspeed from the STM32 HAL layer + APB1clk1 = HAL_RCC_GetPCLK1Freq(); + i2c_MHz = APB1clk1 / 1000000UL; + // Enable clocks RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;//(1 << 21); // Enable I2C CLOCK // Reset the I2C1 peripheral to initial state RCC->APB1RSTR |= RCC_APB1RSTR_I2C1RST; @@ -181,7 +187,8 @@ void I2CManagerClass::I2C_init() s->CR2 &= 0xE000; // Set I2C peripheral clock frequency - s->CR2 |= I2C_PERIPH_CLK; + // s->CR2 |= I2C_PERIPH_CLK; + s->CR2 |= i2c_MHz; // set own address to 00 - not used in master mode I2C1->OAR1 = (1 << 14); // bit 14 should be kept at 1 according to the datasheet @@ -210,10 +217,14 @@ void I2CManagerClass::I2C_init() // Bit 15: I2C Master mode, 0=standard, 1=Fast Mode // Bit 14: Duty, fast mode duty cycle // Bit 11-0: so CCR divisor would be clk / 2 / 100000 (where clk is in Hz) - s->CCR = I2C_PERIPH_CLK * 5; + // s->CCR = I2C_PERIPH_CLK * 5; + s->CCR &= ~(0x3000); // Clear all bits except 12 and 13 which must remain per reset value + s->CCR |= (APB1clk1 / 2 / 100000UL); // i2c_MHz * 5; + // s->CCR = i2c_MHz * 5; // Configure the rise time register - max allowed is 1000ns, so value = 1000ns * I2C_PERIPH_CLK MHz / 1000 + 1. - s->TRISE = I2C_PERIPH_CLK + 1; // 1000 ns / 50 ns = 20 + 1 = 21 + // s->TRISE = I2C_PERIPH_CLK + 1; // 1000 ns / 50 ns = 20 + 1 = 21 + s->TRISE = i2c_MHz + 1; // Enable the I2C master mode s->CR1 |= I2C_CR1_PE; // Enable I2C From cc3aba1febc310cb5d67afbef27ecd21c84e0611 Mon Sep 17 00:00:00 2001 From: stevet Date: Tue, 25 Apr 2023 16:02:42 -0400 Subject: [PATCH 049/218] Update WiThrottle.cpp Fix: turnout state should be 2/4, not T2/T4 --- WiThrottle.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 60bdf7d..f081270 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -127,7 +127,7 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) { #endif char tchar=Turnout::isClosed(id)?'2':'4'; if (tdesc==NULL) // turnout with no description - StringFormatter::send(stream,F("]\\[%d}|{T%d}|{T%c"), id,id,tchar); + StringFormatter::send(stream,F("]\\[%d}|{T%d}|{%c"), id,id,tchar); else StringFormatter::send(stream,F("]\\[%d}|{%S}|{%c"), id,tdesc,tchar); } From 99521f8a3fd5ac603f926e54243f9ecdf9fa16fc Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 20 May 2023 17:35:09 +0200 Subject: [PATCH 050/218] Support DCC-EX shield --- MotorDrivers.h | 6 ++++++ config.example.h | 1 + version.h | 3 ++- 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/MotorDrivers.h b/MotorDrivers.h index cfb0dd8..3b82858 100644 --- a/MotorDrivers.h +++ b/MotorDrivers.h @@ -50,6 +50,12 @@ new MotorDriver(3, 12, UNUSED_PIN, UNUSED_PIN, A0, 2.99, 2000, UNUSED_PIN), \ new MotorDriver(11, 13, UNUSED_PIN, UNUSED_PIN, A1, 2.99, 2000, UNUSED_PIN) +// DCC-EX TI DRV8874 based motor shield +// This motor shield has reverse sense fault pins thus the -A4 and -A5 pin values. +#define EX8874_SHIELD F("EX8874"), \ + new MotorDriver( 3, 12, UNUSED_PIN, 9, A0, 4.86, 5000, A4), \ + new MotorDriver(11, 13, UNUSED_PIN, 8, A1, 4.86, 5000, A5) + // Pololu Motor Shield #define POLOLU_MOTOR_SHIELD F("POLOLU_MOTOR_SHIELD"), \ new MotorDriver( 9, 7, UNUSED_PIN, -4, A0, 18, 3000, 12), \ diff --git a/config.example.h b/config.example.h index 3dac207..3c9610d 100644 --- a/config.example.h +++ b/config.example.h @@ -41,6 +41,7 @@ The configuration file for DCC-EX Command Station // FIREBOX_MK1 : The Firebox MK1 // FIREBOX_MK1S : The Firebox MK1S // IBT_2_WITH_ARDUINO : Arduino Motor Shield for PROG and IBT-2 for MAIN +// EX8874_SHIELD : DCC-EX TI DRV8874 based motor shield // | // +-----------------------v // diff --git a/version.h b/version.h index d340aa0..23b0a7c 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "4.1.5" +#define VERSION "4.1.6" +// 4.1.6 Support DCC-EX shield // 4.1.5 Bugfix LCN number parsing // 4.1.4 Bugfix for issue #299 TurnoutDescription NULL // 4.1.3 Bugfix: Ethernet init order From 2eb0f4899456e9e292442e4f37251aa18293ac5c Mon Sep 17 00:00:00 2001 From: Fred Date: Thu, 29 Jun 2023 14:24:51 -0400 Subject: [PATCH 051/218] Update .gitignore Updated .gitignore from the devel branch and used "my@.cpp" instead of listing the individual files so we can ignore anything that starts with "my" --- .gitignore | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/.gitignore b/.gitignore index 2bf0b5e..32e70d6 100644 --- a/.gitignore +++ b/.gitignore @@ -7,12 +7,7 @@ Release/* .pio/ .vscode/ config.h -.vscode/* -mySetup.h -mySetup.cpp -myHal.cpp -myAutomation.h -myFilter.cpp -myAutomation.h -myFilter.cpp -myLayout.h +my*.cpp +my*.h +!my*.example.h +compile_commands.json From f2be3aeac325a9ffbacf700c8b444d2693d2bdf5 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 4 Aug 2023 14:45:05 +0200 Subject: [PATCH 052/218] Make work in DC mode --- GITHUB_SHA.h | 2 +- TrackManager.cpp | 2 +- version.h | 3 ++- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 77a75c5..a889ba7 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202308020800Z" +#define GITHUB_SHA "devel-202308041244Z" diff --git a/TrackManager.cpp b/TrackManager.cpp index db5b6b9..0f69235 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -182,7 +182,7 @@ void TrackManager::setPROGSignal( bool on) { // with interrupts turned off around the critical section void TrackManager::setDCSignal(int16_t cab, byte speedbyte) { FOR_EACH_TRACK(t) { - if (trackDCAddr[t]!=cab) continue; + if (trackDCAddr[t]!=cab && cab != 0) continue; if (track[t]->getMode()==TRACK_MODE_DC) track[t]->setDCSignal(speedbyte); else if (track[t]->getMode()==TRACK_MODE_DCX) track[t]->setDCSignal(speedbyte ^ 128); } diff --git a/version.h b/version.h index 465f684..eb2d068 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.68" +#define VERSION "4.2.69" +// 4.2.69 - Bugfix: Make work in DC mode // 4.2.68 - Rename track mode OFF to NONE // 4.2.67 - AVR: Pin specific timer register seting // - Protect Uno user from choosing DC(X) From 3bddf4dfd1cf54c78d299682e790cde2e4f5bad7 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 7 Aug 2023 19:45:45 +0200 Subject: [PATCH 053/218] Make 4.2.69 the 5.0.0 release --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index a889ba7..9d4f5f6 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202308041244Z" +#define GITHUB_SHA "master-202308071744Z" diff --git a/version.h b/version.h index eb2d068..3eafa76 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.69" +#define VERSION "5.0.0" +// 5.0.0 - Make 4.2.69 the 5.0.0 release // 4.2.69 - Bugfix: Make work in DC mode // 4.2.68 - Rename track mode OFF to NONE // 4.2.67 - AVR: Pin specific timer register seting From fd58a749ef2ac71b85539b2e9b18fecaa48c5441 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 7 Aug 2023 19:45:45 +0200 Subject: [PATCH 054/218] Committing a SHA --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index a889ba7..ae005b5 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202308041244Z" +#define GITHUB_SHA "3bddf4d" diff --git a/version.h b/version.h index eb2d068..3eafa76 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "4.2.69" +#define VERSION "5.0.0" +// 5.0.0 - Make 4.2.69 the 5.0.0 release // 4.2.69 - Bugfix: Make work in DC mode // 4.2.68 - Rename track mode OFF to NONE // 4.2.67 - AVR: Pin specific timer register seting From 9f38dae8ba99aea18bb82ecade79dad5c2ffec01 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 11 Aug 2023 00:07:02 +0200 Subject: [PATCH 055/218] Check bad AT firmware version --- GITHUB_SHA.h | 2 +- WifiInterface.cpp | 16 +++++++++++++++- version.h | 4 +++- 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index a889ba7..de4f953 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202308041244Z" +#define GITHUB_SHA "devel-202308102205Z" diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 7511af6..ab36957 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -200,7 +200,21 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password, // Display the AT version information StringFormatter::send(wifiStream, F("AT+GMR\r\n")); - checkForOK(2000, true, false); // Makes this visible on the console + if (checkForOK(2000, F("AT version:"), true, false)) { + char version[] = "0.0.0.0"; + for (int i=0; i<8;i++) { + while(!wifiStream->available()); + version[i]=wifiStream->read(); + StringFormatter::printEscape(version[i]); + if ((version[0] == '0') || + (version[0] == '2' && version[2] == '0') || + (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) { + SSid = F("DCCEX_SAYS_BROKEN_FIRMWARE"); + forceAP = true; + } + } + } + checkForOK(2000, true, false); #ifdef DONT_TOUCH_WIFI_CONF DIAG(F("DONT_TOUCH_WIFI_CONF was set: Using existing config")); diff --git a/version.h b/version.h index eb2d068..076daef 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "4.2.69" +#define VERSION "5.0.1" +// 5.0.1 - Check bad AT firmware version +// 5.0.0 - Make 4.2.69 the 5.0.0 release // 4.2.69 - Bugfix: Make work in DC mode // 4.2.68 - Rename track mode OFF to NONE // 4.2.67 - AVR: Pin specific timer register seting From e327e0ae8d93248fd933b5affcc67f112c2a6fb8 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Sat, 12 Aug 2023 18:40:48 +0100 Subject: [PATCH 056/218] Added ONOVERLOAD Added code changes to create ONOVERLOAD command in EXRAIL --- EXRAIL2.cpp | 15 ++++++++++++++- EXRAIL2.h | 3 ++- EXRAIL2MacroReset.h | 4 +++- EXRAILMacros.h | 3 ++- MotorDriver.cpp | 5 +++++ 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 0e17ea9..0c60c68 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -2,7 +2,7 @@ * © 2021 Neil McKechnie * © 2021-2023 Harald Barth * © 2020-2023 Chris Harlow - * © 2022 Colin Murdoch + * © 2022-2023 Colin Murdoch * All rights reserved. * * This file is part of CommandStation-EX @@ -94,6 +94,7 @@ LookList * RMFT2::onAmberLookup=NULL; LookList * RMFT2::onGreenLookup=NULL; LookList * RMFT2::onChangeLookup=NULL; LookList * RMFT2::onClockLookup=NULL; +LookList * RMFT2::onOverloadLookup=NULL; #define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter) #define SKIPOP progCounter+=3 @@ -175,6 +176,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { onGreenLookup=LookListLoader(OPCODE_ONGREEN); onChangeLookup=LookListLoader(OPCODE_ONCHANGE); onClockLookup=LookListLoader(OPCODE_ONTIME); + onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD); // Second pass startup, define any turnouts or servos, set signals red @@ -986,6 +988,7 @@ void RMFT2::loop2() { case OPCODE_ONGREEN: case OPCODE_ONCHANGE: case OPCODE_ONTIME: + case OPCODE_ONOVERLOAD: break; @@ -1140,6 +1143,16 @@ void RMFT2::clockEvent(int16_t clocktime, bool change) { } } +void RMFT2::powerEvent(char track, bool overload) { + // Hunt for an ONOVERLOAD for this item + if (Diag::CMD) + DIAG(F("Looking for Power event on track : %c"), track); + if (overload) { + handleEvent(F("POWER"),onOverloadLookup,track); + } +} + + void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) { int pc= handlers->find(id); if (pc<0) return; diff --git a/EXRAIL2.h b/EXRAIL2.h index 4d106e6..6b3a9dd 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -1,7 +1,7 @@ /* * © 2021 Neil McKechnie * © 2020-2022 Chris Harlow - * © 2022 Colin Murdoch + * © 2022-2023 Colin Murdoch * © 2023 Harald Barth * All rights reserved. * @@ -188,6 +188,7 @@ private: static LookList * onGreenLookup; static LookList * onChangeLookup; static LookList * onClockLookup; + static LookList * onOverloadLookup; // Local variables - exist for each instance/task RMFT2 *next; // loop chain diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 588a417..5ac3ab7 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -1,6 +1,6 @@ /* * © 2020-2022 Chris Harlow. All rights reserved. - * © 2022 Colin Murdoch + * © 2022-2023 Colin Murdoch * © 2023 Harald Barth * * This file is part of CommandStation-EX @@ -93,6 +93,7 @@ #undef ONTIME #undef ONCLOCKTIME #undef ONCLOCKMINS +#undef ONOVERLOAD #undef ONGREEN #undef ONRED #undef ONTHROW @@ -215,6 +216,7 @@ #define ONTIME(value) #define ONCLOCKTIME(hours,mins) #define ONCLOCKMINS(mins) +#define ONOVERLOAD(track_id) #define ONDEACTIVATE(addr,subaddr) #define ONDEACTIVATEL(linear) #define ONCLOSE(turnout_id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 66b0111..69b5995 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -1,7 +1,7 @@ /* * © 2021 Neil McKechnie * © 2020-2022 Chris Harlow - * © 2022 Colin Murdoch + * © 2022-2023 Colin Murdoch * © 2023 Harald Barth * All rights reserved. * @@ -320,6 +320,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ONTIME(value) OPCODE_ONTIME,V(value), #define ONCLOCKTIME(hours,mins) OPCODE_ONTIME,V((STRIP_ZERO(hours)*60)+STRIP_ZERO(mins)), #define ONCLOCKMINS(mins) ONCLOCKTIME(25,mins) +#define ONOVERLOAD(track_id) OPCODE_ONOVERLOAD,V(TRACK_NUMBER_##track_id), #define ONDEACTIVATE(addr,subaddr) OPCODE_ONDEACTIVATE,V(addr<<2|subaddr), #define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3), #define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id), diff --git a/MotorDriver.cpp b/MotorDriver.cpp index d5dca13..d2b4495 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -4,6 +4,7 @@ * © 2021 Fred Decker * © 2020-2023 Harald Barth * © 2020-2021 Chris Harlow + * © 2023 Colin Murdoch * All rights reserved. * * This file is part of CommandStation-EX @@ -574,6 +575,8 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { DIAG(F("TRACK %c FAULT PIN detected after %4M. Pause %4M)"), trackno + 'A', mslpc, power_sample_overload_wait); throttleInrush(false); setPower(POWERMODE::OVERLOAD); + DIAG(F("Calling EXRAIL")); + RMFT2::powerEvent(trackno, true); // Tell EXRAIL we have an overload break; } if (checkCurrent(useProgLimit)) { @@ -591,6 +594,8 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { trackno + 'A', mA, maxmA, mslpc, power_sample_overload_wait); throttleInrush(false); setPower(POWERMODE::OVERLOAD); + DIAG(F("Calling EXRAIL")); + RMFT2::powerEvent(trackno, true); // Tell EXRAIL we have an overload break; } // all well From 247763ac0005aca3551194587b7e418bd928d8e3 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Sat, 12 Aug 2023 19:10:35 +0100 Subject: [PATCH 057/218] Code Corrections Code corrections --- EXRAIL2.cpp | 2 +- EXRAIL2.h | 2 ++ MotorDriver.cpp | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 0c60c68..ac277a5 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1143,7 +1143,7 @@ void RMFT2::clockEvent(int16_t clocktime, bool change) { } } -void RMFT2::powerEvent(char track, bool overload) { +void RMFT2::powerEvent(int16_t track, bool overload) { // Hunt for an ONOVERLOAD for this item if (Diag::CMD) DIAG(F("Looking for Power event on track : %c"), track); diff --git a/EXRAIL2.h b/EXRAIL2.h index 6b3a9dd..700cd6b 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -62,6 +62,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONCHANGE, OPCODE_ONCLOCKTIME, OPCODE_ONTIME, + OPCODE_ONOVERLOAD, // OPcodes below this point are skip-nesting IF operations // placed here so that they may be skipped as a group @@ -130,6 +131,7 @@ class LookList { static void activateEvent(int16_t addr, bool active); static void changeEvent(int16_t id, bool change); static void clockEvent(int16_t clocktime, bool change); + static void powerEvent(int16_t track, bool overload); static const int16_t SERVO_SIGNAL_FLAG=0x4000; static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000; static const int16_t DCC_SIGNAL_FLAG=0x1000; diff --git a/MotorDriver.cpp b/MotorDriver.cpp index d2b4495..e3ecd64 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -27,6 +27,7 @@ #include "DCCWaveform.h" #include "DCCTimer.h" #include "DIAG.h" +#include "EXRAIL2.h" unsigned long MotorDriver::globalOverloadStart = 0; From 085762e80078e793abb57ae6313ae294631d7f6b Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 18 Aug 2023 18:52:34 +1000 Subject: [PATCH 058/218] Add OPCODE list to DCCEXParser.cpp --- DCCEXParser.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index aaf733c..7610d45 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -25,6 +25,79 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ + +/* +List of single character OPCODEs in use for reference. + +When determining a new OPCODE for a new feature, refer to this list as the source of truth. + +Once a new OPCODE is decided upon, update this list. + + Character, Usage + /, |EX-R| interactive commands + -, Remove from reminder table + =, |TM| configuration + !, Emergency stop + @, Reserved for future use - LCD messages to JMRI + #, Request number of supported cabs/locos; heartbeat + +, WiFi AT commands + ?, Reserved for future use + 0, Track power off + 1, Track power on + a, DCC accessory control + A, + b, Write CV bit on main + B, Write CV bit + c, Request current command + C, + d, + D, Diagnostic commands + e, Erase EEPROM + E, Store configuration in EEPROM + f, Loco decoder function control (deprecated) + F, Loco decoder function control + g, + G, + h, + H, Turnout state broadcast + i, Reserved for future use - Turntable object broadcast + I, Reserved for future use - Turntable object command and control + j, Throttle responses + J, Throttle queries + k, Reserved for future use - Potentially Railcom + K, Reserved for future use - Potentially Railcom + l, Loco speedbyte/function map broadcast + L, + m, + M, Write DCC packet + n, + N, + o, + O, Output broadcast + p, Broadcast power state + P, Write DCC packet + q, Sensor deactivated + Q, Sensor activated + r, Broadcast address read on programming track + R, Read CVs + s, Display status + S, Sensor configuration + t, Cab/loco update command + T, Turnout configuration/control + u, Reserved for user commands + U, Reserved for user commands + v, + V, Verify CVs + w, Write CV on main + W, Write CV + x, + X, Invalid command + y, + Y, Output broadcast + z, + Z, Output configuration/control +*/ + #include "StringFormatter.h" #include "DCCEXParser.h" #include "DCC.h" From e27cceeb74ad293d36b302d5044793cf43ac7c5b Mon Sep 17 00:00:00 2001 From: kempe63 <78020007+kempe63@users.noreply.github.com> Date: Fri, 18 Aug 2023 11:30:37 +0100 Subject: [PATCH 059/218] Update PCA9555.h inconsistencies to IO_MCP23017.h causing IO_PCA9555.h compile error when configure for Mux Updated Class PCA9555 definition reflecting changes in IO_MCP23017.h to support PCA9548 mux. Checked with PCA9555 base board, compiles and run EXRAIL script with output driver --- IO_PCA9555.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IO_PCA9555.h b/IO_PCA9555.h index 137e287..e493165 100644 --- a/IO_PCA9555.h +++ b/IO_PCA9555.h @@ -30,8 +30,8 @@ class PCA9555 : public GPIOBase { public: - static void create(VPIN vpin, int nPins, uint8_t I2CAddress, int interruptPin=-1) { - new PCA9555(vpin, min(nPins,16), I2CAddress, interruptPin); + static void create(VPIN vpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1) { + if (checkNoOverlap(vpin, nPins, i2cAddress)) new PCA9555(vpin,nPins, i2cAddress, interruptPin); } // Constructor From 4b2c0702a49f3b11ab0f8af5e9d6eac5e745db6b Mon Sep 17 00:00:00 2001 From: kempe63 <78020007+kempe63@users.noreply.github.com> Date: Fri, 18 Aug 2023 11:40:34 +0100 Subject: [PATCH 060/218] Update version.h Update version.h for IO_PCA9555 changes --- version.h | 1 + 1 file changed, 1 insertion(+) diff --git a/version.h b/version.h index 076daef..cf1fd26 100644 --- a/version.h +++ b/version.h @@ -5,6 +5,7 @@ #define VERSION "5.0.1" // 5.0.1 - Check bad AT firmware version +// - Update IO_PCA9555.h reflecting IO_MCP23017.h changes to support PCA9548 mux // 5.0.0 - Make 4.2.69 the 5.0.0 release // 4.2.69 - Bugfix: Make work in DC mode // 4.2.68 - Rename track mode OFF to NONE From 1491da48134852b0f8623b8455eb8c356e566e53 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 20 Aug 2023 19:26:04 +1000 Subject: [PATCH 061/218] Starting, very broken --- CommandDistributor.cpp | 4 + CommandDistributor.h | 1 + DCCEXParser.cpp | 29 +++++++ DCCEXParser.h | 13 +-- IODevice.h | 1 + Turntables.cpp | 176 +++++++++++++++++++++++++++++++++++++++++ Turntables.h | 164 ++++++++++++++++++++++++++++++++++++++ 7 files changed, 382 insertions(+), 6 deletions(-) create mode 100644 Turntables.cpp create mode 100644 Turntables.h diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index ab6b52f..eef84a9 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -161,6 +161,10 @@ void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) { #endif } +void CommandDistributor::broadcastTurntable(int16_t id, uint8_t position) { + broadcastReply(COMMAND_TYPE, F("\n"), id, position); +} + void CommandDistributor::broadcastClockTime(int16_t time, int8_t rate) { // The JMRI clock command is of the form : PFT65871<;>4 // The CS broadcast is of the form " + if (parseI(stream, params, p)) + return; + break; + default: //anything else will diagnose and drop out to DIAG(F("Opcode=%c params=%d"), opcode, params); for (int i = 0; i < params; i++) @@ -1013,6 +1019,29 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) return false; } +// ========================== +// Turntable +bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) +{ + switch (params) + { + case 0: // list turntable objects + return Turntable::printAll(stream); + + case 1: // delete turntable + if (!Turntable::remove(p[0])) + return false; + StringFormatter::send(stream, F("\n")); + return true; + + case 2: // - rotate to position for DCC turntables + case 3: // rotate to position for EX-Turntable + { + + } + } +} + // CALLBACKS must be static bool DCCEXParser::stashCallback(Print *stream, int16_t p[MAX_COMMAND_PARAMS], RingStream * ringStream) { diff --git a/DCCEXParser.h b/DCCEXParser.h index bb05ebf..bf067a7 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -45,13 +45,14 @@ struct DCCEXParser static int16_t splitValues( int16_t result[MAX_COMMAND_PARAMS], const byte * command, bool usehex); static bool parseT(Print * stream, int16_t params, int16_t p[]); - static bool parseZ(Print * stream, int16_t params, int16_t p[]); - static bool parseS(Print * stream, int16_t params, int16_t p[]); - static bool parsef(Print * stream, int16_t params, int16_t p[]); - static bool parseD(Print * stream, int16_t params, int16_t p[]); + static bool parseZ(Print * stream, int16_t params, int16_t p[]); + static bool parseS(Print * stream, int16_t params, int16_t p[]); + static bool parsef(Print * stream, int16_t params, int16_t p[]); + static bool parseD(Print * stream, int16_t params, int16_t p[]); + static bool parseI(Print * stream, int16_t params, int16_t p[]); - static Print * getAsyncReplyStream(); - static void commitAsyncReplyStream(); + static Print * getAsyncReplyStream(); + static void commitAsyncReplyStream(); static bool stashBusy; static byte stashTarget; diff --git a/IODevice.h b/IODevice.h index 769e111..8b8f3fe 100644 --- a/IODevice.h +++ b/IODevice.h @@ -542,6 +542,7 @@ protected: #include "IO_PCF8575.h" #include "IO_duinoNodes.h" #include "IO_EXIOExpander.h" +#include "IO_EXTurntable.h" #endif // iodevice_h diff --git a/Turntables.cpp b/Turntables.cpp new file mode 100644 index 0000000..45a1d05 --- /dev/null +++ b/Turntables.cpp @@ -0,0 +1,176 @@ +/* + * © 2023 Peter Cole + * 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 "defines.h" +#include +#include "Turntables.h" +#include "StringFormatter.h" +#include "CommandDistributor.h" +#include "EXRAIL2.h" +#include "DCC.h" + + +/* + * Protected static data + */ +Turntable *Turntable::_firstTurntable = 0; + + +/* + * Public static data + */ +int Turntable::_turntablelistHash = 0; + + +/* + * Protected static functions + */ +// Add new turntable to end of list +void Turntable::add(Turntable *tto) { + if (!_firstTurntable) { + _firstTurntable = tto; + } else { + Turntable *ptr = _firstTurntable; + for ( ; ptr->_nextTurntable!=0; ptr=ptr->_nextTurntable) {} + ptr->_nextTurntable = tto; + } + turntablelistHash++; +} + +// Find turntable from list +Turntable *Turntable::get(uint16_t id) { + for (Turntable *tto = _firstTurntable; tto != NULL; tto = tto->_nextTurntable) + if (tto->_turntableData.id == id) return tto; + return NULL; +} + +// Remove specified turntable from list and delete it +bool Turntable::remove(uint16_t id) { + Turntable *tto, *pp=NULL; + + for (tto=_firstTurntable; tto!=NULL && tto->_turntableData.id!=id; pp=tto, tto=tto->_nextTurntable) {} + if (tto == NULL) return false; + if (tto == _firstTurntable) { + _firstTurntable = tto->_nextTurntable; + } else { + pp->_nextTurntable = tto->_nextTurntable; + } + + delete (EXTTTurntable *)tto; + + turntablelistHash++; + return true; +} + + +/* + * Public static functions + */ +bool Turntable::isPosition(uint16_t id, uint8_t position) { + Turntable *tto = get(id); + if (!tto) return false; + if (tto) { + if (tto->getPosition() == position) { + return true; + } else { + return false; + } + } +} + +bool Turntable::setPositionStateOnly(uint16_t id, uint8_t position) { + Turntable *tto = get(id); + if (!tto) return false; + CommandDistributor::broadcastTurntable(id, position); +#if defined(EXRAIL_ACTIVE) + // RMFT2::turntableEvent(id, position); +#endif +} + +bool Turntable::setPosition(uint16_t id, uint8_t position) { +#if defined(DIAG_IO) + DIAG(F("Turntable(%d, %d)"), id, position); +#endif + Turntable *tto = Turntable::get(id); + if (!tto) return false; + bool ok = tto->setPositionInternal(position); + + if (ok) { + tto->setPositionStateOnly(id, position); + } + return ok; +} + +/************************************************************************************* + * EXTTTurntable - EX-Turntable device. + * + *************************************************************************************/ +// Private constructor +EXTTTurntable::EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin, long **positions) : + Turntable(id, TURNTABLE_EXTT) +{ + _exttTurntableData.i2caddress = i2caddress; + _exttTurntableData.vpin = vpin; + _exttTurntableData.positions = **positions; +} + +// Create function +#ifndef IO_NO_HAL + Turntable *EXTTTurntable::create(uint16_t id, uint8_t i2caddress, VPIN vpin, long **positions) { + Turntable *tto = get(id); + if (tto) { + if (tto->isType(TURNTABLE_EXTT)) { + EXTTTurntable *extt = (EXTTTurntable *)tto; + extt->_exttTurntableData.i2caddress = i2caddress; + extt->_exttTurntableData.vpin = vpin; + extt->_exttTurntableData.positions = positions; + return tto; + } else { + remove(id); + } + } + tto = (Turntable *)new EXTTTurntable(id, i2caddress, vpin, **positions); + DIAG(F("Turntable 0x%x"), tto); + return tto; +#else + (void)id; + (void)i2caddress; + (void)vpin; + (void)positions; + return NULL; +#endif + } + + void EXTTTurntable::print(Print *stream) { + StringFormatter::send(stream, F("\n"), _turntableData.id, _exttTurntableData.i2caddress, _exttTurntableData.vpin); + } + + // EX-Turntable specific code for moving to the specified position + bool EXTTTurntable::setPositionInternal(uint8_t position, uint8_t activity) { +#ifndef IO_NO_HAL + // Get step value from positions + int value = _exttTurntableData.positions[position]; + // Set position via device driver + EXTurntable::_writeAnalogue(vpin, value, activity, 0); +#else + (void)position; +#endif + return true; + } diff --git a/Turntables.h b/Turntables.h new file mode 100644 index 0000000..5a6f346 --- /dev/null +++ b/Turntables.h @@ -0,0 +1,164 @@ +/* + * © 2023 Peter Cole + * 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 TURNTABLES_H +#define TURNTABLES_H + +#include +#include "IODevice.h" +#include "StringFormatter.h" + +// Turntable type definitions +// EXTT = EX-Turntable +// DCC = DCC accessory turntables - to be added later +enum { + TURNTABLE_EXTT = 1, + // TURNTABLE_DCC = 2, +}; + +/************************************************************************************* + * Turntable - Base class for turntables. + * + *************************************************************************************/ + +class Turntable { +protected: + /* + * Object data + */ + + // Data common to all turntable types + struct TurntableData { + union { + struct { + bool hidden : 1; + uint8_t turntableType : 2; + uint8_t position : 5; // Allows up to 38 positions including 0/home + }; + uint8_t flags; + }; + uint16_t id; + } _turntableData; + + // Pointer to next turntable object + Turntable *_nextTurntable = 0; + + /* + * Constructor + */ + Turntable(uint16_t id, uint8_t turntableType) { + _turntableData.id = id; + _turntableData.turntableType = turntableType; + _turntableData.hidden = false; + add(this); + } + + /* + * Static data + */ + static Turntable *_firstTurntable; + static int _turntablelistHash; + + /* + * Virtual functions + */ + virtual bool setPositionInternal(uint8_t position, uint8_t activity) = 0; + + /* + * Static functions + */ + static void add(Turntable *tto); + +public: + static Turntable *get(uint16_t id); + + /* + * Static data + */ + static int turntablelistHash; + + /* + * Public base class functions + */ + inline uint8_t getPosition() { return _turntableData.position; } + inline bool isHidden() { return _turntableData.hidden; } + inline void setHidden(bool h) {_turntableData.hidden=h; } + inline bool isType(uint8_t type) { return _turntableData.turntableType == type; } + inline uint16_t getId() { return _turntableData.id; } + inline Turntable *next() { return _nextTurntable; } + void printState(Print *stream); + + /* + * Virtual functions + */ + virtual void print(Print *stream) { + (void)stream; // suppress compiler warnings + } + virtual ~Turntable() {} // Destructor + + /* + * Public static functions + */ + inline static bool exists(uint16_t id) { return get(id) != 0; } + static bool remove(uint16_t id); + static bool isPosition(uint16_t id, uint8_t position); + static bool setPosition(uint16_t id, uint8_t position); + static bool setPositionStateOnly(uint16_t id, uint8_t position); + inline static Turntable *first() { return _firstTurntable; } + static bool printAll(Print *stream) { + bool gotOne = false; + for (Turntable *tto = _firstTurntable; tto != 0; tto = tto->_nextTurntable) + if (!tto->isHidden()) { + gotOne = true; + StringFormatter::send(stream, F("\n"), tto->getId(), tto->getPosition()); + } + return gotOne; + } + +}; + +/************************************************************************************* + * EXTTTurntable - EX-Turntable device. + * + *************************************************************************************/ +class EXTTTurntable : public Turntable { +private: + // EXTTTurntableData contains device specific data + struct EXTTTurntableData { + uint8_t i2caddress; + VPIN vpin; + long **positions; // Array of longs to store step positions + } _exttTurntableData; + + // Constructor + EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin, long **positions); + +public: + // Create function + static Turntable *create(uint16_t id, uint8_t i2caddress, VPIN vpin, long **positions); + void print(Print *stream) override; + +protected: + // EX-Turntable specific code for setting position + bool setPositionInternal(uint8_t position, uint8_t activity) override; + +}; + +#endif From 98f8022268ad536cf6e2183e472a56ba7a6bcf04 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Mon, 21 Aug 2023 06:43:06 +1000 Subject: [PATCH 062/218] Fix device driver, disable objects, start parser --- DCCEXParser.cpp | 33 +++++++++++++++++++++----- IODevice.h | 1 - IO_EXTurntable.h => IO_EXTurntable.cpp | 4 ++-- Turntables.cpp => Turntables.cpp.txt | 0 Turntables.h => Turntables.h.txt | 0 5 files changed, 29 insertions(+), 9 deletions(-) rename IO_EXTurntable.h => IO_EXTurntable.cpp (97%) rename Turntables.cpp => Turntables.cpp.txt (100%) rename Turntables.h => Turntables.h.txt (100%) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 4a32902..cfe2563 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -41,7 +41,7 @@ #include "TrackManager.h" #include "DCCTimer.h" #include "EXRAIL2.h" -#include "Turntables.h" +// #include "Turntables.h" // This macro can't be created easily as a portable function because the // flashlist requires a far pointer for high flash access. @@ -96,6 +96,7 @@ const int16_t HASH_KEYWORD_ANOUT = -26399; const int16_t HASH_KEYWORD_WIFI = -5583; const int16_t HASH_KEYWORD_ETHERNET = -30767; const int16_t HASH_KEYWORD_WIT = 31594; +const int16_t HASH_KEYWORD_EXTT = 8573; int16_t DCCEXParser::stashP[MAX_COMMAND_PARAMS]; bool DCCEXParser::stashBusy; @@ -1021,25 +1022,45 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) // ========================== // Turntable +// - list all +// - delete +// - operate (DCC) +// - operate (EXTT) +// - create EXTT +// - create DCC? - This TBA bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) { switch (params) { case 0: // list turntable objects - return Turntable::printAll(stream); + StringFormatter::send(stream, F("\n")); + return true; + // return Turntable::printAll(stream); case 1: // delete turntable - if (!Turntable::remove(p[0])) - return false; - StringFormatter::send(stream, F("\n")); + // if (!Turntable::remove(p[0])) + // return false; + StringFormatter::send(stream, F("\n")); return true; case 2: // - rotate to position for DCC turntables case 3: // rotate to position for EX-Turntable { - + DIAG(F("Got %d parameters (2 or 3)"), params); + return true; } + + default: + { + if (params < 40) { + DIAG(F("Create turntable with %d parameters"), params); + return true; + } + } + break; + } + return false; } // CALLBACKS must be static diff --git a/IODevice.h b/IODevice.h index 8b8f3fe..769e111 100644 --- a/IODevice.h +++ b/IODevice.h @@ -542,7 +542,6 @@ protected: #include "IO_PCF8575.h" #include "IO_duinoNodes.h" #include "IO_EXIOExpander.h" -#include "IO_EXTurntable.h" #endif // iodevice_h diff --git a/IO_EXTurntable.h b/IO_EXTurntable.cpp similarity index 97% rename from IO_EXTurntable.h rename to IO_EXTurntable.cpp index 29ce679..f1a108b 100644 --- a/IO_EXTurntable.h +++ b/IO_EXTurntable.cpp @@ -20,9 +20,9 @@ /* * The IO_EXTurntable device driver is used to control a turntable via an Arduino with a stepper motor over I2C. * -* The EX-Turntable code lives in a separate repo (https://github.com/DCC-EX/Turntable-EX) and contains the stepper motor logic. +* The EX-Turntable code lives in a separate repo (https://github.com/DCC-EX/EX-Turntable) and contains the stepper motor logic. * -* This device driver sends a step position to Turntable-EX to indicate the step position to move to using either of these commands: +* This device driver sends a step position to EX-Turntable to indicate the step position to move to using either of these commands: * in the serial console * MOVETT(vpin, steps, activity) in EX-RAIL * Refer to the documentation for further information including the valid activities. diff --git a/Turntables.cpp b/Turntables.cpp.txt similarity index 100% rename from Turntables.cpp rename to Turntables.cpp.txt diff --git a/Turntables.h b/Turntables.h.txt similarity index 100% rename from Turntables.h rename to Turntables.h.txt From c4febd1d0f15d2dec5fbba1784c84d9b3f58327c Mon Sep 17 00:00:00 2001 From: peteGSX Date: Mon, 21 Aug 2023 19:33:45 +1000 Subject: [PATCH 063/218] More parser --- DCCEXParser.cpp | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index cfe2563..55a6cf7 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1044,18 +1044,23 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) return true; case 2: // - rotate to position for DCC turntables + DIAG(F("Rotate DCC turntable %d to position %d"), p[0], p[1]); + return true; + case 3: // rotate to position for EX-Turntable - { - DIAG(F("Got %d parameters (2 or 3)"), params); - return true; - } + DIAG(F("Rotate EXTT turntable %d to angle %d with activity %d"), p[0], p[1], p[2]); + return true; - default: + default: // If we're here, it must be creating a turntable object { - if (params < 40) { - DIAG(F("Create turntable with %d parameters"), params); + if (params < 41 && p[1] == HASH_KEYWORD_EXTT) { + DIAG(F("Create EXTT turntable %d on vpin %d and address %s with %d positions"), p[0], p[2], p[3], params - 4); + return true; + } else if (params < 39 && p[1] == HASH_KEYWORD_DCC) { + DIAG(F("Create DCC turntable %d at base address %d with %d positions"), p[0], p[2], params - 2); return true; } + } break; From b277d204f00b171b6d32407d3910da422a2334e8 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 22 Aug 2023 19:30:22 +1000 Subject: [PATCH 064/218] Progress! --- DCCEXParser.cpp | 28 ++++++++++++---------------- Turntables.cpp.txt => Turntables.cpp | 26 +++++++++++++------------- Turntables.h.txt => Turntables.h | 8 +++++--- 3 files changed, 30 insertions(+), 32 deletions(-) rename Turntables.cpp.txt => Turntables.cpp (88%) rename Turntables.h.txt => Turntables.h (96%) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 55a6cf7..4823e8e 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -41,7 +41,7 @@ #include "TrackManager.h" #include "DCCTimer.h" #include "EXRAIL2.h" -// #include "Turntables.h" +#include "Turntables.h" // This macro can't be created easily as a portable function because the // flashlist requires a far pointer for high flash access. @@ -1033,14 +1033,12 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) switch (params) { case 0: // list turntable objects - StringFormatter::send(stream, F("\n")); - return true; - // return Turntable::printAll(stream); + return Turntable::printAll(stream); case 1: // delete turntable - // if (!Turntable::remove(p[0])) - // return false; - StringFormatter::send(stream, F("\n")); + if (!Turntable::remove(p[0])) + return false; + StringFormatter::send(stream, F("\n")); return true; case 2: // - rotate to position for DCC turntables @@ -1053,19 +1051,17 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) default: // If we're here, it must be creating a turntable object { - if (params < 41 && p[1] == HASH_KEYWORD_EXTT) { - DIAG(F("Create EXTT turntable %d on vpin %d and address %s with %d positions"), p[0], p[2], p[3], params - 4); - return true; - } else if (params < 39 && p[1] == HASH_KEYWORD_DCC) { + if (params > 5 && params < 41 && p[1] == HASH_KEYWORD_EXTT) { + DIAG(F("Create EXTT turntable %d on vpin %d and address %d with %d positions"), p[0], p[2], p[3], params - 4); + if (!EXTTTurntable::create(p[0], (uint8_t)p[1], (VPIN)p[2])) return false; + } else if (params > 3 && params < 39 && p[1] == HASH_KEYWORD_DCC) { DIAG(F("Create DCC turntable %d at base address %d with %d positions"), p[0], p[2], params - 2); - return true; + } else { + return false; } - } - break; - + return true; } - return false; } // CALLBACKS must be static diff --git a/Turntables.cpp.txt b/Turntables.cpp similarity index 88% rename from Turntables.cpp.txt rename to Turntables.cpp index 45a1d05..67dc8c0 100644 --- a/Turntables.cpp.txt +++ b/Turntables.cpp @@ -36,13 +36,14 @@ Turntable *Turntable::_firstTurntable = 0; /* * Public static data */ -int Turntable::_turntablelistHash = 0; +int Turntable::turntablelistHash = 0; /* * Protected static functions */ // Add new turntable to end of list + void Turntable::add(Turntable *tto) { if (!_firstTurntable) { _firstTurntable = tto; @@ -73,25 +74,25 @@ bool Turntable::remove(uint16_t id) { pp->_nextTurntable = tto->_nextTurntable; } - delete (EXTTTurntable *)tto; + // delete (EXTTTurntable *)tto; turntablelistHash++; return true; } - /* * Public static functions */ bool Turntable::isPosition(uint16_t id, uint8_t position) { Turntable *tto = get(id); - if (!tto) return false; if (tto) { if (tto->getPosition() == position) { return true; } else { return false; } + } else { + return false; } } @@ -102,15 +103,16 @@ bool Turntable::setPositionStateOnly(uint16_t id, uint8_t position) { #if defined(EXRAIL_ACTIVE) // RMFT2::turntableEvent(id, position); #endif + return true; } -bool Turntable::setPosition(uint16_t id, uint8_t position) { +bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { #if defined(DIAG_IO) DIAG(F("Turntable(%d, %d)"), id, position); #endif Turntable *tto = Turntable::get(id); if (!tto) return false; - bool ok = tto->setPositionInternal(position); + bool ok = tto->setPositionInternal(position, activity); if (ok) { tto->setPositionStateOnly(id, position); @@ -123,30 +125,28 @@ bool Turntable::setPosition(uint16_t id, uint8_t position) { * *************************************************************************************/ // Private constructor -EXTTTurntable::EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin, long **positions) : +EXTTTurntable::EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin) : Turntable(id, TURNTABLE_EXTT) { _exttTurntableData.i2caddress = i2caddress; _exttTurntableData.vpin = vpin; - _exttTurntableData.positions = **positions; } // Create function #ifndef IO_NO_HAL - Turntable *EXTTTurntable::create(uint16_t id, uint8_t i2caddress, VPIN vpin, long **positions) { + Turntable *EXTTTurntable::create(uint16_t id, uint8_t i2caddress, VPIN vpin) { Turntable *tto = get(id); if (tto) { if (tto->isType(TURNTABLE_EXTT)) { EXTTTurntable *extt = (EXTTTurntable *)tto; extt->_exttTurntableData.i2caddress = i2caddress; extt->_exttTurntableData.vpin = vpin; - extt->_exttTurntableData.positions = positions; return tto; } else { remove(id); } } - tto = (Turntable *)new EXTTTurntable(id, i2caddress, vpin, **positions); + tto = (Turntable *)new EXTTTurntable(id, i2caddress, vpin); DIAG(F("Turntable 0x%x"), tto); return tto; #else @@ -166,9 +166,9 @@ EXTTTurntable::EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin, long ** bool EXTTTurntable::setPositionInternal(uint8_t position, uint8_t activity) { #ifndef IO_NO_HAL // Get step value from positions - int value = _exttTurntableData.positions[position]; + // int value = _exttTurntableData.positions[position]; // Set position via device driver - EXTurntable::_writeAnalogue(vpin, value, activity, 0); + // EXTurntable::_writeAnalogue(vpin, value, activity, 0); #else (void)position; #endif diff --git a/Turntables.h.txt b/Turntables.h similarity index 96% rename from Turntables.h.txt rename to Turntables.h index 5a6f346..2fa4255 100644 --- a/Turntables.h.txt +++ b/Turntables.h @@ -104,6 +104,7 @@ public: inline uint16_t getId() { return _turntableData.id; } inline Turntable *next() { return _nextTurntable; } void printState(Print *stream); + /* * Virtual functions @@ -112,6 +113,7 @@ public: (void)stream; // suppress compiler warnings } virtual ~Turntable() {} // Destructor + /* * Public static functions @@ -119,7 +121,7 @@ public: inline static bool exists(uint16_t id) { return get(id) != 0; } static bool remove(uint16_t id); static bool isPosition(uint16_t id, uint8_t position); - static bool setPosition(uint16_t id, uint8_t position); + static bool setPosition(uint16_t id, uint8_t position, uint8_t activity=0); static bool setPositionStateOnly(uint16_t id, uint8_t position); inline static Turntable *first() { return _firstTurntable; } static bool printAll(Print *stream) { @@ -148,11 +150,11 @@ private: } _exttTurntableData; // Constructor - EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin, long **positions); + EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin); public: // Create function - static Turntable *create(uint16_t id, uint8_t i2caddress, VPIN vpin, long **positions); + static Turntable *create(uint16_t id, uint8_t i2caddress, VPIN vpin); void print(Print *stream) override; protected: From ff9c558b61b9213f54d68e3db2e667cf040e91e4 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Wed, 23 Aug 2023 19:08:04 +1000 Subject: [PATCH 065/218] Not much progress --- DCCEXParser.cpp | 6 +++--- Turntables.cpp | 2 +- Turntables.h | 7 ++++++- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 4823e8e..e6bd814 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1035,17 +1035,17 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) case 0: // list turntable objects return Turntable::printAll(stream); - case 1: // delete turntable + case 1: // delete turntable if (!Turntable::remove(p[0])) return false; StringFormatter::send(stream, F("\n")); return true; - case 2: // - rotate to position for DCC turntables + case 2: // - rotate to position for DCC turntables DIAG(F("Rotate DCC turntable %d to position %d"), p[0], p[1]); return true; - case 3: // rotate to position for EX-Turntable + case 3: // rotate to position for EX-Turntable DIAG(F("Rotate EXTT turntable %d to angle %d with activity %d"), p[0], p[1], p[2]); return true; diff --git a/Turntables.cpp b/Turntables.cpp index 67dc8c0..5aaf9f0 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -74,7 +74,7 @@ bool Turntable::remove(uint16_t id) { pp->_nextTurntable = tto->_nextTurntable; } - // delete (EXTTTurntable *)tto; + delete (EXTTTurntable *)tto; turntablelistHash++; return true; diff --git a/Turntables.h b/Turntables.h index 2fa4255..c7bb394 100644 --- a/Turntables.h +++ b/Turntables.h @@ -57,6 +57,12 @@ protected: uint16_t id; } _turntableData; + // Linked list to store either positions (EXTT) or DCC addresses (DCC) + struct Position { + int16_t position; + Position *next; + }; + // Pointer to next turntable object Turntable *_nextTurntable = 0; @@ -146,7 +152,6 @@ private: struct EXTTTurntableData { uint8_t i2caddress; VPIN vpin; - long **positions; // Array of longs to store step positions } _exttTurntableData; // Constructor From 57d4655d54be41357d7a87469c45847044b8f51b Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 24 Aug 2023 07:22:37 +1000 Subject: [PATCH 066/218] Fix Uno/Nano build errors --- Turntables.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Turntables.cpp b/Turntables.cpp index 5aaf9f0..2f2a05e 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -133,8 +133,8 @@ EXTTTurntable::EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin) : } // Create function -#ifndef IO_NO_HAL Turntable *EXTTTurntable::create(uint16_t id, uint8_t i2caddress, VPIN vpin) { +#ifndef IO_NO_HAL Turntable *tto = get(id); if (tto) { if (tto->isType(TURNTABLE_EXTT)) { @@ -153,7 +153,6 @@ EXTTTurntable::EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin) : (void)id; (void)i2caddress; (void)vpin; - (void)positions; return NULL; #endif } From 6cd7002e911f3bd09421e4570a7fc6129bc8a193 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 24 Aug 2023 10:03:29 +0200 Subject: [PATCH 067/218] Bugfix: execute 30ms off time before rejoin --- DCCACK.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCACK.cpp b/DCCACK.cpp index 1b339ee..8a074b4 100644 --- a/DCCACK.cpp +++ b/DCCACK.cpp @@ -351,7 +351,7 @@ void DCCACK::callback(int value) { switch (callbackState) { case AFTER_READ: - if (ackManagerRejoin && autoPowerOff) { + if (ackManagerRejoin && !autoPowerOff) { progDriver->setPower(POWERMODE::OFF); callbackStart=millis(); callbackState=WAITING_30; From 42f3c7c12836a545bc6ca7e6e3577e9d9fa31b2f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 24 Aug 2023 10:05:31 +0200 Subject: [PATCH 068/218] version number update --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 3eafa76..a6e05ac 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.0.0" +#define VERSION "5.0.1" +// 5.0.1 - Bugfix: execute 30ms off time before rejoin // 5.0.0 - Make 4.2.69 the 5.0.0 release // 4.2.69 - Bugfix: Make work in DC mode // 4.2.68 - Rename track mode OFF to NONE From fa0aa27d466264378b680675b54fd1d86d96f8a5 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 18 Aug 2023 18:52:34 +1000 Subject: [PATCH 069/218] Add OPCODE list to DCCEXParser.cpp --- DCCEXParser.cpp | 73 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 73 insertions(+) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index aaf733c..7610d45 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -25,6 +25,79 @@ * You should have received a copy of the GNU General Public License * along with CommandStation. If not, see . */ + +/* +List of single character OPCODEs in use for reference. + +When determining a new OPCODE for a new feature, refer to this list as the source of truth. + +Once a new OPCODE is decided upon, update this list. + + Character, Usage + /, |EX-R| interactive commands + -, Remove from reminder table + =, |TM| configuration + !, Emergency stop + @, Reserved for future use - LCD messages to JMRI + #, Request number of supported cabs/locos; heartbeat + +, WiFi AT commands + ?, Reserved for future use + 0, Track power off + 1, Track power on + a, DCC accessory control + A, + b, Write CV bit on main + B, Write CV bit + c, Request current command + C, + d, + D, Diagnostic commands + e, Erase EEPROM + E, Store configuration in EEPROM + f, Loco decoder function control (deprecated) + F, Loco decoder function control + g, + G, + h, + H, Turnout state broadcast + i, Reserved for future use - Turntable object broadcast + I, Reserved for future use - Turntable object command and control + j, Throttle responses + J, Throttle queries + k, Reserved for future use - Potentially Railcom + K, Reserved for future use - Potentially Railcom + l, Loco speedbyte/function map broadcast + L, + m, + M, Write DCC packet + n, + N, + o, + O, Output broadcast + p, Broadcast power state + P, Write DCC packet + q, Sensor deactivated + Q, Sensor activated + r, Broadcast address read on programming track + R, Read CVs + s, Display status + S, Sensor configuration + t, Cab/loco update command + T, Turnout configuration/control + u, Reserved for user commands + U, Reserved for user commands + v, + V, Verify CVs + w, Write CV on main + W, Write CV + x, + X, Invalid command + y, + Y, Output broadcast + z, + Z, Output configuration/control +*/ + #include "StringFormatter.h" #include "DCCEXParser.h" #include "DCC.h" From 9842ea8a429ba877579f615a181dfeabacaa7446 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 24 Aug 2023 10:03:29 +0200 Subject: [PATCH 070/218] Bugfix: execute 30ms off time before rejoin --- DCCACK.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCACK.cpp b/DCCACK.cpp index 1b339ee..8a074b4 100644 --- a/DCCACK.cpp +++ b/DCCACK.cpp @@ -351,7 +351,7 @@ void DCCACK::callback(int value) { switch (callbackState) { case AFTER_READ: - if (ackManagerRejoin && autoPowerOff) { + if (ackManagerRejoin && !autoPowerOff) { progDriver->setPower(POWERMODE::OFF); callbackStart=millis(); callbackState=WAITING_30; From 25f8852af6d72fd506c0b7d2f87a43a2ac9cb9fc Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 24 Aug 2023 10:09:38 +0200 Subject: [PATCH 071/218] call devel for 5.1.X version number update --- GITHUB_SHA.h | 2 +- version.h | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index de4f953..9de7154 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202308102205Z" +#define GITHUB_SHA "devel-202308240808Z" diff --git a/version.h b/version.h index cf1fd26..bd45999 100644 --- a/version.h +++ b/version.h @@ -3,9 +3,10 @@ #include "StringFormatter.h" -#define VERSION "5.0.1" -// 5.0.1 - Check bad AT firmware version +#define VERSION "5.1.1" +// 5.1.1 - Check bad AT firmware version // - Update IO_PCA9555.h reflecting IO_MCP23017.h changes to support PCA9548 mux +// 5.0.1 - Bugfix: execute 30ms off time before rejoin // 5.0.0 - Make 4.2.69 the 5.0.0 release // 4.2.69 - Bugfix: Make work in DC mode // 4.2.68 - Rename track mode OFF to NONE From 6392c74eadac6b8ed4a26e79a1e59f3d9b53c4b6 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Thu, 24 Aug 2023 14:53:01 +0100 Subject: [PATCH 072/218] Update myHal.cpp_example.txt Added missing ::create to LiquidCrystal --- myHal.cpp_example.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index d93ea5c..5e9fec4 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -51,7 +51,7 @@ void halSetup() { // Create a 20x4 LCD display device as display number 2 // (line 0 is written by EX-RAIL 'SCREEN(2, 0, "text")'). - // HALDisplay(2, 0x27, 20, 4); + // HALDisplay::create(2, 0x27, 20, 4); //======================================================================= From fb226311e5ed62cfae2b1dce42fd1a41293b9004 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Thu, 24 Aug 2023 14:54:33 +0100 Subject: [PATCH 073/218] Update myHal.cpp_example.txt Added missing ::create to LiquidCrystal HAL definition --- myHal.cpp_example.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index d93ea5c..5e9fec4 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -51,7 +51,7 @@ void halSetup() { // Create a 20x4 LCD display device as display number 2 // (line 0 is written by EX-RAIL 'SCREEN(2, 0, "text")'). - // HALDisplay(2, 0x27, 20, 4); + // HALDisplay::create(2, 0x27, 20, 4); //======================================================================= From 210d96a3e37c75ef50f59ab0f0bab1abd7746124 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 25 Aug 2023 19:07:57 +0200 Subject: [PATCH 074/218] Bugfix: ESP32 30ms off time --- TrackManager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index 0f69235..91c78ea 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -53,7 +53,7 @@ bool TrackManager::progTrackSyncMain=false; bool TrackManager::progTrackBoosted=false; int16_t TrackManager::joinRelay=UNUSED_PIN; #ifdef ARDUINO_ARCH_ESP32 -byte TrackManager::tempProgTrack=MAX_TRACKS+1; +byte TrackManager::tempProgTrack=MAX_TRACKS+1; // MAX_TRACKS+1 is the unused flag #endif #ifdef ANALOG_READ_INTERRUPT @@ -505,7 +505,12 @@ void TrackManager::setJoin(bool joined) { } } else { if (tempProgTrack != MAX_TRACKS+1) { + // as setTrackMode with TRACK_MODE_PROG defaults to + // power off, we will take the current power state + // of our track and then preserve that state. + POWERMODE tPTmode = track[tempProgTrack]->getPower(); //get current power status of this track setTrackMode(tempProgTrack, TRACK_MODE_PROG); + track[tempProgTrack]->setPower(tPTmode); //set track status as it was before tempProgTrack = MAX_TRACKS+1; } } From c55fa9f9d29f29968db96e45c21fef6e6c9c04fc Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 25 Aug 2023 19:08:58 +0200 Subject: [PATCH 075/218] version number update --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index a6e05ac..d52902a 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.0.1" +#define VERSION "5.0.2" +// 5.0.2 - Bugfix: ESP32 30ms off time // 5.0.1 - Bugfix: execute 30ms off time before rejoin // 5.0.0 - Make 4.2.69 the 5.0.0 release // 4.2.69 - Bugfix: Make work in DC mode From 3453da067130c9deee9cbbd12a823f157062f796 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 25 Aug 2023 19:07:57 +0200 Subject: [PATCH 076/218] Bugfix: ESP32 30ms off time --- TrackManager.cpp | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index 0f69235..91c78ea 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -53,7 +53,7 @@ bool TrackManager::progTrackSyncMain=false; bool TrackManager::progTrackBoosted=false; int16_t TrackManager::joinRelay=UNUSED_PIN; #ifdef ARDUINO_ARCH_ESP32 -byte TrackManager::tempProgTrack=MAX_TRACKS+1; +byte TrackManager::tempProgTrack=MAX_TRACKS+1; // MAX_TRACKS+1 is the unused flag #endif #ifdef ANALOG_READ_INTERRUPT @@ -505,7 +505,12 @@ void TrackManager::setJoin(bool joined) { } } else { if (tempProgTrack != MAX_TRACKS+1) { + // as setTrackMode with TRACK_MODE_PROG defaults to + // power off, we will take the current power state + // of our track and then preserve that state. + POWERMODE tPTmode = track[tempProgTrack]->getPower(); //get current power status of this track setTrackMode(tempProgTrack, TRACK_MODE_PROG); + track[tempProgTrack]->setPower(tPTmode); //set track status as it was before tempProgTrack = MAX_TRACKS+1; } } From 25426d076dde591e94e85879e660344f799f4d91 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 25 Aug 2023 19:14:03 +0200 Subject: [PATCH 077/218] version number update --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 9de7154..8ae1d2b 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202308240808Z" +#define GITHUB_SHA "devel-202308251713Z" diff --git a/version.h b/version.h index bd45999..4bcf6b5 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.1" +#define VERSION "5.1.2" +// 5.1.2 - Bugfix: ESP32 30ms off time // 5.1.1 - Check bad AT firmware version // - Update IO_PCA9555.h reflecting IO_MCP23017.h changes to support PCA9548 mux // 5.0.1 - Bugfix: execute 30ms off time before rejoin From b823a647ac82a90ad1f95b3797933e92854d0eca Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Sat, 26 Aug 2023 10:26:01 +1000 Subject: [PATCH 078/218] Some progress --- DCCEXParser.cpp | 26 ++++++++++++++++++++------ Turntables.cpp | 29 ++++++++++++++--------------- Turntables.h | 49 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 75 insertions(+), 29 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index e6bd814..835dd32 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1035,18 +1035,27 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) case 0: // list turntable objects return Turntable::printAll(stream); - case 1: // delete turntable - if (!Turntable::remove(p[0])) - return false; - StringFormatter::send(stream, F("\n")); - return true; + case 1: // nothing here for the moment at least + return false; case 2: // - rotate to position for DCC turntables DIAG(F("Rotate DCC turntable %d to position %d"), p[0], p[1]); return true; case 3: // rotate to position for EX-Turntable - DIAG(F("Rotate EXTT turntable %d to angle %d with activity %d"), p[0], p[1], p[2]); + { + Turntable *tto = Turntable::get(p[0]); + if (tto) { + uint16_t value = tto->getPositionValue(p[1]); + if (value) { + DIAG(F("Position %d value is %d"), p[1], value); + } else { + return false; + } + } else { + return false; + } + } return true; default: // If we're here, it must be creating a turntable object @@ -1054,6 +1063,11 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) if (params > 5 && params < 41 && p[1] == HASH_KEYWORD_EXTT) { DIAG(F("Create EXTT turntable %d on vpin %d and address %d with %d positions"), p[0], p[2], p[3], params - 4); if (!EXTTTurntable::create(p[0], (uint8_t)p[1], (VPIN)p[2])) return false; + Turntable *tto = Turntable::get(p[0]); + for ( uint8_t i = 0; i < params - 4; i++) { + tto->addPosition(p[i + 3]); + DIAG(F("Add position %d"), p[i + 3]); + } } else if (params > 3 && params < 39 && p[1] == HASH_KEYWORD_DCC) { DIAG(F("Create DCC turntable %d at base address %d with %d positions"), p[0], p[2], params - 2); } else { diff --git a/Turntables.cpp b/Turntables.cpp index 2f2a05e..1ab5a70 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -62,22 +62,23 @@ Turntable *Turntable::get(uint16_t id) { return NULL; } -// Remove specified turntable from list and delete it -bool Turntable::remove(uint16_t id) { - Turntable *tto, *pp=NULL; +// Add a position +void Turntable::addPosition(uint16_t value) { + _turntablePositions.insert(value); +} - for (tto=_firstTurntable; tto!=NULL && tto->_turntableData.id!=id; pp=tto, tto=tto->_nextTurntable) {} - if (tto == NULL) return false; - if (tto == _firstTurntable) { - _firstTurntable = tto->_nextTurntable; - } else { - pp->_nextTurntable = tto->_nextTurntable; +// Get value for position +uint16_t Turntable::getPositionValue(size_t position) { + TurntablePosition* currentPosition = _turntablePositions.getHead(); + for (size_t i = 0; i < position && currentPosition; i++) { + currentPosition = currentPosition->next; } - delete (EXTTTurntable *)tto; - - turntablelistHash++; - return true; + if (currentPosition) { + return currentPosition->data; + } else { + return false; + } } /* @@ -142,8 +143,6 @@ EXTTTurntable::EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin) : extt->_exttTurntableData.i2caddress = i2caddress; extt->_exttTurntableData.vpin = vpin; return tto; - } else { - remove(id); } } tto = (Turntable *)new EXTTTurntable(id, i2caddress, vpin); diff --git a/Turntables.h b/Turntables.h index c7bb394..3708d2a 100644 --- a/Turntables.h +++ b/Turntables.h @@ -33,6 +33,41 @@ enum { // TURNTABLE_DCC = 2, }; +/************************************************************************************* + * Turntable positions. + * + *************************************************************************************/ +struct TurntablePosition { + uint16_t data; + TurntablePosition* next; + + TurntablePosition(uint16_t value) : data(value), next(nullptr) {} +}; + +class TurntablePositionList { +public: + TurntablePositionList() : head(nullptr) {} + + void insert(uint16_t value) { + TurntablePosition* newPosition = new TurntablePosition(value); + if(!head) { + head = newPosition; + } else { + newPosition->next = head; + head = newPosition; + } + } + + TurntablePosition* getHead() { + return head; + } + +private: + TurntablePosition* head; + +}; + + /************************************************************************************* * Turntable - Base class for turntables. * @@ -57,15 +92,12 @@ protected: uint16_t id; } _turntableData; - // Linked list to store either positions (EXTT) or DCC addresses (DCC) - struct Position { - int16_t position; - Position *next; - }; - // Pointer to next turntable object Turntable *_nextTurntable = 0; + // Linked list for positions + TurntablePositionList _turntablePositions; + /* * Constructor */ @@ -73,6 +105,7 @@ protected: _turntableData.id = id; _turntableData.turntableType = turntableType; _turntableData.hidden = false; + _turntableData.position = 0; add(this); } @@ -110,7 +143,8 @@ public: inline uint16_t getId() { return _turntableData.id; } inline Turntable *next() { return _nextTurntable; } void printState(Print *stream); - + void addPosition(uint16_t value); + uint16_t getPositionValue(size_t position); /* * Virtual functions @@ -125,7 +159,6 @@ public: * Public static functions */ inline static bool exists(uint16_t id) { return get(id) != 0; } - static bool remove(uint16_t id); static bool isPosition(uint16_t id, uint8_t position); static bool setPosition(uint16_t id, uint8_t position, uint8_t activity=0); static bool setPositionStateOnly(uint16_t id, uint8_t position); From 1425da20b500f254f7f16b2a1ba4493f463753ce Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 26 Aug 2023 19:41:17 +1000 Subject: [PATCH 079/218] Correct order --- DCCEXParser.cpp | 5 +++-- IO_EXTurntable.cpp | 1 + Turntables.cpp | 12 ++++++------ Turntables.h | 6 +++--- 4 files changed, 13 insertions(+), 11 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 835dd32..995162b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1060,11 +1060,12 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) default: // If we're here, it must be creating a turntable object { + DIAG(F("Params: %d"), params); if (params > 5 && params < 41 && p[1] == HASH_KEYWORD_EXTT) { DIAG(F("Create EXTT turntable %d on vpin %d and address %d with %d positions"), p[0], p[2], p[3], params - 4); - if (!EXTTTurntable::create(p[0], (uint8_t)p[1], (VPIN)p[2])) return false; + if (!EXTTTurntable::create(p[0], (VPIN)p[2], (uint8_t)p[3])) return false; Turntable *tto = Turntable::get(p[0]); - for ( uint8_t i = 0; i < params - 4; i++) { + for ( uint8_t i = params - 4; i > 0; i--) { tto->addPosition(p[i + 3]); DIAG(F("Add position %d"), p[i + 3]); } diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index f1a108b..b57fa25 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -100,6 +100,7 @@ int EXTurntable::_read(VPIN vpin) { // Acc_Off = 9 // Turn accessory pin off void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) { if (_deviceState == DEVSTATE_FAILED) return; + if (value < 0) return; uint8_t stepsMSB = value >> 8; uint8_t stepsLSB = value & 0xFF; #ifdef DIAG_IO diff --git a/Turntables.cpp b/Turntables.cpp index 1ab5a70..f75b30d 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -126,26 +126,26 @@ bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { * *************************************************************************************/ // Private constructor -EXTTTurntable::EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin) : +EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress) : Turntable(id, TURNTABLE_EXTT) { - _exttTurntableData.i2caddress = i2caddress; _exttTurntableData.vpin = vpin; + _exttTurntableData.i2caddress = i2caddress; } // Create function - Turntable *EXTTTurntable::create(uint16_t id, uint8_t i2caddress, VPIN vpin) { + Turntable *EXTTTurntable::create(uint16_t id, VPIN vpin, uint8_t i2caddress) { #ifndef IO_NO_HAL Turntable *tto = get(id); if (tto) { if (tto->isType(TURNTABLE_EXTT)) { EXTTTurntable *extt = (EXTTTurntable *)tto; - extt->_exttTurntableData.i2caddress = i2caddress; extt->_exttTurntableData.vpin = vpin; + extt->_exttTurntableData.i2caddress = i2caddress; return tto; } } - tto = (Turntable *)new EXTTTurntable(id, i2caddress, vpin); + tto = (Turntable *)new EXTTTurntable(id, vpin, i2caddress); DIAG(F("Turntable 0x%x"), tto); return tto; #else @@ -157,7 +157,7 @@ EXTTTurntable::EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin) : } void EXTTTurntable::print(Print *stream) { - StringFormatter::send(stream, F("\n"), _turntableData.id, _exttTurntableData.i2caddress, _exttTurntableData.vpin); + StringFormatter::send(stream, F("\n"), _turntableData.id, _exttTurntableData.vpin, _exttTurntableData.i2caddress); } // EX-Turntable specific code for moving to the specified position diff --git a/Turntables.h b/Turntables.h index 3708d2a..303c542 100644 --- a/Turntables.h +++ b/Turntables.h @@ -183,16 +183,16 @@ class EXTTTurntable : public Turntable { private: // EXTTTurntableData contains device specific data struct EXTTTurntableData { - uint8_t i2caddress; VPIN vpin; + uint8_t i2caddress; } _exttTurntableData; // Constructor - EXTTTurntable(uint16_t id, uint8_t i2caddress, VPIN vpin); + EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress); public: // Create function - static Turntable *create(uint16_t id, uint8_t i2caddress, VPIN vpin); + static Turntable *create(uint16_t id, VPIN vpin, uint8_t i2caddress); void print(Print *stream) override; protected: From 2202cb0c5eddbc4ca3c1a3df1c95604278f64353 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 27 Aug 2023 19:30:56 +1000 Subject: [PATCH 080/218] Minor progress --- DCCEXParser.cpp | 6 +++--- IO_EXTurntable.cpp | 1 + 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 995162b..48c45fb 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1060,15 +1060,15 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) default: // If we're here, it must be creating a turntable object { - DIAG(F("Params: %d"), params); if (params > 5 && params < 41 && p[1] == HASH_KEYWORD_EXTT) { DIAG(F("Create EXTT turntable %d on vpin %d and address %d with %d positions"), p[0], p[2], p[3], params - 4); + if (Turntable::get(p[0])) return false; if (!EXTTTurntable::create(p[0], (VPIN)p[2], (uint8_t)p[3])) return false; Turntable *tto = Turntable::get(p[0]); - for ( uint8_t i = params - 4; i > 0; i--) { + for (uint8_t i = params - 4; i > 0; i--) { tto->addPosition(p[i + 3]); - DIAG(F("Add position %d"), p[i + 3]); } + tto->addPosition(0); // Need to add position 0 as 0 so positions start at 1 } else if (params > 3 && params < 39 && p[1] == HASH_KEYWORD_DCC) { DIAG(F("Create DCC turntable %d at base address %d with %d positions"), p[0], p[2], params - 2); } else { diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index b57fa25..8c7057b 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -51,6 +51,7 @@ EXTurntable::EXTurntable(VPIN firstVpin, int nPins, I2CAddress I2CAddress) { void EXTurntable::_begin() { I2CManager.begin(); if (I2CManager.exists(_I2CAddress)) { + DIAG(F("EX-Turntable device found, I2C:%s"), _I2CAddress.toString()); #ifdef DIAG_IO _display(); #endif From df4a501e8a34fd7748675bf3801be10419334649 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Mon, 28 Aug 2023 08:36:09 +1000 Subject: [PATCH 081/218] Writing to driver --- DCCEXParser.cpp | 23 +++++++++++++++-------- Turntables.cpp | 22 ++++++---------------- Turntables.h | 1 - 3 files changed, 21 insertions(+), 25 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 48c45fb..45e8a73 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1039,19 +1039,27 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) return false; case 2: // - rotate to position for DCC turntables - DIAG(F("Rotate DCC turntable %d to position %d"), p[0], p[1]); + { + Turntable *tto = Turntable::get(p[0]); + if (tto) { + if (tto->getPosition() == p[1]) return true; + uint16_t value = tto->getPositionValue(p[1]); + if (value) { + DIAG(F("Rotate DCC turntable %d to position %d"), p[0], p[1]); + } else { + return false; + } + } else { + return false; + } + } return true; case 3: // rotate to position for EX-Turntable { Turntable *tto = Turntable::get(p[0]); if (tto) { - uint16_t value = tto->getPositionValue(p[1]); - if (value) { - DIAG(F("Position %d value is %d"), p[1], value); - } else { - return false; - } + if (!tto->setPosition(p[0], p[1], p[2])) return false; } else { return false; } @@ -1061,7 +1069,6 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) default: // If we're here, it must be creating a turntable object { if (params > 5 && params < 41 && p[1] == HASH_KEYWORD_EXTT) { - DIAG(F("Create EXTT turntable %d on vpin %d and address %d with %d positions"), p[0], p[2], p[3], params - 4); if (Turntable::get(p[0])) return false; if (!EXTTTurntable::create(p[0], (VPIN)p[2], (uint8_t)p[3])) return false; Turntable *tto = Turntable::get(p[0]); diff --git a/Turntables.cpp b/Turntables.cpp index f75b30d..ec31a00 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -84,19 +84,6 @@ uint16_t Turntable::getPositionValue(size_t position) { /* * Public static functions */ -bool Turntable::isPosition(uint16_t id, uint8_t position) { - Turntable *tto = get(id); - if (tto) { - if (tto->getPosition() == position) { - return true; - } else { - return false; - } - } else { - return false; - } -} - bool Turntable::setPositionStateOnly(uint16_t id, uint8_t position) { Turntable *tto = get(id); if (!tto) return false; @@ -117,6 +104,7 @@ bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { if (ok) { tto->setPositionStateOnly(id, position); + tto->_turntableData.position = position; } return ok; } @@ -163,10 +151,12 @@ EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress) : // EX-Turntable specific code for moving to the specified position bool EXTTTurntable::setPositionInternal(uint8_t position, uint8_t activity) { #ifndef IO_NO_HAL - // Get step value from positions - // int value = _exttTurntableData.positions[position]; + DIAG(F("Set EXTT %d to position %d with activity %d"), _exttTurntableData.vpin, position, activity); + // Get position value from position list + int16_t value = getPositionValue(position); + if (!value) return false; // Return false if it's not a valid position // Set position via device driver - // EXTurntable::_writeAnalogue(vpin, value, activity, 0); + EXTurntable::writeAnalogue(_exttTurntableData.vpin, value, activity); #else (void)position; #endif diff --git a/Turntables.h b/Turntables.h index 303c542..f3a3321 100644 --- a/Turntables.h +++ b/Turntables.h @@ -159,7 +159,6 @@ public: * Public static functions */ inline static bool exists(uint16_t id) { return get(id) != 0; } - static bool isPosition(uint16_t id, uint8_t position); static bool setPosition(uint16_t id, uint8_t position, uint8_t activity=0); static bool setPositionStateOnly(uint16_t id, uint8_t position); inline static Turntable *first() { return _firstTurntable; } From 3bfdd16288af0424c7d162f6f13787a79030dc25 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Mon, 28 Aug 2023 13:11:37 +1000 Subject: [PATCH 082/218] Start on JO --- DCCEXParser.cpp | 26 +++++++++++++++++++++++++- Turntables.cpp | 4 ++-- 2 files changed, 27 insertions(+), 3 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 45e8a73..f8dd9d4 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -85,6 +85,7 @@ const int16_t HASH_KEYWORD_A='A'; const int16_t HASH_KEYWORD_C='C'; const int16_t HASH_KEYWORD_G='G'; const int16_t HASH_KEYWORD_I='I'; +const int16_t HASH_KEYWORD_O='O'; const int16_t HASH_KEYWORD_R='R'; const int16_t HASH_KEYWORD_T='T'; const int16_t HASH_KEYWORD_X='X'; @@ -691,6 +692,29 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } StringFormatter::send(stream, F(">\n")); return; + case HASH_KEYWORD_O: // + for ( Turntable * tto=Turntable::first(); tto; tto=tto->next()) { + if (tto->isHidden()) continue; + StringFormatter::send(stream, F(" %d"),tto->getId()); + } + } else { // + Turntable *tto=Turntable::get(id); + if (!tto || tto->isHidden()) { + StringFormatter::send(stream, F(" %d X"), id); + } else { + uint8_t pos = tto->getPosition(); + const FSH *todesc = NULL; +#ifdef EXRAIL_ACTIVE + // todesc = RMFT2::getTurntableDescription(id); +#endif + if (todesc == NULL) todesc = F(""); + StringFormatter::send(stream, F(" %d %d"), id, pos); + } + } + StringFormatter::send(stream, F(">\n")); + return; default: break; } // switch(p[1]) break; // case J @@ -1075,7 +1099,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) for (uint8_t i = params - 4; i > 0; i--) { tto->addPosition(p[i + 3]); } - tto->addPosition(0); // Need to add position 0 as 0 so positions start at 1 + tto->addPosition(p[3]); // Allow setting a value for the home angle for throttles to draw it } else if (params > 3 && params < 39 && p[1] == HASH_KEYWORD_DCC) { DIAG(F("Create DCC turntable %d at base address %d with %d positions"), p[0], p[2], params - 2); } else { diff --git a/Turntables.cpp b/Turntables.cpp index ec31a00..5332aa5 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -152,8 +152,8 @@ EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress) : bool EXTTTurntable::setPositionInternal(uint8_t position, uint8_t activity) { #ifndef IO_NO_HAL DIAG(F("Set EXTT %d to position %d with activity %d"), _exttTurntableData.vpin, position, activity); - // Get position value from position list - int16_t value = getPositionValue(position); + if (position == 0) return false; // Position 0 is just so throttles know where home is + int16_t value = getPositionValue(position); // Get position value from position list if (!value) return false; // Return false if it's not a valid position // Set position via device driver EXTurntable::writeAnalogue(_exttTurntableData.vpin, value, activity); From b0d85101275b3184f902547a9b0f6a684571e5e9 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 29 Aug 2023 13:38:52 +1000 Subject: [PATCH 083/218] Working but limited --- DCCEXParser.cpp | 17 +++++++++++------ Turntables.cpp | 32 +++++++++++++++++++++++++------- Turntables.h | 2 ++ 3 files changed, 38 insertions(+), 13 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index f8dd9d4..9047cad 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -695,7 +695,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case HASH_KEYWORD_O: // - for ( Turntable * tto=Turntable::first(); tto; tto=tto->next()) { + for (Turntable * tto=Turntable::first(); tto; tto=tto->next()) { if (tto->isHidden()) continue; StringFormatter::send(stream, F(" %d"),tto->getId()); } @@ -705,12 +705,18 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F(" %d X"), id); } else { uint8_t pos = tto->getPosition(); + uint8_t type = tto->getType(); + uint8_t posCount = tto->getPositionCount(); const FSH *todesc = NULL; #ifdef EXRAIL_ACTIVE // todesc = RMFT2::getTurntableDescription(id); #endif if (todesc == NULL) todesc = F(""); - StringFormatter::send(stream, F(" %d %d"), id, pos); + StringFormatter::send(stream, F(" %d %d %d %d"), id, type, pos, posCount); + for (uint8_t p = 0; p < posCount; p++) { + int16_t value = tto->getPositionValue(p); + StringFormatter::send(stream, F(" %d"), value); + } } } StringFormatter::send(stream, F(">\n")); @@ -1047,7 +1053,6 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) // ========================== // Turntable // - list all -// - delete // - operate (DCC) // - operate (EXTT) // - create EXTT @@ -1096,10 +1101,10 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) if (Turntable::get(p[0])) return false; if (!EXTTTurntable::create(p[0], (VPIN)p[2], (uint8_t)p[3])) return false; Turntable *tto = Turntable::get(p[0]); - for (uint8_t i = params - 4; i > 0; i--) { - tto->addPosition(p[i + 3]); + for (uint8_t i = params - 5; i > 0; i--) { + tto->addPosition(p[i + 4]); } - tto->addPosition(p[3]); // Allow setting a value for the home angle for throttles to draw it + tto->addPosition(p[4]); // Allow setting a value for the home angle for throttles to draw it } else if (params > 3 && params < 39 && p[1] == HASH_KEYWORD_DCC) { DIAG(F("Create DCC turntable %d at base address %d with %d positions"), p[0], p[2], params - 2); } else { diff --git a/Turntables.cpp b/Turntables.cpp index 5332aa5..bcc0a94 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -81,6 +81,17 @@ uint16_t Turntable::getPositionValue(size_t position) { } } +// Get the count of positions associated with the turntable +uint8_t Turntable::getPositionCount() { + TurntablePosition* currentPosition = _turntablePositions.getHead(); + uint8_t count = 0; + while (currentPosition) { + count++; + currentPosition = currentPosition->next; + } + return count; +} + /* * Public static functions */ @@ -103,8 +114,11 @@ bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { bool ok = tto->setPositionInternal(position, activity); if (ok) { - tto->setPositionStateOnly(id, position); - tto->_turntableData.position = position; + // Broadcast a position change only if non zero has been set, or home/calibration sent + if (position > 0 || (position == 0 && (activity == 2 || activity == 3))) { + tto->setPositionStateOnly(id, position); + tto->_turntableData.position = position; + } } return ok; } @@ -134,7 +148,7 @@ EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress) : } } tto = (Turntable *)new EXTTTurntable(id, vpin, i2caddress); - DIAG(F("Turntable 0x%x"), tto); + DIAG(F("Turntable 0x%x size %d size %d"), tto, sizeof(Turntable), sizeof(struct TurntableData)); return tto; #else (void)id; @@ -151,10 +165,14 @@ EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress) : // EX-Turntable specific code for moving to the specified position bool EXTTTurntable::setPositionInternal(uint8_t position, uint8_t activity) { #ifndef IO_NO_HAL - DIAG(F("Set EXTT %d to position %d with activity %d"), _exttTurntableData.vpin, position, activity); - if (position == 0) return false; // Position 0 is just so throttles know where home is - int16_t value = getPositionValue(position); // Get position value from position list - if (!value) return false; // Return false if it's not a valid position + int16_t value; + if (position == 0) { + value = 0; // Position 0 is just to send activities + } else { + if (activity > 1) return false; // If sending a position update, only phase changes valid (0|1) + value = getPositionValue(position); // Get position value from position list + } + if (position > 0 && !value) return false; // Return false if it's not a valid position // Set position via device driver EXTurntable::writeAnalogue(_exttTurntableData.vpin, value, activity); #else diff --git a/Turntables.h b/Turntables.h index f3a3321..1016140 100644 --- a/Turntables.h +++ b/Turntables.h @@ -140,11 +140,13 @@ public: inline bool isHidden() { return _turntableData.hidden; } inline void setHidden(bool h) {_turntableData.hidden=h; } inline bool isType(uint8_t type) { return _turntableData.turntableType == type; } + inline uint8_t getType() { return _turntableData.turntableType; } inline uint16_t getId() { return _turntableData.id; } inline Turntable *next() { return _nextTurntable; } void printState(Print *stream); void addPosition(uint16_t value); uint16_t getPositionValue(size_t position); + uint8_t getPositionCount(); /* * Virtual functions From 6cad794411f60f2022548cb9fb9c6272a0daafd2 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 29 Aug 2023 19:04:45 +1000 Subject: [PATCH 084/218] Working with 15 positions --- DCCEXParser.cpp | 10 +++++++++- DCCEXParser.h | 5 ++++- IODevice.h | 6 ------ Turntables.cpp | 4 ++++ Turntables.h | 5 +++++ defines.h | 20 ++++++++++++++++++++ 6 files changed, 42 insertions(+), 8 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 9047cad..a37e5d0 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -692,6 +692,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } StringFormatter::send(stream, F(">\n")); return; +// No turntables without HAL support +#ifndef IO_NO_HAL case HASH_KEYWORD_O: // @@ -721,15 +723,19 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } StringFormatter::send(stream, F(">\n")); return; +#endif default: break; } // switch(p[1]) break; // case J } +// No turntables without HAL support +#ifndef IO_NO_HAL case 'I': // TURNTABLE if (parseI(stream, params, p)) return; break; +#endif default: //anything else will diagnose and drop out to DIAG(F("Opcode=%c params=%d"), opcode, params); @@ -1051,12 +1057,13 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) } // ========================== -// Turntable +// Turntable - no support if no HAL // - list all // - operate (DCC) // - operate (EXTT) // - create EXTT // - create DCC? - This TBA +#ifndef IO_NO_HAL bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) { switch (params) @@ -1114,6 +1121,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) return true; } } +#endif // CALLBACKS must be static bool DCCEXParser::stashCallback(Print *stream, int16_t p[MAX_COMMAND_PARAMS], RingStream * ringStream) diff --git a/DCCEXParser.h b/DCCEXParser.h index bf067a7..a3d5e10 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -24,6 +24,7 @@ #include #include "FSH.h" #include "RingStream.h" +#include "defines.h" typedef void (*FILTER_CALLBACK)(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); typedef void (*AT_COMMAND_CALLBACK)(HardwareSerial * stream,const byte * command); @@ -37,7 +38,7 @@ struct DCCEXParser static void setFilter(FILTER_CALLBACK filter); static void setRMFTFilter(FILTER_CALLBACK filter); static void setAtCommandCallback(AT_COMMAND_CALLBACK filter); - static const int MAX_COMMAND_PARAMS=10; // Must not exceed this + static const int MAX_COMMAND_PARAMS=MAX_PARSER_PARAMS; // Must not exceed this private: @@ -49,7 +50,9 @@ struct DCCEXParser static bool parseS(Print * stream, int16_t params, int16_t p[]); static bool parsef(Print * stream, int16_t params, int16_t p[]); static bool parseD(Print * stream, int16_t params, int16_t p[]); +#ifndef IO_NO_HAL static bool parseI(Print * stream, int16_t params, int16_t p[]); +#endif static Print * getAsyncReplyStream(); static void commitAsyncReplyStream(); diff --git a/IODevice.h b/IODevice.h index 769e111..d2c80a4 100644 --- a/IODevice.h +++ b/IODevice.h @@ -27,12 +27,6 @@ // Define symbol DIAG_LOOPTIMES to enable CS loop execution time to be reported //#define DIAG_LOOPTIMES -// Define symbol IO_NO_HAL to reduce FLASH footprint when HAL features not required -// The HAL is disabled by default on Nano and Uno platforms, because of limited flash space. -#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) -#define IO_NO_HAL -#endif - // Define symbol IO_SWITCH_OFF_SERVO to set the PCA9685 output to 0 when an // animation has completed. This switches off the servo motor, preventing // the continuous buzz sometimes found on servos, and reducing the diff --git a/Turntables.cpp b/Turntables.cpp index bcc0a94..6b4260f 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -26,6 +26,8 @@ #include "EXRAIL2.h" #include "DCC.h" +// No turntable support without HAL +#ifndef IO_NO_HAL /* * Protected static data @@ -180,3 +182,5 @@ EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress) : #endif return true; } + +#endif diff --git a/Turntables.h b/Turntables.h index 1016140..67017df 100644 --- a/Turntables.h +++ b/Turntables.h @@ -25,6 +25,9 @@ #include "IODevice.h" #include "StringFormatter.h" +// No turntable support without HAL +#ifndef IO_NO_HAL + // Turntable type definitions // EXTT = EX-Turntable // DCC = DCC accessory turntables - to be added later @@ -203,3 +206,5 @@ protected: }; #endif + +#endif diff --git a/defines.h b/defines.h index f3822ca..342d1b6 100644 --- a/defines.h +++ b/defines.h @@ -213,6 +213,26 @@ // #define WIFI_SERIAL_LINK_SPEED 115200 +//////////////////////////////////////////////////////////////////////////////// +// +// Define symbol IO_NO_HAL to reduce FLASH footprint when HAL features not required +// The HAL is disabled by default on Nano and Uno platforms, because of limited flash space. +// +#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) +#define IO_NO_HAL +#endif + +//////////////////////////////////////////////////////////////////////////////// +// +// This defines the maximum number of parameters DCCEXParser will accept. +// The increase is required to allow for a sufficient number of turntable positions. +// +#ifdef IO_NO_HAL + #define MAX_PARSER_PARAMS 10 +#else + #define MAX_PARSER_PARAMS 20 +#endif + #if __has_include ( "myAutomation.h") #if defined(HAS_ENOUGH_MEMORY) || defined(DISABLE_EEPROM) || defined(DISABLE_PROG) #define EXRAIL_ACTIVE From 26ddd27ecf769f1be45e3b7acaf16f5265023783 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 29 Aug 2023 14:27:38 +0200 Subject: [PATCH 085/218] let user disable command in favour for HAL on the Uno platform --- DCCEXParser.cpp | 4 ++-- IODevice.h | 9 +++++++-- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 7610d45..49d2d20 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -638,12 +638,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case ' ': // < > StringFormatter::send(stream, F("\n")); return; - +#ifndef DISABLE_DIAG case 'D': // < > if (parseD(stream, params, p)) return; return; - +#endif case '=': // <= Track manager control > if (TrackManager::parseJ(stream, params, p)) return; diff --git a/IODevice.h b/IODevice.h index 769e111..4eb24e5 100644 --- a/IODevice.h +++ b/IODevice.h @@ -29,8 +29,13 @@ // Define symbol IO_NO_HAL to reduce FLASH footprint when HAL features not required // The HAL is disabled by default on Nano and Uno platforms, because of limited flash space. -#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) -#define IO_NO_HAL +#include "defines.h" +#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) + #if defined(DISABLE_DIAG) && defined(DISABLE_EEPROM) && defined(DISABLE_PROG) + #warning you have sacrificed DIAG for HAL + #else + #define IO_NO_HAL + #endif #endif // Define symbol IO_SWITCH_OFF_SERVO to set the PCA9685 output to 0 when an From 232ac993ecf58451457d6b959cd0845616ff18b0 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Wed, 30 Aug 2023 08:45:11 +1000 Subject: [PATCH 086/218] Separate add from create --- DCCEXParser.cpp | 65 +++++++++++++++++++++++++++++++------------------ Turntables.cpp | 14 +++++------ Turntables.h | 18 ++++++++------ 3 files changed, 57 insertions(+), 40 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index a37e5d0..24a7914 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -98,6 +98,7 @@ const int16_t HASH_KEYWORD_WIFI = -5583; const int16_t HASH_KEYWORD_ETHERNET = -30767; const int16_t HASH_KEYWORD_WIT = 31594; const int16_t HASH_KEYWORD_EXTT = 8573; +const int16_t HASH_KEYWORD_ADD = 3201; int16_t DCCEXParser::stashP[MAX_COMMAND_PARAMS]; bool DCCEXParser::stashBusy; @@ -714,7 +715,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) // todesc = RMFT2::getTurntableDescription(id); #endif if (todesc == NULL) todesc = F(""); - StringFormatter::send(stream, F(" %d %d %d %d"), id, type, pos, posCount); + StringFormatter::send(stream, F(" %d %d %d"), id, type, pos); for (uint8_t p = 0; p < posCount; p++) { int16_t value = tto->getPositionValue(p); StringFormatter::send(stream, F(" %d"), value); @@ -1059,10 +1060,12 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) // ========================== // Turntable - no support if no HAL // - list all +// - broadcast type and current position +// - create DCC - This is TBA // - operate (DCC) // - operate (EXTT) -// - create EXTT -// - create DCC? - This TBA +// - add position +// - create EXTT #ifndef IO_NO_HAL bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) { @@ -1071,47 +1074,58 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) case 0: // list turntable objects return Turntable::printAll(stream); - case 1: // nothing here for the moment at least + case 1: // broadcast type and current position return false; case 2: // - rotate to position for DCC turntables { - Turntable *tto = Turntable::get(p[0]); - if (tto) { - if (tto->getPosition() == p[1]) return true; - uint16_t value = tto->getPositionValue(p[1]); - if (value) { - DIAG(F("Rotate DCC turntable %d to position %d"), p[0], p[1]); + if (p[1] == HASH_KEYWORD_DCC) { // Create a DCC turntable + DIAG(F("Create DCC turntable %d"), p[0]); + } else { // Otherwise move a DCC turntable + Turntable *tto = Turntable::get(p[0]); + if (tto) { + if (tto->getPosition() == p[1]) return true; + uint16_t value = tto->getPositionValue(p[1]); + if (value) { + DIAG(F("Rotate DCC turntable %d to position %d"), p[0], p[1]); + } else { + return false; + } } else { return false; } - } else { - return false; } } return true; - case 3: // rotate to position for EX-Turntable + case 3: { - Turntable *tto = Turntable::get(p[0]); - if (tto) { - if (!tto->setPosition(p[0], p[1], p[2])) return false; - } else { - return false; + if (p[1] == HASH_KEYWORD_ADD) { // add position value to turntable + Turntable *tto = Turntable::get(p[0]); + if (tto) { + tto->addPosition(p[2]); + StringFormatter::send(stream, F("\n")); + } else { + return false; + } + } else { // rotate to position for EX-Turntable + Turntable *tto = Turntable::get(p[0]); + if (tto) { + if (!tto->setPosition(p[0], p[1], p[2])) return false; + } else { + return false; + } } } return true; - default: // If we're here, it must be creating a turntable object + case 5: // create an EXTT turntable { - if (params > 5 && params < 41 && p[1] == HASH_KEYWORD_EXTT) { + if (p[1] == HASH_KEYWORD_EXTT) { if (Turntable::get(p[0])) return false; if (!EXTTTurntable::create(p[0], (VPIN)p[2], (uint8_t)p[3])) return false; Turntable *tto = Turntable::get(p[0]); - for (uint8_t i = params - 5; i > 0; i--) { - tto->addPosition(p[i + 4]); - } - tto->addPosition(p[4]); // Allow setting a value for the home angle for throttles to draw it + tto->addPosition(p[4]); } else if (params > 3 && params < 39 && p[1] == HASH_KEYWORD_DCC) { DIAG(F("Create DCC turntable %d at base address %d with %d positions"), p[0], p[2], params - 2); } else { @@ -1119,6 +1133,9 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) } } return true; + + default: // Anything else is invalid + return false; } } #endif diff --git a/Turntables.cpp b/Turntables.cpp index 6b4260f..a7d42ab 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -70,17 +70,15 @@ void Turntable::addPosition(uint16_t value) { } // Get value for position -uint16_t Turntable::getPositionValue(size_t position) { +uint16_t Turntable::getPositionValue(uint8_t position) { TurntablePosition* currentPosition = _turntablePositions.getHead(); - for (size_t i = 0; i < position && currentPosition; i++) { + while (currentPosition) { + if (currentPosition->index == position) { + return currentPosition->data; + } currentPosition = currentPosition->next; } - - if (currentPosition) { - return currentPosition->data; - } else { - return false; - } + return false; } // Get the count of positions associated with the turntable diff --git a/Turntables.h b/Turntables.h index 67017df..36f5b90 100644 --- a/Turntables.h +++ b/Turntables.h @@ -32,8 +32,8 @@ // EXTT = EX-Turntable // DCC = DCC accessory turntables - to be added later enum { - TURNTABLE_EXTT = 1, - // TURNTABLE_DCC = 2, + TURNTABLE_EXTT = 0, + TURNTABLE_DCC = 1, }; /************************************************************************************* @@ -41,18 +41,19 @@ enum { * *************************************************************************************/ struct TurntablePosition { + uint8_t index; uint16_t data; TurntablePosition* next; - TurntablePosition(uint16_t value) : data(value), next(nullptr) {} + TurntablePosition(uint8_t idx, uint16_t value) : index(idx), data(value), next(nullptr) {} }; class TurntablePositionList { public: - TurntablePositionList() : head(nullptr) {} + TurntablePositionList() : head(nullptr), currentIndex(0) {} void insert(uint16_t value) { - TurntablePosition* newPosition = new TurntablePosition(value); + TurntablePosition* newPosition = new TurntablePosition(currentIndex++, value); if(!head) { head = newPosition; } else { @@ -67,6 +68,7 @@ public: private: TurntablePosition* head; + uint8_t currentIndex; }; @@ -87,8 +89,8 @@ protected: union { struct { bool hidden : 1; - uint8_t turntableType : 2; - uint8_t position : 5; // Allows up to 38 positions including 0/home + bool turntableType : 1; + uint8_t position : 6; // Allows up to 63 positions including 0/home }; uint8_t flags; }; @@ -148,7 +150,7 @@ public: inline Turntable *next() { return _nextTurntable; } void printState(Print *stream); void addPosition(uint16_t value); - uint16_t getPositionValue(size_t position); + uint16_t getPositionValue(uint8_t position); uint8_t getPositionCount(); /* From dbf053858b61d5315790427adb5ed4da08e7a8ae Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Wed, 30 Aug 2023 13:40:09 +1000 Subject: [PATCH 087/218] Undo max params --- DCCEXParser.h | 2 +- defines.h | 11 ----------- 2 files changed, 1 insertion(+), 12 deletions(-) diff --git a/DCCEXParser.h b/DCCEXParser.h index a3d5e10..8a7367a 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -38,7 +38,7 @@ struct DCCEXParser static void setFilter(FILTER_CALLBACK filter); static void setRMFTFilter(FILTER_CALLBACK filter); static void setAtCommandCallback(AT_COMMAND_CALLBACK filter); - static const int MAX_COMMAND_PARAMS=MAX_PARSER_PARAMS; // Must not exceed this + static const int MAX_COMMAND_PARAMS=10; // Must not exceed this private: diff --git a/defines.h b/defines.h index 342d1b6..3286aca 100644 --- a/defines.h +++ b/defines.h @@ -222,17 +222,6 @@ #define IO_NO_HAL #endif -//////////////////////////////////////////////////////////////////////////////// -// -// This defines the maximum number of parameters DCCEXParser will accept. -// The increase is required to allow for a sufficient number of turntable positions. -// -#ifdef IO_NO_HAL - #define MAX_PARSER_PARAMS 10 -#else - #define MAX_PARSER_PARAMS 20 -#endif - #if __has_include ( "myAutomation.h") #if defined(HAS_ENOUGH_MEMORY) || defined(DISABLE_EEPROM) || defined(DISABLE_PROG) #define EXRAIL_ACTIVE From a0c1ad182cb9f76be7da471a6f18ea7698431cde Mon Sep 17 00:00:00 2001 From: peteGSX Date: Wed, 30 Aug 2023 19:48:30 +1000 Subject: [PATCH 088/218] Start on callback --- CommandDistributor.cpp | 4 ++-- CommandDistributor.h | 2 +- DCCEXParser.cpp | 5 +++++ DCCEXParser.h | 1 + Turntables.cpp | 10 +++++++--- Turntables.h | 5 ++++- 6 files changed, 20 insertions(+), 7 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index eef84a9..ee760b0 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -161,8 +161,8 @@ void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) { #endif } -void CommandDistributor::broadcastTurntable(int16_t id, uint8_t position) { - broadcastReply(COMMAND_TYPE, F("\n"), id, position); +void CommandDistributor::broadcastTurntable(int16_t id, uint8_t position, bool moving) { + broadcastReply(COMMAND_TYPE, F("\n"), id, position, moving); } void CommandDistributor::broadcastClockTime(int16_t time, int8_t rate) { diff --git a/CommandDistributor.h b/CommandDistributor.h index b1af10a..a51d58a 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -49,7 +49,7 @@ public : static void broadcastLoco(byte slot); static void broadcastSensor(int16_t id, bool value); static void broadcastTurnout(int16_t id, bool isClosed); - static void broadcastTurntable(int16_t id, uint8_t position); + static void broadcastTurntable(int16_t id, uint8_t position, bool moving); static void broadcastClockTime(int16_t time, int8_t rate); static void setClockTime(int16_t time, int8_t rate, byte opt); static int16_t retClockTime(); diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 24a7914..2b0e06f 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1226,3 +1226,8 @@ void DCCEXParser::callback_Wloco(int16_t result) StringFormatter::send(getAsyncReplyStream(), F("\n"), result); commitAsyncReplyStream(); } + +void DCCEXParser::callback_Imoving(bool moving) { + if (!moving) StringFormatter::send(getAsyncReplyStream(), F("")); + commitAsyncReplyStream(); +} diff --git a/DCCEXParser.h b/DCCEXParser.h index 8a7367a..7a7ef89 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -72,6 +72,7 @@ struct DCCEXParser static void callback_Wloco(int16_t result); static void callback_Vbit(int16_t result); static void callback_Vbyte(int16_t result); + static void callback_Imoving(bool moving); static FILTER_CALLBACK filterCallback; static FILTER_CALLBACK filterRMFTCallback; static AT_COMMAND_CALLBACK atCommandCallback; diff --git a/Turntables.cpp b/Turntables.cpp index a7d42ab..a0419b1 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -95,10 +95,10 @@ uint8_t Turntable::getPositionCount() { /* * Public static functions */ -bool Turntable::setPositionStateOnly(uint16_t id, uint8_t position) { +bool Turntable::setPositionStateOnly(uint16_t id, uint8_t position, bool moving) { Turntable *tto = get(id); if (!tto) return false; - CommandDistributor::broadcastTurntable(id, position); + CommandDistributor::broadcastTurntable(id, position, moving); #if defined(EXRAIL_ACTIVE) // RMFT2::turntableEvent(id, position); #endif @@ -116,7 +116,11 @@ bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { if (ok) { // Broadcast a position change only if non zero has been set, or home/calibration sent if (position > 0 || (position == 0 && (activity == 2 || activity == 3))) { - tto->setPositionStateOnly(id, position); + if (tto->getType() == TURNTABLE_EXTT) { + tto->setPositionStateOnly(id, position, 1); + } else { + tto->setPositionStateOnly(id, position, 0); + } tto->_turntableData.position = position; } } diff --git a/Turntables.h b/Turntables.h index 36f5b90..d59bc80 100644 --- a/Turntables.h +++ b/Turntables.h @@ -36,6 +36,9 @@ enum { TURNTABLE_DCC = 1, }; +// Callback needs to return a bool: 1 = moving, 0 = stopped +typedef void (*EXTT_CALLBACK)(bool moving); + /************************************************************************************* * Turntable positions. * @@ -167,7 +170,7 @@ public: */ inline static bool exists(uint16_t id) { return get(id) != 0; } static bool setPosition(uint16_t id, uint8_t position, uint8_t activity=0); - static bool setPositionStateOnly(uint16_t id, uint8_t position); + static bool setPositionStateOnly(uint16_t id, uint8_t position, bool moving); inline static Turntable *first() { return _firstTurntable; } static bool printAll(Print *stream) { bool gotOne = false; From b3cafd126e51f2fe954a031fa54dc36fb5c19089 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 30 Aug 2023 23:26:20 +0200 Subject: [PATCH 089/218] sample file corrections --- myHal.cpp_example.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/myHal.cpp_example.txt b/myHal.cpp_example.txt index d93ea5c..5533554 100644 --- a/myHal.cpp_example.txt +++ b/myHal.cpp_example.txt @@ -24,6 +24,7 @@ //#include "IO_TouchKeypad.h // Touch keypad with 16 keys //#include "IO_EXTurntable.h" // Turntable-EX turntable controller //#include "IO_EXFastClock.h" // FastClock driver +//#include "IO_PCA9555.h" // 16-bit I/O expander (NXP & Texas Instruments). //========================================================================== // The function halSetup() is invoked from CS if it exists within the build. @@ -51,7 +52,7 @@ void halSetup() { // Create a 20x4 LCD display device as display number 2 // (line 0 is written by EX-RAIL 'SCREEN(2, 0, "text")'). - // HALDisplay(2, 0x27, 20, 4); + // HALDisplay::create(2, 0x27, 20, 4); //======================================================================= From 01919b33df5b7a2854e3cbae8508de8e23ef8ef9 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 30 Aug 2023 23:55:39 +0200 Subject: [PATCH 090/218] Make parser more fool proof --- DCCEXParser.cpp | 28 ++++++++++++++++++---------- 1 file changed, 18 insertions(+), 10 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 49d2d20..53cb5ce 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -451,12 +451,16 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) #ifndef DISABLE_PROG case 'w': // WRITE CV on MAIN - DCC::writeCVByteMain(p[0], p[1], p[2]); - return; + if (params != 3) + break; + DCC::writeCVByteMain(p[0], p[1], p[2]); + return; case 'b': // WRITE CV BIT ON MAIN - DCC::writeCVBitMain(p[0], p[1], p[2], p[3]); - return; + if (params != 4) + break; + DCC::writeCVBitMain(p[0], p[1], p[2], p[3]); + return; #endif case 'M': // WRITE TRANSPARENT DCC PACKET MAIN @@ -479,14 +483,16 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) #ifndef DISABLE_PROG case 'W': // WRITE CV ON PROG - if (!stashCallback(stream, p, ringStream)) - break; + if (!stashCallback(stream, p, ringStream)) + break; if (params == 1) // Write new loco id (clearing consist and managing short/long) DCC::setLocoId(p[0],callback_Wloco); else if (params == 4) // WRITE CV ON PROG DCC::writeCVByte(p[0], p[1], callback_W4); - else // WRITE CV ON PROG + else if (params == 2) // WRITE CV ON PROG DCC::writeCVByte(p[0], p[1], callback_W); + else + break; return; case 'V': // VERIFY CV ON PROG @@ -506,9 +512,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } break; - case 'B': // WRITE CV BIT ON PROG + case 'B': // WRITE CV BIT ON PROG or + if (params != 3 && params != 5) + break; if (!stashCallback(stream, p, ringStream)) - break; + break; DCC::writeCVBit(p[0], p[1], p[2], callback_B); return; @@ -642,7 +650,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case 'D': // < > if (parseD(stream, params, p)) return; - return; + break; #endif case '=': // <= Track manager control > if (TrackManager::parseJ(stream, params, p)) From 44d8154223f9dbc57a3fab24f6fa9309d4c84c9d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 30 Aug 2023 23:57:49 +0200 Subject: [PATCH 091/218] version number update --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 8ae1d2b..98afa03 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202308251713Z" +#define GITHUB_SHA "devel-202308302157Z" diff --git a/version.h b/version.h index 4bcf6b5..d03eab8 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.2" +#define VERSION "5.1.3" +// 5.1.3 - Make parser more fool proof // 5.1.2 - Bugfix: ESP32 30ms off time // 5.1.1 - Check bad AT firmware version // - Update IO_PCA9555.h reflecting IO_MCP23017.h changes to support PCA9548 mux From 9fa213e198f66c877a8fdc6db7bb03ee9ea3e1e4 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 31 Aug 2023 13:51:25 +1000 Subject: [PATCH 092/218] Undo callback --- DCCEXParser.cpp | 5 ----- DCCEXParser.h | 1 - 2 files changed, 6 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 2b0e06f..24a7914 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1226,8 +1226,3 @@ void DCCEXParser::callback_Wloco(int16_t result) StringFormatter::send(getAsyncReplyStream(), F("\n"), result); commitAsyncReplyStream(); } - -void DCCEXParser::callback_Imoving(bool moving) { - if (!moving) StringFormatter::send(getAsyncReplyStream(), F("")); - commitAsyncReplyStream(); -} diff --git a/DCCEXParser.h b/DCCEXParser.h index 7a7ef89..8a7367a 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -72,7 +72,6 @@ struct DCCEXParser static void callback_Wloco(int16_t result); static void callback_Vbit(int16_t result); static void callback_Vbyte(int16_t result); - static void callback_Imoving(bool moving); static FILTER_CALLBACK filterCallback; static FILTER_CALLBACK filterRMFTCallback; static AT_COMMAND_CALLBACK atCommandCallback; From f40d57d8bde35ef7b075a602e65ec213a7058b2f Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Fri, 1 Sep 2023 08:44:32 +1000 Subject: [PATCH 093/218] Add DCC type, EXTT broadcast from driver --- DCCEXParser.cpp | 55 ++++++++++++++-------------- IODevice.cpp | 7 ++++ IODevice.h | 5 +++ IO_EXTurntable.cpp | 16 +++++++++ Turntables.cpp | 90 +++++++++++++++++++++++++++++++++++++--------- Turntables.h | 29 ++++++++++++--- 6 files changed, 152 insertions(+), 50 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 24a7914..7473c26 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -708,7 +708,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F(" %d X"), id); } else { uint8_t pos = tto->getPosition(); - uint8_t type = tto->getType(); + uint8_t type = tto->isEXTT(); uint8_t posCount = tto->getPositionCount(); const FSH *todesc = NULL; #ifdef EXRAIL_ACTIVE @@ -1075,22 +1075,29 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) return Turntable::printAll(stream); case 1: // broadcast type and current position - return false; + { + Turntable *tto = Turntable::get(p[0]); + if (tto) { + bool type = tto->isEXTT(); + uint8_t position = tto->getPosition(); + StringFormatter::send(stream, F("\n"), type, position); + } else { + return false; + } + } + return true; case 2: // - rotate to position for DCC turntables { + Turntable *tto = Turntable::get(p[0]); if (p[1] == HASH_KEYWORD_DCC) { // Create a DCC turntable - DIAG(F("Create DCC turntable %d"), p[0]); - } else { // Otherwise move a DCC turntable + if (tto) return false; + if (!DCCTurntable::create(p[0])) return false; Turntable *tto = Turntable::get(p[0]); + tto->addPosition(0); + } else { // Otherwise move a DCC turntable if (tto) { - if (tto->getPosition() == p[1]) return true; - uint16_t value = tto->getPositionValue(p[1]); - if (value) { - DIAG(F("Rotate DCC turntable %d to position %d"), p[0], p[1]); - } else { - return false; - } + if (!tto->setPosition(p[0], p[1])) return false; } else { return false; } @@ -1100,34 +1107,24 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) case 3: { + Turntable *tto = Turntable::get(p[0]); + if (!tto) return false; if (p[1] == HASH_KEYWORD_ADD) { // add position value to turntable - Turntable *tto = Turntable::get(p[0]); - if (tto) { - tto->addPosition(p[2]); - StringFormatter::send(stream, F("\n")); - } else { - return false; - } + tto->addPosition(p[2]); + StringFormatter::send(stream, F("\n")); } else { // rotate to position for EX-Turntable - Turntable *tto = Turntable::get(p[0]); - if (tto) { - if (!tto->setPosition(p[0], p[1], p[2])) return false; - } else { - return false; - } + if (!tto->setPosition(p[0], p[1], p[2])) return false; } } return true; - case 5: // create an EXTT turntable + case 4: // create an EXTT turntable { if (p[1] == HASH_KEYWORD_EXTT) { if (Turntable::get(p[0])) return false; - if (!EXTTTurntable::create(p[0], (VPIN)p[2], (uint8_t)p[3])) return false; + if (!EXTTTurntable::create(p[0], (VPIN)p[2])) return false; Turntable *tto = Turntable::get(p[0]); - tto->addPosition(p[4]); - } else if (params > 3 && params < 39 && p[1] == HASH_KEYWORD_DCC) { - DIAG(F("Create DCC turntable %d at base address %d with %d positions"), p[0], p[2], params - 2); + tto->addPosition(p[3]); } else { return false; } diff --git a/IODevice.cpp b/IODevice.cpp index 2ed21b6..e811fff 100644 --- a/IODevice.cpp +++ b/IODevice.cpp @@ -176,6 +176,13 @@ bool IODevice::exists(VPIN vpin) { return findDevice(vpin) != NULL; } +// Return the status of the device att vpin. +uint8_t IODevice::getStatus(VPIN vpin) { + IODevice *dev = findDevice(vpin); + if (!dev) return false; + return dev->_deviceState; +} + // check whether the pin supports notification. If so, then regular _read calls are not required. bool IODevice::hasCallback(VPIN vpin) { IODevice *dev = findDevice(vpin); diff --git a/IODevice.h b/IODevice.h index d2c80a4..a03a64d 100644 --- a/IODevice.h +++ b/IODevice.h @@ -154,6 +154,9 @@ public: // exists checks whether there is a device owning the specified vpin static bool exists(VPIN vpin); + // getStatus returns the state of the device at the specified vpin + static uint8_t getStatus(VPIN vpin); + // Enable shared interrupt on specified pin for GPIO extender modules. The extender module // should pull down this pin when requesting a scan. The pin may be shared by multiple modules. // Without the shared interrupt, input states are scanned periodically to detect changes on @@ -405,9 +408,11 @@ private: void _begin() override; void _loop(unsigned long currentMicros) override; int _read(VPIN vpin) override; + void _broadcastStatus (VPIN vpin, uint8_t status); void _writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) override; void _display() override; uint8_t _stepperStatus; + uint8_t _previousStatus; }; ///////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index 8c7057b..fc1f9c1 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -34,6 +34,8 @@ #include "IODevice.h" #include "I2CManager.h" #include "DIAG.h" +#include "Turntables.h" +#include "CommandDistributor.h" void EXTurntable::create(VPIN firstVpin, int nPins, I2CAddress I2CAddress) { new EXTurntable(firstVpin, nPins, I2CAddress); @@ -44,6 +46,8 @@ EXTurntable::EXTurntable(VPIN firstVpin, int nPins, I2CAddress I2CAddress) { _firstVpin = firstVpin; _nPins = nPins; _I2CAddress = I2CAddress; + _stepperStatus = 0; + _previousStatus = 0; addDevice(this); } @@ -80,10 +84,22 @@ int EXTurntable::_read(VPIN vpin) { if (_stepperStatus > 1) { return false; } else { + if (_stepperStatus != _previousStatus) { + _broadcastStatus(vpin, _stepperStatus); + _previousStatus = _stepperStatus; + } return _stepperStatus; } } +// If a status change has occurred for a turntable object, broadcast it +void EXTurntable::_broadcastStatus (VPIN vpin, uint8_t status) { + Turntable *tto = Turntable::getByVpin(vpin); + if (tto) { + CommandDistributor::broadcastTurntable(tto->getId(), tto->getPosition(), status); + } +} + // writeAnalogue to send the steps and activity to Turntable-EX. // Sends 3 bytes containing the MSB and LSB of the step count, and activity. // value contains the steps, bit shifted to MSB + LSB. diff --git a/Turntables.cpp b/Turntables.cpp index a0419b1..3e33d2b 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -57,13 +57,6 @@ void Turntable::add(Turntable *tto) { turntablelistHash++; } -// Find turntable from list -Turntable *Turntable::get(uint16_t id) { - for (Turntable *tto = _firstTurntable; tto != NULL; tto = tto->_nextTurntable) - if (tto->_turntableData.id == id) return tto; - return NULL; -} - // Add a position void Turntable::addPosition(uint16_t value) { _turntablePositions.insert(value); @@ -95,16 +88,39 @@ uint8_t Turntable::getPositionCount() { /* * Public static functions */ +// Find turntable from list +Turntable *Turntable::get(uint16_t id) { + for (Turntable *tto = _firstTurntable; tto != nullptr; tto = tto->_nextTurntable) + if (tto->_turntableData.id == id) return tto; + return NULL; +} + +// Find turntable via Vpin +Turntable *Turntable::getByVpin(VPIN vpin) { + for (Turntable *tto = _firstTurntable; tto != nullptr; tto = tto->_nextTurntable) { + if (tto->isEXTT()) { + EXTTTurntable *exttTto = static_cast(tto); + if (exttTto->getVpin() == vpin) { + return tto; + } + } + } + return nullptr; +} + +// Broadcast position changes bool Turntable::setPositionStateOnly(uint16_t id, uint8_t position, bool moving) { Turntable *tto = get(id); if (!tto) return false; - CommandDistributor::broadcastTurntable(id, position, moving); + // Only need to broadcast from here if it's a DCC type, device driver broadcasts EXTT + if (!tto->isEXTT()) { CommandDistributor::broadcastTurntable(id, position, moving); } #if defined(EXRAIL_ACTIVE) // RMFT2::turntableEvent(id, position); #endif return true; } +// Initiate a turntable move bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { #if defined(DIAG_IO) DIAG(F("Turntable(%d, %d)"), id, position); @@ -116,12 +132,12 @@ bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { if (ok) { // Broadcast a position change only if non zero has been set, or home/calibration sent if (position > 0 || (position == 0 && (activity == 2 || activity == 3))) { - if (tto->getType() == TURNTABLE_EXTT) { + tto->_turntableData.position = position; + if (tto->isEXTT()) { tto->setPositionStateOnly(id, position, 1); } else { tto->setPositionStateOnly(id, position, 0); } - tto->_turntableData.position = position; } } return ok; @@ -132,38 +148,39 @@ bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { * *************************************************************************************/ // Private constructor -EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress) : +EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin) : Turntable(id, TURNTABLE_EXTT) { _exttTurntableData.vpin = vpin; - _exttTurntableData.i2caddress = i2caddress; } +using DevState = IODevice::DeviceStateEnum; + // Create function - Turntable *EXTTTurntable::create(uint16_t id, VPIN vpin, uint8_t i2caddress) { + Turntable *EXTTTurntable::create(uint16_t id, VPIN vpin) { #ifndef IO_NO_HAL Turntable *tto = get(id); if (tto) { if (tto->isType(TURNTABLE_EXTT)) { EXTTTurntable *extt = (EXTTTurntable *)tto; extt->_exttTurntableData.vpin = vpin; - extt->_exttTurntableData.i2caddress = i2caddress; return tto; } } - tto = (Turntable *)new EXTTTurntable(id, vpin, i2caddress); + if (!IODevice::exists(vpin)) return nullptr; + if (IODevice::getStatus(vpin) == DevState::DEVSTATE_FAILED) return nullptr; + tto = (Turntable *)new EXTTTurntable(id, vpin); DIAG(F("Turntable 0x%x size %d size %d"), tto, sizeof(Turntable), sizeof(struct TurntableData)); return tto; #else (void)id; - (void)i2caddress; (void)vpin; return NULL; #endif } void EXTTTurntable::print(Print *stream) { - StringFormatter::send(stream, F("\n"), _turntableData.id, _exttTurntableData.vpin, _exttTurntableData.i2caddress); + StringFormatter::send(stream, F("\n"), _turntableData.id, _exttTurntableData.vpin); } // EX-Turntable specific code for moving to the specified position @@ -185,4 +202,43 @@ EXTTTurntable::EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress) : return true; } +/************************************************************************************* + * DCCTurntable - DCC Turntable device. + * + *************************************************************************************/ +// Private constructor +DCCTurntable::DCCTurntable(uint16_t id) : Turntable(id, TURNTABLE_DCC) {} + +// Create function + Turntable *DCCTurntable::create(uint16_t id) { +#ifndef IO_NO_HAL + Turntable *tto = get(id); + if (!tto) { + tto = (Turntable *)new DCCTurntable(id); + DIAG(F("Turntable 0x%x size %d size %d"), tto, sizeof(Turntable), sizeof(struct TurntableData)); + } + return tto; +#else + (void)id; + return NULL; +#endif + } + + void DCCTurntable::print(Print *stream) { + StringFormatter::send(stream, F("\n"), _turntableData.id); + } + + // EX-Turntable specific code for moving to the specified position + bool DCCTurntable::setPositionInternal(uint8_t position, uint8_t activity) { +#ifndef IO_NO_HAL + int16_t value = getPositionValue(position); + if (position == 0 || !value) return false; // Return false if it's not a valid position + // Set position via device driver + // DCC activate function here +#else + (void)position; +#endif + return true; + } + #endif diff --git a/Turntables.h b/Turntables.h index d59bc80..e318724 100644 --- a/Turntables.h +++ b/Turntables.h @@ -135,6 +135,7 @@ protected: public: static Turntable *get(uint16_t id); + static Turntable *getByVpin(VPIN vpin); /* * Static data @@ -148,7 +149,7 @@ public: inline bool isHidden() { return _turntableData.hidden; } inline void setHidden(bool h) {_turntableData.hidden=h; } inline bool isType(uint8_t type) { return _turntableData.turntableType == type; } - inline uint8_t getType() { return _turntableData.turntableType; } + inline bool isEXTT() const { return _turntableData.turntableType == TURNTABLE_EXTT; } inline uint16_t getId() { return _turntableData.id; } inline Turntable *next() { return _nextTurntable; } void printState(Print *stream); @@ -193,16 +194,16 @@ private: // EXTTTurntableData contains device specific data struct EXTTTurntableData { VPIN vpin; - uint8_t i2caddress; } _exttTurntableData; // Constructor - EXTTTurntable(uint16_t id, VPIN vpin, uint8_t i2caddress); + EXTTTurntable(uint16_t id, VPIN vpin); public: // Create function - static Turntable *create(uint16_t id, VPIN vpin, uint8_t i2caddress); + static Turntable *create(uint16_t id, VPIN vpin); void print(Print *stream) override; + VPIN getVpin() const { return _exttTurntableData.vpin; } protected: // EX-Turntable specific code for setting position @@ -210,6 +211,26 @@ protected: }; +/************************************************************************************* + * DCCTurntable - DCC accessory Turntable device. + * + *************************************************************************************/ +class DCCTurntable : public Turntable { +private: + // Constructor + DCCTurntable(uint16_t id); + +public: + // Create function + static Turntable *create(uint16_t id); + void print(Print *stream) override; + +protected: + // DCC specific code for setting position + bool setPositionInternal(uint8_t position, uint8_t activity=0) override; + +}; + #endif #endif From df2f09f4d2efaaf61722d1f98072e1ee13330898 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Fri, 1 Sep 2023 09:04:48 +1000 Subject: [PATCH 094/218] Fix build errors --- IODevice.h | 2 ++ IO_EXTurntable.cpp | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/IODevice.h b/IODevice.h index a03a64d..4803f56 100644 --- a/IODevice.h +++ b/IODevice.h @@ -380,6 +380,7 @@ private: uint8_t *_pinInUse; }; +#ifndef IO_NO_HAL ///////////////////////////////////////////////////////////////////////////////////////////////////// /* * IODevice subclass for EX-Turntable. @@ -414,6 +415,7 @@ private: uint8_t _stepperStatus; uint8_t _previousStatus; }; +#endif ///////////////////////////////////////////////////////////////////////////////////////////////////// diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index fc1f9c1..8741c0c 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -28,8 +28,7 @@ * Refer to the documentation for further information including the valid activities. */ -#ifndef IO_EXTurntable_h -#define IO_EXTurntable_h +#ifndef IO_NO_HAL #include "IODevice.h" #include "I2CManager.h" From 798241927f1909e0fb30272f1d4170d842d7fb22 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Fri, 1 Sep 2023 13:28:24 +1000 Subject: [PATCH 095/218] Really fix build errors --- IO_EXTurntable.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index 8741c0c..bf06e62 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -28,14 +28,14 @@ * Refer to the documentation for further information including the valid activities. */ -#ifndef IO_NO_HAL - #include "IODevice.h" #include "I2CManager.h" #include "DIAG.h" #include "Turntables.h" #include "CommandDistributor.h" +#ifndef IO_NO_HAL + void EXTurntable::create(VPIN firstVpin, int nPins, I2CAddress I2CAddress) { new EXTurntable(firstVpin, nPins, I2CAddress); } From bcb250bacfc8b2e7a60810dd5b20d0f1da737863 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 1 Sep 2023 18:30:02 +1000 Subject: [PATCH 096/218] Broadcasts working --- IO_EXTurntable.cpp | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index bf06e62..59a65d9 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -71,6 +71,12 @@ void EXTurntable::_loop(unsigned long currentMicros) { uint8_t readBuffer[1]; I2CManager.read(_I2CAddress, readBuffer, 1); _stepperStatus = readBuffer[0]; + if (_stepperStatus < 2) { + if (_stepperStatus != _previousStatus) { + _broadcastStatus(_firstVpin, _stepperStatus); + _previousStatus = _stepperStatus; + } + } // DIAG(F("Turntable-EX returned status: %d"), _stepperStatus); delayUntil(currentMicros + 500000); // Wait 500ms before checking again, turntables turn slowly } @@ -83,10 +89,6 @@ int EXTurntable::_read(VPIN vpin) { if (_stepperStatus > 1) { return false; } else { - if (_stepperStatus != _previousStatus) { - _broadcastStatus(vpin, _stepperStatus); - _previousStatus = _stepperStatus; - } return _stepperStatus; } } From e734661d1b83eb0f3d79d5116e5e9404f07e4b36 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 2 Sep 2023 08:29:49 +1000 Subject: [PATCH 097/218] EXRAIL ready for testing --- EXRAIL2.cpp | 53 +++++++++++++++++++++++++++++++++++++++++++++ EXRAIL2.h | 8 +++++++ EXRAIL2MacroReset.h | 28 +++++++++++++++++------- EXRAILMacros.h | 32 ++++++++++++++++++++++++++- Turntables.cpp | 12 +++++++++- Turntables.h | 1 + 6 files changed, 124 insertions(+), 10 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 0e17ea9..c01189c 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -52,6 +52,7 @@ #include "Turnouts.h" #include "CommandDistributor.h" #include "TrackManager.h" +#include "Turntables.h" // Command parsing keywords const int16_t HASH_KEYWORD_EXRAIL=15435; @@ -94,6 +95,7 @@ LookList * RMFT2::onAmberLookup=NULL; LookList * RMFT2::onGreenLookup=NULL; LookList * RMFT2::onChangeLookup=NULL; LookList * RMFT2::onClockLookup=NULL; +LookList * RMFT2::onRotateLookup=NULL; #define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter) #define SKIPOP progCounter+=3 @@ -175,6 +177,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { onGreenLookup=LookListLoader(OPCODE_ONGREEN); onChangeLookup=LookListLoader(OPCODE_ONCHANGE); onClockLookup=LookListLoader(OPCODE_ONTIME); + onRotateLookup=LookListLoader(OPCODE_ONROTATE); // Second pass startup, define any turnouts or servos, set signals red @@ -238,6 +241,32 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { setTurnoutHiddenState(VpinTurnout::create(id,pin)); break; } + + case OPCODE_DCCTURNTABLE: { + VPIN id=operand; + setTurntableHiddenState(DCCTurntable::create(id)); + Turntable *tto=Turntable::get(id); + tto->addPosition(0); + break; + } + + case OPCODE_EXTTTURNTABLE: { + VPIN id=operand; + VPIN pin=getOperand(progCounter,1); + int home=getOperand(progCounter,2); + setTurntableHiddenState(EXTTTurntable::create(id,pin)); + Turntable *tto=Turntable::get(id); + tto->addPosition(home); + break; + } + + case OPCODE_TTADDPOSITION: { + VPIN id=operand; + int value=getOperand(progCounter,1); + Turntable *tto=Turntable::get(id); + tto->addPosition(value); + break; + } case OPCODE_AUTOSTART: // automatically create a task from here at startup. @@ -263,6 +292,10 @@ void RMFT2::setTurnoutHiddenState(Turnout * t) { t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01); } +void RMFT2::setTurntableHiddenState(Turntable * tto) { + tto->setHidden(GETFLASH(getTurntableDescription(tto->getId()))==0x01); +} + char RMFT2::getRouteType(int16_t id) { for (int16_t i=0;;i+=2) { int16_t rid= GETHIGHFLASHW(routeIdList,i); @@ -598,6 +631,13 @@ void RMFT2::loop2() { case OPCODE_CLOSE: Turnout::setClosed(operand, true); break; + + case OPCODE_ROTATE: + uint8_t activity; + activity=getOperand(2); + if (!activity) activity=0; + Turntable::setPosition(operand,getOperand(1),activity); + break; case OPCODE_REV: forward = false; @@ -788,6 +828,10 @@ void RMFT2::loop2() { case OPCODE_IFCLOSED: skipIf=Turnout::isThrown(operand); break; + + case OPCODE_IFTTPOSITION: // do block if turntable at this position + skipIf=Turntable::getPosition(operand)!=(int)getOperand(1); + break; case OPCODE_ENDIF: break; @@ -986,6 +1030,10 @@ void RMFT2::loop2() { case OPCODE_ONGREEN: case OPCODE_ONCHANGE: case OPCODE_ONTIME: + case OPCODE_DCCTURNTABLE: // Turntable definition ignored at runtime + case OPCODE_EXTTTURNTABLE: // Turntable definition ignored at runtime + case OPCODE_TTADDPOSITION: // Turntable position definition ignored at runtime + case OPCODE_ONROTATE: break; @@ -1130,6 +1178,11 @@ void RMFT2::changeEvent(int16_t vpin, bool change) { if (change) handleEvent(F("CHANGE"),onChangeLookup,vpin); } +void RMFT2::rotateEvent(int16_t turntableId, bool change) { + // Hunt or an ONROTATE for this turntable + if (change) handleEvent(F("ROTATE"),onRotateLookup,turntableId); +} + void RMFT2::clockEvent(int16_t clocktime, bool change) { // Hunt for an ONTIME for this time if (Diag::CMD) diff --git a/EXRAIL2.h b/EXRAIL2.h index 4d106e6..96bc7db 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -25,6 +25,7 @@ #include "FSH.h" #include "IODevice.h" #include "Turnouts.h" +#include "Turntables.h" // The following are the operation codes (or instructions) for a kind of virtual machine. // Each instruction is normally 3 bytes long with an operation code followed by a parameter. @@ -62,6 +63,8 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONCHANGE, OPCODE_ONCLOCKTIME, OPCODE_ONTIME, + OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE, + OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_IFTTPOSITION, // OPcodes below this point are skip-nesting IF operations // placed here so that they may be skipped as a group @@ -130,6 +133,7 @@ class LookList { static void activateEvent(int16_t addr, bool active); static void changeEvent(int16_t id, bool change); static void clockEvent(int16_t clocktime, bool change); + static void rotateEvent(int16_t id, bool change); static const int16_t SERVO_SIGNAL_FLAG=0x4000; static const int16_t ACTIVE_HIGH_SIGNAL_FLAG=0x2000; static const int16_t DCC_SIGNAL_FLAG=0x1000; @@ -144,6 +148,8 @@ class LookList { static const FSH * getTurnoutDescription(int16_t id); static const FSH * getRosterName(int16_t id); static const FSH * getRosterFunctions(int16_t id); + static const FSH * getTurntableDescription(int16_t id); + // static const FSH * getTurntablePositionDescription(int16_t id, uint8_t position); private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); @@ -156,6 +162,7 @@ private: static bool isSignal(int16_t id,char rag); static int16_t getSignalSlot(int16_t id); static void setTurnoutHiddenState(Turnout * t); + static void setTurntableHiddenState(Turntable * tto); static LookList* LookListLoader(OPCODE op1, OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); static void handleEvent(const FSH* reason,LookList* handlers, int16_t id); @@ -188,6 +195,7 @@ private: static LookList * onGreenLookup; static LookList * onChangeLookup; static LookList * onClockLookup; + static LookList * onRotateLookup; // Local variables - exist for each instance/task RMFT2 *next; // loop chain diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 588a417..7c690b7 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -40,6 +40,7 @@ #undef CALL #undef CLOSE #undef DCC_SIGNAL +#undef DCC_TURNTABLE #undef DEACTIVATE #undef DEACTIVATEL #undef DELAY @@ -51,8 +52,9 @@ #undef ENDEXRAIL #undef ENDIF #undef ENDTASK -#undef ESTOP -#undef EXRAIL +#undef ESTOP +#undef EXRAIL +#undef EXTT_TURNTABLE #undef FADE #undef FOFF #undef FOLLOW @@ -75,6 +77,7 @@ #undef IFRESERVE #undef IFTHROWN #undef IFTIMEOUT +#undef IFTTPOSITION #undef IFRE #undef INVERT_DIRECTION #undef JOIN @@ -95,6 +98,7 @@ #undef ONCLOCKMINS #undef ONGREEN #undef ONRED +#undef ONROTATE #undef ONTHROW #undef ONCHANGE #undef PARSE @@ -113,7 +117,8 @@ #undef RESUME #undef RETURN #undef REV -#undef ROSTER +#undef ROSTER +#undef ROTATE #undef ROUTE #undef SENDLOCO #undef SEQUENCE @@ -136,7 +141,8 @@ #undef SPEED #undef START #undef STOP -#undef THROW +#undef THROW +#undef TT_ADDPOSITION #undef TURNOUT #undef TURNOUTL #undef UNJOIN @@ -165,6 +171,7 @@ #define CALL(route) #define CLOSE(id) #define DCC_SIGNAL(id,add,subaddr) +#define DCC_TURNTABLE(id,description) #define DEACTIVATE(addr,subaddr) #define DEACTIVATEL(addr) #define DELAY(mindelay) @@ -177,7 +184,8 @@ #define ENDIF #define ENDTASK #define ESTOP -#define EXRAIL +#define EXRAIL +#define EXTT_TURNTABLE(id,vpin,home,description) #define FADE(pin,value,ms) #define FOFF(func) #define FOLLOW(route) @@ -200,6 +208,7 @@ #define IFTHROWN(turnout_id) #define IFRESERVE(block) #define IFTIMEOUT +#define IFTTPOSITION(turntable_id,position) #define IFRE(sensor_id,value) #define INVERT_DIRECTION #define JOIN @@ -219,7 +228,8 @@ #define ONDEACTIVATEL(linear) #define ONCLOSE(turnout_id) #define ONGREEN(signal_id) -#define ONRED(signal_id) +#define ONRED(signal_id) +#define ONROTATE(turntable_id) #define ONTHROW(turnout_id) #define ONCHANGE(sensor_id) #define PAUSE @@ -238,8 +248,9 @@ #define RESUME #define RETURN #define REV(speed) -#define ROUTE(id,description) +#define ROTATE(turntable_id,position,activity) #define ROSTER(cab,name,funcmap...) +#define ROUTE(id,description) #define SENDLOCO(cab,route) #define SEQUENCE(id) #define SERIAL(msg) @@ -261,7 +272,8 @@ #define SPEED(speed) #define START(route) #define STOP -#define THROW(id) +#define THROW(id) +#define TT_ADDPOSITION(turntable_id,value,description) #define TURNOUT(id,addr,subaddr,description...) #define TURNOUTL(id,addr,description...) #define UNJOIN diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 66b0111..fc8dbca 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -187,6 +187,30 @@ const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) { return NULL; } +// Pass to get turntable descriptions (optional) +#include "EXRAIL2MacroReset.h" +#undef DCC_TURNTABLE +#define DCC_TURNTABLE(id,description...) O_DESC(id,description) +#undef EXTT_TURNTABLE +#define EXTT_TURNTABLE(id,vpin,home,description...) O_DESC(id,description) + +const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { + switch (turntableId) { + #include "myAutomation.h" + default:break; + } + return NULL; +} + +// Pass to get turntable position descriptions (optional) +// #include "EXRAIL2MacroReset.h" +// #undef TT_ADDPOSITION +// #define TT_ADDPOSITION(turntable_id,value,description...) 0_DESC(id,description) + +// const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) { + +// } + // Pass 6: Roster IDs (count) #include "EXRAIL2MacroReset.h" #undef ROSTER @@ -268,6 +292,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define BROADCAST(msg) PRINT(msg) #define CALL(route) OPCODE_CALL,V(route), #define CLOSE(id) OPCODE_CLOSE,V(id), +#define DCC_TURNTABLE(id,description...) OPCODE_DCCTURNTABLE,V(id), #define DEACTIVATE(addr,subaddr) OPCODE_DCCACTIVATE,V(addr<<3 | subaddr<<1), #define DEACTIVATEL(addr) OPCODE_DCCACTIVATE,V((addr+3)<<1), #define DELAY(ms) ms<30000?OPCODE_DELAYMS:OPCODE_DELAY,V(ms/(ms<30000?1L:100L)), @@ -281,7 +306,8 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ENDIF OPCODE_ENDIF,0,0, #define ENDTASK OPCODE_ENDTASK,0,0, #define ESTOP OPCODE_SPEED,V(1), -#define EXRAIL +#define EXRAIL +#define EXTT_TURNTABLE(id,vpin,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(home), #define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L), #define FOFF(func) OPCODE_FOFF,V(func), #define FOLLOW(route) OPCODE_FOLLOW,V(route), @@ -304,6 +330,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define IFRESERVE(block) OPCODE_IFRESERVE,V(block), #define IFTHROWN(turnout_id) OPCODE_IFTHROWN,V(turnout_id), #define IFTIMEOUT OPCODE_IFTIMEOUT,0,0, +#define IFTTPOSITION(id,position) OPCODE_IFTTPOSITION,V(id),OPCODE_PAD,V(position), #define IFRE(sensor_id,value) OPCODE_IFRE,V(sensor_id),OPCODE_PAD,V(value), #define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,0,0, #define JOIN OPCODE_JOIN,0,0, @@ -324,6 +351,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3), #define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id), #define ONRED(signal_id) OPCODE_ONRED,V(signal_id), +#define ONROTATE(id) OPCODE_ONROTATE,V(id), #define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id), #define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id), #define PAUSE OPCODE_PAUSE,0,0, @@ -343,6 +371,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define RETURN OPCODE_RETURN,0,0, #define REV(speed) OPCODE_REV,V(speed), #define ROSTER(cabid,name,funcmap...) +#define ROTATE(id,position,activity) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(activity), #define ROUTE(id, description) OPCODE_ROUTE, V(id), #define SENDLOCO(cab,route) OPCODE_SENDLOCO,V(cab),OPCODE_PAD,V(route), #define SEQUENCE(id) OPCODE_SEQUENCE, V(id), @@ -366,6 +395,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define START(route) OPCODE_START,V(route), #define STOP OPCODE_SPEED,V(0), #define THROW(id) OPCODE_THROW,V(id), +#define TT_ADDPOSITION(id,value,description...) OPCODE_TTADDPOSITION,V(id),OPCODE_PAD,V(value), #define TURNOUT(id,addr,subaddr,description...) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr), #define TURNOUTL(id,addr,description...) TURNOUT(id,(addr-1)/4+1,(addr-1)%4, description) #define UNJOIN OPCODE_UNJOIN,0,0, diff --git a/Turntables.cpp b/Turntables.cpp index 3e33d2b..dcf50a0 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -108,6 +108,13 @@ Turntable *Turntable::getByVpin(VPIN vpin) { return nullptr; } +// Get the current position for turntable with the specified ID +uint8_t Turntable::getPosition(uint16_t id) { + Turntable *tto = get(id); + if (!tto) return false; + return tto->getPosition(); +} + // Broadcast position changes bool Turntable::setPositionStateOnly(uint16_t id, uint8_t position, bool moving) { Turntable *tto = get(id); @@ -234,7 +241,10 @@ DCCTurntable::DCCTurntable(uint16_t id) : Turntable(id, TURNTABLE_DCC) {} int16_t value = getPositionValue(position); if (position == 0 || !value) return false; // Return false if it's not a valid position // Set position via device driver - // DCC activate function here + int16_t addr=value>>3; + int16_t subaddr=(value>>1) & 0x03; + bool active=value & 0x01; + DCC::setAccessory(addr, subaddr, active); #else (void)position; #endif diff --git a/Turntables.h b/Turntables.h index e318724..b95c9c0 100644 --- a/Turntables.h +++ b/Turntables.h @@ -172,6 +172,7 @@ public: inline static bool exists(uint16_t id) { return get(id) != 0; } static bool setPosition(uint16_t id, uint8_t position, uint8_t activity=0); static bool setPositionStateOnly(uint16_t id, uint8_t position, bool moving); + static uint8_t getPosition(uint16_t id); inline static Turntable *first() { return _firstTurntable; } static bool printAll(Print *stream) { bool gotOne = false; From 004d10ee586a5efb5eb6ae7c2704b75e4fe8b100 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 2 Sep 2023 18:45:59 +1000 Subject: [PATCH 098/218] Fix build errors --- EXRAIL2.cpp | 14 +++++++++++--- EXRAIL2.h | 2 ++ EXRAILMacros.h | 12 ++++++++++++ 3 files changed, 25 insertions(+), 3 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index c01189c..77a9f88 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -242,6 +242,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { break; } +#ifndef IO_NO_HAL case OPCODE_DCCTURNTABLE: { VPIN id=operand; setTurntableHiddenState(DCCTurntable::create(id)); @@ -267,7 +268,8 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { tto->addPosition(value); break; } - +#endif + case OPCODE_AUTOSTART: // automatically create a task from here at startup. // Removed if (progCounter>0) check 4.2.31 because @@ -292,9 +294,11 @@ void RMFT2::setTurnoutHiddenState(Turnout * t) { t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01); } +#ifndef IO_NO_HAL void RMFT2::setTurntableHiddenState(Turntable * tto) { tto->setHidden(GETFLASH(getTurntableDescription(tto->getId()))==0x01); } +#endif char RMFT2::getRouteType(int16_t id) { for (int16_t i=0;;i+=2) { @@ -631,13 +635,15 @@ void RMFT2::loop2() { case OPCODE_CLOSE: Turnout::setClosed(operand, true); break; - + +#ifndef IO_NO_HAL case OPCODE_ROTATE: uint8_t activity; activity=getOperand(2); if (!activity) activity=0; Turntable::setPosition(operand,getOperand(1),activity); break; +#endif case OPCODE_REV: forward = false; @@ -829,10 +835,12 @@ void RMFT2::loop2() { skipIf=Turnout::isThrown(operand); break; +#ifndef IO_NO_HAL case OPCODE_IFTTPOSITION: // do block if turntable at this position skipIf=Turntable::getPosition(operand)!=(int)getOperand(1); break; - +#endif + case OPCODE_ENDIF: break; diff --git a/EXRAIL2.h b/EXRAIL2.h index 96bc7db..16fff82 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -162,7 +162,9 @@ private: static bool isSignal(int16_t id,char rag); static int16_t getSignalSlot(int16_t id); static void setTurnoutHiddenState(Turnout * t); + #ifndef IO_NO_HAL static void setTurntableHiddenState(Turntable * tto); + #endif static LookList* LookListLoader(OPCODE op1, OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); static void handleEvent(const FSH* reason,LookList* handlers, int16_t id); diff --git a/EXRAILMacros.h b/EXRAILMacros.h index fc8dbca..f5d905e 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -292,7 +292,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define BROADCAST(msg) PRINT(msg) #define CALL(route) OPCODE_CALL,V(route), #define CLOSE(id) OPCODE_CLOSE,V(id), +#ifndef IO_NO_HAL #define DCC_TURNTABLE(id,description...) OPCODE_DCCTURNTABLE,V(id), +#endif #define DEACTIVATE(addr,subaddr) OPCODE_DCCACTIVATE,V(addr<<3 | subaddr<<1), #define DEACTIVATEL(addr) OPCODE_DCCACTIVATE,V((addr+3)<<1), #define DELAY(ms) ms<30000?OPCODE_DELAYMS:OPCODE_DELAY,V(ms/(ms<30000?1L:100L)), @@ -307,7 +309,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ENDTASK OPCODE_ENDTASK,0,0, #define ESTOP OPCODE_SPEED,V(1), #define EXRAIL +#ifndef IO_NO_HAL #define EXTT_TURNTABLE(id,vpin,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(home), +#endif #define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L), #define FOFF(func) OPCODE_FOFF,V(func), #define FOLLOW(route) OPCODE_FOLLOW,V(route), @@ -330,7 +334,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define IFRESERVE(block) OPCODE_IFRESERVE,V(block), #define IFTHROWN(turnout_id) OPCODE_IFTHROWN,V(turnout_id), #define IFTIMEOUT OPCODE_IFTIMEOUT,0,0, +#ifndef IO_NO_HAL #define IFTTPOSITION(id,position) OPCODE_IFTTPOSITION,V(id),OPCODE_PAD,V(position), +#endif #define IFRE(sensor_id,value) OPCODE_IFRE,V(sensor_id),OPCODE_PAD,V(value), #define INVERT_DIRECTION OPCODE_INVERT_DIRECTION,0,0, #define JOIN OPCODE_JOIN,0,0, @@ -351,7 +357,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ONDEACTIVATEL(linear) OPCODE_ONDEACTIVATE,V(linear+3), #define ONGREEN(signal_id) OPCODE_ONGREEN,V(signal_id), #define ONRED(signal_id) OPCODE_ONRED,V(signal_id), +#ifndef IO_NO_HAL #define ONROTATE(id) OPCODE_ONROTATE,V(id), +#endif #define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id), #define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id), #define PAUSE OPCODE_PAUSE,0,0, @@ -371,7 +379,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define RETURN OPCODE_RETURN,0,0, #define REV(speed) OPCODE_REV,V(speed), #define ROSTER(cabid,name,funcmap...) +#ifndef IO_NO_HAL #define ROTATE(id,position,activity) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(activity), +#endif #define ROUTE(id, description) OPCODE_ROUTE, V(id), #define SENDLOCO(cab,route) OPCODE_SENDLOCO,V(cab),OPCODE_PAD,V(route), #define SEQUENCE(id) OPCODE_SEQUENCE, V(id), @@ -395,7 +405,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define START(route) OPCODE_START,V(route), #define STOP OPCODE_SPEED,V(0), #define THROW(id) OPCODE_THROW,V(id), +#ifndef IO_NO_HAL #define TT_ADDPOSITION(id,value,description...) OPCODE_TTADDPOSITION,V(id),OPCODE_PAD,V(value), +#endif #define TURNOUT(id,addr,subaddr,description...) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr), #define TURNOUTL(id,addr,description...) TURNOUT(id,(addr-1)/4+1,(addr-1)%4, description) #define UNJOIN OPCODE_UNJOIN,0,0, From 1e48c59cd8d4b0a16370731078e60e8abb949999 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 3 Sep 2023 18:54:56 +1000 Subject: [PATCH 099/218] Capture progress --- IO_EXTurntable.cpp | 12 +++++------- Turntables.cpp | 5 ++++- Turntables.h | 3 +++ 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index 59a65d9..eba8321 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -71,14 +71,11 @@ void EXTurntable::_loop(unsigned long currentMicros) { uint8_t readBuffer[1]; I2CManager.read(_I2CAddress, readBuffer, 1); _stepperStatus = readBuffer[0]; - if (_stepperStatus < 2) { - if (_stepperStatus != _previousStatus) { - _broadcastStatus(_firstVpin, _stepperStatus); - _previousStatus = _stepperStatus; - } + if (_stepperStatus != _previousStatus && _stepperStatus == 0) { // Broadcast when a rotation finishes + _broadcastStatus(_firstVpin, _stepperStatus); + _previousStatus = _stepperStatus; } - // DIAG(F("Turntable-EX returned status: %d"), _stepperStatus); - delayUntil(currentMicros + 500000); // Wait 500ms before checking again, turntables turn slowly + delayUntil(currentMicros + 100000); // Wait 100ms before checking again } // Read returns status as obtained in our loop. @@ -128,6 +125,7 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_ _I2CAddress.toString(), stepsMSB, stepsLSB, activity); #endif _stepperStatus = 1; // Tell the device driver Turntable-EX is busy + _broadcastStatus(vpin, _stepperStatus); // Broadcast when the rotation starts I2CManager.write(_I2CAddress, 3, stepsMSB, stepsLSB, activity); } diff --git a/Turntables.cpp b/Turntables.cpp index dcf50a0..f7b2a46 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -122,7 +122,9 @@ bool Turntable::setPositionStateOnly(uint16_t id, uint8_t position, bool moving) // Only need to broadcast from here if it's a DCC type, device driver broadcasts EXTT if (!tto->isEXTT()) { CommandDistributor::broadcastTurntable(id, position, moving); } #if defined(EXRAIL_ACTIVE) - // RMFT2::turntableEvent(id, position); + bool rotated = false; + if (position != tto->_previousPosition) rotated = true; + if (!tto->isEXTT()) { RMFT2::rotateEvent(id, rotated); } #endif return true; } @@ -138,6 +140,7 @@ bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { if (ok) { // Broadcast a position change only if non zero has been set, or home/calibration sent + tto->_previousPosition = tto->getPosition(); if (position > 0 || (position == 0 && (activity == 2 || activity == 3))) { tto->_turntableData.position = position; if (tto->isEXTT()) { diff --git a/Turntables.h b/Turntables.h index b95c9c0..67ab0e3 100644 --- a/Turntables.h +++ b/Turntables.h @@ -105,6 +105,9 @@ protected: // Linked list for positions TurntablePositionList _turntablePositions; + + // Store the previous position to allow checking for changes + uint8_t _previousPosition = 0; /* * Constructor From dd890e65bfe2b307ff19c501ac3b87c69f0b8256 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Mon, 4 Sep 2023 07:38:26 +1000 Subject: [PATCH 100/218] Add move check --- IO_EXTurntable.cpp | 1 + Turntables.h | 5 +++++ 2 files changed, 6 insertions(+) diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index eba8321..dcbe018 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -95,6 +95,7 @@ void EXTurntable::_broadcastStatus (VPIN vpin, uint8_t status) { Turntable *tto = Turntable::getByVpin(vpin); if (tto) { CommandDistributor::broadcastTurntable(tto->getId(), tto->getPosition(), status); + tto->setMoving(status); } } diff --git a/Turntables.h b/Turntables.h index 67ab0e3..30cba82 100644 --- a/Turntables.h +++ b/Turntables.h @@ -109,6 +109,9 @@ protected: // Store the previous position to allow checking for changes uint8_t _previousPosition = 0; + // Store the current state of the turntable + bool _isMoving = false; + /* * Constructor */ @@ -159,6 +162,8 @@ public: void addPosition(uint16_t value); uint16_t getPositionValue(uint8_t position); uint8_t getPositionCount(); + bool isMoving() { return _isMoving; } + void setMoving(bool moving) { _isMoving=moving; } /* * Virtual functions From 86f45675565ab4df0d4beddd0d07e52b06c340b1 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Mon, 4 Sep 2023 18:46:28 +1000 Subject: [PATCH 101/218] Revisiting logic --- DCCEXParser.cpp | 9 +++++---- Turntables.cpp | 3 ++- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 7473c26..2014762 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1087,7 +1087,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) } return true; - case 2: // - rotate to position for DCC turntables + case 2: // | - rotate or create DCC turntable { Turntable *tto = Turntable::get(p[0]); if (p[1] == HASH_KEYWORD_DCC) { // Create a DCC turntable @@ -1096,7 +1096,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) Turntable *tto = Turntable::get(p[0]); tto->addPosition(0); } else { // Otherwise move a DCC turntable - if (tto) { + if (tto && !tto->isEXTT()) { if (!tto->setPosition(p[0], p[1])) return false; } else { return false; @@ -1105,14 +1105,15 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) } return true; - case 3: + case 3: // | { Turntable *tto = Turntable::get(p[0]); if (!tto) return false; - if (p[1] == HASH_KEYWORD_ADD) { // add position value to turntable + if (p[1] == HASH_KEYWORD_ADD) { // Add position value to turntable tto->addPosition(p[2]); StringFormatter::send(stream, F("\n")); } else { // rotate to position for EX-Turntable + if (!tto->isEXTT()) return false; if (!tto->setPosition(p[0], p[1], p[2])) return false; } } diff --git a/Turntables.cpp b/Turntables.cpp index f7b2a46..f96a3a8 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -140,8 +140,8 @@ bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { if (ok) { // Broadcast a position change only if non zero has been set, or home/calibration sent - tto->_previousPosition = tto->getPosition(); if (position > 0 || (position == 0 && (activity == 2 || activity == 3))) { + tto->_previousPosition = tto->getPosition(); tto->_turntableData.position = position; if (tto->isEXTT()) { tto->setPositionStateOnly(id, position, 1); @@ -179,6 +179,7 @@ using DevState = IODevice::DeviceStateEnum; } if (!IODevice::exists(vpin)) return nullptr; if (IODevice::getStatus(vpin) == DevState::DEVSTATE_FAILED) return nullptr; + if (Turntable::getByVpin(vpin)) return nullptr; tto = (Turntable *)new EXTTTurntable(id, vpin); DIAG(F("Turntable 0x%x size %d size %d"), tto, sizeof(Turntable), sizeof(struct TurntableData)); return tto; From 3094c52bf8509d600fa829995d8cf7be263b6365 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Tue, 5 Sep 2023 08:38:37 +1000 Subject: [PATCH 102/218] Ready to test --- IO_EXTurntable.cpp | 2 +- Turntables.cpp | 39 +++++++++++++++------------------------ Turntables.h | 1 - 3 files changed, 16 insertions(+), 26 deletions(-) diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index dcbe018..b36237f 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -94,8 +94,8 @@ int EXTurntable::_read(VPIN vpin) { void EXTurntable::_broadcastStatus (VPIN vpin, uint8_t status) { Turntable *tto = Turntable::getByVpin(vpin); if (tto) { - CommandDistributor::broadcastTurntable(tto->getId(), tto->getPosition(), status); tto->setMoving(status); + CommandDistributor::broadcastTurntable(tto->getId(), tto->getPosition(), status); } } diff --git a/Turntables.cpp b/Turntables.cpp index f96a3a8..120ac2f 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -115,40 +115,27 @@ uint8_t Turntable::getPosition(uint16_t id) { return tto->getPosition(); } -// Broadcast position changes -bool Turntable::setPositionStateOnly(uint16_t id, uint8_t position, bool moving) { - Turntable *tto = get(id); - if (!tto) return false; - // Only need to broadcast from here if it's a DCC type, device driver broadcasts EXTT - if (!tto->isEXTT()) { CommandDistributor::broadcastTurntable(id, position, moving); } -#if defined(EXRAIL_ACTIVE) - bool rotated = false; - if (position != tto->_previousPosition) rotated = true; - if (!tto->isEXTT()) { RMFT2::rotateEvent(id, rotated); } -#endif - return true; -} - // Initiate a turntable move bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { #if defined(DIAG_IO) - DIAG(F("Turntable(%d, %d)"), id, position); + DIAG(F("Rotate turntable %d to position %d, activity %d)"), id, position, activity); #endif Turntable *tto = Turntable::get(id); if (!tto) return false; + if (tto->isMoving()) return false; bool ok = tto->setPositionInternal(position, activity); if (ok) { - // Broadcast a position change only if non zero has been set, or home/calibration sent - if (position > 0 || (position == 0 && (activity == 2 || activity == 3))) { - tto->_previousPosition = tto->getPosition(); - tto->_turntableData.position = position; - if (tto->isEXTT()) { - tto->setPositionStateOnly(id, position, 1); - } else { - tto->setPositionStateOnly(id, position, 0); - } + // We only deal with broadcasts for DCC turntables here, EXTT in the device driver + if (!tto->isEXTT()) { + CommandDistributor::broadcastTurntable(id, position, false); } + // Trigger EXRAIL rotateEvent for both types here if changed +#if defined(EXRAIL_ACTIVE) + bool rotated = false; + if (position != tto->_previousPosition) rotated = true; + RMFT2::rotateEvent(id, rotated); +#endif } return ok; } @@ -206,6 +193,8 @@ using DevState = IODevice::DeviceStateEnum; } if (position > 0 && !value) return false; // Return false if it's not a valid position // Set position via device driver + _previousPosition = _turntableData.position; + _turntableData.position = position; EXTurntable::writeAnalogue(_exttTurntableData.vpin, value, activity); #else (void)position; @@ -248,6 +237,8 @@ DCCTurntable::DCCTurntable(uint16_t id) : Turntable(id, TURNTABLE_DCC) {} int16_t addr=value>>3; int16_t subaddr=(value>>1) & 0x03; bool active=value & 0x01; + _previousPosition = _turntableData.position; + _turntableData.position = position; DCC::setAccessory(addr, subaddr, active); #else (void)position; diff --git a/Turntables.h b/Turntables.h index 30cba82..de0151c 100644 --- a/Turntables.h +++ b/Turntables.h @@ -179,7 +179,6 @@ public: */ inline static bool exists(uint16_t id) { return get(id) != 0; } static bool setPosition(uint16_t id, uint8_t position, uint8_t activity=0); - static bool setPositionStateOnly(uint16_t id, uint8_t position, bool moving); static uint8_t getPosition(uint16_t id); inline static Turntable *first() { return _firstTurntable; } static bool printAll(Print *stream) { From 152f9850bbd0bd8c1ff7f5cfe4a8c5a8ecad8b90 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 5 Sep 2023 19:05:18 +1000 Subject: [PATCH 103/218] Working --- IO_EXTurntable.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index b36237f..af220a3 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -82,7 +82,6 @@ void EXTurntable::_loop(unsigned long currentMicros) { // Return false if our status value is invalid. int EXTurntable::_read(VPIN vpin) { if (_deviceState == DEVSTATE_FAILED) return 0; - // DIAG(F("_read status: %d"), _stepperStatus); if (_stepperStatus > 1) { return false; } else { @@ -126,6 +125,7 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_ _I2CAddress.toString(), stepsMSB, stepsLSB, activity); #endif _stepperStatus = 1; // Tell the device driver Turntable-EX is busy + _previousStatus = _stepperStatus; _broadcastStatus(vpin, _stepperStatus); // Broadcast when the rotation starts I2CManager.write(_I2CAddress, 3, stepsMSB, stepsLSB, activity); } From 2f8e915b1e19c088bc742fdddcd4bbb110ff3e73 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Tue, 5 Sep 2023 12:21:09 +0100 Subject: [PATCH 104/218] Added AFTEROVERLOAD Added the AFTEROVERLOAD option - as yet untested. --- EXRAIL2.cpp | 13 ++++++++++++- EXRAIL2.h | 3 ++- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 1 + TrackManager.cpp | 8 ++++++++ TrackManager.h | 3 +++ 6 files changed, 28 insertions(+), 2 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index ac277a5..f9935d7 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -197,6 +197,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { case OPCODE_AT: case OPCODE_ATTIMEOUT2: case OPCODE_AFTER: + case OPCODE_AFTEROVERLOAD: case OPCODE_IF: case OPCODE_IFNOT: { int16_t pin = (int16_t)operand; @@ -686,7 +687,17 @@ void RMFT2::loop2() { } if (millis()-waitAfter < 500 ) return; break; - + + case OPCODE_AFTEROVERLOAD: // waits for the power to be turned back on - either by power routine or button + if (!TrackManager::isPowerOn(operand)) { + // reset timer to half a second and keep waiting + waitAfter=millis(); + delayMe(50); + return; + } + if (millis()-waitAfter < 500 ) return; + break; + case OPCODE_LATCH: setFlag(operand,LATCH_FLAG); break; diff --git a/EXRAIL2.h b/EXRAIL2.h index 700cd6b..6e00561 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -35,7 +35,8 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_FWD,OPCODE_REV,OPCODE_SPEED,OPCODE_INVERT_DIRECTION, OPCODE_RESERVE,OPCODE_FREE, - OPCODE_AT,OPCODE_AFTER,OPCODE_AUTOSTART, + OPCODE_AT,OPCODE_AFTER, + OPCODE_AFTEROVERLOAD,OPCODE_AUTOSTART, OPCODE_ATGTE,OPCODE_ATLT, OPCODE_ATTIMEOUT1,OPCODE_ATTIMEOUT2, OPCODE_LATCH,OPCODE_UNLATCH,OPCODE_SET,OPCODE_RESET, diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 5ac3ab7..22cf562 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -27,6 +27,7 @@ #undef ACTIVATE #undef ACTIVATEL #undef AFTER +#undef AFTEROVERLOAD #undef ALIAS #undef AMBER #undef ANOUT @@ -153,6 +154,7 @@ #define ACTIVATE(addr,subaddr) #define ACTIVATEL(addr) #define AFTER(sensor_id) +#define AFTEROVERLOAD(track_id) #define ALIAS(name,value...) #define AMBER(signal_id) #define ANOUT(vpin,value,param1,param2) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 69b5995..d1cad0c 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -256,6 +256,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ACTIVATE(addr,subaddr) OPCODE_DCCACTIVATE,V(addr<<3 | subaddr<<1 | 1), #define ACTIVATEL(addr) OPCODE_DCCACTIVATE,V((addr+3)<<1 | 1), #define AFTER(sensor_id) OPCODE_AT,V(sensor_id),OPCODE_AFTER,V(sensor_id), +#define AFTEROVERLOAD(track_id) OPCODE_AFTEROVERLOAD,V(TRACK_NUMBER_##track_id), #define ALIAS(name,value...) #define AMBER(signal_id) OPCODE_AMBER,V(signal_id), #define ANOUT(vpin,value,param1,param2) OPCODE_SERVO,V(vpin),OPCODE_PAD,V(value),OPCODE_PAD,V(param1),OPCODE_PAD,V(param2), diff --git a/TrackManager.cpp b/TrackManager.cpp index 0f69235..9942563 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -1,6 +1,7 @@ /* * © 2022 Chris Harlow * © 2022 Harald Barth + * © 2023 Colin Murdoch * All rights reserved. * * This file is part of DCC++EX @@ -513,3 +514,10 @@ void TrackManager::setJoin(bool joined) { progTrackSyncMain=joined; if (joinRelay!=UNUSED_PIN) digitalWrite(joinRelay,joined?HIGH:LOW); } + +bool TrackManager::isPowerOn(byte t) { + if (track[t]->getPower()==POWERMODE::ON) + return true; + return false; + } + diff --git a/TrackManager.h b/TrackManager.h index 965cfa3..60d5f24 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -1,6 +1,8 @@ /* * © 2022 Chris Harlow * © 2022 Harald Barth + * © 2023 Colin Murdoch + * * All rights reserved. * * This file is part of CommandStation-EX @@ -77,6 +79,7 @@ class TrackManager { static void reportCurrent(Print* stream); static void reportObsoleteCurrent(Print* stream); static void streamTrackState(Print* stream, byte t); + static bool isPowerOn(byte t); static int16_t joinRelay; static bool progTrackSyncMain; // true when prog track is a siding switched to main From 1ac104704e5922860cd1f76178c00fb64e3123f6 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Tue, 5 Sep 2023 20:52:18 +0100 Subject: [PATCH 105/218] Update TrackManager.cpp Reverse logic in TM::IisPowerOn() --- TrackManager.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index 9942563..edaac6e 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -516,8 +516,8 @@ void TrackManager::setJoin(bool joined) { } bool TrackManager::isPowerOn(byte t) { - if (track[t]->getPower()==POWERMODE::ON) - return true; - return false; + if (track[t]->getPower()!=POWERMODE::ON) + return false; + return true; } From 6adff43f4b0f5b7231ca61e555318232c1fd98f5 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Wed, 6 Sep 2023 07:59:43 +1000 Subject: [PATCH 106/218] Update add position --- DCCEXParser.cpp | 24 ++++++++++++------------ EXRAIL2.cpp | 9 +++++---- EXRAIL2MacroReset.h | 2 +- EXRAILMacros.h | 2 +- Turntables.cpp | 4 ++-- Turntables.h | 9 ++++----- 6 files changed, 25 insertions(+), 25 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 2014762..de1618c 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1094,7 +1094,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) if (tto) return false; if (!DCCTurntable::create(p[0])) return false; Turntable *tto = Turntable::get(p[0]); - tto->addPosition(0); + tto->addPosition(0, 0); } else { // Otherwise move a DCC turntable if (tto && !tto->isEXTT()) { if (!tto->setPosition(p[0], p[1])) return false; @@ -1105,27 +1105,27 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) } return true; - case 3: // | + case 3: // rotate to position for EX-Turntable { Turntable *tto = Turntable::get(p[0]); if (!tto) return false; - if (p[1] == HASH_KEYWORD_ADD) { // Add position value to turntable - tto->addPosition(p[2]); - StringFormatter::send(stream, F("\n")); - } else { // rotate to position for EX-Turntable - if (!tto->isEXTT()) return false; - if (!tto->setPosition(p[0], p[1], p[2])) return false; - } + if (!tto->isEXTT()) return false; + if (!tto->setPosition(p[0], p[1], p[2])) return false; } return true; - case 4: // create an EXTT turntable + case 4: // | create an EXTT turntable or add position { + Turntable *tto = Turntable::get(p[0]); if (p[1] == HASH_KEYWORD_EXTT) { - if (Turntable::get(p[0])) return false; + if (tto) return false; if (!EXTTTurntable::create(p[0], (VPIN)p[2])) return false; Turntable *tto = Turntable::get(p[0]); - tto->addPosition(p[3]); + tto->addPosition(0, p[3]); + } else if (p[1] == HASH_KEYWORD_ADD) { + if (!tto) return false; + tto->addPosition(p[2], p[3]); + StringFormatter::send(stream, F("\n")); } else { return false; } diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 77a9f88..d88a6f4 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -247,7 +247,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { VPIN id=operand; setTurntableHiddenState(DCCTurntable::create(id)); Turntable *tto=Turntable::get(id); - tto->addPosition(0); + tto->addPosition(0,0); break; } @@ -257,15 +257,16 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { int home=getOperand(progCounter,2); setTurntableHiddenState(EXTTTurntable::create(id,pin)); Turntable *tto=Turntable::get(id); - tto->addPosition(home); + tto->addPosition(0,home); break; } case OPCODE_TTADDPOSITION: { VPIN id=operand; - int value=getOperand(progCounter,1); + int position=getOperand(progCounter,1); + int value=getOperand(progCounter,2); Turntable *tto=Turntable::get(id); - tto->addPosition(value); + tto->addPosition(position,value); break; } #endif diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 7c690b7..21d4f18 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -273,7 +273,7 @@ #define START(route) #define STOP #define THROW(id) -#define TT_ADDPOSITION(turntable_id,value,description) +#define TT_ADDPOSITION(turntable_id,position,value,description) #define TURNOUT(id,addr,subaddr,description...) #define TURNOUTL(id,addr,description...) #define UNJOIN diff --git a/EXRAILMacros.h b/EXRAILMacros.h index f5d905e..68f3bf0 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -406,7 +406,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define STOP OPCODE_SPEED,V(0), #define THROW(id) OPCODE_THROW,V(id), #ifndef IO_NO_HAL -#define TT_ADDPOSITION(id,value,description...) OPCODE_TTADDPOSITION,V(id),OPCODE_PAD,V(value), +#define TT_ADDPOSITION(id,position,value,description...) OPCODE_TTADDPOSITION,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(value), #endif #define TURNOUT(id,addr,subaddr,description...) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr), #define TURNOUTL(id,addr,description...) TURNOUT(id,(addr-1)/4+1,(addr-1)%4, description) diff --git a/Turntables.cpp b/Turntables.cpp index 120ac2f..abbac81 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -58,8 +58,8 @@ void Turntable::add(Turntable *tto) { } // Add a position -void Turntable::addPosition(uint16_t value) { - _turntablePositions.insert(value); +void Turntable::addPosition(uint8_t idx, uint16_t value) { + _turntablePositions.insert(idx, value); } // Get value for position diff --git a/Turntables.h b/Turntables.h index de0151c..15333c9 100644 --- a/Turntables.h +++ b/Turntables.h @@ -53,10 +53,10 @@ struct TurntablePosition { class TurntablePositionList { public: - TurntablePositionList() : head(nullptr), currentIndex(0) {} + TurntablePositionList() : head(nullptr) {} - void insert(uint16_t value) { - TurntablePosition* newPosition = new TurntablePosition(currentIndex++, value); + void insert(uint8_t idx, uint16_t value) { + TurntablePosition* newPosition = new TurntablePosition(idx, value); if(!head) { head = newPosition; } else { @@ -71,7 +71,6 @@ public: private: TurntablePosition* head; - uint8_t currentIndex; }; @@ -159,7 +158,7 @@ public: inline uint16_t getId() { return _turntableData.id; } inline Turntable *next() { return _nextTurntable; } void printState(Print *stream); - void addPosition(uint16_t value); + void addPosition(uint8_t idx, uint16_t value); uint16_t getPositionValue(uint8_t position); uint8_t getPositionCount(); bool isMoving() { return _isMoving; } From 1f5f7754c1a4b5c928fadf10046715cd4099aea5 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Wed, 6 Sep 2023 15:16:46 +1000 Subject: [PATCH 107/218] Start on position description --- DCCEXParser.cpp | 5 +++-- EXRAIL2.h | 2 +- EXRAILMacros.h | 8 ++++++-- 3 files changed, 10 insertions(+), 5 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index de1618c..404685d 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -712,10 +712,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) uint8_t posCount = tto->getPositionCount(); const FSH *todesc = NULL; #ifdef EXRAIL_ACTIVE - // todesc = RMFT2::getTurntableDescription(id); + todesc = RMFT2::getTurntableDescription(id); #endif if (todesc == NULL) todesc = F(""); - StringFormatter::send(stream, F(" %d %d %d"), id, type, pos); + StringFormatter::send(stream, F(" %d %d %d"), id, type, pos, todesc); + for (uint8_t p = 0; p < posCount; p++) { int16_t value = tto->getPositionValue(p); StringFormatter::send(stream, F(" %d"), value); diff --git a/EXRAIL2.h b/EXRAIL2.h index 16fff82..f6c4807 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -149,7 +149,7 @@ class LookList { static const FSH * getRosterName(int16_t id); static const FSH * getRosterFunctions(int16_t id); static const FSH * getTurntableDescription(int16_t id); - // static const FSH * getTurntablePositionDescription(int16_t id, uint8_t position); + static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId); private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 68f3bf0..c413989 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -205,10 +205,14 @@ const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { // Pass to get turntable position descriptions (optional) // #include "EXRAIL2MacroReset.h" // #undef TT_ADDPOSITION -// #define TT_ADDPOSITION(turntable_id,value,description...) 0_DESC(id,description) +// #define TT_ADDPOSITION(turntable_id,value,description...) O_DESC(turntable_id,description) // const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) { - +// switch (turntableId) { +// #include "myAutomation.h" +// default:break; +// } +// return NULL; // } // Pass 6: Roster IDs (count) From ab393047c10c4ec041347982d27e24fa0296805b Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Wed, 6 Sep 2023 11:20:23 +0100 Subject: [PATCH 108/218] Update MotorDriver.cpp Replace duplicate call to EXRAIL with single in overload. --- MotorDriver.cpp | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index e3ecd64..4644ad5 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -576,8 +576,6 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { DIAG(F("TRACK %c FAULT PIN detected after %4M. Pause %4M)"), trackno + 'A', mslpc, power_sample_overload_wait); throttleInrush(false); setPower(POWERMODE::OVERLOAD); - DIAG(F("Calling EXRAIL")); - RMFT2::powerEvent(trackno, true); // Tell EXRAIL we have an overload break; } if (checkCurrent(useProgLimit)) { @@ -595,8 +593,6 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { trackno + 'A', mA, maxmA, mslpc, power_sample_overload_wait); throttleInrush(false); setPower(POWERMODE::OVERLOAD); - DIAG(F("Calling EXRAIL")); - RMFT2::powerEvent(trackno, true); // Tell EXRAIL we have an overload break; } // all well @@ -619,7 +615,9 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { // adjust next wait time power_sample_overload_wait *= 2; if (power_sample_overload_wait > POWER_SAMPLE_RETRY_MAX) - power_sample_overload_wait = POWER_SAMPLE_RETRY_MAX; + power_sample_overload_wait = POWER_SAMPLE_RETRY_MAX; + DIAG(F("Calling EXRAIL")); + RMFT2::powerEvent(trackno, true); // Tell EXRAIL we have an overload // power on test DIAG(F("TRACK %c POWER RESTORE (after %4M)"), trackno + 'A', mslpc); setPower(POWERMODE::ALERT); From 21ce87eb3e72a7d8925ea5fec407222ffbd2ce6c Mon Sep 17 00:00:00 2001 From: peteGSX Date: Thu, 7 Sep 2023 05:33:26 +1000 Subject: [PATCH 109/218] Descriptions available --- DCCEXParser.cpp | 13 ++++++++++--- EXRAILMacros.h | 19 +++++++++---------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 404685d..405512c 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -702,6 +702,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) if (tto->isHidden()) continue; StringFormatter::send(stream, F(" %d"),tto->getId()); } + StringFormatter::send(stream, F(">\n")); } else { // Turntable *tto=Turntable::get(id); if (!tto || tto->isHidden()) { @@ -711,19 +712,25 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) uint8_t type = tto->isEXTT(); uint8_t posCount = tto->getPositionCount(); const FSH *todesc = NULL; + const FSH *tpdesc = NULL; #ifdef EXRAIL_ACTIVE todesc = RMFT2::getTurntableDescription(id); #endif if (todesc == NULL) todesc = F(""); - StringFormatter::send(stream, F(" %d %d %d"), id, type, pos, todesc); + StringFormatter::send(stream, F(" %d %d %d %d \"%S\">\n"), id, type, pos, posCount, todesc); for (uint8_t p = 0; p < posCount; p++) { + StringFormatter::send(stream, F("jO>")); int16_t value = tto->getPositionValue(p); - StringFormatter::send(stream, F(" %d"), value); +#ifdef EXRAIL_ACTIVE + tpdesc = RMFT2::getTurntablePositionDescription(id, p); +#endif + if (tpdesc == NULL) todesc = F(""); + StringFormatter::send(stream, F(" %d \"%S\""), value, tpdesc); + StringFormatter::send(stream, F(">\n")); } } } - StringFormatter::send(stream, F(">\n")); return; #endif default: break; diff --git a/EXRAILMacros.h b/EXRAILMacros.h index c413989..97604c5 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -54,6 +54,8 @@ // helper macro for turnout descriptions, creates NULL for missing description #define O_DESC(id, desc) case id: return ("" desc)[0]?F("" desc):NULL; +// helper macro for turntable descriptions, creates NULL for missing description +#define T_DESC(tid,pid,desc) if(turntableId==tid && positionId==pid) return ("" desc)[0]?F("" desc):NULL; // helper macro for turnout description as HIDDEN #define HIDDEN "\x01" @@ -203,17 +205,14 @@ const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { } // Pass to get turntable position descriptions (optional) -// #include "EXRAIL2MacroReset.h" -// #undef TT_ADDPOSITION -// #define TT_ADDPOSITION(turntable_id,value,description...) O_DESC(turntable_id,description) +#include "EXRAIL2MacroReset.h" +#undef TT_ADDPOSITION +#define TT_ADDPOSITION(turntable_id,position,value,description...) T_DESC(turntable_id,position,description) -// const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) { -// switch (turntableId) { -// #include "myAutomation.h" -// default:break; -// } -// return NULL; -// } +const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) { + #include "myAutomation.h" + return NULL; +} // Pass 6: Roster IDs (count) #include "EXRAIL2MacroReset.h" From 004d7b66317f34a43006e4319cdbc7bf3a0f066d Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 7 Sep 2023 07:32:54 +1000 Subject: [PATCH 110/218] JO and JP working --- DCCEXParser.cpp | 24 ++++++++++++++++++------ platformio.ini | 2 +- 2 files changed, 19 insertions(+), 7 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 405512c..94b711e 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -86,6 +86,7 @@ const int16_t HASH_KEYWORD_C='C'; const int16_t HASH_KEYWORD_G='G'; const int16_t HASH_KEYWORD_I='I'; const int16_t HASH_KEYWORD_O='O'; +const int16_t HASH_KEYWORD_P='P'; const int16_t HASH_KEYWORD_R='R'; const int16_t HASH_KEYWORD_T='T'; const int16_t HASH_KEYWORD_X='X'; @@ -706,30 +707,41 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } else { // Turntable *tto=Turntable::get(id); if (!tto || tto->isHidden()) { - StringFormatter::send(stream, F(" %d X"), id); + StringFormatter::send(stream, F(" %d X>\n"), id); } else { uint8_t pos = tto->getPosition(); uint8_t type = tto->isEXTT(); uint8_t posCount = tto->getPositionCount(); const FSH *todesc = NULL; - const FSH *tpdesc = NULL; #ifdef EXRAIL_ACTIVE todesc = RMFT2::getTurntableDescription(id); #endif if (todesc == NULL) todesc = F(""); StringFormatter::send(stream, F(" %d %d %d %d \"%S\">\n"), id, type, pos, posCount, todesc); - + } + } + return; + case HASH_KEYWORD_P: // returns turntable position list for the turntable id + if (params==2) { // + Turntable *tto=Turntable::get(id); + if (!tto || tto->isHidden()) { + StringFormatter::send(stream, F(" %d X>\n"), id); + } else { + uint8_t posCount = tto->getPositionCount(); + const FSH *tpdesc = NULL; for (uint8_t p = 0; p < posCount; p++) { - StringFormatter::send(stream, F("jO>")); + StringFormatter::send(stream, F("getPositionValue(p); #ifdef EXRAIL_ACTIVE tpdesc = RMFT2::getTurntablePositionDescription(id, p); #endif - if (tpdesc == NULL) todesc = F(""); - StringFormatter::send(stream, F(" %d \"%S\""), value, tpdesc); + if (tpdesc == NULL) tpdesc = F(""); + StringFormatter::send(stream, F(" %d %d %d \"%S\""), id, p, value, tpdesc); StringFormatter::send(stream, F(">\n")); } } + } else { + StringFormatter::send(stream, F("\n")); } return; #endif diff --git a/platformio.ini b/platformio.ini index 1a87770..ac2d598 100644 --- a/platformio.ini +++ b/platformio.ini @@ -30,7 +30,7 @@ include_dir = . [env] build_flags = -Wall -Wextra -monitor_filters = time +; monitor_filters = time ; lib_deps = adafruit/Adafruit ST7735 and ST7789 Library @ ^1.10.0 [env:samd21-dev-usb] From bd02d1c15bf4cc35848d07b15c5630dcc352ebd8 Mon Sep 17 00:00:00 2001 From: peteGSX <97784652+peteGSX@users.noreply.github.com> Date: Thu, 7 Sep 2023 07:58:19 +1000 Subject: [PATCH 111/218] WAITFORTT ready for testing --- EXRAIL2.cpp | 20 ++++++++++++++++++-- EXRAIL2.h | 6 +++++- EXRAIL2MacroReset.h | 6 ++++++ EXRAILMacros.h | 3 +++ Turntables.cpp | 6 ++++++ Turntables.h | 1 + 6 files changed, 39 insertions(+), 3 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index d88a6f4..2d379a7 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -95,7 +95,9 @@ LookList * RMFT2::onAmberLookup=NULL; LookList * RMFT2::onGreenLookup=NULL; LookList * RMFT2::onChangeLookup=NULL; LookList * RMFT2::onClockLookup=NULL; +#ifndef IO_NO_HAL LookList * RMFT2::onRotateLookup=NULL; +#endif #define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter) #define SKIPOP progCounter+=3 @@ -177,8 +179,9 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { onGreenLookup=LookListLoader(OPCODE_ONGREEN); onChangeLookup=LookListLoader(OPCODE_ONCHANGE); onClockLookup=LookListLoader(OPCODE_ONTIME); +#ifndef IO_NO_HAL onRotateLookup=LookListLoader(OPCODE_ONROTATE); - +#endif // Second pass startup, define any turnouts or servos, set signals red // add sequences onRoutines to the lookups @@ -1014,7 +1017,16 @@ void RMFT2::loop2() { return; } break; - + +#ifndef IO_NO_HAL + case OPCODE_WAITFORTT: // OPCODE_WAITFOR,V(turntable_id) + if (Turntable::ttMoving(operand)) { + delayMe(100); + return; + } + break; +#endif + case OPCODE_PRINT: printMessage(operand); break; @@ -1039,10 +1051,12 @@ void RMFT2::loop2() { case OPCODE_ONGREEN: case OPCODE_ONCHANGE: case OPCODE_ONTIME: +#ifndef IO_NO_HAL case OPCODE_DCCTURNTABLE: // Turntable definition ignored at runtime case OPCODE_EXTTTURNTABLE: // Turntable definition ignored at runtime case OPCODE_TTADDPOSITION: // Turntable position definition ignored at runtime case OPCODE_ONROTATE: +#endif break; @@ -1187,10 +1201,12 @@ void RMFT2::changeEvent(int16_t vpin, bool change) { if (change) handleEvent(F("CHANGE"),onChangeLookup,vpin); } +#ifndef IO_NO_HAL void RMFT2::rotateEvent(int16_t turntableId, bool change) { // Hunt or an ONROTATE for this turntable if (change) handleEvent(F("ROTATE"),onRotateLookup,turntableId); } +#endif void RMFT2::clockEvent(int16_t clocktime, bool change) { // Hunt for an ONTIME for this time diff --git a/EXRAIL2.h b/EXRAIL2.h index f6c4807..5247e3e 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -63,8 +63,10 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONCHANGE, OPCODE_ONCLOCKTIME, OPCODE_ONTIME, +#ifndef IO_NO_HAL OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE, - OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_IFTTPOSITION, + OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_IFTTPOSITION,OPCODE_WAITFORTT, +#endif // OPcodes below this point are skip-nesting IF operations // placed here so that they may be skipped as a group @@ -197,7 +199,9 @@ private: static LookList * onGreenLookup; static LookList * onChangeLookup; static LookList * onClockLookup; +#ifndef IO_NO_HAL static LookList * onRotateLookup; +#endif // Local variables - exist for each instance/task RMFT2 *next; // loop chain diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 21d4f18..efe91a5 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -150,6 +150,9 @@ #undef VIRTUAL_SIGNAL #undef VIRTUAL_TURNOUT #undef WAITFOR +#ifndef IO_NO_HAL +#undef WAITFORTT +#endif #undef WITHROTTLE #undef XFOFF #undef XFON @@ -281,6 +284,9 @@ #define VIRTUAL_SIGNAL(id) #define VIRTUAL_TURNOUT(id,description...) #define WAITFOR(pin) +#ifndef IO_NO_HAL +#define WAITFORTT(turntable_id) +#endif #define WITHROTTLE(msg) #define XFOFF(cab,func) #define XFON(cab,func) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 97604c5..e3ce129 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -419,6 +419,9 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define VIRTUAL_TURNOUT(id,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(0), #define WITHROTTLE(msg) PRINT(msg) #define WAITFOR(pin) OPCODE_WAITFOR,V(pin), +#ifndef IO_NO_HAL +#define WAITFORTT(turntable_id) OPCODE_WAITFORTT,V(turntable_id), +#endif #define XFOFF(cab,func) OPCODE_XFOFF,V(cab),OPCODE_PAD,V(func), #define XFON(cab,func) OPCODE_XFON,V(cab),OPCODE_PAD,V(func), diff --git a/Turntables.cpp b/Turntables.cpp index abbac81..d43a515 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -115,6 +115,12 @@ uint8_t Turntable::getPosition(uint16_t id) { return tto->getPosition(); } +bool Turntable::ttMoving(uint16_t id) { + Turntable *tto = get(id); + if (!tto) return false; + return tto->isMoving(); +} + // Initiate a turntable move bool Turntable::setPosition(uint16_t id, uint8_t position, uint8_t activity) { #if defined(DIAG_IO) diff --git a/Turntables.h b/Turntables.h index 15333c9..e7b7415 100644 --- a/Turntables.h +++ b/Turntables.h @@ -179,6 +179,7 @@ public: inline static bool exists(uint16_t id) { return get(id) != 0; } static bool setPosition(uint16_t id, uint8_t position, uint8_t activity=0); static uint8_t getPosition(uint16_t id); + static bool ttMoving(uint16_t id); inline static Turntable *first() { return _firstTurntable; } static bool printAll(Print *stream) { bool gotOne = false; From dca023ffd7f78f002a4979700a9896f033256a0b Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Thu, 7 Sep 2023 20:27:25 +0100 Subject: [PATCH 112/218] Update version.h Added ONOVERLOAD and AFTEROVERLOAD as 5.1.4 --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index d03eab8..abcc577 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.3" +#define VERSION "5.1.4" +// 5.1.4 - Added ONOVERLOAD & AFTEROVERLOAD to EXRAIL // 5.1.3 - Make parser more fool proof // 5.1.2 - Bugfix: ESP32 30ms off time // 5.1.1 - Check bad AT firmware version From be10be5a1a9e55ef6bbb35cfac156966f5612a45 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 9 Sep 2023 07:22:10 +1000 Subject: [PATCH 113/218] Added angles --- DCCEXParser.cpp | 57 +++++++++++++++++++++++++++------------------ EXRAIL2.cpp | 9 ++++--- EXRAIL2MacroReset.h | 6 +++-- EXRAILMacros.h | 11 +++++---- Turntables.cpp | 17 ++++++++++++-- Turntables.h | 13 +++++------ 6 files changed, 71 insertions(+), 42 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 94b711e..40ea635 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -732,11 +732,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) for (uint8_t p = 0; p < posCount; p++) { StringFormatter::send(stream, F("getPositionValue(p); + int16_t angle = tto->getPositionAngle(p); #ifdef EXRAIL_ACTIVE tpdesc = RMFT2::getTurntablePositionDescription(id, p); #endif if (tpdesc == NULL) tpdesc = F(""); - StringFormatter::send(stream, F(" %d %d %d \"%S\""), id, p, value, tpdesc); + StringFormatter::send(stream, F(" %d %d %d %d \"%S\""), id, p, value, angle, tpdesc); StringFormatter::send(stream, F(">\n")); } } @@ -1107,44 +1108,54 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) } return true; - case 2: // | - rotate or create DCC turntable + case 2: // - rotate a DCC turntable { Turntable *tto = Turntable::get(p[0]); - if (p[1] == HASH_KEYWORD_DCC) { // Create a DCC turntable - if (tto) return false; - if (!DCCTurntable::create(p[0])) return false; - Turntable *tto = Turntable::get(p[0]); - tto->addPosition(0, 0); - } else { // Otherwise move a DCC turntable - if (tto && !tto->isEXTT()) { - if (!tto->setPosition(p[0], p[1])) return false; - } else { - return false; - } + if (tto && !tto->isEXTT()) { + if (!tto->setPosition(p[0], p[1])) return false; + } else { + return false; } } return true; - case 3: // rotate to position for EX-Turntable + case 3: // | - rotate to position for EX-Turntable or create DCC turntable { Turntable *tto = Turntable::get(p[0]); - if (!tto) return false; - if (!tto->isEXTT()) return false; - if (!tto->setPosition(p[0], p[1], p[2])) return false; + if (p[1] == HASH_KEYWORD_DCC) { + if (tto || p[2] < 0 || p[2] > 3600) return false; + if (!DCCTurntable::create(p[0])) return false; + Turntable *tto = Turntable::get(p[0]); + tto->addPosition(0, 0, p[2]); + } else { + if (!tto) return false; + if (!tto->isEXTT()) return false; + if (!tto->setPosition(p[0], p[1], p[2])) return false; + } } return true; - case 4: // | create an EXTT turntable or add position + case 4: // create an EXTT turntable { Turntable *tto = Turntable::get(p[0]); if (p[1] == HASH_KEYWORD_EXTT) { - if (tto) return false; + if (tto || p[3] < 0 || p[3] > 3600) return false; if (!EXTTTurntable::create(p[0], (VPIN)p[2])) return false; Turntable *tto = Turntable::get(p[0]); - tto->addPosition(0, p[3]); - } else if (p[1] == HASH_KEYWORD_ADD) { - if (!tto) return false; - tto->addPosition(p[2], p[3]); + tto->addPosition(0, 0, p[3]); + } else { + return false; + } + } + return true; + + case 5: // add a position + { + Turntable *tto = Turntable::get(p[0]); + if (p[1] == HASH_KEYWORD_ADD) { + // tto must exist, no more than 48 positions, angle 0 - 3600 + if (!tto || p[2] > 48 || p[4] < 0 || p[4] > 3600) return false; + tto->addPosition(p[2], p[3], p[4]); StringFormatter::send(stream, F("\n")); } else { return false; diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 2d379a7..32d2795 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -53,6 +53,7 @@ #include "CommandDistributor.h" #include "TrackManager.h" #include "Turntables.h" +#include "IODevice.h" // Command parsing keywords const int16_t HASH_KEYWORD_EXRAIL=15435; @@ -248,9 +249,10 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { #ifndef IO_NO_HAL case OPCODE_DCCTURNTABLE: { VPIN id=operand; + int home=getOperand(progCounter,1); setTurntableHiddenState(DCCTurntable::create(id)); Turntable *tto=Turntable::get(id); - tto->addPosition(0,0); + tto->addPosition(0,0,home); break; } @@ -260,7 +262,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { int home=getOperand(progCounter,2); setTurntableHiddenState(EXTTTurntable::create(id,pin)); Turntable *tto=Turntable::get(id); - tto->addPosition(0,home); + tto->addPosition(0,0,home); break; } @@ -268,8 +270,9 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { VPIN id=operand; int position=getOperand(progCounter,1); int value=getOperand(progCounter,2); + int angle=getOperand(progCounter,3); Turntable *tto=Turntable::get(id); - tto->addPosition(position,value); + tto->addPosition(position,value,angle); break; } #endif diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index efe91a5..b54a9d9 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -119,6 +119,7 @@ #undef REV #undef ROSTER #undef ROTATE +#undef ROTATE_DCC #undef ROUTE #undef SENDLOCO #undef SEQUENCE @@ -174,7 +175,7 @@ #define CALL(route) #define CLOSE(id) #define DCC_SIGNAL(id,add,subaddr) -#define DCC_TURNTABLE(id,description) +#define DCC_TURNTABLE(id,home,description) #define DEACTIVATE(addr,subaddr) #define DEACTIVATEL(addr) #define DELAY(mindelay) @@ -252,6 +253,7 @@ #define RETURN #define REV(speed) #define ROTATE(turntable_id,position,activity) +#define ROTATE_DCC(turntable_id,position) #define ROSTER(cab,name,funcmap...) #define ROUTE(id,description) #define SENDLOCO(cab,route) @@ -276,7 +278,7 @@ #define START(route) #define STOP #define THROW(id) -#define TT_ADDPOSITION(turntable_id,position,value,description) +#define TT_ADDPOSITION(turntable_id,position,value,angle,description...) #define TURNOUT(id,addr,subaddr,description...) #define TURNOUTL(id,addr,description...) #define UNJOIN diff --git a/EXRAILMacros.h b/EXRAILMacros.h index e3ce129..440a619 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -192,7 +192,7 @@ const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) { // Pass to get turntable descriptions (optional) #include "EXRAIL2MacroReset.h" #undef DCC_TURNTABLE -#define DCC_TURNTABLE(id,description...) O_DESC(id,description) +#define DCC_TURNTABLE(id,home,description...) O_DESC(id,description) #undef EXTT_TURNTABLE #define EXTT_TURNTABLE(id,vpin,home,description...) O_DESC(id,description) @@ -207,7 +207,7 @@ const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { // Pass to get turntable position descriptions (optional) #include "EXRAIL2MacroReset.h" #undef TT_ADDPOSITION -#define TT_ADDPOSITION(turntable_id,position,value,description...) T_DESC(turntable_id,position,description) +#define TT_ADDPOSITION(turntable_id,position,value,home,description...) T_DESC(turntable_id,position,description) const FSH * RMFT2::getTurntablePositionDescription(int16_t turntableId, uint8_t positionId) { #include "myAutomation.h" @@ -296,7 +296,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define CALL(route) OPCODE_CALL,V(route), #define CLOSE(id) OPCODE_CLOSE,V(id), #ifndef IO_NO_HAL -#define DCC_TURNTABLE(id,description...) OPCODE_DCCTURNTABLE,V(id), +#define DCC_TURNTABLE(id,home,description...) OPCODE_DCCTURNTABLE,V(id),OPCODE_PAD,V(home), #endif #define DEACTIVATE(addr,subaddr) OPCODE_DCCACTIVATE,V(addr<<3 | subaddr<<1), #define DEACTIVATEL(addr) OPCODE_DCCACTIVATE,V((addr+3)<<1), @@ -383,7 +383,8 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define REV(speed) OPCODE_REV,V(speed), #define ROSTER(cabid,name,funcmap...) #ifndef IO_NO_HAL -#define ROTATE(id,position,activity) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(activity), +#define ROTATE(id,position,activity) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(EXTurntable::activity), +#define ROTATE_DCC(id,position) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(0), #endif #define ROUTE(id, description) OPCODE_ROUTE, V(id), #define SENDLOCO(cab,route) OPCODE_SENDLOCO,V(cab),OPCODE_PAD,V(route), @@ -409,7 +410,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define STOP OPCODE_SPEED,V(0), #define THROW(id) OPCODE_THROW,V(id), #ifndef IO_NO_HAL -#define TT_ADDPOSITION(id,position,value,description...) OPCODE_TTADDPOSITION,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(value), +#define TT_ADDPOSITION(id,position,value,angle,description...) OPCODE_TTADDPOSITION,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(value),OPCODE_PAD,V(angle), #endif #define TURNOUT(id,addr,subaddr,description...) OPCODE_TURNOUT,V(id),OPCODE_PAD,V(addr),OPCODE_PAD,V(subaddr), #define TURNOUTL(id,addr,description...) TURNOUT(id,(addr-1)/4+1,(addr-1)%4, description) diff --git a/Turntables.cpp b/Turntables.cpp index d43a515..ba143cb 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -58,8 +58,8 @@ void Turntable::add(Turntable *tto) { } // Add a position -void Turntable::addPosition(uint8_t idx, uint16_t value) { - _turntablePositions.insert(idx, value); +void Turntable::addPosition(uint8_t idx, uint16_t value, uint16_t angle) { + _turntablePositions.insert(idx, value, angle); } // Get value for position @@ -74,6 +74,18 @@ uint16_t Turntable::getPositionValue(uint8_t position) { return false; } +// Get value for position +uint16_t Turntable::getPositionAngle(uint8_t position) { + TurntablePosition* currentPosition = _turntablePositions.getHead(); + while (currentPosition) { + if (currentPosition->index == position) { + return currentPosition->angle; + } + currentPosition = currentPosition->next; + } + return false; +} + // Get the count of positions associated with the turntable uint8_t Turntable::getPositionCount() { TurntablePosition* currentPosition = _turntablePositions.getHead(); @@ -115,6 +127,7 @@ uint8_t Turntable::getPosition(uint16_t id) { return tto->getPosition(); } +// Got the moving state of the specified turntable bool Turntable::ttMoving(uint16_t id) { Turntable *tto = get(id); if (!tto) return false; diff --git a/Turntables.h b/Turntables.h index e7b7415..6c15bab 100644 --- a/Turntables.h +++ b/Turntables.h @@ -36,9 +36,6 @@ enum { TURNTABLE_DCC = 1, }; -// Callback needs to return a bool: 1 = moving, 0 = stopped -typedef void (*EXTT_CALLBACK)(bool moving); - /************************************************************************************* * Turntable positions. * @@ -46,17 +43,18 @@ typedef void (*EXTT_CALLBACK)(bool moving); struct TurntablePosition { uint8_t index; uint16_t data; + uint16_t angle; TurntablePosition* next; - TurntablePosition(uint8_t idx, uint16_t value) : index(idx), data(value), next(nullptr) {} + TurntablePosition(uint8_t idx, uint16_t value, uint16_t angle) : index(idx), data(value), angle(angle), next(nullptr) {} }; class TurntablePositionList { public: TurntablePositionList() : head(nullptr) {} - void insert(uint8_t idx, uint16_t value) { - TurntablePosition* newPosition = new TurntablePosition(idx, value); + void insert(uint8_t idx, uint16_t value, uint16_t angle) { + TurntablePosition* newPosition = new TurntablePosition(idx, value, angle); if(!head) { head = newPosition; } else { @@ -158,8 +156,9 @@ public: inline uint16_t getId() { return _turntableData.id; } inline Turntable *next() { return _nextTurntable; } void printState(Print *stream); - void addPosition(uint8_t idx, uint16_t value); + void addPosition(uint8_t idx, uint16_t value, uint16_t angle); uint16_t getPositionValue(uint8_t position); + uint16_t getPositionAngle(uint8_t position); uint8_t getPositionCount(); bool isMoving() { return _isMoving; } void setMoving(bool moving) { _isMoving=moving; } From dba5d35aa231ebbe732a755462f8b95043554c64 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sat, 9 Sep 2023 09:23:10 +1000 Subject: [PATCH 114/218] Add response to create --- DCCEXParser.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 40ea635..05e0eca 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1127,6 +1127,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) if (!DCCTurntable::create(p[0])) return false; Turntable *tto = Turntable::get(p[0]); tto->addPosition(0, 0, p[2]); + StringFormatter::send(stream, F("\n")); } else { if (!tto) return false; if (!tto->isEXTT()) return false; @@ -1143,6 +1144,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) if (!EXTTTurntable::create(p[0], (VPIN)p[2])) return false; Turntable *tto = Turntable::get(p[0]); tto->addPosition(0, 0, p[3]); + StringFormatter::send(stream, F("\n")); } else { return false; } From 7ee2c29a526078d2f5a395adc0cd671f1d1df0a9 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 10 Sep 2023 05:30:48 +1000 Subject: [PATCH 115/218] Include HAL create with EXTT_TURNTABLE --- EXRAIL2.cpp | 2 +- EXRAIL2MacroReset.h | 2 +- EXRAILMacros.h | 9 ++++++--- 3 files changed, 8 insertions(+), 5 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 32d2795..c8ff8f3 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -259,7 +259,7 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { case OPCODE_EXTTTURNTABLE: { VPIN id=operand; VPIN pin=getOperand(progCounter,1); - int home=getOperand(progCounter,2); + int home=getOperand(progCounter,3); setTurntableHiddenState(EXTTTurntable::create(id,pin)); Turntable *tto=Turntable::get(id); tto->addPosition(0,0,home); diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index b54a9d9..c8ff395 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -189,7 +189,7 @@ #define ENDTASK #define ESTOP #define EXRAIL -#define EXTT_TURNTABLE(id,vpin,home,description) +#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description) #define FADE(pin,value,ms) #define FOFF(func) #define FOLLOW(route) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 440a619..39e5dda 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -69,10 +69,13 @@ #define ALIAS(name,value...) const int name= 1##value##0 ==10 ? -__COUNTER__ : value##0/10; #include "myAutomation.h" -// Pass 1h Implements HAL macro by creating exrailHalSetup function +// Pass 1h Implements HAL macro by creating exrailHalSetup function +// Also allows creating EXTurntable object #include "EXRAIL2MacroReset.h" #undef HAL #define HAL(haltype,params...) haltype::create(params); +#undef EXTT_TURNTABLE +#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) EXTurntable::create(vpin,1,i2c_address); void exrailHalSetup() { #include "myAutomation.h" } @@ -194,7 +197,7 @@ const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) { #undef DCC_TURNTABLE #define DCC_TURNTABLE(id,home,description...) O_DESC(id,description) #undef EXTT_TURNTABLE -#define EXTT_TURNTABLE(id,vpin,home,description...) O_DESC(id,description) +#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) O_DESC(id,description) const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { switch (turntableId) { @@ -313,7 +316,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ESTOP OPCODE_SPEED,V(1), #define EXRAIL #ifndef IO_NO_HAL -#define EXTT_TURNTABLE(id,vpin,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(home), +#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(i2c_address),OPCODE_PAD,V(home), #endif #define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L), #define FOFF(func) OPCODE_FOFF,V(func), From a0562fdf5c0e1af6ffb0a95091535d827d3e0343 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 10 Sep 2023 07:06:27 +1000 Subject: [PATCH 116/218] Update defines to match changes in devel --- defines.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/defines.h b/defines.h index 3286aca..49aff71 100644 --- a/defines.h +++ b/defines.h @@ -218,8 +218,12 @@ // Define symbol IO_NO_HAL to reduce FLASH footprint when HAL features not required // The HAL is disabled by default on Nano and Uno platforms, because of limited flash space. // -#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) -#define IO_NO_HAL +#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) + #if defined(DISABLE_DIAG) && defined(DISABLE_EEPROM) && defined(DISABLE_PROG) + #warning you have sacrificed DIAG for HAL + #else + #define IO_NO_HAL + #endif #endif #if __has_include ( "myAutomation.h") From 4fcd81a118a1ee8b89410bd5a30f2b0d73b4c61c Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 10 Sep 2023 07:18:54 +1000 Subject: [PATCH 117/218] Update version --- version.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index abcc577..e8ee138 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,10 @@ #include "StringFormatter.h" -#define VERSION "5.1.4" +#define VERSION "5.1.5" +// 5.1.5 - Added turntable object and EXRAIL commands +// - , , - turntable commands +// - DCC_TURNTABLE, EXTT_TURNTABLE, IFTTPOSITION, ONROTATE, ROTATE, ROTATE_DCC, TT_ADDPOSITION, WAITFORTT EXRAIL // 5.1.4 - Added ONOVERLOAD & AFTEROVERLOAD to EXRAIL // 5.1.3 - Make parser more fool proof // 5.1.2 - Bugfix: ESP32 30ms off time From da6a3c442f66d8802112b3b676ec47228f0a94b1 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Sun, 10 Sep 2023 18:41:14 +1000 Subject: [PATCH 118/218] Remove redundant line --- EXRAIL2.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index fae6add..d6d2597 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -650,7 +650,6 @@ void RMFT2::loop2() { case OPCODE_ROTATE: uint8_t activity; activity=getOperand(2); - if (!activity) activity=0; Turntable::setPosition(operand,getOperand(1),activity); break; #endif From ebbeea5fbb8198ed7d3e7679b4dea6bd212a002b Mon Sep 17 00:00:00 2001 From: pmantoine Date: Wed, 13 Sep 2023 16:46:36 +0800 Subject: [PATCH 119/218] STM32F4xx native I2C driver merge --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index e8ee138..c8f6a68 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.5" +#define VERSION "5.1.6" +// 5.1.6 - STM32F4xx native I2C driver added // 5.1.5 - Added turntable object and EXRAIL commands // - , , - turntable commands // - DCC_TURNTABLE, EXTT_TURNTABLE, IFTTPOSITION, ONROTATE, ROTATE, ROTATE_DCC, TT_ADDPOSITION, WAITFORTT EXRAIL From 46289fa78cc1db6dd02b52da36a405aa662ef2a9 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 11 Aug 2023 00:07:02 +0200 Subject: [PATCH 120/218] Check bad AT firmware version --- WifiInterface.cpp | 16 +++++++++++++++- version.h | 3 ++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 7511af6..ab36957 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -200,7 +200,21 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password, // Display the AT version information StringFormatter::send(wifiStream, F("AT+GMR\r\n")); - checkForOK(2000, true, false); // Makes this visible on the console + if (checkForOK(2000, F("AT version:"), true, false)) { + char version[] = "0.0.0.0"; + for (int i=0; i<8;i++) { + while(!wifiStream->available()); + version[i]=wifiStream->read(); + StringFormatter::printEscape(version[i]); + if ((version[0] == '0') || + (version[0] == '2' && version[2] == '0') || + (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) { + SSid = F("DCCEX_SAYS_BROKEN_FIRMWARE"); + forceAP = true; + } + } + } + checkForOK(2000, true, false); #ifdef DONT_TOUCH_WIFI_CONF DIAG(F("DONT_TOUCH_WIFI_CONF was set: Using existing config")); diff --git a/version.h b/version.h index d52902a..a76aef2 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.0.2" +#define VERSION "5.0.3" +// 5.0.3 - Check bad AT firmware version // 5.0.2 - Bugfix: ESP32 30ms off time // 5.0.1 - Bugfix: execute 30ms off time before rejoin // 5.0.0 - Make 4.2.69 the 5.0.0 release From 8437b0e7aa994d78153a1cbf367559c830b0a314 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 15 Sep 2023 06:26:29 +1000 Subject: [PATCH 121/218] Updated broadcasts --- DCCEXParser.cpp | 3 +-- IODevice.h | 3 ++- IO_EXTurntable.cpp | 17 +++++++++++------ version.h | 3 ++- 4 files changed, 16 insertions(+), 10 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 08aae01..29c252d 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -812,13 +812,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) const FSH *tpdesc = NULL; for (uint8_t p = 0; p < posCount; p++) { StringFormatter::send(stream, F("getPositionValue(p); int16_t angle = tto->getPositionAngle(p); #ifdef EXRAIL_ACTIVE tpdesc = RMFT2::getTurntablePositionDescription(id, p); #endif if (tpdesc == NULL) tpdesc = F(""); - StringFormatter::send(stream, F(" %d %d %d %d \"%S\""), id, p, value, angle, tpdesc); + StringFormatter::send(stream, F(" %d %d %d \"%S\""), id, p, angle, tpdesc); StringFormatter::send(stream, F(">\n")); } } diff --git a/IODevice.h b/IODevice.h index 4803f56..74fe49b 100644 --- a/IODevice.h +++ b/IODevice.h @@ -409,11 +409,12 @@ private: void _begin() override; void _loop(unsigned long currentMicros) override; int _read(VPIN vpin) override; - void _broadcastStatus (VPIN vpin, uint8_t status); + void _broadcastStatus (VPIN vpin, uint8_t status, uint8_t activity); void _writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_t duration) override; void _display() override; uint8_t _stepperStatus; uint8_t _previousStatus; + uint8_t _currentActivity; }; #endif diff --git a/IO_EXTurntable.cpp b/IO_EXTurntable.cpp index af220a3..aeb935b 100644 --- a/IO_EXTurntable.cpp +++ b/IO_EXTurntable.cpp @@ -72,7 +72,9 @@ void EXTurntable::_loop(unsigned long currentMicros) { I2CManager.read(_I2CAddress, readBuffer, 1); _stepperStatus = readBuffer[0]; if (_stepperStatus != _previousStatus && _stepperStatus == 0) { // Broadcast when a rotation finishes - _broadcastStatus(_firstVpin, _stepperStatus); + if ( _currentActivity < 4) { + _broadcastStatus(_firstVpin, _stepperStatus, _currentActivity); + } _previousStatus = _stepperStatus; } delayUntil(currentMicros + 100000); // Wait 100ms before checking again @@ -90,11 +92,13 @@ int EXTurntable::_read(VPIN vpin) { } // If a status change has occurred for a turntable object, broadcast it -void EXTurntable::_broadcastStatus (VPIN vpin, uint8_t status) { +void EXTurntable::_broadcastStatus (VPIN vpin, uint8_t status, uint8_t activity) { Turntable *tto = Turntable::getByVpin(vpin); if (tto) { - tto->setMoving(status); - CommandDistributor::broadcastTurntable(tto->getId(), tto->getPosition(), status); + if (activity < 4) { + tto->setMoving(status); + CommandDistributor::broadcastTurntable(tto->getId(), tto->getPosition(), status); + } } } @@ -124,9 +128,10 @@ void EXTurntable::_writeAnalogue(VPIN vpin, int value, uint8_t activity, uint16_ DIAG(F("I2CManager write I2C Address:%d stepsMSB:%d stepsLSB:%d activity:%d"), _I2CAddress.toString(), stepsMSB, stepsLSB, activity); #endif - _stepperStatus = 1; // Tell the device driver Turntable-EX is busy + if (activity < 4) _stepperStatus = 1; // Tell the device driver Turntable-EX is busy _previousStatus = _stepperStatus; - _broadcastStatus(vpin, _stepperStatus); // Broadcast when the rotation starts + _currentActivity = activity; + _broadcastStatus(vpin, _stepperStatus, activity); // Broadcast when the rotation starts I2CManager.write(_I2CAddress, 3, stepsMSB, stepsLSB, activity); } diff --git a/version.h b/version.h index c8f6a68..35d3f1a 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.6" +#define VERSION "5.1.7" +// 5.1.7 - Fix turntable broadcasts for non-movement activities and result // 5.1.6 - STM32F4xx native I2C driver added // 5.1.5 - Added turntable object and EXRAIL commands // - , , - turntable commands From 550ad58c4d2fec42657c5e17374ebf03d160635a Mon Sep 17 00:00:00 2001 From: pmantoine Date: Thu, 21 Sep 2023 14:39:25 +0800 Subject: [PATCH 122/218] STM32 ADCee extended to support ADC2 and ADC3 --- DCCTimer.h | 5 ++ DCCTimerSTM32.cpp | 181 ++++++++++++++++++++++++++++++++++++---------- 2 files changed, 147 insertions(+), 39 deletions(-) diff --git a/DCCTimer.h b/DCCTimer.h index 7402f16..3b14fd6 100644 --- a/DCCTimer.h +++ b/DCCTimer.h @@ -125,8 +125,13 @@ private: // On platforms that scan, it is called from waveform ISR // only on a regular basis. static void scan(); + #if defined (ARDUINO_ARCH_STM32) + // bit array of used pins (max 32) + static uint32_t usedpins; +#else // bit array of used pins (max 16) static uint16_t usedpins; +#endif static uint8_t highestPin; // cached analog values (malloc:ed to actual number of ADC channels) static int *analogvals; diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index cffae40..f57499a 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -52,7 +52,7 @@ HardwareSerial Serial6(PA12, PA11); // Rx=PA12, Tx=PA11 -- CN10 pins 12 and 14 HardwareSerial Serial3(PC11, PC10); // Rx=PC11, Tx=PC10 -- USART3 - F446RE HardwareSerial Serial5(PD2, PC12); // Rx=PC7, Tx=PC6 -- UART5 - F446RE // On the F446RE, Serial4 and Serial6 also use pins we can't readily map while using the Arduino pins -#elif defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)|| defined(ARDUINO_NUCLEO_F412ZG) +#elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) // Nucleo-144 boards don't have Serial1 defined by default HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6 // Serial3 is defined to use USART3 by default, but is in fact used as the diag console @@ -235,22 +235,19 @@ void DCCTimer::reset() { while(true) {}; } -// TODO: may need to use uint32_t on STMF4xx variants with > 16 analog inputs! -#if defined(ARDUINO_NUCLEO_F446RE) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) -#warning STM32 board selected not fully supported - only use ADC1 inputs 0-15 for current sensing! -#endif -// For now, define the max of 16 ports - some variants have more, but this not **yet** supported -#define NUM_ADC_INPUTS 16 -// #define NUM_ADC_INPUTS NUM_ANALOG_INPUTS +// Now we can handle more ADCs, maybe this works! +#define NUM_ADC_INPUTS NUM_ANALOG_INPUTS -uint16_t ADCee::usedpins = 0; -uint8_t ADCee::highestPin = 0; -int * ADCee::analogvals = NULL; -uint32_t * analogchans = NULL; -bool adc1configured = false; +uint32_t ADCee::usedpins = 0; // Max of 32 ADC input channels! +uint8_t ADCee::highestPin = 0; // Highest pin to scan +int * ADCee::analogvals = NULL; // Array of analog values last captured +uint32_t * analogchans = NULL; // Array of channel numbers to be scanned +// bool adc1configured = false; +ADC_TypeDef * * adcchans = NULL; // Array to capture which ADC is each input channel on -int16_t ADCee::ADCmax() { - return 4095; +int16_t ADCee::ADCmax() +{ + return 4095; } int ADCee::init(uint8_t pin) { @@ -261,11 +258,33 @@ int ADCee::init(uint8_t pin) { return -1024; // some silly value as error uint32_t stmgpio = STM_PORT(stmpin); // converts to the GPIO port (16-bits per port group on STM32) - uint32_t adcchan = STM_PIN_CHANNEL(pinmap_function(stmpin, PinMap_ADC)); // find ADC channel (only valid for ADC1!) - GPIO_TypeDef * gpioBase; + uint32_t adcchan = STM_PIN_CHANNEL(pinmap_function(stmpin, PinMap_ADC)); // find ADC input channel + ADC_TypeDef *adc = (ADC_TypeDef *)pinmap_find_peripheral(stmpin, PinMap_ADC); // find which ADC this pin is on ADC1/2/3 etc. + int adcnum = 1; + if (adc == ADC1) + DIAG(F("ADCee::init(): found pin %d on ADC1"), pin); +// Checking for ADC2 and ADC3 being defined helps cater for more variants later +#if defined(ADC2) + else if (adc == ADC2) + { + DIAG(F("ADCee::init(): found pin %d on ADC2"), pin); + adcnum = 2; + } +#endif +#if defined(ADC3) + else if (adc == ADC3) + { + DIAG(F("ADCee::init(): found pin %d on ADC3"), pin); + adcnum = 3; + } +#endif + else DIAG(F("ADCee::init(): found pin %d on unknown ADC!"), pin); - // Port config - find which port we're on and power it up - switch(stmgpio) { + // Port config - find which port we're on and power it up + GPIO_TypeDef *gpioBase; + + switch (stmgpio) + { case 0x00: RCC->AHB1ENR |= RCC_AHB1ENR_GPIOAEN; //Power up PORTA gpioBase = GPIOA; @@ -278,6 +297,20 @@ int ADCee::init(uint8_t pin) { RCC->AHB1ENR |= RCC_AHB1ENR_GPIOCEN; //Power up PORTC gpioBase = GPIOC; break; + case 0x03: + RCC->AHB1ENR |= RCC_AHB1ENR_GPIODEN; //Power up PORTD + gpioBase = GPIOD; + break; + case 0x04: + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOEEN; //Power up PORTE + gpioBase = GPIOE; + break; +#if defined(GPIOF) + case 0x05: + RCC->AHB1ENR |= RCC_AHB1ENR_GPIOFEN; //Power up PORTF + gpioBase = GPIOF; + break; +#endif default: return -1023; // some silly value as error } @@ -293,31 +326,33 @@ int ADCee::init(uint8_t pin) { if (adcchan > 18) return -1022; // silly value as error if (adcchan < 10) - ADC1->SMPR2 |= (0b111 << (adcchan * 3)); // Channel sampling rate 480 cycles + adc->SMPR2 |= (0b111 << (adcchan * 3)); // Channel sampling rate 480 cycles else - ADC1->SMPR1 |= (0b111 << ((adcchan - 10) * 3)); // Channel sampling rate 480 cycles + adc->SMPR1 |= (0b111 << ((adcchan - 10) * 3)); // Channel sampling rate 480 cycles // Read the inital ADC value for this analog input - ADC1->SQR3 = adcchan; // 1st conversion in regular sequence - ADC1->CR2 |= (1 << 30); // Start 1st conversion SWSTART - while(!(ADC1->SR & (1 << 1))); // Wait until conversion is complete - value = ADC1->DR; // Read value from register + adc->SQR3 = adcchan; // 1st conversion in regular sequence + adc->CR2 |= ADC_CR2_SWSTART; //(1 << 30); // Start 1st conversion SWSTART + while(!(adc->SR & (1 << 1))); // Wait until conversion is complete + value = adc->DR; // Read value from register uint8_t id = pin - PNUM_ANALOG_BASE; - if (id > 15) { // today we have not enough bits in the mask to support more - return -1021; - } + // if (id > 15) { // today we have not enough bits in the mask to support more + // return -1021; + // } - if (analogvals == NULL) { // allocate analogvals and analogchans if this is the first invocation of init. + if (analogvals == NULL) { // allocate analogvals, analogchans and adcchans if this is the first invocation of init analogvals = (int *)calloc(NUM_ADC_INPUTS+1, sizeof(int)); analogchans = (uint32_t *)calloc(NUM_ADC_INPUTS+1, sizeof(uint32_t)); + adcchans = (ADC_TypeDef **)calloc(NUM_ADC_INPUTS+1, sizeof(ADC_TypeDef)); } analogvals[id] = value; // Store sampled value analogchans[id] = adcchan; // Keep track of which ADC channel is used for reading this pin - usedpins |= (1 << id); // This pin is now ready + adcchans[id] = adc; // Keep track of which ADC this channel is on + usedpins |= (1 << id); // This pin is now ready if (id > highestPin) highestPin = id; // Store our highest pin in use - DIAG(F("ADCee::init(): value=%d, channel=%d, id=%d"), value, adcchan, id); + DIAG(F("ADCee::init(): value=%d, ADC%d: channel=%d, id=%d"), value, adcnum, adcchan, id); return value; } @@ -344,13 +379,16 @@ void ADCee::scan() { static uint8_t id = 0; // id and mask are the same thing but it is faster to static uint16_t mask = 1; // increment and shift instead to calculate mask from id static bool waiting = false; + static ADC_TypeDef *adc; - if (waiting) { + adc = adcchans[id]; + if (waiting) + { // look if we have a result - if (!(ADC1->SR & (1 << 1))) + if (!(adc->SR & (1 << 1))) return; // no result, continue to wait // found value - analogvals[id] = ADC1->DR; + analogvals[id] = adc->DR; // advance at least one track #ifdef DEBUG_ADC if (id == 1) TrackManager::track[1]->setBrake(0); @@ -369,9 +407,10 @@ void ADCee::scan() { // look for a valid track to sample or until we are around while (true) { if (mask & usedpins) { - // start new ADC aquire on id - ADC1->SQR3 = analogchans[id]; //1st conversion in regular sequence - ADC1->CR2 |= (1 << 30); //Start 1st conversion SWSTART + // start new ADC aquire on id + adc = adcchans[id]; + adc->SQR3 = analogchans[id]; // 1st conversion in regular sequence + adc->CR2 |= (1 << 30); // Start 1st conversion SWSTART #ifdef DEBUG_ADC if (id == 1) TrackManager::track[1]->setBrake(1); #endif @@ -392,19 +431,83 @@ void ADCee::scan() { void ADCee::begin() { noInterrupts(); //ADC1 config sequence - // TODO: currently defaults to ADC1, may need more to handle other members of STM32F4xx family - RCC->APB2ENR |= (1 << 8); //Enable ADC1 clock (Bit8) + RCC->APB2ENR |= RCC_APB2ENR_ADC1EN; // Enable ADC1 clock // Set ADC prescaler - DIV8 ~ 40ms, DIV6 ~ 30ms, DIV4 ~ 20ms, DIV2 ~ 11ms ADC->CCR = (0 << 16); // Set prescaler 0=DIV2, 1=DIV4, 2=DIV6, 3=DIV8 ADC1->CR1 &= ~(1 << 8); //SCAN mode disabled (Bit8) ADC1->CR1 &= ~(3 << 24); //12bit resolution (Bit24,25 0b00) ADC1->SQR1 = (1 << 20); //Set number of conversions projected (L[3:0] 0b0001) -> 1 conversion + // Disable the DMA controller for ADC1 + ADC1->CR2 &= ~ADC_CR2_DMA; ADC1->CR2 &= ~(1 << 1); //Single conversion ADC1->CR2 &= ~(1 << 11); //Right alignment of data bits bit12....bit0 ADC1->SQR1 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register ADC1->SQR2 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register ADC1->SQR3 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register ADC1->CR2 |= (1 << 0); // Switch on ADC1 + // Wait for ADC1 to become ready (calibration complete) + while (!(ADC1->CR2 & ADC_CR2_ADON)) { + } +#if defined(ADC2) + // Enable the ADC2 clock + RCC->APB2ENR |= RCC_APB2ENR_ADC2EN; + + // Initialize ADC2 + ADC2->CR1 = 0; // Disable all channels + ADC2->CR2 = 0; // Clear CR2 register + + ADC2->CR1 &= ~(1 << 8); //SCAN mode disabled (Bit8) + ADC2->CR1 &= ~(3 << 24); //12bit resolution (Bit24,25 0b00) + ADC2->SQR1 = (1 << 20); //Set number of conversions projected (L[3:0] 0b0001) -> 1 conversion + ADC2->CR2 &= ~ADC_CR2_DMA; // Disable the DMA controller for ADC3 + ADC2->CR2 &= ~(1 << 1); //Single conversion + ADC2->CR2 &= ~(1 << 11); //Right alignment of data bits bit12....bit0 + ADC2->SQR1 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register + ADC2->SQR2 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register + ADC2->SQR3 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register + + // Enable the ADC + ADC2->CR2 |= ADC_CR2_ADON; + + // Wait for ADC2 to become ready (calibration complete) + while (!(ADC2->CR2 & ADC_CR2_ADON)) { + } + + // Perform ADC3 calibration (optional) + // ADC3->CR2 |= ADC_CR2_CAL; + // while (ADC3->CR2 & ADC_CR2_CAL) { + // } +#endif +#if defined(ADC3) + // Enable the ADC3 clock + RCC->APB2ENR |= RCC_APB2ENR_ADC3EN; + + // Initialize ADC3 + ADC3->CR1 = 0; // Disable all channels + ADC3->CR2 = 0; // Clear CR2 register + + ADC3->CR1 &= ~(1 << 8); //SCAN mode disabled (Bit8) + ADC3->CR1 &= ~(3 << 24); //12bit resolution (Bit24,25 0b00) + ADC3->SQR1 = (1 << 20); //Set number of conversions projected (L[3:0] 0b0001) -> 1 conversion + ADC3->CR2 &= ~ADC_CR2_DMA; // Disable the DMA controller for ADC3 + ADC3->CR2 &= ~(1 << 1); //Single conversion + ADC3->CR2 &= ~(1 << 11); //Right alignment of data bits bit12....bit0 + ADC3->SQR1 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register + ADC3->SQR2 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register + ADC3->SQR3 &= ~(0x3FFFFFFF); //Clear whole 1st 30bits in register + + // Enable the ADC + ADC3->CR2 |= ADC_CR2_ADON; + + // Wait for ADC3 to become ready (calibration complete) + while (!(ADC3->CR2 & ADC_CR2_ADON)) { + } + + // Perform ADC3 calibration (optional) + // ADC3->CR2 |= ADC_CR2_CAL; + // while (ADC3->CR2 & ADC_CR2_CAL) { + // } +#endif interrupts(); } #endif From 5d810a620b759cc8dfa7fff275b68d78c9767a0a Mon Sep 17 00:00:00 2001 From: pmantoine Date: Thu, 21 Sep 2023 16:05:35 +0800 Subject: [PATCH 123/218] STM32 variant support extension and tidy --- I2CManager_STM32.h | 6 ++-- platformio.ini | 79 +++++++++++++++++++++++++++++++++++++++++----- 2 files changed, 75 insertions(+), 10 deletions(-) diff --git a/I2CManager_STM32.h b/I2CManager_STM32.h index cde4f20..86b9b74 100644 --- a/I2CManager_STM32.h +++ b/I2CManager_STM32.h @@ -37,9 +37,11 @@ * I2C bus, or more than one I2C bus on the STM32 architecture *****************************************************************************/ #if defined(I2C_USE_INTERRUPTS) && defined(ARDUINO_ARCH_STM32) -#if defined(ARDUINO_NUCLEO_F411RE) || defined(ARDUINO_NUCLEO_F446RE) || defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) +#if defined(ARDUINO_NUCLEO_F401RE) || defined(ARDUINO_NUCLEO_F411RE) || defined(ARDUINO_NUCLEO_F446RE) \ + || defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) \ + || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) // Assume I2C1 for now - default I2C bus on Nucleo-F411RE and likely all Nucleo-64 -// and Nucleo-144variants +// and Nucleo-144 variants I2C_TypeDef *s = I2C1; // In init we will ask the STM32 HAL layer for the configured APB1 clock frequency in Hz diff --git a/platformio.ini b/platformio.ini index ac2d598..8767ef1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -31,7 +31,6 @@ include_dir = . [env] build_flags = -Wall -Wextra ; monitor_filters = time -; lib_deps = adafruit/Adafruit ST7735 and ST7789 Library @ ^1.10.0 [env:samd21-dev-usb] platform = atmelsam @@ -60,7 +59,7 @@ framework = arduino lib_deps = ${env.lib_deps} monitor_speed = 115200 monitor_echo = yes -build_flags = -std=c++17 ; -DI2C_USE_WIRE -DDIAG_LOOPTIMES -DDIAG_IO +build_flags = -std=c++17 [env:mega2560-debug] platform = atmelavr @@ -72,7 +71,7 @@ lib_deps = SPI monitor_speed = 115200 monitor_echo = yes -build_flags = -DDIAG_IO=2 -DDIAG_LOOPTIMES +build_flags = -DDIAG_IO=2 -DDIAG_LOOPTIMES [env:mega2560-no-HAL] platform = atmelavr @@ -84,7 +83,7 @@ lib_deps = SPI monitor_speed = 115200 monitor_echo = yes -build_flags = -DIO_NO_HAL +build_flags = -DIO_NO_HAL [env:mega2560-I2C-wire] platform = atmelavr @@ -108,7 +107,7 @@ lib_deps = SPI monitor_speed = 115200 monitor_echo = yes -build_flags = ; -DDIAG_LOOPTIMES +build_flags = [env:mega328] platform = atmelavr @@ -190,10 +189,75 @@ platform = ststm32 board = nucleo_f446re framework = arduino lib_deps = ${env.lib_deps} -build_flags = -std=c++17 -Os -g2 -Wunused-variable ; -DDIAG_LOOPTIMES ; -DDIAG_IO +build_flags = -std=c++17 -Os -g2 -Wunused-variable monitor_speed = 115200 monitor_echo = yes +; Experimental - no reason this should not work, but not +; tested as yet +; +[env:Nucleo-F401RE] +platform = ststm32 +board = nucleo_f401re +framework = arduino +lib_deps = ${env.lib_deps} +build_flags = -std=c++17 -Os -g2 -Wunused-variable +monitor_speed = 115200 +monitor_echo = yes + +; Commented out by default as the F13ZH has variant files +; but NOT the nucleo_f413zh.json file which needs to be +; installed before you can let PlatformIO see this +; +; [env:Nucleo-F413ZH] +; platform = ststm32 +; board = nucleo_f413zh +; framework = arduino +; lib_deps = ${env.lib_deps} +; build_flags = -std=c++17 -Os -g2 -Wunused-variable +; monitor_speed = 115200 +; monitor_echo = yes + +; Commented out by default as the F446ZE needs variant files +; installed before you can let PlatformIO see this +; +; [env:Nucleo-F446ZE] +; platform = ststm32 +; board = nucleo_f446ze +; framework = arduino +; lib_deps = ${env.lib_deps} +; build_flags = -std=c++17 -Os -g2 -Wunused-variable +; monitor_speed = 115200 +; monitor_echo = yes + +; Commented out by default as the F412ZG needs variant files +; installed before you can let PlatformIO see this +; +; [env:Nucleo-F412ZG] +; platform = ststm32 +; board = blah_f412zg +; framework = arduino +; lib_deps = ${env.lib_deps} +; build_flags = -std=c++17 -Os -g2 -Wunused-variable +; monitor_speed = 115200 +; monitor_echo = yes +; upload_protocol = stlink + +; Experimental - Ethernet work still in progress +; +; [env:Nucleo-F429ZI] +; platform = ststm32 +; board = nucleo_f429zi +; framework = arduino +; lib_deps = ${env.lib_deps} +; arduino-libraries/Ethernet @ ^2.0.1 +; stm32duino/STM32Ethernet @ ^1.3.0 +; stm32duino/STM32duino LwIP @ ^2.1.2 +; build_flags = -std=c++17 -Os -g2 -Wunused-variable +; monitor_speed = 115200 +; monitor_echo = yes +; upload_protocol = stlink + [env:Teensy3_2] platform = teensy board = teensy31 @@ -232,5 +296,4 @@ board = teensy41 framework = arduino build_flags = -std=c++17 -Os -g2 lib_deps = ${env.lib_deps} -lib_ignore = - +lib_ignore = From dfe3e9d42c590c6a821406f049df34f3094d51a1 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Thu, 21 Sep 2023 16:09:04 +0800 Subject: [PATCH 124/218] STM32 ADCee extensions --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 35d3f1a..f10ceee 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.7" +#define VERSION "5.1.8" +// 5.1.8 - STM32Fxx ADCee extension to support ADCs #2 and #3 // 5.1.7 - Fix turntable broadcasts for non-movement activities and result // 5.1.6 - STM32F4xx native I2C driver added // 5.1.5 - Added turntable object and EXRAIL commands From 8052090e0f1c47d7c87220cf1b925354539d1125 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Fri, 22 Sep 2023 17:03:40 +0100 Subject: [PATCH 125/218] Added Single Track Power On/Off Added power On/Off <> commands --- CommandDistributor.cpp | 4 +- CommandDistributor.h | 2 +- DCCEXParser.cpp | 51 ++++++++++++++++--- TrackManager.cpp | 111 +++++++++++++++++++++++++---------------- TrackManager.h | 9 ++-- 5 files changed, 121 insertions(+), 56 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index ee760b0..32f9874 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -269,6 +269,6 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) { broadcastReply(type, F("%s"),msg); } -void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter,int16_t dcAddr) { - broadcastReply(COMMAND_TYPE, format,trackLetter,dcAddr); +void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter,char pmode, int16_t dcAddr) { + broadcastReply(COMMAND_TYPE, format,trackLetter,pmode, dcAddr); } diff --git a/CommandDistributor.h b/CommandDistributor.h index a51d58a..99b9f1e 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -55,7 +55,7 @@ public : static int16_t retClockTime(); static void broadcastPower(); static void broadcastRaw(clientType type,char * msg); - static void broadcastTrackState(const FSH* format,byte trackLetter,int16_t dcAddr); + static void broadcastTrackState(const FSH* format,byte trackLetter,char pmode, int16_t dcAddr); template static void broadcastReply(clientType type, Targs... msg); static void forget(byte clientId); diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 29c252d..150070c 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -555,6 +555,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) bool main=false; bool prog=false; bool join=false; + bool singletrack=false; if (params > 1) break; if (params==0) { // All main=true; @@ -574,12 +575,28 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) prog=true; } #endif + else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> + uint8_t t = (p[0] - 'A'); + if (TrackManager::isProg(t)) { + main = false; + prog = true; + } + else + { + main=true; + prog=false; + } + singletrack=true; + if (main) TrackManager::SetMainTrackPower(POWERMODE::ON, t); + if (prog) TrackManager::SetProgTrackPower(POWERMODE::ON, t); + } else break; // will reply } TrackManager::setJoin(join); - if (main) TrackManager::setMainPower(POWERMODE::ON); - if (prog) TrackManager::setProgPower(POWERMODE::ON); - + if (!singletrack) { + if (main) TrackManager::setMainPower(POWERMODE::ON); + if (prog) TrackManager::setProgPower(POWERMODE::ON); + } CommandDistributor::broadcastPower(); return; } @@ -588,6 +605,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) { bool main=false; bool prog=false; + bool singletrack=false; if (params > 1) break; if (params==0) { // All main=true; @@ -601,17 +619,34 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG> prog=true; } -#endif +#endif + else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> + uint8_t t = (p[0] - 'A'); + if (TrackManager::isProg(t)) { + main = false; + prog = true; + } + else + { + main=true; + prog=false; + } + singletrack=true; + if (main) TrackManager::SetMainTrackPower(POWERMODE::OFF, t); + if (prog) TrackManager::SetProgTrackPower(POWERMODE::OFF, t); + } + else break; // will reply } TrackManager::setJoin(false); + if (!singletrack) { if (main) TrackManager::setMainPower(POWERMODE::OFF); - if (prog) { - TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off - TrackManager::setProgPower(POWERMODE::OFF); + if (prog) { + TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off + TrackManager::setProgPower(POWERMODE::OFF); + } } - CommandDistributor::broadcastPower(); return; } diff --git a/TrackManager.cpp b/TrackManager.cpp index bd7c623..413fd95 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -353,32 +353,48 @@ void TrackManager::streamTrackState(Print* stream, byte t) { // null stream means send to commandDistributor for broadcast if (track[t]==NULL) return; auto format=F(""); + bool pstate = TrackManager::isPowerOn(t); + //char PMODE[] = "OFF"; + //if (pstate) PMODE="ON "; + //if (TrackManager::isPowerOn(t)) {char PMODE[] = "ON";} + // else {char PMODE[] = "OFF";} + switch(track[t]->getMode()) { case TRACK_MODE_MAIN: - format=F("<= %c MAIN>\n"); + format=F("<= %c MAIN %c>\n"); break; #ifndef DISABLE_PROG case TRACK_MODE_PROG: - format=F("<= %c PROG>\n"); + format=F("<= %c PROG %c>\n"); break; #endif case TRACK_MODE_NONE: - format=F("<= %c NONE>\n"); + format=F("<= %c NONE %c>\n"); break; case TRACK_MODE_EXT: - format=F("<= %c EXT>\n"); + format=F("<= %c EXT %c>\n"); break; case TRACK_MODE_DC: - format=F("<= %c DC %d>\n"); + format=F("<= %c DC %c %d>\n"); break; case TRACK_MODE_DCX: - format=F("<= %c DCX %d>\n"); + format=F("<= %c DCX %c %d>\n"); break; default: break; // unknown, dont care } - if (stream) StringFormatter::send(stream,format,'A'+t,trackDCAddr[t]); - else CommandDistributor::broadcastTrackState(format,'A'+t,trackDCAddr[t]); + switch (pstate) { + case true: + if (stream) StringFormatter::send(stream,format,'A'+t,"ON", trackDCAddr[t]); + else CommandDistributor::broadcastTrackState(format,'A'+t,"ON", trackDCAddr[t]); + break; + case false: + if (stream) StringFormatter::send(stream,format,'A'+t,"OFF", trackDCAddr[t]); + else CommandDistributor::broadcastTrackState(format,'A'+t,"OFF", trackDCAddr[t]); + break; + } + //if (stream) StringFormatter::send(stream,format,'A'+t,PMODE, trackDCAddr[t]); + //else CommandDistributor::broadcastTrackState(format,'A'+t,PMODE, trackDCAddr[t]); } byte TrackManager::nextCycleTrack=MAX_TRACKS; @@ -412,45 +428,51 @@ std::vectorTrackManager::getMainDrivers() { } #endif -void TrackManager::setPower2(bool setProg,POWERMODE mode) { +void TrackManager::setPower2(bool setProg,POWERMODE mode, bool doall, uint8_t thistrack) { if (!setProg) mainPowerGuess=mode; FOR_EACH_TRACK(t) { - MotorDriver * driver=track[t]; - if (!driver) continue; - switch (track[t]->getMode()) { - case TRACK_MODE_MAIN: - if (setProg) break; - // toggle brake before turning power on - resets overcurrent error - // on the Pololu board if brake is wired to ^D2. - // XXX see if we can make this conditional - driver->setBrake(true); - driver->setBrake(false); // DCC runs with brake off - driver->setPower(mode); - break; - case TRACK_MODE_DC: - case TRACK_MODE_DCX: - if (setProg) break; - driver->setBrake(true); // DC starts with brake on - applyDCSpeed(t); // speed match DCC throttles - driver->setPower(mode); - break; - case TRACK_MODE_PROG: - if (!setProg) break; - driver->setBrake(true); - driver->setBrake(false); - driver->setPower(mode); - break; - case TRACK_MODE_EXT: - driver->setBrake(true); - driver->setBrake(false); - driver->setPower(mode); - break; - case TRACK_MODE_NONE: - break; + if (doall==false && thistrack != t) break; + MotorDriver * driver=track[t]; + if (!driver) continue; + switch (track[t]->getMode()) { + case TRACK_MODE_MAIN: + if (setProg) break; + // toggle brake before turning power on - resets overcurrent error + // on the Pololu board if brake is wired to ^D2. + // XXX see if we can make this conditional + driver->setBrake(true); + driver->setBrake(false); // DCC runs with brake off + driver->setPower(mode); + break; + case TRACK_MODE_DC: + case TRACK_MODE_DCX: + if (setProg) break; + driver->setBrake(true); // DC starts with brake on + applyDCSpeed(t); // speed match DCC throttles + driver->setPower(mode); + break; + case TRACK_MODE_PROG: + if (!setProg) break; + driver->setBrake(true); + driver->setBrake(false); + driver->setPower(mode); + break; + case TRACK_MODE_EXT: + driver->setBrake(true); + driver->setBrake(false); + driver->setPower(mode); + break; + case TRACK_MODE_NONE: + break; } + } } - + +// void TrackManager::setTrackPower(bool progTrack,POWERMODE mode, uint8_t track) { +// // write the code for this. +// } + POWERMODE TrackManager::getProgPower() { FOR_EACH_TRACK(t) if (track[t]->getMode()==TRACK_MODE_PROG) @@ -526,3 +548,8 @@ bool TrackManager::isPowerOn(byte t) { return true; } +bool TrackManager::isProg(byte t) { + if (track[t]->getMode()==TRACK_MODE_PROG) + return true; + return false; +} \ No newline at end of file diff --git a/TrackManager.h b/TrackManager.h index 60d5f24..3f9c73f 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -60,10 +60,12 @@ class TrackManager { #ifdef ARDUINO_ARCH_ESP32 static std::vectorgetMainDrivers(); #endif - static void setPower2(bool progTrack,POWERMODE mode); + static void setPower2(bool progTrack,POWERMODE mode, bool doall, uint8_t thistrack); static void setPower(POWERMODE mode) {setMainPower(mode); setProgPower(mode);} - static void setMainPower(POWERMODE mode) {setPower2(false,mode);} - static void setProgPower(POWERMODE mode) {setPower2(true,mode);} + static void setMainPower(POWERMODE mode) {setPower2(false,mode,true,0);} + static void setProgPower(POWERMODE mode) {setPower2(true,mode,true,0);} + static void SetMainTrackPower(POWERMODE mode, uint8_t track) {setPower2(false,mode,false,track);} + static void SetProgTrackPower(POWERMODE mode, uint8_t track) {setPower2(true,mode,false,track);} static const int16_t MAX_TRACKS=8; static bool setTrackMode(byte track, TRACK_MODE mode, int16_t DCaddr=0); @@ -80,6 +82,7 @@ class TrackManager { static void reportObsoleteCurrent(Print* stream); static void streamTrackState(Print* stream, byte t); static bool isPowerOn(byte t); + static bool isProg(byte t); static int16_t joinRelay; static bool progTrackSyncMain; // true when prog track is a siding switched to main From c9d4f5e94dc31fcdc0e55d53eeae3a89008ec783 Mon Sep 17 00:00:00 2001 From: kempe63 <78020007+kempe63@users.noreply.github.com> Date: Sat, 23 Sep 2023 15:56:09 +0100 Subject: [PATCH 126/218] Update IO_PCA9555.h --- IO_PCA9555.h | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/IO_PCA9555.h b/IO_PCA9555.h index e493165..75f2c38 100644 --- a/IO_PCA9555.h +++ b/IO_PCA9555.h @@ -33,17 +33,16 @@ public: static void create(VPIN vpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1) { if (checkNoOverlap(vpin, nPins, i2cAddress)) new PCA9555(vpin,nPins, i2cAddress, interruptPin); } - + +private: // Constructor - PCA9555(VPIN vpin, int nPins, uint8_t I2CAddress, int interruptPin=-1) + PCA9555(VPIN vpin, uint8_t nPins, I2CAddress I2CAddress, int interruptPin=-1) : GPIOBase((FSH *)F("PCA9555"), vpin, nPins, I2CAddress, interruptPin) { requestBlock.setRequestParams(_I2CAddress, inputBuffer, sizeof(inputBuffer), outputBuffer, sizeof(outputBuffer)); outputBuffer[0] = REG_INPUT_P0; } - -private: void _writeGpioPort() override { I2CManager.write(_I2CAddress, 3, REG_OUTPUT_P0, _portOutputState, _portOutputState>>8); } From 6ad5326f1d1e44b8d17ffb5b3a7bd2312d9f7ae8 Mon Sep 17 00:00:00 2001 From: kempe63 <78020007+kempe63@users.noreply.github.com> Date: Sat, 23 Sep 2023 16:06:14 +0100 Subject: [PATCH 127/218] PCA9555 compiles and tested on PCA9548 mux IO_PCA9555.h added changes that had been applied to IO_MCP23017 for support on PCA9548 Mux. Constructor now also private and type casting of variables made the same for IO_PCA9555. Tested with MCP23017 and PCA9555 simultaneous on the same Mux Subbus --- IO_PCA9555.h | 1 + 1 file changed, 1 insertion(+) diff --git a/IO_PCA9555.h b/IO_PCA9555.h index 75f2c38..607e1a8 100644 --- a/IO_PCA9555.h +++ b/IO_PCA9555.h @@ -34,6 +34,7 @@ public: if (checkNoOverlap(vpin, nPins, i2cAddress)) new PCA9555(vpin,nPins, i2cAddress, interruptPin); } + private: // Constructor PCA9555(VPIN vpin, uint8_t nPins, I2CAddress I2CAddress, int interruptPin=-1) From dab02ec65960764b750a0cd7bb2250f6b7315b97 Mon Sep 17 00:00:00 2001 From: kempe63 <78020007+kempe63@users.noreply.github.com> Date: Sat, 23 Sep 2023 16:10:16 +0100 Subject: [PATCH 128/218] Update IO_PCA9555.h Now compiles and works on PCA9548 Mux. Tested with MCP23017 and PCA9555 on the same Subbus --- IO_PCA9555.h | 1 - 1 file changed, 1 deletion(-) diff --git a/IO_PCA9555.h b/IO_PCA9555.h index 607e1a8..75f2c38 100644 --- a/IO_PCA9555.h +++ b/IO_PCA9555.h @@ -34,7 +34,6 @@ public: if (checkNoOverlap(vpin, nPins, i2cAddress)) new PCA9555(vpin,nPins, i2cAddress, interruptPin); } - private: // Constructor PCA9555(VPIN vpin, uint8_t nPins, I2CAddress I2CAddress, int interruptPin=-1) From d57b5ba537c86bd9c6d6963b196d0a7febe25424 Mon Sep 17 00:00:00 2001 From: kempe63 <78020007+kempe63@users.noreply.github.com> Date: Sat, 23 Sep 2023 20:09:01 +0100 Subject: [PATCH 129/218] Update version.h to 5.1.7 --- version.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/version.h b/version.h index f10ceee..9b7c8ef 100644 --- a/version.h +++ b/version.h @@ -3,9 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.8" -// 5.1.8 - STM32Fxx ADCee extension to support ADCs #2 and #3 -// 5.1.7 - Fix turntable broadcasts for non-movement activities and result +#define VERSION "5.1.7" +// 5.1.7 - IO_PCA9555.h fixed to work on PCA9548 Subbus, tested // 5.1.6 - STM32F4xx native I2C driver added // 5.1.5 - Added turntable object and EXRAIL commands // - , , - turntable commands From d07718be8cc34ffc852c771ba6cf4754eb7aebe8 Mon Sep 17 00:00:00 2001 From: kempe63 <78020007+kempe63@users.noreply.github.com> Date: Sat, 23 Sep 2023 20:18:59 +0100 Subject: [PATCH 130/218] Revert "Update version.h to 5.1.7" This reverts commit d57b5ba537c86bd9c6d6963b196d0a7febe25424. --- version.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/version.h b/version.h index 9b7c8ef..f10ceee 100644 --- a/version.h +++ b/version.h @@ -3,8 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.1.7" -// 5.1.7 - IO_PCA9555.h fixed to work on PCA9548 Subbus, tested +#define VERSION "5.1.8" +// 5.1.8 - STM32Fxx ADCee extension to support ADCs #2 and #3 +// 5.1.7 - Fix turntable broadcasts for non-movement activities and result // 5.1.6 - STM32F4xx native I2C driver added // 5.1.5 - Added turntable object and EXRAIL commands // - , , - turntable commands From 11b9fd4ef56d417d3e73a81ea0c390cad7b02233 Mon Sep 17 00:00:00 2001 From: kempe63 <78020007+kempe63@users.noreply.github.com> Date: Sat, 23 Sep 2023 20:25:10 +0100 Subject: [PATCH 131/218] Fixed IO_PCA9555.h to work with PCA9548 mux --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index f10ceee..b7c157b 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.8" +#define VERSION "5.1.9" +// 5.1.9 - Fixed IO_PCA9555'h to work with PCA9548 mux, tested OK // 5.1.8 - STM32Fxx ADCee extension to support ADCs #2 and #3 // 5.1.7 - Fix turntable broadcasts for non-movement activities and result // 5.1.6 - STM32F4xx native I2C driver added From aacb980dc873cc52742d5840aebb215ef0b573ce Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Sun, 24 Sep 2023 15:40:42 +0100 Subject: [PATCH 132/218] Power control plus EXRAIL Power Control <0 A> etc plus EXRAIL SET_POWER Not yet fully tested. --- CommandDistributor.cpp | 4 ++-- CommandDistributor.h | 2 +- DCCEXParser.cpp | 18 ++++++++++------- EXRAIL2.cpp | 14 +++++++++++++ EXRAIL2.h | 2 +- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 8 +++++++- TrackManager.cpp | 46 +++++++++++++++++------------------------- TrackManager.h | 15 +++++++++----- 9 files changed, 66 insertions(+), 45 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 32f9874..a521b52 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -269,6 +269,6 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) { broadcastReply(type, F("%s"),msg); } -void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter,char pmode, int16_t dcAddr) { - broadcastReply(COMMAND_TYPE, format,trackLetter,pmode, dcAddr); +void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr) { + broadcastReply(COMMAND_TYPE, format,trackLetter, dcAddr); } diff --git a/CommandDistributor.h b/CommandDistributor.h index 99b9f1e..d54ef31 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -55,7 +55,7 @@ public : static int16_t retClockTime(); static void broadcastPower(); static void broadcastRaw(clientType type,char * msg); - static void broadcastTrackState(const FSH* format,byte trackLetter,char pmode, int16_t dcAddr); + static void broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr); template static void broadcastReply(clientType type, Targs... msg); static void forget(byte clientId); diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 150070c..9c94f82 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -576,7 +576,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } #endif else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> - uint8_t t = (p[0] - 'A'); + byte t = (p[0] - 'A'); + DIAG(F("Processing track - %d "), t); if (TrackManager::isProg(t)) { main = false; prog = true; @@ -587,8 +588,10 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) prog=false; } singletrack=true; - if (main) TrackManager::SetMainTrackPower(POWERMODE::ON, t); - if (prog) TrackManager::SetProgTrackPower(POWERMODE::ON, t); + DIAG(F("Calling SetPower %d - %d - %d"), POWERMODE::ON, false, t); + if (main) TrackManager::setTrackPower(POWERMODE::ON, t); + //if (main) TrackManager::SetMainTrackPower(POWERMODE::ON, t); + //if (prog) TrackManager::SetProgTrackPower(POWERMODE::ON, t); } else break; // will reply } @@ -621,8 +624,9 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } #endif else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> - uint8_t t = (p[0] - 'A'); - if (TrackManager::isProg(t)) { + byte t = (p[0] - 'A'); + DIAG(F("Processing track - %d "), t); + if (TrackManager::isProg(t)) { main = false; prog = true; } @@ -632,8 +636,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) prog=false; } singletrack=true; - if (main) TrackManager::SetMainTrackPower(POWERMODE::OFF, t); - if (prog) TrackManager::SetProgTrackPower(POWERMODE::OFF, t); + DIAG(F("Calling SetPower %d - %d - %d"), POWERMODE::OFF, false, t); + if (main) TrackManager::setTrackPower(POWERMODE::OFF, t); } else break; // will reply diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index d6d2597..07f4768 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -780,6 +780,20 @@ void RMFT2::loop2() { TrackManager::setJoin(false); CommandDistributor::broadcastPower(); break; + + case OPCODE_SET_POWER: + // operand is TRACK_POWER , trackid + + switch (operand) { + case TRACK_POWER_0: + TrackManager::setTrackPower(POWERMODE::OFF, getOperand(1)); + break; + case TRACK_POWER_1: + TrackManager::setTrackPower(POWERMODE::ON, getOperand(1)); + break; + } + + break; case OPCODE_SET_TRACK: // operand is trackmode<<8 | track id diff --git a/EXRAIL2.h b/EXRAIL2.h index 8af36d2..ad399ec 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -59,7 +59,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ROSTER,OPCODE_KILLALL, OPCODE_ROUTE,OPCODE_AUTOMATION,OPCODE_SEQUENCE, OPCODE_ENDTASK,OPCODE_ENDEXRAIL, - OPCODE_SET_TRACK, + OPCODE_SET_TRACK,OPCODE_SET_POWER, OPCODE_ONRED,OPCODE_ONAMBER,OPCODE_ONGREEN, OPCODE_ONCHANGE, OPCODE_ONCLOCKTIME, diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 11c598b..03e310e 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -138,6 +138,7 @@ #undef SERVO_SIGNAL #undef SET #undef SET_TRACK +#undef SET_POWER #undef SETLOCO #undef SIGNAL #undef SIGNALH @@ -275,6 +276,7 @@ #define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) #define SET(pin) #define SET_TRACK(track,mode) +#define SET_POWER(track,onoff) #define SETLOCO(loco) #define SIGNAL(redpin,amberpin,greenpin) #define SIGNALH(redpin,amberpin,greenpin) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 0784242..62b852a 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -63,6 +63,11 @@ // (10#mins)%100) #define STRIP_ZERO(value) 10##value%100 +// These constants help EXRAIL macros convert Track Power e.g. SET_POWER(A ON|OFF). +//const byte TRACK_POWER_0=0, TRACK_POWER_OFF=0; +//const byte TRACK_POWER_1=1, TRACK_POWER_ON=1; + + // Pass 1 Implements aliases #include "EXRAIL2MacroReset.h" #undef ALIAS @@ -407,11 +412,12 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) OPCODE_SERVOTURNOUT,V(id),OPCODE_PAD,V(pin),OPCODE_PAD,V(activeAngle),OPCODE_PAD,V(inactiveAngle),OPCODE_PAD,V(PCA9685::ProfileType::profile), #define SET(pin) OPCODE_SET,V(pin), #define SET_TRACK(track,mode) OPCODE_SET_TRACK,V(TRACK_MODE_##mode <<8 | TRACK_NUMBER_##track), +#define SET_POWER(track,onoff) OPCODE_SET_POWER,V(TRACK_POWER_##onoff, TRACK_NUMBER_##track), #define SETLOCO(loco) OPCODE_SETLOCO,V(loco), #define SIGNAL(redpin,amberpin,greenpin) #define SIGNALH(redpin,amberpin,greenpin) #define SPEED(speed) OPCODE_SPEED,V(speed), -#define START(route) OPCODE_START,V(route), +#define START(route) OPCODE_START,V(route), #define STOP OPCODE_SPEED,V(0), #define THROW(id) OPCODE_THROW,V(id), #ifndef IO_NO_HAL diff --git a/TrackManager.cpp b/TrackManager.cpp index 413fd95..ad7ef6a 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -354,47 +354,32 @@ void TrackManager::streamTrackState(Print* stream, byte t) { if (track[t]==NULL) return; auto format=F(""); bool pstate = TrackManager::isPowerOn(t); - //char PMODE[] = "OFF"; - //if (pstate) PMODE="ON "; - //if (TrackManager::isPowerOn(t)) {char PMODE[] = "ON";} - // else {char PMODE[] = "OFF";} - switch(track[t]->getMode()) { case TRACK_MODE_MAIN: - format=F("<= %c MAIN %c>\n"); + if (pstate) {format=F("<= %c MAIN ON \n");} else {format = F("<= %c MAIN OFF \n");} break; #ifndef DISABLE_PROG case TRACK_MODE_PROG: - format=F("<= %c PROG %c>\n"); + if (pstate) {format=F("<= %c PROG ON>\n");} else {format=F("<= %c PROG OFF>\n");} break; #endif case TRACK_MODE_NONE: - format=F("<= %c NONE %c>\n"); + if (pstate) {format=F("<= %c NONE %ON>\n");} else {format=F("<= %c NONE %OFF>\n");} break; case TRACK_MODE_EXT: - format=F("<= %c EXT %c>\n"); + if (pstate) {format=F("<= %c EXT ON>\n");} else {format=F("<= %c EXT OFF>\n");} break; case TRACK_MODE_DC: - format=F("<= %c DC %c %d>\n"); + if (pstate) {format=F("<= %c DC ON %d>\n");} else {format=F("<= %c DC OFF %d>\n");} break; case TRACK_MODE_DCX: - format=F("<= %c DCX %c %d>\n"); + if (pstate) {format=F("<= %c DCX ON %d>\n");} else {format=F("<= %c DCX OFF %d>\n");} break; default: break; // unknown, dont care } - switch (pstate) { - case true: - if (stream) StringFormatter::send(stream,format,'A'+t,"ON", trackDCAddr[t]); - else CommandDistributor::broadcastTrackState(format,'A'+t,"ON", trackDCAddr[t]); - break; - case false: - if (stream) StringFormatter::send(stream,format,'A'+t,"OFF", trackDCAddr[t]); - else CommandDistributor::broadcastTrackState(format,'A'+t,"OFF", trackDCAddr[t]); - break; - } - //if (stream) StringFormatter::send(stream,format,'A'+t,PMODE, trackDCAddr[t]); - //else CommandDistributor::broadcastTrackState(format,'A'+t,PMODE, trackDCAddr[t]); + if (stream) StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); + else CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); } byte TrackManager::nextCycleTrack=MAX_TRACKS; @@ -428,10 +413,9 @@ std::vectorTrackManager::getMainDrivers() { } #endif -void TrackManager::setPower2(bool setProg,POWERMODE mode, bool doall, uint8_t thistrack) { +void TrackManager::setPower2(bool setProg,POWERMODE mode) { if (!setProg) mainPowerGuess=mode; FOR_EACH_TRACK(t) { - if (doall==false && thistrack != t) break; MotorDriver * driver=track[t]; if (!driver) continue; switch (track[t]->getMode()) { @@ -469,9 +453,15 @@ void TrackManager::setPower2(bool setProg,POWERMODE mode, bool doall, uint8_t th } } -// void TrackManager::setTrackPower(bool progTrack,POWERMODE mode, uint8_t track) { -// // write the code for this. -// } +void TrackManager::setTrackPower(POWERMODE mode, byte thistrack) { + + DIAG(F("SetPower Processing Track %d"), thistrack); + MotorDriver * driver=track[thistrack]; + if (!driver) return; + + driver->setPower(mode); + + } POWERMODE TrackManager::getProgPower() { FOR_EACH_TRACK(t) diff --git a/TrackManager.h b/TrackManager.h index 3f9c73f..c6eea5c 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -39,6 +39,10 @@ const byte TRACK_NUMBER_5=5, TRACK_NUMBER_F=5; const byte TRACK_NUMBER_6=6, TRACK_NUMBER_G=6; const byte TRACK_NUMBER_7=7, TRACK_NUMBER_H=7; +// These constants help EXRAIL macros convert Track Power e.g. SET_POWER(A ON|OFF). +const byte TRACK_POWER_0=0, TRACK_POWER_OFF=0; +const byte TRACK_POWER_1=1, TRACK_POWER_ON=1; + class TrackManager { public: static void Setup(const FSH * shieldName, @@ -60,12 +64,13 @@ class TrackManager { #ifdef ARDUINO_ARCH_ESP32 static std::vectorgetMainDrivers(); #endif - static void setPower2(bool progTrack,POWERMODE mode, bool doall, uint8_t thistrack); + static void setTrackPower(POWERMODE mode, byte thistrack); + static void setPower2(bool progTrack,POWERMODE mode); static void setPower(POWERMODE mode) {setMainPower(mode); setProgPower(mode);} - static void setMainPower(POWERMODE mode) {setPower2(false,mode,true,0);} - static void setProgPower(POWERMODE mode) {setPower2(true,mode,true,0);} - static void SetMainTrackPower(POWERMODE mode, uint8_t track) {setPower2(false,mode,false,track);} - static void SetProgTrackPower(POWERMODE mode, uint8_t track) {setPower2(true,mode,false,track);} + static void setMainPower(POWERMODE mode) {setPower2(false,mode);} + static void setProgPower(POWERMODE mode) {setPower2(true,mode);} + + static const int16_t MAX_TRACKS=8; static bool setTrackMode(byte track, TRACK_MODE mode, int16_t DCaddr=0); From 9333beda4941b739d50bc47d5ec22390a77b1fa6 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 24 Sep 2023 20:54:17 +0200 Subject: [PATCH 133/218] correct return when requesting D RAM --- DCCEXParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 7610d45..fe21549 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -985,7 +985,7 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) case HASH_KEYWORD_RAM: // StringFormatter::send(stream, F("Free memory=%d\n"), DCCTimer::getMinimumFreeMemory()); - break; + return true; #ifndef DISABLE_PROG case HASH_KEYWORD_ACK: // From 5a7f278b1e94251a971eb65bd49da48cc722c29d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 24 Sep 2023 20:54:17 +0200 Subject: [PATCH 134/218] correct return when requesting D RAM --- DCCEXParser.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 29c252d..c9f31f0 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -1057,7 +1057,7 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) case HASH_KEYWORD_RAM: // StringFormatter::send(stream, F("Free memory=%d\n"), DCCTimer::getMinimumFreeMemory()); - break; + return true; #ifndef DISABLE_PROG case HASH_KEYWORD_ACK: // From 624656ebc97a60df1c8085fda0fd9e8cb4d33b99 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sun, 24 Sep 2023 20:56:27 +0200 Subject: [PATCH 135/218] date tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 98afa03..d46ef22 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202308302157Z" +#define GITHUB_SHA "devel-202309241855Z" From 9e3ae21bb8fc26e25b5e448498217cea53159207 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Mon, 25 Sep 2023 09:59:17 +0100 Subject: [PATCH 136/218] Change to EXRAIL Set_Power Change to EXRAIL SET_Power --- DCCEXParser.cpp | 6 +----- EXRAILMacros.h | 2 +- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 9c94f82..a8c257b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -588,10 +588,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) prog=false; } singletrack=true; - DIAG(F("Calling SetPower %d - %d - %d"), POWERMODE::ON, false, t); if (main) TrackManager::setTrackPower(POWERMODE::ON, t); - //if (main) TrackManager::SetMainTrackPower(POWERMODE::ON, t); - //if (prog) TrackManager::SetProgTrackPower(POWERMODE::ON, t); } else break; // will reply } @@ -622,7 +619,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG> prog=true; } -#endif +#endif <= else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> byte t = (p[0] - 'A'); DIAG(F("Processing track - %d "), t); @@ -636,7 +633,6 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) prog=false; } singletrack=true; - DIAG(F("Calling SetPower %d - %d - %d"), POWERMODE::OFF, false, t); if (main) TrackManager::setTrackPower(POWERMODE::OFF, t); } diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 62b852a..b85b68a 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -412,7 +412,7 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define SERVO_TURNOUT(id,pin,activeAngle,inactiveAngle,profile,description...) OPCODE_SERVOTURNOUT,V(id),OPCODE_PAD,V(pin),OPCODE_PAD,V(activeAngle),OPCODE_PAD,V(inactiveAngle),OPCODE_PAD,V(PCA9685::ProfileType::profile), #define SET(pin) OPCODE_SET,V(pin), #define SET_TRACK(track,mode) OPCODE_SET_TRACK,V(TRACK_MODE_##mode <<8 | TRACK_NUMBER_##track), -#define SET_POWER(track,onoff) OPCODE_SET_POWER,V(TRACK_POWER_##onoff, TRACK_NUMBER_##track), +#define SET_POWER(track,onoff) OPCODE_SET_POWER,V(TRACK_POWER_##onoff),OPCODE_PAD, V(TRACK_NUMBER_##track), #define SETLOCO(loco) OPCODE_SETLOCO,V(loco), #define SIGNAL(redpin,amberpin,greenpin) #define SIGNALH(redpin,amberpin,greenpin) From 17c004aecf558baa980bf9bb620cd7e456cf7969 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Mon, 25 Sep 2023 14:32:54 +0100 Subject: [PATCH 137/218] Code corrections code corrections --- DCCEXParser.cpp | 37 ++++++++++++++++--------- EXRAIL2.cpp | 6 ++--- TrackManager.cpp | 70 +++++++++++++++++++++++++++++++++++------------- TrackManager.h | 2 +- 4 files changed, 81 insertions(+), 34 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index a8c257b..a68b398 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -556,6 +556,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) bool prog=false; bool join=false; bool singletrack=false; + byte t=0; if (params > 1) break; if (params==0) { // All main=true; @@ -576,8 +577,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } #endif else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> - byte t = (p[0] - 'A'); - DIAG(F("Processing track - %d "), t); + t = (p[0] - 'A'); + //DIAG(F("Processing track - %d "), t); if (TrackManager::isProg(t)) { main = false; prog = true; @@ -588,7 +589,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) prog=false; } singletrack=true; - if (main) TrackManager::setTrackPower(POWERMODE::ON, t); + } else break; // will reply } @@ -597,6 +598,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) if (main) TrackManager::setMainPower(POWERMODE::ON); if (prog) TrackManager::setProgPower(POWERMODE::ON); } + else { + if (main) TrackManager::setTrackPower(false, POWERMODE::ON, t); + if (prog) { + TrackManager::setTrackPower(true, POWERMODE::ON, t); + } + } CommandDistributor::broadcastPower(); return; } @@ -606,6 +613,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) bool main=false; bool prog=false; bool singletrack=false; + byte t=0; if (params > 1) break; if (params==0) { // All main=true; @@ -621,8 +629,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } #endif <= else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> - byte t = (p[0] - 'A'); - DIAG(F("Processing track - %d "), t); + t = (p[0] - 'A'); + //DIAG(F("Processing track - %d "), t); if (TrackManager::isProg(t)) { main = false; prog = true; @@ -632,24 +640,29 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) main=true; prog=false; } - singletrack=true; - if (main) TrackManager::setTrackPower(POWERMODE::OFF, t); - } - + singletrack=true; + } else break; // will reply } TrackManager::setJoin(false); if (!singletrack) { - if (main) TrackManager::setMainPower(POWERMODE::OFF); + if (main) TrackManager::setMainPower(POWERMODE::OFF); + if (prog) { + TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off + TrackManager::setProgPower(POWERMODE::OFF); + } + } + else { + if (main) TrackManager::setTrackPower(false, POWERMODE::OFF, t); if (prog) { TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off - TrackManager::setProgPower(POWERMODE::OFF); + TrackManager::setTrackPower(true, POWERMODE::OFF, t); } } CommandDistributor::broadcastPower(); return; - } + } case '!': // ESTOP ALL DCC::setThrottle(0,1,1); // this broadcasts speed 1(estop) and sets all reminders to speed 1. diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 07f4768..8fd9532 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -783,13 +783,13 @@ void RMFT2::loop2() { case OPCODE_SET_POWER: // operand is TRACK_POWER , trackid - + //byte thistrack=getOperand(1); switch (operand) { case TRACK_POWER_0: - TrackManager::setTrackPower(POWERMODE::OFF, getOperand(1)); + TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), POWERMODE::OFF, getOperand(1)); break; case TRACK_POWER_1: - TrackManager::setTrackPower(POWERMODE::ON, getOperand(1)); + TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), POWERMODE::ON, getOperand(1)); break; } diff --git a/TrackManager.cpp b/TrackManager.cpp index ad7ef6a..d653645 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -364,16 +364,16 @@ void TrackManager::streamTrackState(Print* stream, byte t) { break; #endif case TRACK_MODE_NONE: - if (pstate) {format=F("<= %c NONE %ON>\n");} else {format=F("<= %c NONE %OFF>\n");} + if (pstate) {format=F("<= %c NONE ON>\n");} else {format=F("<= %c NONE OFF>\n");} break; case TRACK_MODE_EXT: if (pstate) {format=F("<= %c EXT ON>\n");} else {format=F("<= %c EXT OFF>\n");} break; case TRACK_MODE_DC: - if (pstate) {format=F("<= %c DC ON %d>\n");} else {format=F("<= %c DC OFF %d>\n");} + if (pstate) {format=F("<= %c DC %d ON>\n");} else {format=F("<= %c DC %d OFF>\n");} break; case TRACK_MODE_DCX: - if (pstate) {format=F("<= %c DCX ON %d>\n");} else {format=F("<= %c DCX OFF %d>\n");} + if (pstate) {format=F("<= %c DCX %d ON>\n");} else {format=F("<= %c DCX %d OFF>\n");} break; default: break; // unknown, dont care @@ -416,9 +416,51 @@ std::vectorTrackManager::getMainDrivers() { void TrackManager::setPower2(bool setProg,POWERMODE mode) { if (!setProg) mainPowerGuess=mode; FOR_EACH_TRACK(t) { - MotorDriver * driver=track[t]; - if (!driver) continue; - switch (track[t]->getMode()) { + TrackManager::setTrackPower(setProg, mode, t); + // MotorDriver * driver=track[t]; + // if (!driver) continue; + // switch (track[t]->getMode()) { + // case TRACK_MODE_MAIN: + // if (setProg) break; + // // toggle brake before turning power on - resets overcurrent error + // // on the Pololu board if brake is wired to ^D2. + // // XXX see if we can make this conditional + // driver->setBrake(true); + // driver->setBrake(false); // DCC runs with brake off + // driver->setPower(mode); + // break; + // case TRACK_MODE_DC: + // case TRACK_MODE_DCX: + // if (setProg) break; + // driver->setBrake(true); // DC starts with brake on + // applyDCSpeed(t); // speed match DCC throttles + // driver->setPower(mode); + // break; + // case TRACK_MODE_PROG: + // if (!setProg) break; + // driver->setBrake(true); + // driver->setBrake(false); + // driver->setPower(mode); + // break; + // case TRACK_MODE_EXT: + // driver->setBrake(true); + // driver->setBrake(false); + // driver->setPower(mode); + // break; + // case TRACK_MODE_NONE: + // break; + // } + + } +} + +void TrackManager::setTrackPower(bool setProg, POWERMODE mode, byte thistrack) { + + DIAG(F("SetTrackPower Processing Track %d"), thistrack); + MotorDriver * driver=track[thistrack]; + if (!driver) return; + + switch (track[thistrack]->getMode()) { case TRACK_MODE_MAIN: if (setProg) break; // toggle brake before turning power on - resets overcurrent error @@ -432,7 +474,7 @@ void TrackManager::setPower2(bool setProg,POWERMODE mode) { case TRACK_MODE_DCX: if (setProg) break; driver->setBrake(true); // DC starts with brake on - applyDCSpeed(t); // speed match DCC throttles + applyDCSpeed(thistrack); // speed match DCC throttles driver->setPower(mode); break; case TRACK_MODE_PROG: @@ -449,17 +491,9 @@ void TrackManager::setPower2(bool setProg,POWERMODE mode) { case TRACK_MODE_NONE: break; } - - } -} - -void TrackManager::setTrackPower(POWERMODE mode, byte thistrack) { - - DIAG(F("SetPower Processing Track %d"), thistrack); - MotorDriver * driver=track[thistrack]; - if (!driver) return; - - driver->setPower(mode); + if (mode == POWERMODE::ON) {DIAG(F("Power Track %d ON"), thistrack);} + else {DIAG(F("Power Track %d OFF"), thistrack);} + //driver->setPower(mode); } diff --git a/TrackManager.h b/TrackManager.h index c6eea5c..39f708a 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -64,7 +64,7 @@ class TrackManager { #ifdef ARDUINO_ARCH_ESP32 static std::vectorgetMainDrivers(); #endif - static void setTrackPower(POWERMODE mode, byte thistrack); + static void setTrackPower(bool setProg, POWERMODE mode, byte thistrack); static void setPower2(bool progTrack,POWERMODE mode); static void setPower(POWERMODE mode) {setMainPower(mode); setProgPower(mode);} static void setMainPower(POWERMODE mode) {setPower2(false,mode);} From 2a46b960838e3d96e4577001afb24bd3ae7f8016 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Tue, 26 Sep 2023 18:02:39 +0100 Subject: [PATCH 138/218] Updates to power Updates to powere routines and EXRAIL --- DCCEXParser.cpp | 175 +++++++++++++++++++++++++---------------------- TrackManager.cpp | 59 +++++----------- TrackManager.h | 5 +- 3 files changed, 114 insertions(+), 125 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index a68b398..aee7e05 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -552,84 +552,88 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case '1': // POWERON <1 [MAIN|PROG|JOIN]> { - bool main=false; - bool prog=false; - bool join=false; - bool singletrack=false; - byte t=0; - if (params > 1) break; - if (params==0) { // All - main=true; - prog=true; - } - if (params==1) { - if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN> - main=true; - } + bool main=false; + bool prog=false; + bool join=false; + bool singletrack=false; + //byte t=0; + if (params > 1) break; + if (params==0) { // All + main=true; + prog=true; + } + if (params==1) { + if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN> + main=true; + } #ifndef DISABLE_PROG - else if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN> - main=true; - prog=true; - join=true; - } - else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG> - prog=true; - } + else if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN> + main=true; + prog=true; + join=true; + } + else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG> + prog=true; + } #endif - else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> - t = (p[0] - 'A'); - //DIAG(F("Processing track - %d "), t); - if (TrackManager::isProg(t)) { - main = false; - prog = true; - } - else - { - main=true; - prog=false; - } - singletrack=true; + else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> + byte t = (p[0] - 'A'); + //DIAG(F("Processing track - %d "), t); + if (TrackManager::isProg(t)) { + main = false; + prog = true; + } + else + { + main=true; + prog=false; + } + singletrack=true; + if (main) TrackManager::setTrackPower(false, POWERMODE::ON, t); + if (prog) TrackManager::setTrackPower(true, POWERMODE::ON, t); + //CommandDistributor::broadcastPower(); + //TrackManager::streamTrackState(stream, t); + TrackManager::streamTrackState(NULL,t); + StringFormatter::send(stream, F("Track %d ON\n"), t); + return; + } + + else break; // will reply + } - } - else break; // will reply - } - TrackManager::setJoin(join); if (!singletrack) { + TrackManager::setJoin(join); if (main) TrackManager::setMainPower(POWERMODE::ON); if (prog) TrackManager::setProgPower(POWERMODE::ON); - } - else { - if (main) TrackManager::setTrackPower(false, POWERMODE::ON, t); - if (prog) { - TrackManager::setTrackPower(true, POWERMODE::ON, t); + CommandDistributor::broadcastPower(); + + return; } - } - CommandDistributor::broadcastPower(); - return; + } case '0': // POWEROFF <0 [MAIN | PROG] > { - bool main=false; - bool prog=false; - bool singletrack=false; - byte t=0; - if (params > 1) break; - if (params==0) { // All - main=true; - prog=true; - } - if (params==1) { - if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN> - main=true; - } + bool main=false; + bool prog=false; + bool singletrack=false; + //byte t=0; + if (params > 1) break; + if (params==0) { // All + main=true; + prog=true; + } + if (params==1) { + if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN> + main=true; + } #ifndef DISABLE_PROG - else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG> - prog=true; - } -#endif <= - else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> - t = (p[0] - 'A'); + else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG> + prog=true; + } +#endif + else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> + byte t = (p[0] - 'A'); //DIAG(F("Processing track - %d "), t); if (TrackManager::isProg(t)) { main = false; @@ -640,28 +644,33 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) main=true; prog=false; } - singletrack=true; - } - else break; // will reply - } - - TrackManager::setJoin(false); - if (!singletrack) { - if (main) TrackManager::setMainPower(POWERMODE::OFF); + singletrack=true; + TrackManager::setJoin(false); + if (main) TrackManager::setTrackPower(false, POWERMODE::OFF, t); if (prog) { + TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off + TrackManager::setTrackPower(true, POWERMODE::OFF, t); + CommandDistributor::broadcastPower(); + } + StringFormatter::send(stream, F("Track %d OFF\n"), t); + TrackManager::streamTrackState(NULL, t); + return; + } + + else break; // will reply + } + + if (!singletrack) { + TrackManager::setJoin(false); + + if (main) TrackManager::setMainPower(POWERMODE::OFF); + if (prog) { TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off TrackManager::setProgPower(POWERMODE::OFF); } - } - else { - if (main) TrackManager::setTrackPower(false, POWERMODE::OFF, t); - if (prog) { - TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off - TrackManager::setTrackPower(true, POWERMODE::OFF, t); - } + CommandDistributor::broadcastPower(); + return; } - CommandDistributor::broadcastPower(); - return; } case '!': // ESTOP ALL diff --git a/TrackManager.cpp b/TrackManager.cpp index d653645..f66fb4d 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -26,7 +26,8 @@ #include "MotorDriver.h" #include "DCCTimer.h" #include "DIAG.h" -#include"CommandDistributor.h" +#include "CommandDistributor.h" +#include "DCCEXParser.h" // Virtualised Motor shield multi-track hardware Interface #define FOR_EACH_TRACK(t) for (byte t=0;t<=lastTrack;t++) @@ -319,6 +320,7 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) FOR_EACH_TRACK(t) streamTrackState(stream,t); return true; + } p[0]-=HASH_KEYWORD_A; // convert A... to 0.... @@ -378,8 +380,10 @@ void TrackManager::streamTrackState(Print* stream, byte t) { default: break; // unknown, dont care } - if (stream) StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); + + if (stream) StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); else CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); + } byte TrackManager::nextCycleTrack=MAX_TRACKS; @@ -416,47 +420,16 @@ std::vectorTrackManager::getMainDrivers() { void TrackManager::setPower2(bool setProg,POWERMODE mode) { if (!setProg) mainPowerGuess=mode; FOR_EACH_TRACK(t) { + TrackManager::setTrackPower(setProg, mode, t); - // MotorDriver * driver=track[t]; - // if (!driver) continue; - // switch (track[t]->getMode()) { - // case TRACK_MODE_MAIN: - // if (setProg) break; - // // toggle brake before turning power on - resets overcurrent error - // // on the Pololu board if brake is wired to ^D2. - // // XXX see if we can make this conditional - // driver->setBrake(true); - // driver->setBrake(false); // DCC runs with brake off - // driver->setPower(mode); - // break; - // case TRACK_MODE_DC: - // case TRACK_MODE_DCX: - // if (setProg) break; - // driver->setBrake(true); // DC starts with brake on - // applyDCSpeed(t); // speed match DCC throttles - // driver->setPower(mode); - // break; - // case TRACK_MODE_PROG: - // if (!setProg) break; - // driver->setBrake(true); - // driver->setBrake(false); - // driver->setPower(mode); - // break; - // case TRACK_MODE_EXT: - // driver->setBrake(true); - // driver->setBrake(false); - // driver->setPower(mode); - // break; - // case TRACK_MODE_NONE: - // break; - // } } + return; } void TrackManager::setTrackPower(bool setProg, POWERMODE mode, byte thistrack) { - DIAG(F("SetTrackPower Processing Track %d"), thistrack); + //DIAG(F("SetTrackPower Processing Track %d"), thistrack); MotorDriver * driver=track[thistrack]; if (!driver) return; @@ -491,16 +464,22 @@ void TrackManager::setTrackPower(bool setProg, POWERMODE mode, byte thistrack) { case TRACK_MODE_NONE: break; } - if (mode == POWERMODE::ON) {DIAG(F("Power Track %d ON"), thistrack);} - else {DIAG(F("Power Track %d OFF"), thistrack);} - //driver->setPower(mode); } + void TrackManager::reportPowerChange(Print* stream, byte thistrack) { + // This function is for backward JMRI compatibility only + // It reports the first track only, as main, regardless of track settings. + // + int maxCurrent=track[0]->raw2mA(track[0]->getRawCurrentTripValue()); + StringFormatter::send(stream, F("\n"), + track[0]->raw2mA(track[0]->getCurrentRaw(false)), maxCurrent, maxCurrent); +} + POWERMODE TrackManager::getProgPower() { FOR_EACH_TRACK(t) if (track[t]->getMode()==TRACK_MODE_PROG) - return track[t]->getPower(); + return track[t]->getPower(); return POWERMODE::OFF; } diff --git a/TrackManager.h b/TrackManager.h index 39f708a..cf2d54b 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -64,12 +64,12 @@ class TrackManager { #ifdef ARDUINO_ARCH_ESP32 static std::vectorgetMainDrivers(); #endif - static void setTrackPower(bool setProg, POWERMODE mode, byte thistrack); + static void setPower2(bool progTrack,POWERMODE mode); static void setPower(POWERMODE mode) {setMainPower(mode); setProgPower(mode);} static void setMainPower(POWERMODE mode) {setPower2(false,mode);} static void setProgPower(POWERMODE mode) {setPower2(true,mode);} - + static void setTrackPower(bool setProg, POWERMODE mode, byte thistrack); static const int16_t MAX_TRACKS=8; @@ -84,6 +84,7 @@ class TrackManager { static void sampleCurrent(); static void reportGauges(Print* stream); static void reportCurrent(Print* stream); + static void reportPowerChange(Print* stream, byte thistrack); static void reportObsoleteCurrent(Print* stream); static void streamTrackState(Print* stream, byte t); static bool isPowerOn(byte t); From 25bbfa4c6805bf03cdb188d6d7d3457eec9b642b Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Wed, 27 Sep 2023 14:46:48 +0100 Subject: [PATCH 139/218] Fix <1 JOIN> Fixed <1 JOIN> issue in TrackManager --- DCCEXParser.cpp | 15 +++++++++------ EXRAIL2.cpp | 4 ++-- TrackManager.cpp | 18 ++++++++++-------- TrackManager.h | 9 +++++---- 4 files changed, 26 insertions(+), 20 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index aee7e05..c82175b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -589,8 +589,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) prog=false; } singletrack=true; - if (main) TrackManager::setTrackPower(false, POWERMODE::ON, t); - if (prog) TrackManager::setTrackPower(true, POWERMODE::ON, t); + if (main) TrackManager::setTrackPower(false, false, POWERMODE::ON, t); + if (prog) TrackManager::setTrackPower(true, false, POWERMODE::ON, t); //CommandDistributor::broadcastPower(); //TrackManager::streamTrackState(stream, t); TrackManager::streamTrackState(NULL,t); @@ -603,8 +603,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) if (!singletrack) { TrackManager::setJoin(join); - if (main) TrackManager::setMainPower(POWERMODE::ON); - if (prog) TrackManager::setProgPower(POWERMODE::ON); + if (join) TrackManager::setJoinPower(POWERMODE::ON); + else { + if (main) TrackManager::setMainPower(POWERMODE::ON); + if (prog) TrackManager::setProgPower(POWERMODE::ON); + } CommandDistributor::broadcastPower(); return; @@ -646,10 +649,10 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) } singletrack=true; TrackManager::setJoin(false); - if (main) TrackManager::setTrackPower(false, POWERMODE::OFF, t); + if (main) TrackManager::setTrackPower(false, false, POWERMODE::OFF, t); if (prog) { TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off - TrackManager::setTrackPower(true, POWERMODE::OFF, t); + TrackManager::setTrackPower(true, false, POWERMODE::OFF, t); CommandDistributor::broadcastPower(); } StringFormatter::send(stream, F("Track %d OFF\n"), t); diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 8fd9532..321872d 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -786,10 +786,10 @@ void RMFT2::loop2() { //byte thistrack=getOperand(1); switch (operand) { case TRACK_POWER_0: - TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), POWERMODE::OFF, getOperand(1)); + TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), false, POWERMODE::OFF, getOperand(1)); break; case TRACK_POWER_1: - TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), POWERMODE::ON, getOperand(1)); + TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), false, POWERMODE::ON, getOperand(1)); break; } diff --git a/TrackManager.cpp b/TrackManager.cpp index f66fb4d..dd27d85 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -417,17 +417,17 @@ std::vectorTrackManager::getMainDrivers() { } #endif -void TrackManager::setPower2(bool setProg,POWERMODE mode) { +void TrackManager::setPower2(bool setProg,bool setJoin, POWERMODE mode) { if (!setProg) mainPowerGuess=mode; FOR_EACH_TRACK(t) { - TrackManager::setTrackPower(setProg, mode, t); + TrackManager::setTrackPower(setProg, setJoin, mode, t); } return; } -void TrackManager::setTrackPower(bool setProg, POWERMODE mode, byte thistrack) { +void TrackManager::setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byte thistrack) { //DIAG(F("SetTrackPower Processing Track %d"), thistrack); MotorDriver * driver=track[thistrack]; @@ -445,21 +445,23 @@ void TrackManager::setTrackPower(bool setProg, POWERMODE mode, byte thistrack) { break; case TRACK_MODE_DC: case TRACK_MODE_DCX: - if (setProg) break; + DIAG(F("Processing track - %d setProg %d"), thistrack, setProg); + if (setProg || setJoin) {DIAG(F("Nowt to do")); break;} driver->setBrake(true); // DC starts with brake on applyDCSpeed(thistrack); // speed match DCC throttles driver->setPower(mode); + DIAG(F("Doing it anyway")); break; case TRACK_MODE_PROG: - if (!setProg) break; + if (!setProg && !setJoin) break; driver->setBrake(true); driver->setBrake(false); driver->setPower(mode); break; case TRACK_MODE_EXT: - driver->setBrake(true); - driver->setBrake(false); - driver->setPower(mode); + driver->setBrake(true); + driver->setBrake(false); + driver->setPower(mode); break; case TRACK_MODE_NONE: break; diff --git a/TrackManager.h b/TrackManager.h index cf2d54b..21b8d59 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -65,11 +65,12 @@ class TrackManager { static std::vectorgetMainDrivers(); #endif - static void setPower2(bool progTrack,POWERMODE mode); + static void setPower2(bool progTrack,bool joinTrack,POWERMODE mode); static void setPower(POWERMODE mode) {setMainPower(mode); setProgPower(mode);} - static void setMainPower(POWERMODE mode) {setPower2(false,mode);} - static void setProgPower(POWERMODE mode) {setPower2(true,mode);} - static void setTrackPower(bool setProg, POWERMODE mode, byte thistrack); + static void setMainPower(POWERMODE mode) {setPower2(false,false,mode);} + static void setProgPower(POWERMODE mode) {setPower2(true,false,mode);} + static void setJoinPower(POWERMODE mode) {setPower2(false,true,mode);} + static void setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byte thistrack); static const int16_t MAX_TRACKS=8; From ed0cfee0910fd37a78f0996ccfb2d0c2d2c88236 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Thu, 28 Sep 2023 17:43:22 +0800 Subject: [PATCH 140/218] STM32 DCCEXanalogWrite for TrackManager PWM --- DCCTimerSTM32.cpp | 117 ++++++++++++++++++++++++++++++++++++++++------ MotorDriver.cpp | 66 ++++++++++++++++++++++++-- MotorDriver.h | 38 +++++++++++---- TrackManager.cpp | 12 +++++ version.h | 7 ++- 5 files changed, 214 insertions(+), 26 deletions(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index f57499a..0f9c432 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -1,6 +1,6 @@ /* * © 2023 Neil McKechnie - * © 2022-23 Paul M. Antoine + * © 2022-2023 Paul M. Antoine * © 2021 Mike S * © 2021, 2023 Harald Barth * © 2021 Fred Decker @@ -154,13 +154,28 @@ HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6 /////////////////////////////////////////////////////////////////////////////////////////////// INTERRUPT_CALLBACK interruptHandler=0; -// Let's use STM32's timer #11 until disabused of this notion -// Timer #11 is used for "servo" library, but as DCC-EX is not using -// this libary, we should be free and clear. -HardwareTimer timer(TIM11); + +// On STM32F4xx models that have them, Timers 6 and 7 have no PWM output capability, +// so are good choices for general timer duties - they are used for tone and servo +// in stm32duino so we shall usurp those as DCC-EX doesn't use tone or servo libs. +// NB: the F401, F410 and F411 do **not** have Timer 6 or 7, so we use Timer 11 +#ifndef DCC_EX_TIMER +#if defined(TIM6) +#define DCC_EX_TIMER TIM6 +#elif defined(TIM7) +#define DCC_EX_TIMER TIM7 +#elif defined(TIM11) +#define DCC_EX_TIMER TIM11 +#else +#warning This STM32F4XX variant does not have Timers 6,7 or 11!! +#endif +#endif // ifndef DCC_EX_TIMER + +HardwareTimer dcctimer(DCC_EX_TIMER); +void DCCTimer_Handler() __attribute__((interrupt)); // Timer IRQ handler -void Timer11_Handler() { +void DCCTimer_Handler() { interruptHandler(); } @@ -168,22 +183,24 @@ void DCCTimer::begin(INTERRUPT_CALLBACK callback) { interruptHandler=callback; noInterrupts(); - // adc_set_sample_rate(ADC_SAMPLETIME_480CYCLES); - timer.pause(); - timer.setPrescaleFactor(1); + dcctimer.pause(); + dcctimer.setPrescaleFactor(1); // timer.setOverflow(CLOCK_CYCLES * 2); - timer.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT); - timer.attachInterrupt(Timer11_Handler); - timer.refresh(); - timer.resume(); + dcctimer.setOverflow(DCC_SIGNAL_TIME, MICROSEC_FORMAT); + // dcctimer.attachInterrupt(Timer11_Handler); + dcctimer.attachInterrupt(DCCTimer_Handler); + dcctimer.setInterruptPriority(0, 0); // Set highest preemptive priority! + dcctimer.refresh(); + dcctimer.resume(); interrupts(); } bool DCCTimer::isPWMPin(byte pin) { - //TODO: SAMD whilst this call to digitalPinHasPWM will reveal which pins can do PWM, + //TODO: STM32 whilst this call to digitalPinHasPWM will reveal which pins can do PWM, // there's no support yet for High Accuracy, so for now return false // return digitalPinHasPWM(pin); + (void) pin; return false; } @@ -235,6 +252,78 @@ void DCCTimer::reset() { while(true) {}; } +// TODO: rationalise the size of these... could really use sparse arrays etc. +static HardwareTimer * pin_timer[100] = {0}; +static uint32_t channel_frequency[100] = {0}; +static uint32_t pin_channel[100] = {0}; + +// Using the HardwareTimer library API included in stm32duino core to handle PWM duties +// TODO: in order to use the HA code above which Neil kindly wrote, we may have to do something more +// sophisticated about detecting any clash between the timer we'd like to use for PWM and the ones +// currently used for HA so they don't interfere with one another. For now we'll just make PWM +// work well... then work backwards to integrate with HA mode if we can. +void DCCTimer::DCCEXanalogWriteFrequency(uint8_t pin, uint32_t frequency) +{ + if (pin_timer[pin] == NULL) { + // Automatically retrieve TIM instance and channel associated to pin + // This is used to be compatible with all STM32 series automatically. + TIM_TypeDef *Instance = (TIM_TypeDef *)pinmap_peripheral(digitalPinToPinName(pin), PinMap_PWM); + if (Instance == NULL) { + // We shouldn't get here (famous last words) as it ought to have been caught by brakeCanPWM()! + DIAG(F("DCCEXanalogWriteFrequency::Pin %d has no PWM function!"), pin); + return; + } + pin_channel[pin] = STM_PIN_CHANNEL(pinmap_function(digitalPinToPinName(pin), PinMap_PWM)); + + // Instantiate HardwareTimer object. Thanks to 'new' instantiation, + // HardwareTimer is not destructed when setup function is finished. + pin_timer[pin] = new HardwareTimer(Instance); + // Configure and start PWM + // MyTim->setPWM(channel, pin, 5, 10, NULL, NULL); // No callback required, we can simplify the function call + if (pin_timer[pin] != NULL) + { + pin_timer[pin]->setPWM(pin_channel[pin], pin, frequency, 0); // set frequency in Hertz, 0% dutycycle + DIAG(F("DCCEXanalogWriteFrequency::Pin %d on Timer %d, frequency %d"), pin, pin_channel[pin], frequency); + } + else + DIAG(F("DCCEXanalogWriteFrequency::failed to allocate HardwareTimer instance!")); + } + else + { + // Frequency change request + if (frequency != channel_frequency[pin]) + { + pinmap_pinout(digitalPinToPinName(pin), PinMap_TIM); // ensure the pin has been configured! + pin_timer[pin]->setOverflow(frequency, HERTZ_FORMAT); // Just change the frequency if it's already running! + DIAG(F("DCCEXanalogWriteFrequency::setting frequency to %d"), frequency); + } + } + channel_frequency[pin] = frequency; + return; +} + +void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) { + // Calculate percentage duty cycle from value given + uint32_t duty_cycle = (value * 100 / 256) + 1; + if (pin_timer[pin] != NULL) { + if (duty_cycle == 100) + { + pin_timer[pin]->pauseChannel(pin_channel[pin]); + DIAG(F("DCCEXanalogWrite::Pausing timer channel on pin %d"), pin); + } + else + { + pinmap_pinout(digitalPinToPinName(pin), PinMap_TIM); // ensure the pin has been configured! + pin_timer[pin]->resumeChannel(pin_channel[pin]); + pin_timer[pin]->setCaptureCompare(pin_channel[pin], duty_cycle, PERCENT_COMPARE_FORMAT); // DCC_EX_PWM_FREQ Hertz, duty_cycle% dutycycle + DIAG(F("DCCEXanalogWrite::Pin %d, value %d, duty cycle %d"), pin, value, duty_cycle); + } + } + else + DIAG(F("DCCEXanalogWrite::Pin %d is not configured for PWM!"), pin); +} + + // Now we can handle more ADCs, maybe this works! #define NUM_ADC_INPUTS NUM_ANALOG_INPUTS diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 4644ad5..61e229f 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -34,6 +34,11 @@ unsigned long MotorDriver::globalOverloadStart = 0; volatile portreg_t shadowPORTA; volatile portreg_t shadowPORTB; volatile portreg_t shadowPORTC; +#if defined(ARDUINO_ARCH_STM32) +volatile portreg_t shadowPORTD; +volatile portreg_t shadowPORTE; +volatile portreg_t shadowPORTF; +#endif MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, int16_t brake_pin, byte current_pin, float sense_factor, unsigned int trip_milliamps, int16_t fault_pin) { @@ -68,6 +73,21 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i fastSignalPin.shadowinout = fastSignalPin.inout; fastSignalPin.inout = &shadowPORTC; } + if (HAVE_PORTD(fastSignalPin.inout == &PORTD)) { + DIAG(F("Found PORTD pin %d"),signalPin); + fastSignalPin.shadowinout = fastSignalPin.inout; + fastSignalPin.inout = &shadowPORTD; + } + if (HAVE_PORTE(fastSignalPin.inout == &PORTE)) { + DIAG(F("Found PORTE pin %d"),signalPin); + fastSignalPin.shadowinout = fastSignalPin.inout; + fastSignalPin.inout = &shadowPORTE; + } + if (HAVE_PORTF(fastSignalPin.inout == &PORTF)) { + DIAG(F("Found PORTF pin %d"),signalPin); + fastSignalPin.shadowinout = fastSignalPin.inout; + fastSignalPin.inout = &shadowPORTF; + } signalPin2=signal_pin2; if (signalPin2!=UNUSED_PIN) { @@ -91,6 +111,21 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i fastSignalPin2.shadowinout = fastSignalPin2.inout; fastSignalPin2.inout = &shadowPORTC; } + if (HAVE_PORTD(fastSignalPin2.inout == &PORTD)) { + DIAG(F("Found PORTD pin %d"),signalPin2); + fastSignalPin2.shadowinout = fastSignalPin2.inout; + fastSignalPin2.inout = &shadowPORTD; + } + if (HAVE_PORTE(fastSignalPin2.inout == &PORTE)) { + DIAG(F("Found PORTE pin %d"),signalPin2); + fastSignalPin2.shadowinout = fastSignalPin2.inout; + fastSignalPin2.inout = &shadowPORTE; + } + if (HAVE_PORTF(fastSignalPin2.inout == &PORTF)) { + DIAG(F("Found PORTF pin %d"),signalPin2); + fastSignalPin2.shadowinout = fastSignalPin2.inout; + fastSignalPin2.inout = &shadowPORTF; + } } else dualSignal=false; @@ -279,7 +314,7 @@ void MotorDriver::startCurrentFromHW() { #pragma GCC pop_options #endif //ANALOG_READ_INTERRUPT -#if defined(ARDUINO_ARCH_ESP32) +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32) #ifdef VARIABLE_TONES uint16_t taurustones[28] = { 165, 175, 196, 220, 247, 262, 294, 330, @@ -330,7 +365,7 @@ void MotorDriver::setDCSignal(byte speedcode) { byte tSpeed=speedcode & 0x7F; // DCC Speed with 0,1 stop and speed steps 2 to 127 byte tDir=speedcode & 0x80; byte brake; -#if defined(ARDUINO_ARCH_ESP32) +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32) { int f = 131; #ifdef VARIABLE_TONES @@ -348,7 +383,7 @@ void MotorDriver::setDCSignal(byte speedcode) { else brake = 2 * (128-tSpeed); if (invertBrake) brake=255-brake; -#if defined(ARDUINO_ARCH_ESP32) +#if defined(ARDUINO_ARCH_ESP32) || defined(ARDUINO_ARCH_STM32) DCCTimer::DCCEXanalogWrite(brakePin,brake); #else analogWrite(brakePin,brake); @@ -372,6 +407,24 @@ void MotorDriver::setDCSignal(byte speedcode) { setSignal(tDir); HAVE_PORTC(PORTC=shadowPORTC); interrupts(); + } else if (HAVE_PORTD(fastSignalPin.shadowinout == &PORTD)) { + noInterrupts(); + HAVE_PORTD(shadowPORTD=PORTD); + setSignal(tDir); + HAVE_PORTD(PORTD=shadowPORTD); + interrupts(); + } else if (HAVE_PORTE(fastSignalPin.shadowinout == &PORTE)) { + noInterrupts(); + HAVE_PORTE(shadowPORTE=PORTE); + setSignal(tDir); + HAVE_PORTE(PORTE=shadowPORTE); + interrupts(); + } else if (HAVE_PORTF(fastSignalPin.shadowinout == &PORTF)) { + noInterrupts(); + HAVE_PORTF(shadowPORTF=PORTF); + setSignal(tDir); + HAVE_PORTF(PORTF=shadowPORTF); + interrupts(); } else { noInterrupts(); setSignal(tDir); @@ -393,6 +446,13 @@ void MotorDriver::throttleInrush(bool on) { } else { ledcDetachPin(brakePin); } +#elif defined(ARDUINO_ARCH_STM32) + if(on) { + DCCTimer::DCCEXanalogWriteFrequency(brakePin, 62500); + DCCTimer::DCCEXanalogWrite(brakePin,duty); + } else { + pinMode(brakePin, OUTPUT); + } #else if(on){ switch(brakePin) { diff --git a/MotorDriver.h b/MotorDriver.h index 21bceb6..20a91d3 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -1,5 +1,5 @@ /* - * © 2022 Paul M Antoine + * © 2022-2023 Paul M. Antoine * © 2021 Mike S * © 2021 Fred Decker * © 2020 Chris Harlow @@ -60,6 +60,16 @@ enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PRO #define HAVE_PORTB(X) X #define PORTC GPIOC->ODR #define HAVE_PORTC(X) X +#define PORTD GPIOD->ODR +#define HAVE_PORTD(X) X +#if defined(GPIOE) +#define PORTE GPIOE->ODR +#define HAVE_PORTE(X) X +#endif +#if defined(GPIOF) +#define PORTF GPIOF->ODR +#define HAVE_PORTF(X) X +#endif #endif // if macros not defined as pass-through we define @@ -74,6 +84,15 @@ enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PRO #ifndef HAVE_PORTC #define HAVE_PORTC(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0 #endif +#ifndef HAVE_PORTD +#define HAVE_PORTD(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0 +#endif +#ifndef HAVE_PORTE +#define HAVE_PORTE(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0 +#endif +#ifndef HAVE_PORTF +#define HAVE_PORTF(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0 +#endif // Virtualised Motor shield 1-track hardware Interface @@ -110,6 +129,9 @@ struct FASTPIN { extern volatile portreg_t shadowPORTA; extern volatile portreg_t shadowPORTB; extern volatile portreg_t shadowPORTC; +extern volatile portreg_t shadowPORTD; +extern volatile portreg_t shadowPORTE; +extern volatile portreg_t shadowPORTF; enum class POWERMODE : byte { OFF, ON, OVERLOAD, ALERT }; @@ -163,16 +185,16 @@ class MotorDriver { unsigned int raw2mA( int raw); unsigned int mA2raw( unsigned int mA); inline bool brakeCanPWM() { -#if defined(ARDUINO_ARCH_ESP32) || defined(__arm__) - // TODO: on ARM we can use digitalPinHasPWM, and may wish/need to - return true; -#else -#ifdef digitalPinToTimer +#if defined(ARDUINO_ARCH_ESP32) + return (brakePin != UNUSED_PIN); // This was just (true) but we probably do need to check for UNUSED_PIN! +#elif defined(__arm__) + // On ARM we can use digitalPinHasPWM + return ((brakePin!=UNUSED_PIN) && (digitalPinHasPWM(brakePin))); +#elif defined(digitalPinToTimer) return ((brakePin!=UNUSED_PIN) && (digitalPinToTimer(brakePin))); #else return (brakePin<14 && brakePin >1); -#endif //digitalPinToTimer -#endif //ESP32/ARM +#endif } inline int getRawCurrentTripValue() { return rawCurrentTripValue; diff --git a/TrackManager.cpp b/TrackManager.cpp index bd7c623..687eb8b 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -154,10 +154,16 @@ void TrackManager::setDCCSignal( bool on) { HAVE_PORTA(shadowPORTA=PORTA); HAVE_PORTB(shadowPORTB=PORTB); HAVE_PORTC(shadowPORTC=PORTC); + HAVE_PORTD(shadowPORTD=PORTD); + HAVE_PORTE(shadowPORTE=PORTE); + HAVE_PORTF(shadowPORTF=PORTF); APPLY_BY_MODE(TRACK_MODE_MAIN,setSignal(on)); HAVE_PORTA(PORTA=shadowPORTA); HAVE_PORTB(PORTB=shadowPORTB); HAVE_PORTC(PORTC=shadowPORTC); + HAVE_PORTD(PORTD=shadowPORTD); + HAVE_PORTE(PORTE=shadowPORTE); + HAVE_PORTF(PORTF=shadowPORTF); } void TrackManager::setCutout( bool on) { @@ -172,10 +178,16 @@ void TrackManager::setPROGSignal( bool on) { HAVE_PORTA(shadowPORTA=PORTA); HAVE_PORTB(shadowPORTB=PORTB); HAVE_PORTC(shadowPORTC=PORTC); + HAVE_PORTD(shadowPORTD=PORTD); + HAVE_PORTE(shadowPORTE=PORTE); + HAVE_PORTF(shadowPORTF=PORTF); APPLY_BY_MODE(TRACK_MODE_PROG,setSignal(on)); HAVE_PORTA(PORTA=shadowPORTA); HAVE_PORTB(PORTB=shadowPORTB); HAVE_PORTC(PORTC=shadowPORTC); + HAVE_PORTD(PORTD=shadowPORTD); + HAVE_PORTE(PORTE=shadowPORTE); + HAVE_PORTF(PORTF=shadowPORTF); } // setDCSignal(), called from normal context diff --git a/version.h b/version.h index b7c157b..95d7461 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,12 @@ #include "StringFormatter.h" -#define VERSION "5.1.9" +#define VERSION "5.1.10" +// 5.1.10 - STM32F4xx DCCEXanalogWrite to handle PWM generation for TrackManager DC/DCX +// - STM32F4xx DCC 58uS timer now using non-PWM output timers where possible +// - ESP32 brakeCanPWM check now detects UNUSED_PIN +// - ARM architecture brakeCanPWM now uses digitalPinHasPWM() +// - STM32F4xx shadowpin extensions to handle pins on ports D, E and F // 5.1.9 - Fixed IO_PCA9555'h to work with PCA9548 mux, tested OK // 5.1.8 - STM32Fxx ADCee extension to support ADCs #2 and #3 // 5.1.7 - Fix turntable broadcasts for non-movement activities and result From 52cfc187543a487cde464a40fd7c7904284dca98 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Thu, 28 Sep 2023 15:02:30 +0100 Subject: [PATCH 141/218] Remove Diags not needed Tidy up Diags and responses - use HASH_KEYWORD in place of 'A' --- DCCEXParser.cpp | 21 ++++++++++++--------- TrackManager.cpp | 7 +++---- 2 files changed, 15 insertions(+), 13 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index c82175b..a561561 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -157,6 +157,7 @@ const int16_t HASH_KEYWORD_VPIN=-415; const int16_t HASH_KEYWORD_A='A'; const int16_t HASH_KEYWORD_C='C'; const int16_t HASH_KEYWORD_G='G'; +const int16_t HASH_KEYWORD_H='H'; const int16_t HASH_KEYWORD_I='I'; const int16_t HASH_KEYWORD_O='O'; const int16_t HASH_KEYWORD_P='P'; @@ -576,7 +577,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) prog=true; } #endif - else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> + //else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> + else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H> byte t = (p[0] - 'A'); //DIAG(F("Processing track - %d "), t); if (TrackManager::isProg(t)) { @@ -591,10 +593,10 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) singletrack=true; if (main) TrackManager::setTrackPower(false, false, POWERMODE::ON, t); if (prog) TrackManager::setTrackPower(true, false, POWERMODE::ON, t); + + StringFormatter::send(stream, F("<1 %c>\n"), t+'A'); //CommandDistributor::broadcastPower(); - //TrackManager::streamTrackState(stream, t); - TrackManager::streamTrackState(NULL,t); - StringFormatter::send(stream, F("Track %d ON\n"), t); + //TrackManager::streamTrackState(NULL,t); return; } @@ -635,7 +637,8 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) prog=true; } #endif - else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> + //else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> + else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H> byte t = (p[0] - 'A'); //DIAG(F("Processing track - %d "), t); if (TrackManager::isProg(t)) { @@ -652,11 +655,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) if (main) TrackManager::setTrackPower(false, false, POWERMODE::OFF, t); if (prog) { TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off - TrackManager::setTrackPower(true, false, POWERMODE::OFF, t); - CommandDistributor::broadcastPower(); + TrackManager::setTrackPower(true, false, POWERMODE::OFF, t); } - StringFormatter::send(stream, F("Track %d OFF\n"), t); - TrackManager::streamTrackState(NULL, t); + StringFormatter::send(stream, F("<0 %c>\n"), t+'A'); + //CommandDistributor::broadcastPower(); + //TrackManager::streamTrackState(NULL, t); return; } diff --git a/TrackManager.cpp b/TrackManager.cpp index dd27d85..bab202d 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -358,7 +358,7 @@ void TrackManager::streamTrackState(Print* stream, byte t) { bool pstate = TrackManager::isPowerOn(t); switch(track[t]->getMode()) { case TRACK_MODE_MAIN: - if (pstate) {format=F("<= %c MAIN ON \n");} else {format = F("<= %c MAIN OFF \n");} + if (pstate) {format=F("<= %c MAIN ON>\n");} else {format = F("<= %c MAIN OFF>\n");} break; #ifndef DISABLE_PROG case TRACK_MODE_PROG: @@ -445,12 +445,11 @@ void TrackManager::setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byt break; case TRACK_MODE_DC: case TRACK_MODE_DCX: - DIAG(F("Processing track - %d setProg %d"), thistrack, setProg); - if (setProg || setJoin) {DIAG(F("Nowt to do")); break;} + //DIAG(F("Processing track - %d setProg %d"), thistrack, setProg); + if (setProg || setJoin) break; driver->setBrake(true); // DC starts with brake on applyDCSpeed(thistrack); // speed match DCC throttles driver->setPower(mode); - DIAG(F("Doing it anyway")); break; case TRACK_MODE_PROG: if (!setProg && !setJoin) break; From 7afd4443d616d95526cef697e7bc7c68fd728b61 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Mon, 2 Oct 2023 12:04:47 +0800 Subject: [PATCH 142/218] STM32 revised I2C clock setup --- I2CManager_STM32.h | 57 ++++++++++++++++++++++++++++++---------------- version.h | 3 ++- 2 files changed, 39 insertions(+), 21 deletions(-) diff --git a/I2CManager_STM32.h b/I2CManager_STM32.h index 86b9b74..7e0f547 100644 --- a/I2CManager_STM32.h +++ b/I2CManager_STM32.h @@ -117,35 +117,46 @@ void I2CManagerClass::I2C_setClock(uint32_t i2cClockSpeed) { // Disable the I2C device, as TRISE can only be programmed whilst disabled s->CR1 &= ~(I2C_CR1_PE); // Disable I2C + s->CR1 |= I2C_CR1_SWRST; // reset the I2C + asm("nop"); // wait a bit... suggestion from online! + s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation - if (i2cClockSpeed > 100000L) + if (i2cClockSpeed > 100000UL) { - if (i2cClockSpeed > 400000L) - i2cClockSpeed = 400000L; + // if (i2cClockSpeed > 400000L) + // i2cClockSpeed = 400000L; t_rise = 300; // nanoseconds } else { - i2cClockSpeed = 100000L; + // i2cClockSpeed = 100000L; t_rise = 1000; // nanoseconds } - // Configure the rise time register - s->TRISE = (t_rise / (1000 / i2c_MHz)) + 1; + // Configure the rise time register - max allowed tRISE is 1000ns, + // so value = 1000ns * I2C_PERIPH_CLK MHz / 1000 + 1. + s->TRISE = (t_rise * i2c_MHz / 1000) + 1; // Bit 15: I2C Master mode, 0=standard, 1=Fast Mode // Bit 14: Duty, fast mode duty cycle (use 2:1) // Bit 11-0: FREQR - if (i2cClockSpeed > 100000L) { - // In fast mode, I2C period is 3 * CCR * TPCLK1. - //APB1clk1 / 3 / i2cClockSpeed = 38, but that results in 306KHz not 400! - ccr_freq = 30; // So 30 gives 396KHz or so! - s->CCR = (uint16_t)(ccr_freq | 0x8000); // We need Fast Mode set - } else { - // In standard mode, I2C period is 2 * CCR * TPCLK1 - ccr_freq = (APB1clk1 / 2 / i2cClockSpeed); // Should be 225 for 45Mhz APB1 clock - s->CCR |= (uint16_t)ccr_freq; - } + // if (i2cClockSpeed > 400000UL) { + // // In fast mode plus, I2C period is 3 * CCR * TPCLK1. + // // s->CCR &= ~(0x3000); // Clear all bits except 12 and 13 which must remain per reset value + // s->CCR = APB1clk1 / 3 / i2cClockSpeed; // Set I2C clockspeed to start! + // s->CCR |= 0xC000; // We need Fast Mode AND DUTY bits set + // } else { + // In standard and fast mode, I2C period is 2 * CCR * TPCLK1 + s->CCR &= ~(0x3000); // Clear all bits except 12 and 13 which must remain per reset value + s->CCR |= (APB1clk1 / 2 / i2cClockSpeed); // Set I2C clockspeed to start! + // s->CCR |= (i2c_MHz * 500 / (i2cClockSpeed / 1000)); // Set I2C clockspeed to start! + // if (i2cClockSpeed > 100000UL) + // s->CCR |= 0xC000; // We need Fast Mode bits set as well + // } + + // DIAG(F("I2C_init() peripheral clock is now: %d, full reg is %x"), (s->CR2 & 0xFF), s->CR2); + // DIAG(F("I2C_init() peripheral CCR is now: %d"), s->CCR); + // DIAG(F("I2C_init() peripheral TRISE is now: %d"), s->TRISE); // Enable the I2C master mode s->CR1 |= I2C_CR1_PE; // Enable I2C @@ -159,6 +170,7 @@ void I2CManagerClass::I2C_init() // Query the clockspeed from the STM32 HAL layer APB1clk1 = HAL_RCC_GetPCLK1Freq(); i2c_MHz = APB1clk1 / 1000000UL; + // DIAG(F("I2C_init() peripheral clock speed is: %d"), i2c_MHz); // Enable clocks RCC->APB1ENR |= RCC_APB1ENR_I2C1EN;//(1 << 21); // Enable I2C CLOCK // Reset the I2C1 peripheral to initial state @@ -181,6 +193,7 @@ void I2CManagerClass::I2C_init() GPIOB->AFR[1] |= (4<<0) | (4<<4); // PB8 on low nibble, PB9 on next nibble up // Software reset the I2C peripheral + I2C1->CR1 &= ~I2C_CR1_PE; // Disable I2C1 peripheral s->CR1 |= I2C_CR1_SWRST; // reset the I2C asm("nop"); // wait a bit... suggestion from online! s->CR1 &= ~(I2C_CR1_SWRST); // Normal operation @@ -191,6 +204,7 @@ void I2CManagerClass::I2C_init() // Set I2C peripheral clock frequency // s->CR2 |= I2C_PERIPH_CLK; s->CR2 |= i2c_MHz; + // DIAG(F("I2C_init() peripheral clock is now: %d"), s->CR2); // set own address to 00 - not used in master mode I2C1->OAR1 = (1 << 14); // bit 14 should be kept at 1 according to the datasheet @@ -214,6 +228,7 @@ void I2CManagerClass::I2C_init() s->CR2 |= (I2C_CR2_ITBUFEN | I2C_CR2_ITEVTEN | I2C_CR2_ITERREN); // Enable Buffer, Event and Error interrupts #endif + // DIAG(F("I2C_init() setting initial I2C clock to 100KHz")); // Calculate baudrate and set default rate for now // Configure the Clock Control Register for 100KHz SCL frequency // Bit 15: I2C Master mode, 0=standard, 1=Fast Mode @@ -221,12 +236,14 @@ void I2CManagerClass::I2C_init() // Bit 11-0: so CCR divisor would be clk / 2 / 100000 (where clk is in Hz) // s->CCR = I2C_PERIPH_CLK * 5; s->CCR &= ~(0x3000); // Clear all bits except 12 and 13 which must remain per reset value - s->CCR |= (APB1clk1 / 2 / 100000UL); // i2c_MHz * 5; - // s->CCR = i2c_MHz * 5; + s->CCR |= (APB1clk1 / 2 / 100000UL); // Set a default of 100KHz I2C clockspeed to start! // Configure the rise time register - max allowed is 1000ns, so value = 1000ns * I2C_PERIPH_CLK MHz / 1000 + 1. - // s->TRISE = I2C_PERIPH_CLK + 1; // 1000 ns / 50 ns = 20 + 1 = 21 - s->TRISE = i2c_MHz + 1; + s->TRISE = (1000 * i2c_MHz / 1000) + 1; + + // DIAG(F("I2C_init() peripheral clock is now: %d, full reg is %x"), (s->CR2 & 0xFF), s->CR2); + // DIAG(F("I2C_init() peripheral CCR is now: %d"), s->CCR); + // DIAG(F("I2C_init() peripheral TRISE is now: %d"), s->TRISE); // Enable the I2C master mode s->CR1 |= I2C_CR1_PE; // Enable I2C diff --git a/version.h b/version.h index 95d7461..21dde65 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.10" +#define VERSION "5.1.11" +// 5.1.11 - STM32F4xx revised I2C clock setup, no correctly sets clock and has fully variable frequency selection // 5.1.10 - STM32F4xx DCCEXanalogWrite to handle PWM generation for TrackManager DC/DCX // - STM32F4xx DCC 58uS timer now using non-PWM output timers where possible // - ESP32 brakeCanPWM check now detects UNUSED_PIN From 2ff1619ad15ed43bb87eaac23b3c402d5df10923 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Wed, 4 Oct 2023 14:54:06 +0800 Subject: [PATCH 143/218] STM32 reinstate 100% duty cycle PWM --- DCCTimerSTM32.cpp | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index 0f9c432..f2d51ff 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -306,18 +306,18 @@ void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) { // Calculate percentage duty cycle from value given uint32_t duty_cycle = (value * 100 / 256) + 1; if (pin_timer[pin] != NULL) { - if (duty_cycle == 100) - { - pin_timer[pin]->pauseChannel(pin_channel[pin]); - DIAG(F("DCCEXanalogWrite::Pausing timer channel on pin %d"), pin); - } - else - { + // if (duty_cycle == 100) + // { + // pin_timer[pin]->pauseChannel(pin_channel[pin]); + // DIAG(F("DCCEXanalogWrite::Pausing timer channel on pin %d"), pin); + // } + // else + // { pinmap_pinout(digitalPinToPinName(pin), PinMap_TIM); // ensure the pin has been configured! - pin_timer[pin]->resumeChannel(pin_channel[pin]); + // pin_timer[pin]->resumeChannel(pin_channel[pin]); pin_timer[pin]->setCaptureCompare(pin_channel[pin], duty_cycle, PERCENT_COMPARE_FORMAT); // DCC_EX_PWM_FREQ Hertz, duty_cycle% dutycycle DIAG(F("DCCEXanalogWrite::Pin %d, value %d, duty cycle %d"), pin, value, duty_cycle); - } + // } } else DIAG(F("DCCEXanalogWrite::Pin %d is not configured for PWM!"), pin); From fe618d0b854a200dec5ce670e24dc92587735464 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Fri, 6 Oct 2023 19:11:11 +0100 Subject: [PATCH 144/218] Add getModeName() Add facility to get the name of the track mode --- .gitignore | 1 + TrackManager.cpp | 22 +++++++++++++++++++++- TrackManager.h | 2 ++ 3 files changed, 24 insertions(+), 1 deletion(-) diff --git a/.gitignore b/.gitignore index 6237359..482da70 100644 --- a/.gitignore +++ b/.gitignore @@ -13,3 +13,4 @@ myFilter.cpp my*.h !my*.example.h compile_commands.json +newcode.txt.old diff --git a/TrackManager.cpp b/TrackManager.cpp index bab202d..3ea9877 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -556,4 +556,24 @@ bool TrackManager::isProg(byte t) { if (track[t]->getMode()==TRACK_MODE_PROG) return true; return false; -} \ No newline at end of file +} + +byte TrackManager::returnMode(byte t) { + return (track[t]->getMode()); +} + +static const char* TrackManager::getModeName(byte Mode) { + + //DIAG(F("PowerMode %d"), Mode); + +switch (Mode) + { + case 1: return "NONE"; + case 2: return "MAIN"; + case 4: return "PROG"; + case 8: return "DC"; + case 16: return "DCX"; + case 32: return "EXT"; + default: return "----"; + } +} diff --git a/TrackManager.h b/TrackManager.h index 21b8d59..486057a 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -90,6 +90,8 @@ class TrackManager { static void streamTrackState(Print* stream, byte t); static bool isPowerOn(byte t); static bool isProg(byte t); + static byte returnMode(byte t); + static const char* getModeName(byte Mode); static int16_t joinRelay; static bool progTrackSyncMain; // true when prog track is a siding switched to main From bef4b2ec35063a85bc9603d3952e67e6eba1577c Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 9 Oct 2023 18:09:48 +0100 Subject: [PATCH 145/218] fix default roster --- DCCEXParser.cpp | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index fe21549..7b4836e 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -121,7 +121,7 @@ Once a new OPCODE is decided upon, update this list. for (int16_t i=0;;i+=sizeof(flashList[0])) { \ int16_t value=GETHIGHFLASHW(flashList,i); \ if (value==INT16_MAX) break; \ - if (value != 0) StringFormatter::send(stream,F(" %d"),value); \ + StringFormatter::send(stream,F(" %d"),value); \ } diff --git a/version.h b/version.h index a76aef2..1610054 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.0.3" +#define VERSION "5.0.4" +// 5.0.4 - Bugfix: misses default roster. // 5.0.3 - Check bad AT firmware version // 5.0.2 - Bugfix: ESP32 30ms off time // 5.0.1 - Bugfix: execute 30ms off time before rejoin From 68fd56e7fccc74be4c06a11eae04c8bec846538e Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Tue, 10 Oct 2023 11:52:46 +0100 Subject: [PATCH 146/218] Added returnDCAddr Added function to return DC address --- TrackManager.cpp | 7 ++++++- TrackManager.h | 1 + 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index 3ea9877..7d86de0 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -356,6 +356,7 @@ void TrackManager::streamTrackState(Print* stream, byte t) { if (track[t]==NULL) return; auto format=F(""); bool pstate = TrackManager::isPowerOn(t); + switch(track[t]->getMode()) { case TRACK_MODE_MAIN: if (pstate) {format=F("<= %c MAIN ON>\n");} else {format = F("<= %c MAIN OFF>\n");} @@ -562,7 +563,11 @@ byte TrackManager::returnMode(byte t) { return (track[t]->getMode()); } -static const char* TrackManager::getModeName(byte Mode) { +int16_t TrackManager::returnDCAddr(byte t) { + return (trackDCAddr[t]); +} + +const char* TrackManager::getModeName(byte Mode) { //DIAG(F("PowerMode %d"), Mode); diff --git a/TrackManager.h b/TrackManager.h index 486057a..d197751 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -91,6 +91,7 @@ class TrackManager { static bool isPowerOn(byte t); static bool isProg(byte t); static byte returnMode(byte t); + static int16_t returnDCAddr(byte t); static const char* getModeName(byte Mode); static int16_t joinRelay; From a092e06a6fec69159de22e14127524d53e65a20c Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Tue, 10 Oct 2023 12:11:49 +0100 Subject: [PATCH 147/218] Update .gitignore added UserAddin.txt to gitignore --- .gitignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.gitignore b/.gitignore index 482da70..759b739 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ my*.h !my*.example.h compile_commands.json newcode.txt.old +UserAddin.txt From ea4f90d5fcd76b59f2a3a4fb0daa5a6f7ceeb815 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Wed, 11 Oct 2023 17:06:56 +0100 Subject: [PATCH 148/218] Merged in Power changes Merge in power changes and EXRAIL command & update to version.h --- version.h | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 21dde65..e9e1e79 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,12 @@ #include "StringFormatter.h" -#define VERSION "5.1.11" +#define VERSION "5.1.12" +// 5.1.12 - Added Power commands <0 A> & <1 A> etc. and update to <=> +// Added EXRAIL SET_POWER(track, ON/OFF) +// Fixed a problem whereby <1 MAIN> also powered on PROG track +// Added functions to TrackManager.cpp to allow UserAddin code for power display on OLED/LCD +// Added - returnMode(byte t), returnDCAddr(byte t) & getModeName(byte Mode) // 5.1.11 - STM32F4xx revised I2C clock setup, no correctly sets clock and has fully variable frequency selection // 5.1.10 - STM32F4xx DCCEXanalogWrite to handle PWM generation for TrackManager DC/DCX // - STM32F4xx DCC 58uS timer now using non-PWM output timers where possible From d5978b1578ef461fdd23317ebc4f92ca47f8b00d Mon Sep 17 00:00:00 2001 From: peteGSX Date: Thu, 12 Oct 2023 13:28:39 +1000 Subject: [PATCH 149/218] Change broadcast --- CommandDistributor.cpp | 2 +- DCCEXParser.cpp | 12 ++++++------ version.h | 3 ++- 3 files changed, 9 insertions(+), 8 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index a521b52..1651771 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -162,7 +162,7 @@ void CommandDistributor::broadcastTurnout(int16_t id, bool isClosed ) { } void CommandDistributor::broadcastTurntable(int16_t id, uint8_t position, bool moving) { - broadcastReply(COMMAND_TYPE, F("\n"), id, position, moving); + broadcastReply(COMMAND_TYPE, F("\n"), id, position, moving); } void CommandDistributor::broadcastClockTime(int16_t time, int8_t rate) { diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 9cff75a..e53b882 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -60,8 +60,8 @@ Once a new OPCODE is decided upon, update this list. G, h, H, Turnout state broadcast - i, Reserved for future use - Turntable object broadcast - I, Reserved for future use - Turntable object command and control + i, Server details string + I, Turntable object command, control, and broadcast j, Throttle responses J, Throttle queries k, Reserved for future use - Potentially Railcom @@ -1244,7 +1244,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) if (tto) { bool type = tto->isEXTT(); uint8_t position = tto->getPosition(); - StringFormatter::send(stream, F("\n"), type, position); + StringFormatter::send(stream, F("\n"), type, position); } else { return false; } @@ -1270,7 +1270,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) if (!DCCTurntable::create(p[0])) return false; Turntable *tto = Turntable::get(p[0]); tto->addPosition(0, 0, p[2]); - StringFormatter::send(stream, F("\n")); + StringFormatter::send(stream, F("\n")); } else { if (!tto) return false; if (!tto->isEXTT()) return false; @@ -1287,7 +1287,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) if (!EXTTTurntable::create(p[0], (VPIN)p[2])) return false; Turntable *tto = Turntable::get(p[0]); tto->addPosition(0, 0, p[3]); - StringFormatter::send(stream, F("\n")); + StringFormatter::send(stream, F("\n")); } else { return false; } @@ -1301,7 +1301,7 @@ bool DCCEXParser::parseI(Print *stream, int16_t params, int16_t p[]) // tto must exist, no more than 48 positions, angle 0 - 3600 if (!tto || p[2] > 48 || p[4] < 0 || p[4] > 3600) return false; tto->addPosition(p[2], p[3], p[4]); - StringFormatter::send(stream, F("\n")); + StringFormatter::send(stream, F("\n")); } else { return false; } diff --git a/version.h b/version.h index e9e1e79..ee65765 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.12" +#define VERSION "5.1.13" +// 5.1.13 - Changed turntable broadcast from i to I due to server string conflict // 5.1.12 - Added Power commands <0 A> & <1 A> etc. and update to <=> // Added EXRAIL SET_POWER(track, ON/OFF) // Fixed a problem whereby <1 MAIN> also powered on PROG track From ce84974967cd9771479116a7cde2dfd2d1b627e9 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Thu, 12 Oct 2023 13:42:14 +1000 Subject: [PATCH 150/218] Missed one i --- Turntables.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Turntables.h b/Turntables.h index 6c15bab..aa089ef 100644 --- a/Turntables.h +++ b/Turntables.h @@ -185,7 +185,7 @@ public: for (Turntable *tto = _firstTurntable; tto != 0; tto = tto->_nextTurntable) if (!tto->isHidden()) { gotOne = true; - StringFormatter::send(stream, F("\n"), tto->getId(), tto->getPosition()); + StringFormatter::send(stream, F("\n"), tto->getId(), tto->getPosition()); } return gotOne; } From 8b8e9e491963bcfdd963942e0ae0abfdf1eaf918 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 12 Oct 2023 11:07:05 +0100 Subject: [PATCH 151/218] clean result from invalid --- DCCEXParser.cpp | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 7b4836e..04c0b6d 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -729,11 +729,15 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) SENDFLASHLIST(stream,RMFT2::rosterIdList) } else { - const FSH * functionNames= RMFT2::getRosterFunctions(id); - StringFormatter::send(stream,F(" %d \"%S\" \"%S\""), - id, RMFT2::getRosterName(id), - functionNames == NULL ? RMFT2::getRosterFunctions(0) : functionNames); - } + auto rosterName= RMFT2::getRosterName(id); + if (!rosterName) rosterName=F(""); + + auto functionNames= RMFT2::getRosterFunctions(id); + if (!functionNames) functionNames=RMFT2::getRosterFunctions(0); + if (!functionNames) functionNames=F(""); + StringFormatter::send(stream,F(" %d \"%S\" \"%S\""), + id, rosterName, functionNames); + } #endif StringFormatter::send(stream, F(">\n")); return; From 0214a55b23599cddc66b4fd24397fddfdcb60527 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Fri, 13 Oct 2023 04:37:38 +1000 Subject: [PATCH 152/218] Fixed --- EXRAIL2.h | 7 +++---- version.h | 3 ++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/EXRAIL2.h b/EXRAIL2.h index ad399ec..7b0a9af 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -64,10 +64,8 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONCHANGE, OPCODE_ONCLOCKTIME, OPCODE_ONTIME, -#ifndef IO_NO_HAL OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE, - OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_IFTTPOSITION,OPCODE_WAITFORTT, -#endif + OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_WAITFORTT, OPCODE_ONOVERLOAD, // OPcodes below this point are skip-nesting IF operations @@ -81,7 +79,8 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_IFRANDOM,OPCODE_IFRESERVE, OPCODE_IFCLOSED,OPCODE_IFTHROWN, OPCODE_IFRE, - OPCODE_IFLOCO + OPCODE_IFLOCO, + OPCODE_IFTTPOSITION }; // Ensure thrunge_lcd is put last as there may be more than one display, diff --git a/version.h b/version.h index ee65765..468971e 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.13" +#define VERSION "5.1.14" +// 5.1.14 - Fixed IFTTPOSITION // 5.1.13 - Changed turntable broadcast from i to I due to server string conflict // 5.1.12 - Added Power commands <0 A> & <1 A> etc. and update to <=> // Added EXRAIL SET_POWER(track, ON/OFF) From 6eb7051fd63f3abe1042507070842f98bb65fde1 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 13 Oct 2023 13:59:06 +0100 Subject: [PATCH 153/218] LCC and signal compile-out LCC commands in EXRAIL for OpenMRN Adapter FIrst use of compile-out of unused features. --- DCCEXParser.cpp | 5 +- EXRAIL2.cpp | 116 ++++++++++++++++++++++++++++++++++++-------- EXRAIL2.h | 13 ++++- EXRAIL2MacroReset.h | 8 ++- EXRAILMacros.h | 43 ++++++++++++++++ StringFormatter.cpp | 14 +++++- StringFormatter.h | 1 + version.h | 3 +- 8 files changed, 178 insertions(+), 25 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 2bd79e9..dab213b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -67,7 +67,7 @@ Once a new OPCODE is decided upon, update this list. k, Reserved for future use - Potentially Railcom K, Reserved for future use - Potentially Railcom l, Loco speedbyte/function map broadcast - L, + L, Reserved for LCC interface (implemented in EXRAIL) m, M, Write DCC packet n, @@ -906,6 +906,9 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) break; #endif + case 'L': // LCC interface implemented in EXRAIL parser + break; // Will if not intercepted by EXRAIL + default: //anything else will diagnose and drop out to DIAG(F("Opcode=%c params=%d"), opcode, params); for (int i = 0; i < params; i++) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 321872d..c902708 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -85,7 +85,7 @@ RMFT2 * RMFT2::pausingTask=NULL; // Task causing a PAUSE. // when pausingTask is set, that is the ONLY task that gets any service, // and all others will have their locos stopped, then resumed after the pausing task resumes. byte RMFT2::flags[MAX_FLAGS]; - +Print * RMFT2::LCCSerial=0; LookList * RMFT2::sequenceLookup=NULL; LookList * RMFT2::onThrowLookup=NULL; LookList * RMFT2::onCloseLookup=NULL; @@ -176,23 +176,26 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) { onCloseLookup=LookListLoader(OPCODE_ONCLOSE); onActivateLookup=LookListLoader(OPCODE_ONACTIVATE); onDeactivateLookup=LookListLoader(OPCODE_ONDEACTIVATE); - onRedLookup=LookListLoader(OPCODE_ONRED); - onAmberLookup=LookListLoader(OPCODE_ONAMBER); - onGreenLookup=LookListLoader(OPCODE_ONGREEN); onChangeLookup=LookListLoader(OPCODE_ONCHANGE); onClockLookup=LookListLoader(OPCODE_ONTIME); #ifndef IO_NO_HAL onRotateLookup=LookListLoader(OPCODE_ONROTATE); #endif onOverloadLookup=LookListLoader(OPCODE_ONOVERLOAD); + // onLCCLookup is not the same so not loaded here. // Second pass startup, define any turnouts or servos, set signals red // add sequences onRoutines to the lookups +if (compileFeatures & FEATURE_SIGNAL) { + onRedLookup=LookListLoader(OPCODE_ONRED); + onAmberLookup=LookListLoader(OPCODE_ONAMBER); + onGreenLookup=LookListLoader(OPCODE_ONGREEN); for (int sigslot=0;;sigslot++) { VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); if (sigid==0) break; // end of signal list doSignal(sigid & SIGNAL_ID_MASK, SIGNAL_RED); } + } int progCounter; for (progCounter=0;; SKIPOP){ @@ -343,13 +346,65 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16 reject=!parseSlash(stream,paramCount,p); opcode=0; break; - + case 'L': + if (compileFeatures & FEATURE_LCC) { + // This entire code block is compiled out if LLC macros not used + if (paramCount==0) { // LCC adapter introducing self + LCCSerial=stream; // now we know where to send events we raise + + // loop through all possible sent events + for (int progCounter=0;; SKIPOP) { + byte opcode=GET_OPCODE; + if (opcode==OPCODE_ENDEXRAIL) break; + if (opcode==OPCODE_LCC) StringFormatter::send(stream,F("\n"),getOperand(progCounter,0)); + if (opcode==OPCODE_LCCX) { // long form LCC + StringFormatter::send(stream,F("\n"), + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + }} + + // we stream the hex events we wish to listen to + // and at the same time build the event index looku. + + + int eventIndex=0; + for (int progCounter=0;; SKIPOP) { + byte opcode=GET_OPCODE; + if (opcode==OPCODE_ENDEXRAIL) break; + if (opcode==OPCODE_ONLCC) { + onLCCLookup[eventIndex]=progCounter; // TODO skip... + StringFormatter::send(stream,F("\n"), + eventIndex, + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + eventIndex++; + } + } + StringFormatter::send(stream,F("\n")); // Ready to rumble + opcode=0; + break; + } + if (paramCount==1) { // LCC event arrived from adapter + int16_t eventid=p[0]; + reject=eventid<0 || eventid>=countLCCLookup; + if (!reject) startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]); + opcode=0; + } + } + break; + default: // other commands pass through break; } if (reject) { opcode=0; - StringFormatter::send(stream,F("")); + StringFormatter::send(stream,F("\n")); } } @@ -377,17 +432,19 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { if (flag & LATCH_FLAG) StringFormatter::send(stream,F(" LATCHED")); } } - // do the signals - // flags[n] represents the state of the nth signal in the table - for (int sigslot=0;;sigslot++) { - VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); - if (sigid==0) break; // end of signal list - byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id - StringFormatter::send(stream,F("\n%S[%d]"), - (flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"), - sigid & SIGNAL_ID_MASK); - } - + + if (compileFeatures & FEATURE_SIGNAL) { + // do the signals + // flags[n] represents the state of the nth signal in the table + for (int sigslot=0;;sigslot++) { + VPIN sigid=GETHIGHFLASHW(RMFT2::SignalDefinitions,sigslot*8); + if (sigid==0) break; // end of signal list + byte flag=flags[sigslot] & SIGNAL_MASK; // obtain signal flags for this id + StringFormatter::send(stream,F("\n%S[%d]"), + (flag == SIGNAL_RED)? F("RED") : (flag==SIGNAL_GREEN) ? F("GREEN") : F("AMBER"), + sigid & SIGNAL_ID_MASK); + } + } StringFormatter::send(stream,F(" *>\n")); return true; } @@ -1034,7 +1091,21 @@ void RMFT2::loop2() { invert=false; } break; - + + case OPCODE_LCC: // short form LCC + if ((compileFeatures & FEATURE_LCC) && LCCSerial) + StringFormatter::send(LCCSerial,F(""),(uint16_t)operand); + break; + + case OPCODE_LCCX: // long form LCC + if ((compileFeatures & FEATURE_LCC) && LCCSerial) + StringFormatter::send(LCCSerial,F("\n"), + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + break; case OPCODE_SERVO: // OPCODE_SERVO,V(vpin),OPCODE_PAD,V(position),OPCODE_PAD,V(profile),OPCODE_PAD,V(duration) IODevice::writeAnalogue(operand,getOperand(1),getOperand(2),getOperand(3)); @@ -1072,6 +1143,7 @@ void RMFT2::loop2() { case OPCODE_SERVOTURNOUT: // Turnout definition ignored at runtime case OPCODE_PINTURNOUT: // Turnout definition ignored at runtime case OPCODE_ONCLOSE: // Turnout event catchers ignored here + case OPCODE_ONLCC: // LCC event catchers ignored here case OPCODE_ONTHROW: case OPCODE_ONACTIVATE: // Activate event catchers ignored here case OPCODE_ONDEACTIVATE: @@ -1141,6 +1213,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { } /* static */ void RMFT2::doSignal(int16_t id,char rag) { + if (!(compileFeatures & FEATURE_SIGNAL)) return; // dont compile code below if (diag) DIAG(F(" doSignal %d %x"),id,rag); // Schedule any event handler for this signal change. @@ -1208,6 +1281,7 @@ int16_t RMFT2::getSignalSlot(int16_t id) { } /* static */ bool RMFT2::isSignal(int16_t id,char rag) { + if (!(compileFeatures & FEATURE_SIGNAL)) return false; int16_t sigslot=getSignalSlot(id); if (sigslot<0) return false; return (flags[sigslot] & SIGNAL_MASK) == rag; @@ -1260,8 +1334,10 @@ void RMFT2::powerEvent(int16_t track, bool overload) { void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) { int pc= handlers->find(id); - if (pc<0) return; - + if (pc>=0) startNonRecursiveTask(reason,id,pc); +} + +void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) { // Check we dont already have a task running this handler RMFT2 * task=loopTask; while(task) { diff --git a/EXRAIL2.h b/EXRAIL2.h index 7b0a9af..de14f11 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -66,6 +66,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONTIME, OPCODE_TTADDPOSITION,OPCODE_DCCTURNTABLE,OPCODE_EXTTTURNTABLE, OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_WAITFORTT, + OPCODE_LCC,OPCODE_LCCX,OPCODE_ONLCC, OPCODE_ONOVERLOAD, // OPcodes below this point are skip-nesting IF operations @@ -94,7 +95,11 @@ enum thrunger: byte { thrunge_lcd, // Must be last!! }; - + // Flag bits for compile time features. + static const byte FEATURE_SIGNAL= 0x80; + static const byte FEATURE_LCC = 0x40; + static const byte FEATURE_ROSTER= 0x20; + // Flag bits for status of hardware and TPL static const byte SECTION_FLAG = 0x80; @@ -173,6 +178,7 @@ private: OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); static void handleEvent(const FSH* reason,LookList* handlers, int16_t id); static uint16_t getOperand(int progCounter,byte n); + static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc); static RMFT2 * loopTask; static RMFT2 * pausingTask; void delayMe(long millisecs); @@ -191,6 +197,7 @@ private: static const HIGHFLASH byte RouteCode[]; static const HIGHFLASH int16_t SignalDefinitions[]; static byte flags[MAX_FLAGS]; + static Print * LCCSerial; static LookList * sequenceLookup; static LookList * onThrowLookup; static LookList * onCloseLookup; @@ -205,6 +212,10 @@ private: static LookList * onRotateLookup; #endif static LookList * onOverloadLookup; + + static const int countLCCLookup; + static int onLCCLookup[]; + static const byte compileFeatures; // Local variables - exist for each instance/task RMFT2 *next; // loop chain diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 03e310e..bdb6420 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -86,6 +86,8 @@ #undef LATCH #undef LCD #undef SCREEN +#undef LCC +#undef LCCX #undef LCN #undef MOVETT #undef ONACTIVATE @@ -94,6 +96,7 @@ #undef ONDEACTIVATE #undef ONDEACTIVATEL #undef ONCLOSE +#undef ONLCC #undef ONTIME #undef ONCLOCKTIME #undef ONCLOCKMINS @@ -221,7 +224,9 @@ #define INVERT_DIRECTION #define JOIN #define KILLALL -#define LATCH(sensor_id) +#define LATCH(sensor_id) +#define LCC(eventid) +#define LCCX(senderid,eventid) #define LCD(row,msg) #define SCREEN(display,row,msg) #define LCN(msg) @@ -236,6 +241,7 @@ #define ONDEACTIVATE(addr,subaddr) #define ONDEACTIVATEL(linear) #define ONCLOSE(turnout_id) +#define ONLCC(sender,event) #define ONGREEN(signal_id) #define ONRED(signal_id) #define ONROTATE(turntable_id) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index b85b68a..3322180 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -85,6 +85,30 @@ void exrailHalSetup() { #include "myAutomation.h" } +// Pass 1c detect compile time featurtes +#include "EXRAIL2MacroReset.h" +#undef SIGNAL +#define SIGNAL(redpin,amberpin,greenpin) | FEATURE_SIGNAL +#undef SIGNALH +#define SIGNALH(redpin,amberpin,greenpin) | FEATURE_SIGNAL +#undef SERVO_SIGNAL +#define SERVO_SIGNAL(vpin,redval,amberval,greenval) | FEATURE_SIGNAL +#undef DCC_SIGNAL +#define DCC_SIGNAL(id,addr,subaddr) | FEATURE_SIGNAL +#undef VIRTUAL_SIGNAL +#define VIRTUAL_SIGNAL(id) | FEATURE_SIGNAL + +#undef LCC +#define LCC(eventid) | FEATURE_LCC +#undef LCCX +#define LCCX(senderid,eventid) | FEATURE_LCC +#undef ONLCC +#define ONLCC(senderid,eventid) | FEATURE_LCC + +const byte RMFT2::compileFeatures = 0 + #include "myAutomation.h" +; + // Pass 2 create throttle route list #include "EXRAIL2MacroReset.h" #undef ROUTE @@ -278,6 +302,16 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #include "myAutomation.h" 0,0,0,0 }; +// Pass 9 ONLCC counter and lookup array +#include "EXRAIL2MacroReset.h" +#undef ONLCC +#define ONLCC(sender,event) +1 + +const int RMFT2::countLCCLookup=0 +#include "myAutomation.h" +; +int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; + // Last Pass : create main routes table // Only undef the macros, not dummy them. #define RMFT2_UNDEF_ONLY @@ -354,6 +388,11 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define JOIN OPCODE_JOIN,0,0, #define KILLALL OPCODE_KILLALL,0,0, #define LATCH(sensor_id) OPCODE_LATCH,V(sensor_id), +#define LCC(eventid) OPCODE_LCC,V(eventid), +#define LCCX(sender,event) OPCODE_LCCX,V(event),\ + OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\ + OPCODE_PAD,V((((uint64_t)sender)>>16)&0xFFFF),\ + OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF), #define LCD(id,msg) PRINT(msg) #define SCREEN(display,id,msg) PRINT(msg) #define LCN(msg) PRINT(msg) @@ -362,6 +401,10 @@ const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #define ONACTIVATEL(linear) OPCODE_ONACTIVATE,V(linear+3), #define ONAMBER(signal_id) OPCODE_ONAMBER,V(signal_id), #define ONCLOSE(turnout_id) OPCODE_ONCLOSE,V(turnout_id), +#define ONLCC(sender,event) OPCODE_ONLCC,V(event),\ + OPCODE_PAD,V((((uint64_t)sender)>>32)&0xFFFF),\ + OPCODE_PAD,V((((uint64_t)sender)>>16)&0xFFFF),\ + OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF), #define ONTIME(value) OPCODE_ONTIME,V(value), #define ONCLOCKTIME(hours,mins) OPCODE_ONTIME,V((STRIP_ZERO(hours)*60)+STRIP_ZERO(mins)), #define ONCLOCKMINS(mins) ONCLOCKTIME(25,mins) diff --git a/StringFormatter.cpp b/StringFormatter.cpp index c475ef0..b40de1c 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -117,6 +117,7 @@ void StringFormatter::send2(Print * stream,const FSH* format, va_list args) { case 'o': stream->print(va_arg(args, int), OCT); break; case 'x': stream->print((unsigned int)va_arg(args, unsigned int), HEX); break; case 'X': stream->print((unsigned long)va_arg(args, unsigned long), HEX); break; + case 'h': printHex(stream,(unsigned int)va_arg(args, unsigned int)); break; case 'M': { // this prints a unsigned long microseconds time in readable format unsigned long time = va_arg(args, long); @@ -218,4 +219,15 @@ void StringFormatter::printPadded(Print* stream, long value, byte width, bool fo if (!formatLeft) stream->print(value, DEC); } - +// printHex prints the full 2 byte hex with leading zeros, unlike print(value,HEX) +const char FLASH hexchars[]="0123456789ABCDEF"; +void StringFormatter::printHex(Print * stream,uint16_t value) { + char result[5]; + for (int i=3;i>=0;i--) { + result[i]=GETFLASH(hexchars+(value & 0x0F)); + value>>=4; + } + result[4]='\0'; + stream->print(result); +} + \ No newline at end of file diff --git a/StringFormatter.h b/StringFormatter.h index 6923c10..1231d54 100644 --- a/StringFormatter.h +++ b/StringFormatter.h @@ -49,6 +49,7 @@ class StringFormatter static void lcd2(uint8_t display, byte row, const FSH* input...); static void printEscapes(char * input); static void printEscape( char c); + static void printHex(Print * stream,uint16_t value); private: static void send2(Print * serial, const FSH* input,va_list args); diff --git a/version.h b/version.h index 468971e..c2fd520 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.14" +#define VERSION "5.1.15" +// 5.1.15 - LCC/Adapter support and Exrail feature-compile-out. // 5.1.14 - Fixed IFTTPOSITION // 5.1.13 - Changed turntable broadcast from i to I due to server string conflict // 5.1.12 - Added Power commands <0 A> & <1 A> etc. and update to <=> From 0978bb0c11a387174b10ef0d72842816e22d1c36 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Mon, 16 Oct 2023 08:12:11 +1000 Subject: [PATCH 154/218] Changes made, but non-functional --- EXRAIL2MacroReset.h | 2 +- EXRAILMacros.h | 6 ++---- Turntables.cpp | 2 +- version.h | 3 ++- 4 files changed, 6 insertions(+), 7 deletions(-) diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index bdb6420..5ca2e91 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -196,7 +196,7 @@ #define ENDTASK #define ESTOP #define EXRAIL -#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description) +#define EXTT_TURNTABLE(id,home,description) #define FADE(pin,value,ms) #define FOFF(func) #define FOLLOW(route) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 3322180..31bf0db 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -79,8 +79,6 @@ #include "EXRAIL2MacroReset.h" #undef HAL #define HAL(haltype,params...) haltype::create(params); -#undef EXTT_TURNTABLE -#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) EXTurntable::create(vpin,1,i2c_address); void exrailHalSetup() { #include "myAutomation.h" } @@ -226,7 +224,7 @@ const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) { #undef DCC_TURNTABLE #define DCC_TURNTABLE(id,home,description...) O_DESC(id,description) #undef EXTT_TURNTABLE -#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) O_DESC(id,description) +#define EXTT_TURNTABLE(id,home,description...) O_DESC(id,description) const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { switch (turntableId) { @@ -356,7 +354,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define ESTOP OPCODE_SPEED,V(1), #define EXRAIL #ifndef IO_NO_HAL -#define EXTT_TURNTABLE(id,vpin,i2c_address,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(i2c_address),OPCODE_PAD,V(home), +#define EXTT_TURNTABLE(id,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(home), #endif #define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L), #define FOFF(func) OPCODE_FOFF,V(func), diff --git a/Turntables.cpp b/Turntables.cpp index ba143cb..daa71fa 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -183,7 +183,7 @@ using DevState = IODevice::DeviceStateEnum; return tto; } } - if (!IODevice::exists(vpin)) return nullptr; + // if (!IODevice::exists(vpin)) return nullptr; if (IODevice::getStatus(vpin) == DevState::DEVSTATE_FAILED) return nullptr; if (Turntable::getByVpin(vpin)) return nullptr; tto = (Turntable *)new EXTTTurntable(id, vpin); diff --git a/version.h b/version.h index c2fd520..ec00f0c 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.15" +#define VERSION "5.1.16" +// 5.1.16 - Change EXTT_TURNTABLE to use HAL and remove vpin/i2c_address // 5.1.15 - LCC/Adapter support and Exrail feature-compile-out. // 5.1.14 - Fixed IFTTPOSITION // 5.1.13 - Changed turntable broadcast from i to I due to server string conflict From 650e411a4f70c779cbbcaf463a5a9447a8ea00df Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 17 Oct 2023 05:06:35 +1000 Subject: [PATCH 155/218] Add vpin parameter --- EXRAIL2MacroReset.h | 2 +- EXRAILMacros.h | 4 ++-- Turntables.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 5ca2e91..d44b244 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -196,7 +196,7 @@ #define ENDTASK #define ESTOP #define EXRAIL -#define EXTT_TURNTABLE(id,home,description) +#define EXTT_TURNTABLE(id,vpin,home,description) #define FADE(pin,value,ms) #define FOFF(func) #define FOLLOW(route) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 31bf0db..8e7fbb9 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -224,7 +224,7 @@ const FSH * RMFT2::getTurnoutDescription(int16_t turnoutid) { #undef DCC_TURNTABLE #define DCC_TURNTABLE(id,home,description...) O_DESC(id,description) #undef EXTT_TURNTABLE -#define EXTT_TURNTABLE(id,home,description...) O_DESC(id,description) +#define EXTT_TURNTABLE(id,vpin,home,description...) O_DESC(id,description) const FSH * RMFT2::getTurntableDescription(int16_t turntableId) { switch (turntableId) { @@ -354,7 +354,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define ESTOP OPCODE_SPEED,V(1), #define EXRAIL #ifndef IO_NO_HAL -#define EXTT_TURNTABLE(id,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(home), +#define EXTT_TURNTABLE(id,vpin,home,description...) OPCODE_EXTTTURNTABLE,V(id),OPCODE_PAD,V(vpin),OPCODE_PAD,V(home), #endif #define FADE(pin,value,ms) OPCODE_SERVO,V(pin),OPCODE_PAD,V(value),OPCODE_PAD,V(PCA9685::ProfileType::UseDuration|PCA9685::NoPowerOff),OPCODE_PAD,V(ms/100L), #define FOFF(func) OPCODE_FOFF,V(func), diff --git a/Turntables.cpp b/Turntables.cpp index daa71fa..ba143cb 100644 --- a/Turntables.cpp +++ b/Turntables.cpp @@ -183,7 +183,7 @@ using DevState = IODevice::DeviceStateEnum; return tto; } } - // if (!IODevice::exists(vpin)) return nullptr; + if (!IODevice::exists(vpin)) return nullptr; if (IODevice::getStatus(vpin) == DevState::DEVSTATE_FAILED) return nullptr; if (Turntable::getByVpin(vpin)) return nullptr; tto = (Turntable *)new EXTTTurntable(id, vpin); From 754bd993810b75243c72299dcdadcc4abee0d2f7 Mon Sep 17 00:00:00 2001 From: peteGSX Date: Tue, 17 Oct 2023 05:08:04 +1000 Subject: [PATCH 156/218] Update version --- version.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/version.h b/version.h index ec00f0c..00fdc37 100644 --- a/version.h +++ b/version.h @@ -4,7 +4,7 @@ #include "StringFormatter.h" #define VERSION "5.1.16" -// 5.1.16 - Change EXTT_TURNTABLE to use HAL and remove vpin/i2c_address +// 5.1.16 - Remove I2C address from EXTT_TURNTABLE macro to work with MUX, requires separate HAL macro to create // 5.1.15 - LCC/Adapter support and Exrail feature-compile-out. // 5.1.14 - Fixed IFTTPOSITION // 5.1.13 - Changed turntable broadcast from i to I due to server string conflict From 7b3b16b21190c5dfdb9fd7d09dcac09a2ed1aea7 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 23 Oct 2023 11:45:52 +0200 Subject: [PATCH 157/218] Divide out C for config and D for diag commands --- DCCEXParser.cpp | 80 +++++++++++++++++++++++++++++-------------------- DCCEXParser.h | 1 + GITHUB_SHA.h | 2 +- version.h | 3 +- 4 files changed, 51 insertions(+), 35 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index dab213b..d79136f 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -49,7 +49,7 @@ Once a new OPCODE is decided upon, update this list. b, Write CV bit on main B, Write CV bit c, Request current command - C, + C, configure the CS d, D, Diagnostic commands e, Erase EEPROM @@ -693,7 +693,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) Sensor::printAll(stream); return; - case 's': // + case 's': // STATUS StringFormatter::send(stream, F("\n"), F(VERSION), F(ARDUINO_TYPE), DCC::getMotorShieldName(), F(GITHUB_SHA)); CommandDistributor::broadcastPower(); // is the only "get power status" command we have Turnout::printAll(stream); //send all Turnout states @@ -714,13 +714,17 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case ' ': // < > StringFormatter::send(stream, F("\n")); return; + case 'C': // CONFIG + if (parseC(stream, params, p)) + return; + break; #ifndef DISABLE_DIAG - case 'D': // < > + case 'D': // DIAG if (parseD(stream, params, p)) return; break; #endif - case '=': // <= Track manager control > + case '=': // TACK MANAGER CONTROL <= [params]> if (TrackManager::parseJ(stream, params, p)) return; break; @@ -1114,19 +1118,28 @@ bool DCCEXParser::parseS(Print *stream, int16_t params, int16_t p[]) return false; } -bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) -{ +bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) { if (params == 0) return false; bool onOff = (params > 0) && (p[1] == 1 || p[1] == HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off switch (p[0]) { - case HASH_KEYWORD_CABS: // - DCC::displayCabList(stream); +#ifndef DISABLE_PROG + case HASH_KEYWORD_PROGBOOST: + TrackManager::progTrackBoosted=true; + return true; +#endif + case HASH_KEYWORD_RESET: + DCCTimer::reset(); + break; // and if we didnt restart + case HASH_KEYWORD_SPEED28: + DCC::setGlobalSpeedsteps(28); + DIAG(F("28 Speedsteps")); return true; - case HASH_KEYWORD_RAM: // - StringFormatter::send(stream, F("Free memory=%d\n"), DCCTimer::getMinimumFreeMemory()); + case HASH_KEYWORD_SPEED128: + DCC::setGlobalSpeedsteps(128); + DIAG(F("128 Speedsteps")); return true; #ifndef DISABLE_PROG @@ -1146,12 +1159,33 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) LCD(0, F("Ack Retry=%d Sum=%d"), p[2], DCCACK::setAckRetry(p[2])); // } } else { - StringFormatter::send(stream, F("Ack diag %S\n"), onOff ? F("on") : F("off")); + DIAG(F("Ack diag %S"), onOff ? F("on") : F("off")); Diag::ACK = onOff; } return true; #endif +default: // invalid/unknown + break; + } + return false; +} + +bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) +{ + if (params == 0) + return false; + bool onOff = (params > 0) && (p[1] == 1 || p[1] == HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off + switch (p[0]) + { + case HASH_KEYWORD_CABS: // + DCC::displayCabList(stream); + return true; + + case HASH_KEYWORD_RAM: // + DIAG(F("Free memory=%d"), DCCTimer::getMinimumFreeMemory()); + return true; + case HASH_KEYWORD_CMD: // Diag::CMD = onOff; return true; @@ -1173,34 +1207,14 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) Diag::LCN = onOff; return true; #endif -#ifndef DISABLE_PROG - case HASH_KEYWORD_PROGBOOST: - TrackManager::progTrackBoosted=true; - return true; -#endif - case HASH_KEYWORD_RESET: - DCCTimer::reset(); - break; // and if we didnt restart - - #ifndef DISABLE_EEPROM case HASH_KEYWORD_EEPROM: // if (params >= 2) EEStore::dump(p[1]); return true; #endif - - case HASH_KEYWORD_SPEED28: - DCC::setGlobalSpeedsteps(28); - StringFormatter::send(stream, F("28 Speedsteps")); - return true; - - case HASH_KEYWORD_SPEED128: - DCC::setGlobalSpeedsteps(128); - StringFormatter::send(stream, F("128 Speedsteps")); - return true; - case HASH_KEYWORD_SERVO: // + case HASH_KEYWORD_ANOUT: // IODevice::writeAnalogue(p[1], p[2], params>3 ? p[3] : 0); break; @@ -1223,7 +1237,7 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[]) break; default: // invalid/unknown - break; + return parseC(stream, params, p); } return false; } diff --git a/DCCEXParser.h b/DCCEXParser.h index 8a7367a..3c3382c 100644 --- a/DCCEXParser.h +++ b/DCCEXParser.h @@ -49,6 +49,7 @@ struct DCCEXParser static bool parseZ(Print * stream, int16_t params, int16_t p[]); static bool parseS(Print * stream, int16_t params, int16_t p[]); static bool parsef(Print * stream, int16_t params, int16_t p[]); + static bool parseC(Print * stream, int16_t params, int16_t p[]); static bool parseD(Print * stream, int16_t params, int16_t p[]); #ifndef IO_NO_HAL static bool parseI(Print * stream, int16_t params, int16_t p[]); diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index d46ef22..d2a7fd1 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202309241855Z" +#define GITHUB_SHA "devel-202310230944Z" diff --git a/version.h b/version.h index 00fdc37..4daafba 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.16" +#define VERSION "5.1.17" +// 5.1.17 - Divide out C for config and D for diag commands // 5.1.16 - Remove I2C address from EXTT_TURNTABLE macro to work with MUX, requires separate HAL macro to create // 5.1.15 - LCC/Adapter support and Exrail feature-compile-out. // 5.1.14 - Fixed IFTTPOSITION From 33b282009565ebf2cdbff968dfb6e6da0e4fd05f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 28 Oct 2023 19:18:59 +0200 Subject: [PATCH 158/218] Bugfix version detection logic and better message --- WifiInterface.cpp | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index ab36957..27830bb 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -206,12 +206,13 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password, while(!wifiStream->available()); version[i]=wifiStream->read(); StringFormatter::printEscape(version[i]); - if ((version[0] == '0') || - (version[0] == '2' && version[2] == '0') || - (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) { - SSid = F("DCCEX_SAYS_BROKEN_FIRMWARE"); - forceAP = true; - } + } + if ((version[0] == '0') || + (version[0] == '2' && version[2] == '0') || + (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) { + DIAG(F("You need to up/downgrade the ESP firmware")); + SSid = F("UPDATE_ESP_FIRMWARE"); + forceAP = true; } } checkForOK(2000, true, false); From 24e0f189e18283d6f2399c402e507f8e84cca263 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Wed, 1 Nov 2023 20:19:59 +0000 Subject: [PATCH 159/218] fix TURNOUTL --- EXRAILMacros.h | 2 ++ version.h | 3 ++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 8e7fbb9..8954f6d 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -204,6 +204,8 @@ void RMFT2::printMessage(uint16_t id) { #include "EXRAIL2MacroReset.h" #undef TURNOUT #define TURNOUT(id,addr,subaddr,description...) O_DESC(id,description) +#undef TURNOUTL +#define TURNOUTL(id,addr,description...) O_DESC(id,description) #undef PIN_TURNOUT #define PIN_TURNOUT(id,pin,description...) O_DESC(id,description) #undef SERVO_TURNOUT diff --git a/version.h b/version.h index 4daafba..5ba1d33 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.17" +#define VERSION "5.1.18" +// 5.1.8 TURNOUTL bugfix // 5.1.17 - Divide out C for config and D for diag commands // 5.1.16 - Remove I2C address from EXTT_TURNTABLE macro to work with MUX, requires separate HAL macro to create // 5.1.15 - LCC/Adapter support and Exrail feature-compile-out. From 043e6fdb263b99d3c48ec7df1e93520f83547ae1 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 6 Nov 2023 22:11:31 +0100 Subject: [PATCH 160/218] Only flag 2.2.0.0-dev as broken, not 2.2.0.0 --- WifiInterface.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 27830bb..8b2251a 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -201,15 +201,16 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password, // Display the AT version information StringFormatter::send(wifiStream, F("AT+GMR\r\n")); if (checkForOK(2000, F("AT version:"), true, false)) { - char version[] = "0.0.0.0"; - for (int i=0; i<8;i++) { + char version[] = "0.0.0.0-xxx"; + for (int i=0; i<11;i++) { while(!wifiStream->available()); version[i]=wifiStream->read(); StringFormatter::printEscape(version[i]); } if ((version[0] == '0') || (version[0] == '2' && version[2] == '0') || - (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0')) { + (version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0' + && version[7] == '-' && version[8] == 'd' && version[9] == 'e' && version[10] == 'v')) { DIAG(F("You need to up/downgrade the ESP firmware")); SSid = F("UPDATE_ESP_FIRMWARE"); forceAP = true; From f2ff1ba22aa8919f0a322472f7a9dda31bb1ef92 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 6 Nov 2023 22:14:39 +0100 Subject: [PATCH 161/218] version 5.1.19 --- version.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/version.h b/version.h index 5ba1d33..9d5a7e1 100644 --- a/version.h +++ b/version.h @@ -3,8 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.1.18" -// 5.1.8 TURNOUTL bugfix +#define VERSION "5.1.19" +// 5.1.19 - Only flag 2.2.0.0-dev as broken, not 2.2.0.0 +// 5.1.18 - TURNOUTL bugfix // 5.1.17 - Divide out C for config and D for diag commands // 5.1.16 - Remove I2C address from EXTT_TURNTABLE macro to work with MUX, requires separate HAL macro to create // 5.1.15 - LCC/Adapter support and Exrail feature-compile-out. From 4e08177b7bc3417475fb90e8ec28f63f9f553115 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 7 Nov 2023 16:27:26 +0000 Subject: [PATCH 162/218] Route state management (part 1) --- CommandDistributor.cpp | 8 ++++++++ CommandDistributor.h | 4 ++++ EXRAIL2.cpp | 11 ++++++++++- EXRAIL2.h | 1 + EXRAIL2MacroReset.h | 8 ++++++++ EXRAILMacros.h | 10 ++++++++++ 6 files changed, 41 insertions(+), 1 deletion(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 1651771..efd4778 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -272,3 +272,11 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) { void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr) { broadcastReply(COMMAND_TYPE, format,trackLetter, dcAddr); } + +void CommandDistributor::broadcastRouteState(uint16_t routeId, RouteState state ) { + broadcastReply(COMMAND_TYPE, F("\n"),routeId,state); +} + +void CommandDistributor::broadcastRouteCaption(uint16_t routeId, const FSH* caption ) { + broadcastReply(COMMAND_TYPE, F("\n"),routeId,caption); +} diff --git a/CommandDistributor.h b/CommandDistributor.h index d54ef31..839bcbf 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -37,6 +37,7 @@ class CommandDistributor { public: enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE}; + enum RouteState: byte {STATE_ACTIVE,STATE_INACTIVE,STATE_HIDDEN}; private: static void broadcastToClients(clientType type); static StringBuffer * broadcastBufferWriter; @@ -58,6 +59,9 @@ public : static void broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr); template static void broadcastReply(clientType type, Targs... msg); static void forget(byte clientId); + static void broadcastRouteState(uint16_t routeId,RouteState state); + static void broadcastRouteCaption(uint16_t routeId,const FSH * caption); + }; diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index c902708..634df29 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1130,7 +1130,16 @@ void RMFT2::loop2() { case OPCODE_PRINT: printMessage(operand); break; - + case OPCODE_ROUTE_HIDDEN: + CommandDistributor::broadcastRouteState(operand,CommandDistributor::RouteState::STATE_HIDDEN); + break; + case OPCODE_ROUTE_ACTIVE: + CommandDistributor::broadcastRouteState(operand,CommandDistributor::RouteState::STATE_ACTIVE); + break; + case OPCODE_ROUTE_INACTIVE: + CommandDistributor::broadcastRouteState(operand,CommandDistributor::RouteState::STATE_INACTIVE); + break; + case OPCODE_ROUTE: case OPCODE_AUTOMATION: case OPCODE_SEQUENCE: diff --git a/EXRAIL2.h b/EXRAIL2.h index de14f11..1898b26 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -68,6 +68,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONROTATE,OPCODE_ROTATE,OPCODE_WAITFORTT, OPCODE_LCC,OPCODE_LCCX,OPCODE_ONLCC, OPCODE_ONOVERLOAD, + OPCODE_ROUTE_ACTIVE,OPCODE_ROUTE_INACTIVE,OPCODE_ROUTE_HIDDEN, // OPcodes below this point are skip-nesting IF operations // placed here so that they may be skipped as a group diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index d44b244..9bfad1b 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -126,6 +126,10 @@ #undef ROTATE #undef ROTATE_DCC #undef ROUTE +#undef ROUTE_ACTVE +#undef ROUTE_INACTVE +#undef ROUTE_HIDDEN +#undef ROUTE_CAPTION #undef SENDLOCO #undef SEQUENCE #undef SERIAL @@ -267,6 +271,10 @@ #define ROTATE_DCC(turntable_id,position) #define ROSTER(cab,name,funcmap...) #define ROUTE(id,description) +#define ROUTE_ACTIVE(id) +#define ROUTE_INACTIVE(id) +#define ROUTE_HIDDEN(id) +#define ROUTE_CAPTION(id,caption) #define SENDLOCO(cab,route) #define SEQUENCE(id) #define SERIAL(msg) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 8954f6d..e11b5db 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -153,6 +153,12 @@ const int StringMacroTracker1=__COUNTER__; #define PRINT(msg) THRUNGE(msg,thrunge_print) #undef LCN #define LCN(msg) THRUNGE(msg,thrunge_lcn) +#undef ROUTE_CAPTION +#define ROUTE_CAPTION(id,caption) \ +case (__COUNTER__ - StringMacroTracker1) : {\ + CommandDistributor::broadcastRouteCaption(id,F(caption));\ + return;\ + } #undef SERIAL #define SERIAL(msg) THRUNGE(msg,thrunge_serial) #undef SERIAL1 @@ -440,6 +446,10 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define ROTATE_DCC(id,position) OPCODE_ROTATE,V(id),OPCODE_PAD,V(position),OPCODE_PAD,V(0), #endif #define ROUTE(id, description) OPCODE_ROUTE, V(id), +#define ROUTE_ACTIVE(id) OPCODE_ROUTE_ACTIVE,V(id), +#define ROUTE_INACTIVE(id) OPCODE_ROUTE_INACTIVE,V(id), +#define ROUTE_HIDDEN(id) OPCODE_ROUTE_HIDDEN,V(id), +#define ROUTE_CAPTION(id,caption) PRINT(caption) #define SENDLOCO(cab,route) OPCODE_SENDLOCO,V(cab),OPCODE_PAD,V(route), #define SEQUENCE(id) OPCODE_SEQUENCE, V(id), #define SERIAL(msg) PRINT(msg) From 26cf28dff7396e3d836117bb2003d78220fa237b Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 9 Nov 2023 19:27:52 +0000 Subject: [PATCH 163/218] fixups --- CommandDistributor.cpp | 2 +- CommandDistributor.h | 3 +-- EXRAIL2.cpp | 28 +++++++++++++++++++++++++--- EXRAIL2.h | 6 +++++- EXRAIL2MacroReset.h | 4 ++-- 5 files changed, 34 insertions(+), 9 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index efd4778..351a18d 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -273,7 +273,7 @@ void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, broadcastReply(COMMAND_TYPE, format,trackLetter, dcAddr); } -void CommandDistributor::broadcastRouteState(uint16_t routeId, RouteState state ) { +void CommandDistributor::broadcastRouteState(uint16_t routeId, byte state ) { broadcastReply(COMMAND_TYPE, F("\n"),routeId,state); } diff --git a/CommandDistributor.h b/CommandDistributor.h index 839bcbf..83bfbbd 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -37,7 +37,6 @@ class CommandDistributor { public: enum clientType: byte {NONE_TYPE,COMMAND_TYPE,WITHROTTLE_TYPE}; - enum RouteState: byte {STATE_ACTIVE,STATE_INACTIVE,STATE_HIDDEN}; private: static void broadcastToClients(clientType type); static StringBuffer * broadcastBufferWriter; @@ -59,7 +58,7 @@ public : static void broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr); template static void broadcastReply(clientType type, Targs... msg); static void forget(byte clientId); - static void broadcastRouteState(uint16_t routeId,RouteState state); + static void broadcastRouteState(uint16_t routeId,byte state); static void broadcastRouteCaption(uint16_t routeId,const FSH * caption); diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 634df29..870ff6d 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -100,6 +100,7 @@ LookList * RMFT2::onClockLookup=NULL; LookList * RMFT2::onRotateLookup=NULL; #endif LookList * RMFT2::onOverloadLookup=NULL; +byte * RMFT2::routeStateArray=nullptr; #define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter) #define SKIPOP progCounter+=3 @@ -139,6 +140,15 @@ int16_t LookList::find(int16_t value) { } return -1; } +int16_t LookList::findPosition(int16_t value) { + for (int16_t i=0;isize(),sizeof(byte)); onThrowLookup=LookListLoader(OPCODE_ONTHROW); onCloseLookup=LookListLoader(OPCODE_ONCLOSE); onActivateLookup=LookListLoader(OPCODE_ONACTIVATE); @@ -1131,13 +1142,13 @@ void RMFT2::loop2() { printMessage(operand); break; case OPCODE_ROUTE_HIDDEN: - CommandDistributor::broadcastRouteState(operand,CommandDistributor::RouteState::STATE_HIDDEN); + manageRoute(operand,2); break; case OPCODE_ROUTE_ACTIVE: - CommandDistributor::broadcastRouteState(operand,CommandDistributor::RouteState::STATE_ACTIVE); + manageRoute(operand,0); break; case OPCODE_ROUTE_INACTIVE: - CommandDistributor::broadcastRouteState(operand,CommandDistributor::RouteState::STATE_INACTIVE); + manageRoute(operand,1); break; case OPCODE_ROUTE: @@ -1462,3 +1473,14 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) { break; } } + +void RMFT2::manageRoute(uint16_t id, byte state) { + CommandDistributor::broadcastRouteState(id,state); + // Route state must be maintained for when new throttles connect. + // locate route id in the Routes lookup + int16_t position=sequenceLookup->findPosition(id); + if (position<0) return; + // set state beside it + routeStateArray[position]=state; +} + \ No newline at end of file diff --git a/EXRAIL2.h b/EXRAIL2.h index 1898b26..ed3e459 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -121,7 +121,9 @@ class LookList { public: LookList(int16_t size); void add(int16_t lookup, int16_t result); - int16_t find(int16_t value); + int16_t find(int16_t value); // finds result value + int16_t findPosition(int16_t value); // finds index + int16_t size(); private: int16_t m_size; int16_t m_loaded; @@ -217,6 +219,8 @@ private: static const int countLCCLookup; static int onLCCLookup[]; static const byte compileFeatures; + static void manageRoute(uint16_t id, byte state); + static byte * routeStateArray; // Local variables - exist for each instance/task RMFT2 *next; // loop chain diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 9bfad1b..ff8dc8d 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -126,8 +126,8 @@ #undef ROTATE #undef ROTATE_DCC #undef ROUTE -#undef ROUTE_ACTVE -#undef ROUTE_INACTVE +#undef ROUTE_ACTIVE +#undef ROUTE_INACTIVE #undef ROUTE_HIDDEN #undef ROUTE_CAPTION #undef SENDLOCO From 2cbcecf9e653477a2032fc572e63481ecaa062d2 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 9 Nov 2023 20:25:10 +0000 Subject: [PATCH 164/218] separate routes and sequences, handle state and captions. --- EXRAIL2.cpp | 56 ++++++++++++++++++++++++++++++++++++----------------- EXRAIL2.h | 10 +++++++--- 2 files changed, 45 insertions(+), 21 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 870ff6d..7b6107d 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -86,7 +86,7 @@ RMFT2 * RMFT2::pausingTask=NULL; // Task causing a PAUSE. // and all others will have their locos stopped, then resumed after the pausing task resumes. byte RMFT2::flags[MAX_FLAGS]; Print * RMFT2::LCCSerial=0; -LookList * RMFT2::sequenceLookup=NULL; +LookList * RMFT2::routeLookup=NULL; LookList * RMFT2::onThrowLookup=NULL; LookList * RMFT2::onCloseLookup=NULL; LookList * RMFT2::onActivateLookup=NULL; @@ -101,6 +101,7 @@ LookList * RMFT2::onRotateLookup=NULL; #endif LookList * RMFT2::onOverloadLookup=NULL; byte * RMFT2::routeStateArray=nullptr; +const FSH * * RMFT2::routeCaptionArray=nullptr; #define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter) #define SKIPOP progCounter+=3 @@ -121,6 +122,7 @@ uint16_t RMFT2::getOperand(int progCounter,byte n) { LookList::LookList(int16_t size) { m_size=size; m_loaded=0; + m_chain=nullptr; if (size) { m_lookupArray=new int16_t[size]; m_resultArray=new int16_t[size]; @@ -138,8 +140,12 @@ int16_t LookList::find(int16_t value) { for (int16_t i=0;ifind(value) :-1; } +void LookList::chain(LookList * chain) { + m_chain=chain; +} + int16_t LookList::findPosition(int16_t value) { for (int16_t i=0;isize(),sizeof(byte)); + routeLookup=LookListLoader(OPCODE_ROUTE, OPCODE_AUTOMATION); + routeLookup->chain(LookListLoader(OPCODE_SEQUENCE)); + routeStateArray=(byte *)calloc(routeLookup->size(),sizeof(byte)); + routeCaptionArray=(const FSH * *)calloc(routeLookup->size(),sizeof(const FSH *)); + onThrowLookup=LookListLoader(OPCODE_ONTHROW); onCloseLookup=LookListLoader(OPCODE_ONCLOSE); onActivateLookup=LookListLoader(OPCODE_ONACTIVATE); @@ -485,7 +494,7 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { { int route=(paramCount==2) ? p[1] : p[2]; uint16_t cab=(paramCount==2)? 0 : p[1]; - int pc=sequenceLookup->find(route); + int pc=routeLookup->find(route); if (pc<0) return false; RMFT2* task=new RMFT2(pc); task->loco=cab; @@ -605,7 +614,7 @@ RMFT2::~RMFT2() { } void RMFT2::createNewTask(int route, uint16_t cab) { - int pc=sequenceLookup->find(route); + int pc=routeLookup->find(route); if (pc<0) return; RMFT2* task=new RMFT2(pc); task->loco=cab; @@ -1006,7 +1015,7 @@ void RMFT2::loop2() { } case OPCODE_FOLLOW: - progCounter=sequenceLookup->find(operand); + progCounter=routeLookup->find(operand); if (progCounter<0) kill(F("FOLLOW unknown"), operand); return; @@ -1016,7 +1025,7 @@ void RMFT2::loop2() { return; } callStack[stackDepth++]=progCounter+3; - progCounter=sequenceLookup->find(operand); + progCounter=routeLookup->find(operand); if (progCounter<0) kill(F("CALL unknown"),operand); return; @@ -1079,7 +1088,7 @@ void RMFT2::loop2() { case OPCODE_START: { - int newPc=sequenceLookup->find(operand); + int newPc=routeLookup->find(operand); if (newPc<0) break; new RMFT2(newPc); } @@ -1087,7 +1096,7 @@ void RMFT2::loop2() { case OPCODE_SENDLOCO: // cab, route { - int newPc=sequenceLookup->find(getOperand(1)); + int newPc=routeLookup->find(getOperand(1)); if (newPc<0) break; RMFT2* newtask=new RMFT2(newPc); // create new task newtask->loco=operand; @@ -1142,13 +1151,13 @@ void RMFT2::loop2() { printMessage(operand); break; case OPCODE_ROUTE_HIDDEN: - manageRoute(operand,2); + manageRouteState(operand,2); break; case OPCODE_ROUTE_ACTIVE: - manageRoute(operand,0); + manageRouteState(operand,0); break; case OPCODE_ROUTE_INACTIVE: - manageRoute(operand,1); + manageRouteState(operand,1); break; case OPCODE_ROUTE: @@ -1474,13 +1483,24 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) { } } -void RMFT2::manageRoute(uint16_t id, byte state) { - CommandDistributor::broadcastRouteState(id,state); +void RMFT2::manageRouteState(uint16_t id, byte state) { // Route state must be maintained for when new throttles connect. // locate route id in the Routes lookup - int16_t position=sequenceLookup->findPosition(id); + int16_t position=routeLookup->findPosition(id); if (position<0) return; // set state beside it - routeStateArray[position]=state; + if (routeStateArray[position]==state) return; + routeStateArray[position]=state; + CommandDistributor::broadcastRouteState(id,state); } - \ No newline at end of file +void RMFT2::manageRouteCaption(uint16_t id,const FSH* caption) { + // Route state must be maintained for when new throttles connect. + // locate route id in the Routes lookup + int16_t position=routeLookup->findPosition(id); + if (position<0) return; + // set state beside it + if (routeCaptionArray[position]==caption) return; + routeCaptionArray[position]=caption; + CommandDistributor::broadcastRouteCaption(id,caption); +} + diff --git a/EXRAIL2.h b/EXRAIL2.h index ed3e459..e5db1d9 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -120,6 +120,7 @@ enum thrunger: byte { class LookList { public: LookList(int16_t size); + void chain(LookList* chainTo); void add(int16_t lookup, int16_t result); int16_t find(int16_t value); // finds result value int16_t findPosition(int16_t value); // finds index @@ -128,7 +129,8 @@ class LookList { int16_t m_size; int16_t m_loaded; int16_t * m_lookupArray; - int16_t * m_resultArray; + int16_t * m_resultArray; + LookList* m_chain; }; class RMFT2 { @@ -201,7 +203,7 @@ private: static const HIGHFLASH int16_t SignalDefinitions[]; static byte flags[MAX_FLAGS]; static Print * LCCSerial; - static LookList * sequenceLookup; + static LookList * routeLookup; static LookList * onThrowLookup; static LookList * onCloseLookup; static LookList * onActivateLookup; @@ -219,8 +221,10 @@ private: static const int countLCCLookup; static int onLCCLookup[]; static const byte compileFeatures; - static void manageRoute(uint16_t id, byte state); + static void manageRouteState(uint16_t id, byte state); + static void manageRouteCaption(uint16_t id, const FSH* caption); static byte * routeStateArray; + static const FSH * * routeCaptionArray; // Local variables - exist for each instance/task RMFT2 *next; // loop chain From a4eabf235e47f48918ce991d069372cbc14b4ad3 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 10 Nov 2023 19:25:24 +0000 Subject: [PATCH 165/218] EXRAIL ROUTE_STATE and ROUTE_CAPTION --- DCCEXParser.cpp | 29 ++--- EXRAIL2.cpp | 303 ++++++---------------------------------------- EXRAIL2.h | 10 +- EXRAIL2Parser.cpp | 287 +++++++++++++++++++++++++++++++++++++++++++ EXRAILMacros.h | 10 +- 5 files changed, 348 insertions(+), 291 deletions(-) create mode 100644 EXRAIL2Parser.cpp diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index d79136f..96596c6 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -781,27 +781,11 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) TrackManager::reportCurrent(stream); // return; - case HASH_KEYWORD_A: // returns automations/routes - StringFormatter::send(stream, F(" -#ifdef EXRAIL_ACTIVE - SENDFLASHLIST(stream,RMFT2::routeIdList) - SENDFLASHLIST(stream,RMFT2::automationIdList) -#endif - } - else { // - StringFormatter::send(stream,F(" %d %c \"%S\""), - id, -#ifdef EXRAIL_ACTIVE - RMFT2::getRouteType(id), // A/R - RMFT2::getRouteDescription(id) -#else - 'X',F("") -#endif - ); - } - StringFormatter::send(stream, F(">\n")); - return; + case HASH_KEYWORD_A: // intercepted by EXRAIL// returns automations/routes + if (params!=1) break; // + StringFormatter::send(stream, F("\n")); + return; + case HASH_KEYWORD_R: // returns rosters StringFormatter::send(stream, F(" 0) && (p[1] == 1 || p[1] == HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off switch (p[0]) { #ifndef DISABLE_PROG @@ -1159,6 +1142,8 @@ bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) { LCD(0, F("Ack Retry=%d Sum=%d"), p[2], DCCACK::setAckRetry(p[2])); // } } else { + bool onOff = (params > 0) && (p[1] == 1 || p[1] == HASH_KEYWORD_ON); // dont care if other stuff or missing... just means off + DIAG(F("Ack diag %S"), onOff ? F("on") : F("off")); Diag::ACK = onOff; } diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 7b6107d..61e0264 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -55,22 +55,6 @@ #include "Turntables.h" #include "IODevice.h" -// Command parsing keywords -const int16_t HASH_KEYWORD_EXRAIL=15435; -const int16_t HASH_KEYWORD_ON = 2657; -const int16_t HASH_KEYWORD_START=23232; -const int16_t HASH_KEYWORD_RESERVE=11392; -const int16_t HASH_KEYWORD_FREE=-23052; -const int16_t HASH_KEYWORD_LATCH=1618; -const int16_t HASH_KEYWORD_UNLATCH=1353; -const int16_t HASH_KEYWORD_PAUSE=-4142; -const int16_t HASH_KEYWORD_RESUME=27609; -const int16_t HASH_KEYWORD_KILL=5218; -const int16_t HASH_KEYWORD_ALL=3457; -const int16_t HASH_KEYWORD_ROUTES=-3702; -const int16_t HASH_KEYWORD_RED=26099; -const int16_t HASH_KEYWORD_AMBER=18713; -const int16_t HASH_KEYWORD_GREEN=-31493; // One instance of RMFT clas is used for each "thread" in the automation. // Each thread manages a loco on a journey through the layout, and/or may manage a scenery automation. @@ -103,9 +87,6 @@ LookList * RMFT2::onOverloadLookup=NULL; byte * RMFT2::routeStateArray=nullptr; const FSH * * RMFT2::routeCaptionArray=nullptr; -#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter) -#define SKIPOP progCounter+=3 - // getOperand instance version, uses progCounter from instance. uint16_t RMFT2::getOperand(byte n) { return getOperand(progCounter,n); @@ -146,6 +127,13 @@ void LookList::chain(LookList * chain) { m_chain=chain; } +void LookList::stream(Print * _stream) { + for (int16_t i=0;iprint(" "); + _stream->print(m_lookupArray[i]); + } +} + int16_t LookList::findPosition(int16_t value) { for (int16_t i=0;ichain(LookListLoader(OPCODE_SEQUENCE)); - routeStateArray=(byte *)calloc(routeLookup->size(),sizeof(byte)); - routeCaptionArray=(const FSH * *)calloc(routeLookup->size(),sizeof(const FSH *)); - + if (compileFeatures && FEATURE_ROUTESTATE) { + routeStateArray=(byte *)calloc(routeLookup->size(),sizeof(byte)); + routeCaptionArray=(const FSH * *)calloc(routeLookup->size(),sizeof(const FSH *)); + } onThrowLookup=LookListLoader(OPCODE_ONTHROW); onCloseLookup=LookListLoader(OPCODE_ONCLOSE); onActivateLookup=LookListLoader(OPCODE_ONACTIVATE); @@ -334,238 +323,15 @@ void RMFT2::setTurntableHiddenState(Turntable * tto) { #endif char RMFT2::getRouteType(int16_t id) { - for (int16_t i=0;;i+=2) { - int16_t rid= GETHIGHFLASHW(routeIdList,i); - if (rid==INT16_MAX) break; - if (rid==id) return 'R'; - } - for (int16_t i=0;;i+=2) { - int16_t rid= GETHIGHFLASHW(automationIdList,i); - if (rid==INT16_MAX) break; - if (rid==id) return 'A'; + int16_t progCounter=routeLookup->find(id); + if (progCounter>=0) { + OPCODE type=GET_OPCODE; + if (type==OPCODE_ROUTE) return 'R'; + if (type==OPCODE_AUTOMATION) return 'A'; } return 'X'; } -// This filter intercepts <> commands to do the following: -// - Implement RMFT specific commands/diagnostics -// - Reject/modify JMRI commands that would interfere with RMFT processing -void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]) { - (void)stream; // avoid compiler warning if we don't access this parameter - bool reject=false; - switch(opcode) { - - case 'D': - if (p[0]==HASH_KEYWORD_EXRAIL) { // - diag = paramCount==2 && (p[1]==HASH_KEYWORD_ON || p[1]==1); - opcode=0; - } - break; - - case '/': // New EXRAIL command - reject=!parseSlash(stream,paramCount,p); - opcode=0; - break; - case 'L': - if (compileFeatures & FEATURE_LCC) { - // This entire code block is compiled out if LLC macros not used - if (paramCount==0) { // LCC adapter introducing self - LCCSerial=stream; // now we know where to send events we raise - - // loop through all possible sent events - for (int progCounter=0;; SKIPOP) { - byte opcode=GET_OPCODE; - if (opcode==OPCODE_ENDEXRAIL) break; - if (opcode==OPCODE_LCC) StringFormatter::send(stream,F("\n"),getOperand(progCounter,0)); - if (opcode==OPCODE_LCCX) { // long form LCC - StringFormatter::send(stream,F("\n"), - getOperand(progCounter,1), - getOperand(progCounter,2), - getOperand(progCounter,3), - getOperand(progCounter,0) - ); - }} - - // we stream the hex events we wish to listen to - // and at the same time build the event index looku. - - - int eventIndex=0; - for (int progCounter=0;; SKIPOP) { - byte opcode=GET_OPCODE; - if (opcode==OPCODE_ENDEXRAIL) break; - if (opcode==OPCODE_ONLCC) { - onLCCLookup[eventIndex]=progCounter; // TODO skip... - StringFormatter::send(stream,F("\n"), - eventIndex, - getOperand(progCounter,1), - getOperand(progCounter,2), - getOperand(progCounter,3), - getOperand(progCounter,0) - ); - eventIndex++; - } - } - StringFormatter::send(stream,F("\n")); // Ready to rumble - opcode=0; - break; - } - if (paramCount==1) { // LCC event arrived from adapter - int16_t eventid=p[0]; - reject=eventid<0 || eventid>=countLCCLookup; - if (!reject) startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]); - opcode=0; - } - } - break; - - default: // other commands pass through - break; - } - if (reject) { - opcode=0; - StringFormatter::send(stream,F("\n")); - } -} - -bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { - - if (paramCount==0) { // STATUS - StringFormatter::send(stream, F("<* EXRAIL STATUS")); - RMFT2 * task=loopTask; - while(task) { - StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d%c,SPEED=%d%c"), - (int)(task->taskId),task->progCounter,task->loco, - task->invert?'I':' ', - task->speedo, - task->forward?'F':'R' - ); - task=task->next; - if (task==loopTask) break; - } - // Now stream the flags - for (int id=0;id\n")); - return true; - } - switch (p[0]) { - case HASH_KEYWORD_PAUSE: // - if (paramCount!=1) return false; - DCC::setThrottle(0,1,true); // pause all locos on the track - pausingTask=(RMFT2 *)1; // Impossible task address - return true; - - case HASH_KEYWORD_RESUME: // - if (paramCount!=1) return false; - pausingTask=NULL; - { - RMFT2 * task=loopTask; - while(task) { - if (task->loco) task->driveLoco(task->speedo); - task=task->next; - if (task==loopTask) break; - } - } - return true; - - - case HASH_KEYWORD_START: // - if (paramCount<2 || paramCount>3) return false; - { - int route=(paramCount==2) ? p[1] : p[2]; - uint16_t cab=(paramCount==2)? 0 : p[1]; - int pc=routeLookup->find(route); - if (pc<0) return false; - RMFT2* task=new RMFT2(pc); - task->loco=cab; - } - return true; - - default: - break; - } - - // check KILL ALL here, otherwise the next validation confuses ALL with a flag - if (p[0]==HASH_KEYWORD_KILL && p[1]==HASH_KEYWORD_ALL) { - while (loopTask) loopTask->kill(F("KILL ALL")); // destructor changes loopTask - return true; - } - - // all other / commands take 1 parameter - if (paramCount!=2 ) return false; - - switch (p[0]) { - case HASH_KEYWORD_KILL: // Kill taskid|ALL - { - if ( p[1]<0 || p[1]>=MAX_FLAGS) return false; - RMFT2 * task=loopTask; - while(task) { - if (task->taskId==p[1]) { - task->kill(F("KILL")); - return true; - } - task=task->next; - if (task==loopTask) break; - } - } - return false; - - case HASH_KEYWORD_RESERVE: // force reserve a section - return setFlag(p[1],SECTION_FLAG); - - case HASH_KEYWORD_FREE: // force free a section - return setFlag(p[1],0,SECTION_FLAG); - - case HASH_KEYWORD_LATCH: - return setFlag(p[1], LATCH_FLAG); - - case HASH_KEYWORD_UNLATCH: - return setFlag(p[1], 0, LATCH_FLAG); - - case HASH_KEYWORD_RED: - doSignal(p[1],SIGNAL_RED); - return true; - - case HASH_KEYWORD_AMBER: - doSignal(p[1],SIGNAL_AMBER); - return true; - - case HASH_KEYWORD_GREEN: - doSignal(p[1],SIGNAL_GREEN); - return true; - - default: - return false; - } -} - - -// This emits Routes and Automations to Withrottle -// Automations are given a state to set the button to "handoff" which implies -// handing over the loco to the automation. -// Routes are given "Set" buttons and do not cause the loco to be handed over. - - RMFT2::RMFT2(int progCtr) { progCounter=progCtr; @@ -1484,23 +1250,28 @@ void RMFT2::thrungeString(uint32_t strfar, thrunger mode, byte id) { } void RMFT2::manageRouteState(uint16_t id, byte state) { - // Route state must be maintained for when new throttles connect. - // locate route id in the Routes lookup - int16_t position=routeLookup->findPosition(id); - if (position<0) return; - // set state beside it - if (routeStateArray[position]==state) return; - routeStateArray[position]=state; - CommandDistributor::broadcastRouteState(id,state); + if (compileFeatures && FEATURE_ROUTESTATE) { + // Route state must be maintained for when new throttles connect. + // locate route id in the Routes lookup + int16_t position=routeLookup->findPosition(id); + if (position<0) return; + // set state beside it + if (routeStateArray[position]==state) return; + routeStateArray[position]=state; + CommandDistributor::broadcastRouteState(id,state); + } } void RMFT2::manageRouteCaption(uint16_t id,const FSH* caption) { - // Route state must be maintained for when new throttles connect. - // locate route id in the Routes lookup - int16_t position=routeLookup->findPosition(id); - if (position<0) return; - // set state beside it - if (routeCaptionArray[position]==caption) return; - routeCaptionArray[position]=caption; - CommandDistributor::broadcastRouteCaption(id,caption); + if (compileFeatures && FEATURE_ROUTESTATE) { + // Route state must be maintained for when new throttles connect. + // locate route id in the Routes lookup + int16_t position=routeLookup->findPosition(id); + if (position<0) return; + // set state beside it + if (routeCaptionArray[position]==caption) return; + routeCaptionArray[position]=caption; + DIAG(F("rCA[%d]=%d, c=%d"),position,routeStateArray[position],caption); + CommandDistributor::broadcastRouteCaption(id,caption); + } } diff --git a/EXRAIL2.h b/EXRAIL2.h index e5db1d9..bcb517e 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -100,6 +100,7 @@ enum thrunger: byte { static const byte FEATURE_SIGNAL= 0x80; static const byte FEATURE_LCC = 0x40; static const byte FEATURE_ROSTER= 0x20; + static const byte FEATURE_ROUTESTATE= 0x10; // Flag bits for status of hardware and TPL @@ -124,7 +125,8 @@ class LookList { void add(int16_t lookup, int16_t result); int16_t find(int16_t value); // finds result value int16_t findPosition(int16_t value); // finds index - int16_t size(); + int16_t size(); + void stream(Print * _stream); private: int16_t m_size; int16_t m_loaded; @@ -224,7 +226,7 @@ private: static void manageRouteState(uint16_t id, byte state); static void manageRouteCaption(uint16_t id, const FSH* caption); static byte * routeStateArray; - static const FSH * * routeCaptionArray; + static const FSH ** routeCaptionArray; // Local variables - exist for each instance/task RMFT2 *next; // loop chain @@ -246,4 +248,8 @@ private: byte stackDepth; int callStack[MAX_STACK_DEPTH]; }; + +#define GET_OPCODE GETHIGHFLASH(RMFT2::RouteCode,progCounter) +#define SKIPOP progCounter+=3 + #endif diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp new file mode 100644 index 0000000..7670118 --- /dev/null +++ b/EXRAIL2Parser.cpp @@ -0,0 +1,287 @@ +/* + * © 2021 Neil McKechnie + * © 2021-2023 Harald Barth + * © 2020-2023 Chris Harlow + * © 2022-2023 Colin Murdoch + * 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 . + */ + +// THIS file is an extension of the RMFT2 class +// normally found in EXRAIL2.cpp + +#include +#include "defines.h" +#include "EXRAIL2.h" +#include "DCC.h" +// Command parsing keywords +const int16_t HASH_KEYWORD_EXRAIL=15435; +const int16_t HASH_KEYWORD_ON = 2657; +const int16_t HASH_KEYWORD_START=23232; +const int16_t HASH_KEYWORD_RESERVE=11392; +const int16_t HASH_KEYWORD_FREE=-23052; +const int16_t HASH_KEYWORD_LATCH=1618; +const int16_t HASH_KEYWORD_UNLATCH=1353; +const int16_t HASH_KEYWORD_PAUSE=-4142; +const int16_t HASH_KEYWORD_RESUME=27609; +const int16_t HASH_KEYWORD_KILL=5218; +const int16_t HASH_KEYWORD_ALL=3457; +const int16_t HASH_KEYWORD_ROUTES=-3702; +const int16_t HASH_KEYWORD_RED=26099; +const int16_t HASH_KEYWORD_AMBER=18713; +const int16_t HASH_KEYWORD_GREEN=-31493; +const int16_t HASH_KEYWORD_A='A'; + +// This filter intercepts <> commands to do the following: +// - Implement RMFT specific commands/diagnostics +// - Reject/modify JMRI commands that would interfere with RMFT processing + +void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]) { + (void)stream; // avoid compiler warning if we don't access this parameter + bool reject=false; + switch(opcode) { + + case 'D': + if (p[0]==HASH_KEYWORD_EXRAIL) { // + diag = paramCount==2 && (p[1]==HASH_KEYWORD_ON || p[1]==1); + opcode=0; + } + break; + + case '/': // New EXRAIL command + reject=!parseSlash(stream,paramCount,p); + opcode=0; + break; + + case 'L': + // This entire code block is compiled out if LLC macros not used + if (!(compileFeatures & FEATURE_LCC)) return; + + if (paramCount==0) { // LCC adapter introducing self + LCCSerial=stream; // now we know where to send events we raise + + // loop through all possible sent events + for (int progCounter=0;; SKIPOP) { + byte opcode=GET_OPCODE; + if (opcode==OPCODE_ENDEXRAIL) break; + if (opcode==OPCODE_LCC) StringFormatter::send(stream,F("\n"),getOperand(progCounter,0)); + if (opcode==OPCODE_LCCX) { // long form LCC + StringFormatter::send(stream,F("\n"), + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + }} + + // we stream the hex events we wish to listen to + // and at the same time build the event index looku. + + + int eventIndex=0; + for (int progCounter=0;; SKIPOP) { + byte opcode=GET_OPCODE; + if (opcode==OPCODE_ENDEXRAIL) break; + if (opcode==OPCODE_ONLCC) { + onLCCLookup[eventIndex]=progCounter; // TODO skip... + StringFormatter::send(stream,F("\n"), + eventIndex, + getOperand(progCounter,1), + getOperand(progCounter,2), + getOperand(progCounter,3), + getOperand(progCounter,0) + ); + eventIndex++; + } + } + StringFormatter::send(stream,F("\n")); // Ready to rumble + opcode=0; + break; + } + if (paramCount==1) { // LCC event arrived from adapter + int16_t eventid=p[0]; + reject=eventid<0 || eventid>=countLCCLookup; + if (!reject) startNonRecursiveTask(F("LCC"),eventid,onLCCLookup[eventid]); + opcode=0; + } + break; + + case 'J': // throttle info commands + // This entire code block is compiled out if FEATURE_ROUTESTATE macros not used + if (paramCount<1 || !(compileFeatures & FEATURE_ROUTESTATE)) return; + switch(p[0]) + case HASH_KEYWORD_A: // returns automations/routes + if (paramCount==1) {// + StringFormatter::send(stream, F("stream(stream); + StringFormatter::send(stream, F(">\n")); + opcode=0; + return; + } + if (paramCount==2) { // + uint16_t id=p[1]; + StringFormatter::send(stream,F("\n"), + id, getRouteType(id), getRouteDescription(id)); + + // Send any non-default button states or captions + int16_t statePos=routeLookup->findPosition(id); + if (statePos>=0) { + if (routeStateArray[statePos]) + StringFormatter::send(stream,F("\n"), id, routeStateArray[statePos]); + if (routeCaptionArray[statePos]) + StringFormatter::send(stream,F("\n"), id,routeCaptionArray[statePos]); + } + opcode=0; + return; + } + break; + default: // other commands pass through + break; + } +} + +bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { + + if (paramCount==0) { // STATUS + StringFormatter::send(stream, F("<* EXRAIL STATUS")); + RMFT2 * task=loopTask; + while(task) { + StringFormatter::send(stream,F("\nID=%d,PC=%d,LOCO=%d%c,SPEED=%d%c"), + (int)(task->taskId),task->progCounter,task->loco, + task->invert?'I':' ', + task->speedo, + task->forward?'F':'R' + ); + task=task->next; + if (task==loopTask) break; + } + // Now stream the flags + for (int id=0;id\n")); + return true; + } + switch (p[0]) { + case HASH_KEYWORD_PAUSE: // + if (paramCount!=1) return false; + DCC::setThrottle(0,1,true); // pause all locos on the track + pausingTask=(RMFT2 *)1; // Impossible task address + return true; + + case HASH_KEYWORD_RESUME: // + if (paramCount!=1) return false; + pausingTask=NULL; + { + RMFT2 * task=loopTask; + while(task) { + if (task->loco) task->driveLoco(task->speedo); + task=task->next; + if (task==loopTask) break; + } + } + return true; + + + case HASH_KEYWORD_START: // + if (paramCount<2 || paramCount>3) return false; + { + int route=(paramCount==2) ? p[1] : p[2]; + uint16_t cab=(paramCount==2)? 0 : p[1]; + int pc=routeLookup->find(route); + if (pc<0) return false; + RMFT2* task=new RMFT2(pc); + task->loco=cab; + } + return true; + + default: + break; + } + + // check KILL ALL here, otherwise the next validation confuses ALL with a flag + if (p[0]==HASH_KEYWORD_KILL && p[1]==HASH_KEYWORD_ALL) { + while (loopTask) loopTask->kill(F("KILL ALL")); // destructor changes loopTask + return true; + } + + // all other / commands take 1 parameter + if (paramCount!=2 ) return false; + + switch (p[0]) { + case HASH_KEYWORD_KILL: // Kill taskid|ALL + { + if ( p[1]<0 || p[1]>=MAX_FLAGS) return false; + RMFT2 * task=loopTask; + while(task) { + if (task->taskId==p[1]) { + task->kill(F("KILL")); + return true; + } + task=task->next; + if (task==loopTask) break; + } + } + return false; + + case HASH_KEYWORD_RESERVE: // force reserve a section + return setFlag(p[1],SECTION_FLAG); + + case HASH_KEYWORD_FREE: // force free a section + return setFlag(p[1],0,SECTION_FLAG); + + case HASH_KEYWORD_LATCH: + return setFlag(p[1], LATCH_FLAG); + + case HASH_KEYWORD_UNLATCH: + return setFlag(p[1], 0, LATCH_FLAG); + + case HASH_KEYWORD_RED: + doSignal(p[1],SIGNAL_RED); + return true; + + case HASH_KEYWORD_AMBER: + doSignal(p[1],SIGNAL_AMBER); + return true; + + case HASH_KEYWORD_GREEN: + doSignal(p[1],SIGNAL_GREEN); + return true; + + default: + return false; + } +} + diff --git a/EXRAILMacros.h b/EXRAILMacros.h index e11b5db..3c58699 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -102,6 +102,14 @@ void exrailHalSetup() { #define LCCX(senderid,eventid) | FEATURE_LCC #undef ONLCC #define ONLCC(senderid,eventid) | FEATURE_LCC +#undef ROUTE_ACTIVE +#define ROUTE_ACTIVE(id) | FEATURE_ROUTESTATE +#undef ROUTE_INACTIVE +#define ROUTE_INACTIVE(id) | FEATURE_ROUTESTATE +#undef ROUTE_HIDDEN +#define ROUTE_HIDDEN(id) | FEATURE_ROUTESTATE +#undef ROUTE_CAPTION +#define ROUTE_CAPTION(id,caption) | FEATURE_ROUTESTATE const byte RMFT2::compileFeatures = 0 #include "myAutomation.h" @@ -156,7 +164,7 @@ const int StringMacroTracker1=__COUNTER__; #undef ROUTE_CAPTION #define ROUTE_CAPTION(id,caption) \ case (__COUNTER__ - StringMacroTracker1) : {\ - CommandDistributor::broadcastRouteCaption(id,F(caption));\ + manageRouteCaption(id,F(caption));\ return;\ } #undef SERIAL From 670645db4be574c27eb9d4d59b30490ac6ff7254 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 10 Nov 2023 19:28:29 +0000 Subject: [PATCH 166/218] Version 20 --- EXRAIL2.cpp | 1 - version.h | 3 ++- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 61e0264..bb672a5 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -1270,7 +1270,6 @@ void RMFT2::manageRouteCaption(uint16_t id,const FSH* caption) { // set state beside it if (routeCaptionArray[position]==caption) return; routeCaptionArray[position]=caption; - DIAG(F("rCA[%d]=%d, c=%d"),position,routeStateArray[position],caption); CommandDistributor::broadcastRouteCaption(id,caption); } } diff --git a/version.h b/version.h index 9d5a7e1..44975df 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.19" +#define VERSION "5.1.20" +// 5.1.20 - EXRAIL Tidy and ROUTE_STATE, ROUTE_CAPTION // 5.1.19 - Only flag 2.2.0.0-dev as broken, not 2.2.0.0 // 5.1.18 - TURNOUTL bugfix // 5.1.17 - Divide out C for config and D for diag commands From 337af77a030ca57f9bfa5e1343965fe4069aa7aa Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 10 Nov 2023 20:33:14 +0100 Subject: [PATCH 167/218] booster test --- MotorDriver.cpp | 4 ++++ MotorDriver.h | 38 ++++++++++++++++++++++++++++++++++++-- TrackManager.cpp | 19 +++++++++++++------ 3 files changed, 53 insertions(+), 8 deletions(-) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 61e229f..15b5607 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -605,6 +605,10 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { DIAG(F("TRACK %c ALERT FAULT"), trackno + 'A'); } setPower(POWERMODE::ALERT); + if (trackMode & (TRACK_MODE_MAIN|TRACK_MODE_EXT)){ // add (&& isAutoreverse) later + DIAG(F("TRACK %c INVERT"), trackno + 'A'); + invertOutput(); + } break; } // all well diff --git a/MotorDriver.h b/MotorDriver.h index 20a91d3..c6e06b8 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -148,7 +148,9 @@ class MotorDriver { // otherwise the call from interrupt context can undo whatever we do // from outside interrupt void setBrake( bool on, bool interruptContext=false); - __attribute__((always_inline)) inline void setSignal( bool high) { + __attribute__((always_inline)) inline void setSignal( bool high) { + if (invertPhase) + high = !high; if (trackPWM) { DCCTimer::setPWM(signalPin,high); } @@ -168,6 +170,12 @@ class MotorDriver { pinMode(signalPin, OUTPUT); else pinMode(signalPin, INPUT); + if (signalPin2 != UNUSED_PIN) { + if (on) + pinMode(signalPin2, OUTPUT); + else + pinMode(signalPin2, INPUT); + } }; inline pinpair getSignalPin() { return pinpair(signalPin,signalPin2); }; void setDCSignal(byte speedByte); @@ -232,6 +240,32 @@ class MotorDriver { #endif inline void setMode(TRACK_MODE m) { trackMode = m; + invertOutput(trackMode & TRACK_MODE_DCX);// change later to TRACK_MODE_INVERTED? + }; + inline void invertOutput() { // toggles output inversion + invertPhase = !invertPhase; + invertOutput(invertPhase); + }; + inline void invertOutput(bool b) { // sets output inverted or not + if (b) + invertPhase = 1; + else + invertPhase = 0; +#if defined(ARDUINO_ARCH_ESP32) + pinpair p = getSignalPin(); + uint32_t *outreg = (uint32_t *)(GPIO_FUNC0_OUT_SEL_CFG_REG + 4*p.pin); + if (invertPhase) // set or clear the invert bit in the gpio out register + *outreg |= ((uint32_t)0x1 << GPIO_FUNC0_OUT_INV_SEL_S); + else + *outreg &= ~((uint32_t)0x1 << GPIO_FUNC0_OUT_INV_SEL_S); + if (p.invpin != UNUSED_PIN) { + outreg = (uint32_t *)(GPIO_FUNC0_OUT_SEL_CFG_REG + 4*p.invpin); + if (invertPhase) // clear or set the invert bit in the gpio out register + *outreg &= ~((uint32_t)0x1 << GPIO_FUNC0_OUT_INV_SEL_S); + else + *outreg |= ((uint32_t)0x1 << GPIO_FUNC0_OUT_INV_SEL_S); + } +#endif }; inline TRACK_MODE getMode() { return trackMode; @@ -263,7 +297,7 @@ class MotorDriver { bool invertBrake; // brake pin passed as negative means pin is inverted bool invertPower; // power pin passed as negative means pin is inverted bool invertFault; // fault pin passed as negative means pin is inverted - + bool invertPhase = 0; // phase of out pin is inverted // Raw to milliamp conversion factors avoiding float data types. // Milliamps=rawADCreading * sensefactorInternal / senseScale // diff --git a/TrackManager.cpp b/TrackManager.cpp index dedf45e..772f64e 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -197,8 +197,8 @@ void TrackManager::setPROGSignal( bool on) { void TrackManager::setDCSignal(int16_t cab, byte speedbyte) { FOR_EACH_TRACK(t) { if (trackDCAddr[t]!=cab && cab != 0) continue; - if (track[t]->getMode()==TRACK_MODE_DC) track[t]->setDCSignal(speedbyte); - else if (track[t]->getMode()==TRACK_MODE_DCX) track[t]->setDCSignal(speedbyte ^ 128); + if (track[t]->getMode() & (TRACK_MODE_DC|TRACK_MODE_DCX)) + track[t]->setDCSignal(speedbyte); } } @@ -223,11 +223,18 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr pinpair p = track[trackToSet]->getSignalPin(); //DIAG(F("Track=%c remove pin %d"),trackToSet+'A', p.pin); gpio_reset_pin((gpio_num_t)p.pin); - pinMode(p.pin, OUTPUT); // gpio_reset_pin may reset to input if (p.invpin != UNUSED_PIN) { //DIAG(F("Track=%c remove ^pin %d"),trackToSet+'A', p.invpin); gpio_reset_pin((gpio_num_t)p.invpin); - pinMode(p.invpin, OUTPUT); // gpio_reset_pin may reset to input + } + + if (mode == TRACK_MODE_EXT) { + pinMode(26, INPUT); + gpio_matrix_in(26, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback + gpio_matrix_out(p.pin, SIG_IN_FUNC228_IDX, false, false); + if (p.invpin != UNUSED_PIN) { + gpio_matrix_out(p.invpin, SIG_IN_FUNC228_IDX, true /*inverted*/, false); + } } #endif #ifndef DISABLE_PROG @@ -261,10 +268,12 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr track[trackToSet]->setBrake(false); } +#ifndef ARDUINO_ARCH_ESP32 // EXT is a special case where the signal pin is // turned off. So unless that is set, the signal // pin should be turned on track[trackToSet]->enableSignal(mode != TRACK_MODE_EXT); +#endif #ifndef ARDUINO_ARCH_ESP32 // re-evaluate HighAccuracy mode @@ -320,8 +329,6 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr void TrackManager::applyDCSpeed(byte t) { uint8_t speedByte=DCC::getThrottleSpeedByte(trackDCAddr[t]); - if (track[t]->getMode()==TRACK_MODE_DCX) - speedByte = speedByte ^ 128; // reverse direction bit track[t]->setDCSignal(speedByte); } From d2d7a5cd1606b41830c814a047245204ff724d3d Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 10 Nov 2023 20:13:33 +0000 Subject: [PATCH 168/218] EXRAIL multiple ON events --- EXRAIL2.cpp | 37 +++++++++++++++++++------------------ EXRAIL2.h | 7 ++++--- version.h | 3 ++- 3 files changed, 25 insertions(+), 22 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index bb672a5..6a9ee40 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -126,6 +126,13 @@ int16_t LookList::find(int16_t value) { void LookList::chain(LookList * chain) { m_chain=chain; } +void LookList::handleEvent(const FSH* reason,int16_t id) { + // New feature... create multiple ONhandlers + for (int i=0;ihandleEvent(F("RED"),id); + else if (rag==SIGNAL_GREEN) onGreenLookup->handleEvent(F("GREEN"),id); + else onAmberLookup->handleEvent(F("AMBER"),id); int16_t sigslot=getSignalSlot(id); if (sigslot<0) return; @@ -1084,26 +1091,26 @@ int16_t RMFT2::getSignalSlot(int16_t id) { void RMFT2::turnoutEvent(int16_t turnoutId, bool closed) { // Hunt for an ONTHROW/ONCLOSE for this turnout - if (closed) handleEvent(F("CLOSE"),onCloseLookup,turnoutId); - else handleEvent(F("THROW"),onThrowLookup,turnoutId); + if (closed) onCloseLookup->handleEvent(F("CLOSE"),turnoutId); + else onThrowLookup->handleEvent(F("THROW"),turnoutId); } void RMFT2::activateEvent(int16_t addr, bool activate) { // Hunt for an ONACTIVATE/ONDEACTIVATE for this accessory - if (activate) handleEvent(F("ACTIVATE"),onActivateLookup,addr); - else handleEvent(F("DEACTIVATE"),onDeactivateLookup,addr); + if (activate) onActivateLookup->handleEvent(F("ACTIVATE"),addr); + else onDeactivateLookup->handleEvent(F("DEACTIVATE"),addr); } void RMFT2::changeEvent(int16_t vpin, bool change) { // Hunt for an ONCHANGE for this sensor - if (change) handleEvent(F("CHANGE"),onChangeLookup,vpin); + if (change) onChangeLookup->handleEvent(F("CHANGE"),vpin); } #ifndef IO_NO_HAL void RMFT2::rotateEvent(int16_t turntableId, bool change) { // Hunt or an ONROTATE for this turntable - if (change) handleEvent(F("ROTATE"),onRotateLookup,turntableId); + if (change) onRotateLookup->handleEvent(F("ROTATE"),turntableId); } #endif @@ -1112,8 +1119,8 @@ void RMFT2::clockEvent(int16_t clocktime, bool change) { if (Diag::CMD) DIAG(F("Looking for clock event at : %d"), clocktime); if (change) { - handleEvent(F("CLOCK"),onClockLookup,clocktime); - handleEvent(F("CLOCK"),onClockLookup,25*60+clocktime%60); + onClockLookup->handleEvent(F("CLOCK"),clocktime); + onClockLookup->handleEvent(F("CLOCK"),25*60+clocktime%60); } } @@ -1122,16 +1129,10 @@ void RMFT2::powerEvent(int16_t track, bool overload) { if (Diag::CMD) DIAG(F("Looking for Power event on track : %c"), track); if (overload) { - handleEvent(F("POWER"),onOverloadLookup,track); + onOverloadLookup->handleEvent(F("POWER"),track); } } - -void RMFT2::handleEvent(const FSH* reason,LookList* handlers, int16_t id) { - int pc= handlers->find(id); - if (pc>=0) startNonRecursiveTask(reason,id,pc); -} - void RMFT2::startNonRecursiveTask(const FSH* reason, int16_t id,int pc) { // Check we dont already have a task running this handler RMFT2 * task=loopTask; diff --git a/EXRAIL2.h b/EXRAIL2.h index bcb517e..a4c0e5f 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -127,6 +127,8 @@ class LookList { int16_t findPosition(int16_t value); // finds index int16_t size(); void stream(Print * _stream); + void handleEvent(const FSH* reason,int16_t id); + private: int16_t m_size; int16_t m_loaded; @@ -166,7 +168,8 @@ class LookList { static const FSH * getRosterFunctions(int16_t id); static const FSH * getTurntableDescription(int16_t id); static const FSH * getTurntablePositionDescription(int16_t turntableId, uint8_t positionId); - + static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc); + private: static void ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16_t p[]); static bool parseSlash(Print * stream, byte & paramCount, int16_t p[]) ; @@ -183,9 +186,7 @@ private: #endif static LookList* LookListLoader(OPCODE op1, OPCODE op2=OPCODE_ENDEXRAIL,OPCODE op3=OPCODE_ENDEXRAIL); - static void handleEvent(const FSH* reason,LookList* handlers, int16_t id); static uint16_t getOperand(int progCounter,byte n); - static void startNonRecursiveTask(const FSH* reason, int16_t id,int pc); static RMFT2 * loopTask; static RMFT2 * pausingTask; void delayMe(long millisecs); diff --git a/version.h b/version.h index 44975df..242d730 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.20" +#define VERSION "5.1.21" +// 5.1.21 - EXRAIL invoke multiple ON handlers for same event // 5.1.20 - EXRAIL Tidy and ROUTE_STATE, ROUTE_CAPTION // 5.1.19 - Only flag 2.2.0.0-dev as broken, not 2.2.0.0 // 5.1.18 - TURNOUTL bugfix From 2f3d489f1836d5ed1f6650b65bafc7d7a104ac54 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Fri, 10 Nov 2023 23:58:30 +0100 Subject: [PATCH 169/218] ESP32: autoreverse and booster prototype --- EXRAIL2.cpp | 2 +- MotorDriver.cpp | 2 +- MotorDriver.h | 12 +++- TrackManager.cpp | 162 ++++++++++++++++++++++++++--------------------- TrackManager.h | 2 +- 5 files changed, 102 insertions(+), 78 deletions(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index c902708..2e9761a 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -857,7 +857,7 @@ void RMFT2::loop2() { // If DC/DCX use my loco for DC address { TRACK_MODE mode = (TRACK_MODE)(operand>>8); - int16_t cab=(mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX) ? loco : 0; + int16_t cab=(mode & TRACK_MODE_DC) ? loco : 0; TrackManager::setTrackMode(operand & 0x0F, mode, cab); } break; diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 15b5607..c9b820e 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -605,7 +605,7 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { DIAG(F("TRACK %c ALERT FAULT"), trackno + 'A'); } setPower(POWERMODE::ALERT); - if (trackMode & (TRACK_MODE_MAIN|TRACK_MODE_EXT)){ // add (&& isAutoreverse) later + if ((trackMode & TRACK_MODE_AUTOINV) && (trackMode & (TRACK_MODE_MAIN|TRACK_MODE_EXT|TRACK_MODE_BOOST))){ DIAG(F("TRACK %c INVERT"), trackno + 'A'); invertOutput(); } diff --git a/MotorDriver.h b/MotorDriver.h index c6e06b8..db2ccc5 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -3,7 +3,7 @@ * © 2021 Mike S * © 2021 Fred Decker * © 2020 Chris Harlow - * © 2022 Harald Barth + * © 2022,2023 Harald Barth * All rights reserved. * * This file is part of CommandStation-EX @@ -28,8 +28,14 @@ #include "DCCTimer.h" // use powers of two so we can do logical and/or on the track modes in if clauses. +// RACK_MODE_DCX is (TRACK_MODE_DC|TRACK_MODE_INV) +template inline T operator~ (T a) { return (T)~(int)a; } +template inline T operator| (T a, T b) { return (T)((int)a | (int)b); } +template inline T operator& (T a, T b) { return (T)((int)a & (int)b); } +template inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); } enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4, - TRACK_MODE_DC = 8, TRACK_MODE_DCX = 16, TRACK_MODE_EXT = 32}; + TRACK_MODE_DC = 8, TRACK_MODE_EXT = 16, TRACK_MODE_BOOST = 32, + TRACK_MODE_INV = 64, TRACK_MODE_DCX = 72, TRACK_MODE_AUTOINV = 128}; #define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH #define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW @@ -240,7 +246,7 @@ class MotorDriver { #endif inline void setMode(TRACK_MODE m) { trackMode = m; - invertOutput(trackMode & TRACK_MODE_DCX);// change later to TRACK_MODE_INVERTED? + invertOutput(trackMode & TRACK_MODE_INV); }; inline void invertOutput() { // toggles output inversion invertPhase = !invertPhase; diff --git a/TrackManager.cpp b/TrackManager.cpp index 772f64e..1fffb0b 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -1,6 +1,6 @@ /* * © 2022 Chris Harlow - * © 2022 Harald Barth + * © 2022,2023 Harald Barth * © 2023 Colin Murdoch * All rights reserved. * @@ -45,6 +45,9 @@ const int16_t HASH_KEYWORD_DC = 2183; const int16_t HASH_KEYWORD_DCX = 6463; // DC reversed polarity const int16_t HASH_KEYWORD_EXT = 8201; // External DCC signal const int16_t HASH_KEYWORD_A = 65; // parser makes single chars the ascii. +const int16_t HASH_KEYWORD_AUTO = -5457; +const int16_t HASH_KEYWORD_BOOST = 11269; +const int16_t HASH_KEYWORD_INV = 11857; MotorDriver * TrackManager::track[MAX_TRACKS]; int16_t TrackManager::trackDCAddr[MAX_TRACKS]; @@ -87,7 +90,7 @@ void TrackManager::sampleCurrent() { if (!waiting) { // look for a valid track to sample or until we are around while (true) { - if (track[tr]->getMode() & ( TRACK_MODE_MAIN|TRACK_MODE_PROG|TRACK_MODE_DC|TRACK_MODE_DCX|TRACK_MODE_EXT )) { + if (track[tr]->getMode() & ( TRACK_MODE_MAIN|TRACK_MODE_PROG|TRACK_MODE_DC|TRACK_MODE_BOOST|TRACK_MODE_EXT )) { track[tr]->startCurrentFromHW(); // for scope debug track[1]->setBrake(1); waiting = true; @@ -197,7 +200,7 @@ void TrackManager::setPROGSignal( bool on) { void TrackManager::setDCSignal(int16_t cab, byte speedbyte) { FOR_EACH_TRACK(t) { if (trackDCAddr[t]!=cab && cab != 0) continue; - if (track[t]->getMode() & (TRACK_MODE_DC|TRACK_MODE_DCX)) + if (track[t]->getMode() & TRACK_MODE_DC) track[t]->setDCSignal(speedbyte); } } @@ -207,7 +210,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr //DIAG(F("Track=%c Mode=%d"),trackToSet+'A', mode); // DC tracks require a motorDriver that can set brake! - if (mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX) { + if (mode & TRACK_MODE_DC) { #if defined(ARDUINO_AVR_UNO) DIAG(F("Uno has no PWM timers available for DC")); return false; @@ -227,24 +230,30 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr //DIAG(F("Track=%c remove ^pin %d"),trackToSet+'A', p.invpin); gpio_reset_pin((gpio_num_t)p.invpin); } - - if (mode == TRACK_MODE_EXT) { - pinMode(26, INPUT); + if (mode & TRACK_MODE_BOOST) { + DIAG(F("Track=%c mode boost pin %d"),trackToSet+'A', p.pin); + pinMode(26, INPUT); // hardcoded XXX gpio_matrix_in(26, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback gpio_matrix_out(p.pin, SIG_IN_FUNC228_IDX, false, false); if (p.invpin != UNUSED_PIN) { gpio_matrix_out(p.invpin, SIG_IN_FUNC228_IDX, true /*inverted*/, false); } + } else if (mode & (TRACK_MODE_MAIN | TRACK_MODE_PROG | TRACK_MODE_DC)) { + // gpio_reset_pin may reset to input + pinMode(p.pin, OUTPUT); + if (p.invpin != UNUSED_PIN) + pinMode(p.invpin, OUTPUT); } + #endif #ifndef DISABLE_PROG - if (mode==TRACK_MODE_PROG) { + if (mode & TRACK_MODE_PROG) { #else if (false) { #endif // only allow 1 track to be prog FOR_EACH_TRACK(t) - if (track[t]->getMode()==TRACK_MODE_PROG && t != trackToSet) { + if ( (track[t]->getMode() & TRACK_MODE_PROG) && t != trackToSet) { track[t]->setPower(POWERMODE::OFF); track[t]->setMode(TRACK_MODE_NONE); track[t]->makeProgTrack(false); // revoke prog track special handling @@ -262,18 +271,20 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr // state, otherwise trains run away or just dont move. // This can be done BEFORE the PWM-Timer evaluation (methinks) - if (!(mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX)) { + if (!(mode & TRACK_MODE_DC)) { // DCC tracks need to have set the PWM to zero or they will not work. track[trackToSet]->detachDCSignal(); track[trackToSet]->setBrake(false); } -#ifndef ARDUINO_ARCH_ESP32 - // EXT is a special case where the signal pin is - // turned off. So unless that is set, the signal - // pin should be turned on - track[trackToSet]->enableSignal(mode != TRACK_MODE_EXT); -#endif + // BOOST: + // Leave it as is + // otherwise: + // EXT is a special case where the signal pin is + // turned off. So unless that is set, the signal + // pin should be turned on + if (!(mode & TRACK_MODE_BOOST)) + track[trackToSet]->enableSignal(!(mode & TRACK_MODE_EXT)); #ifndef ARDUINO_ARCH_ESP32 // re-evaluate HighAccuracy mode @@ -283,7 +294,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr // DC tracks must not have the DCC PWM switched on // so we globally turn it off if one of the PWM // capable tracks is now DC or DCX. - if (track[t]->getMode()==TRACK_MODE_DC || track[t]->getMode()==TRACK_MODE_DCX) { + if (track[t]->getMode() & TRACK_MODE_DC) { if (track[t]->isPWMCapable()) { canDo=false; // this track is capable but can not run PWM break; // in this mode, so abort and prevent globally below @@ -291,7 +302,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr track[t]->trackPWM=false; // this track sure can not run with PWM //DIAG(F("Track %c trackPWM 0 (not capable)"), t+'A'); } - } else if (track[t]->getMode()==TRACK_MODE_MAIN || track[t]->getMode()==TRACK_MODE_PROG) { + } else if (track[t]->getMode() & (TRACK_MODE_MAIN |TRACK_MODE_PROG)) { track[t]->trackPWM = track[t]->isPWMCapable(); // trackPWM is still a guess here //DIAG(F("Track %c trackPWM %d"), t+'A', track[t]->trackPWM); canDo &= track[t]->trackPWM; @@ -309,10 +320,12 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr #else // For ESP32 we just reinitialize the DCC Waveform DCCWaveform::begin(); + // setMode() again AFTER Waveform::begin() of ESP32 fixes INVERTED signal + track[trackToSet]->setMode(mode); #endif // This block must be AFTER the PWM-Timer modifications - if (mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX) { + if (mode & TRACK_MODE_DC) { // DC tracks need to be given speed of the throttle for that cab address // otherwise will not match other tracks on same cab. // This also needs to allow for inverted DCX @@ -321,7 +334,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr // Normal running tracks are set to the global power state track[trackToSet]->setPower( - (mode==TRACK_MODE_MAIN || mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX || mode==TRACK_MODE_EXT) ? + (mode & (TRACK_MODE_MAIN | TRACK_MODE_DC | TRACK_MODE_EXT | TRACK_MODE_BOOST)) ? mainPowerGuess : POWERMODE::OFF); //DIAG(F("TrackMode=%d"),mode); return true; @@ -361,11 +374,20 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) if (params==2 && p[1]==HASH_KEYWORD_EXT) // <= id EXT> return setTrackMode(p[0],TRACK_MODE_EXT); + if (params==2 && p[1]==HASH_KEYWORD_BOOST) // <= id BOOST> + return setTrackMode(p[0],TRACK_MODE_BOOST); + + if (params==2 && p[1]==HASH_KEYWORD_AUTO) // <= id AUTO> + return setTrackMode(p[0], track[p[0]]->getMode() | TRACK_MODE_AUTOINV); + + if (params==2 && p[1]==HASH_KEYWORD_INV) // <= id AUTO> + return setTrackMode(p[0], track[p[0]]->getMode() | TRACK_MODE_INV); + if (params==3 && p[1]==HASH_KEYWORD_DC && p[2]>0) // <= id DC cab> return setTrackMode(p[0],TRACK_MODE_DC,p[2]); if (params==3 && p[1]==HASH_KEYWORD_DCX && p[2]>0) // <= id DCX cab> - return setTrackMode(p[0],TRACK_MODE_DCX,p[2]); + return setTrackMode(p[0],TRACK_MODE_DC|TRACK_MODE_INV,p[2]); return false; } @@ -374,35 +396,38 @@ void TrackManager::streamTrackState(Print* stream, byte t) { // null stream means send to commandDistributor for broadcast if (track[t]==NULL) return; auto format=F(""); - bool pstate = TrackManager::isPowerOn(t); - - switch(track[t]->getMode()) { - case TRACK_MODE_MAIN: - if (pstate) {format=F("<= %c MAIN ON>\n");} else {format = F("<= %c MAIN OFF>\n");} - break; +// bool pstate = TrackManager::isPowerOn(t); +// char *statestr; +// if (pstate) +// statestr = (char *)"ON"; +// else +// statestr = (char *)"OFF"; + TRACK_MODE tm = track[t]->getMode(); + if (tm & TRACK_MODE_MAIN) + format=F("<= %c MAIN>\n"); #ifndef DISABLE_PROG - case TRACK_MODE_PROG: - if (pstate) {format=F("<= %c PROG ON>\n");} else {format=F("<= %c PROG OFF>\n");} - break; + else if (tm & TRACK_MODE_PROG) + format=F("<= %c PROG>\n"); #endif - case TRACK_MODE_NONE: - if (pstate) {format=F("<= %c NONE ON>\n");} else {format=F("<= %c NONE OFF>\n");} - break; - case TRACK_MODE_EXT: - if (pstate) {format=F("<= %c EXT ON>\n");} else {format=F("<= %c EXT OFF>\n");} - break; - case TRACK_MODE_DC: - if (pstate) {format=F("<= %c DC %d ON>\n");} else {format=F("<= %c DC %d OFF>\n");} - break; - case TRACK_MODE_DCX: - if (pstate) {format=F("<= %c DCX %d ON>\n");} else {format=F("<= %c DCX %d OFF>\n");} - break; - default: - break; // unknown, dont care + else if (tm & TRACK_MODE_NONE) + format=F("<= %c NONE>\n"); + else if(tm & TRACK_MODE_EXT) + format=F("<= %c EXT>\n"); + else if(tm & TRACK_MODE_BOOST) + format=F("<= %c BOOST>\n"); + else if (tm & TRACK_MODE_DC) { + if (tm & TRACK_MODE_INV) + format=F("<= %c DCX>\n"); + else + format=F("<= %c DC>\n"); } + else + format=F("<= %c XXX>\n"); - if (stream) StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); - else CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); + if (stream) + StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); + else + CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); } @@ -418,13 +443,13 @@ void TrackManager::loop() { if (nextCycleTrack>lastTrack) nextCycleTrack=0; if (track[nextCycleTrack]==NULL) return; MotorDriver * motorDriver=track[nextCycleTrack]; - bool useProgLimit=dontLimitProg? false: track[nextCycleTrack]->getMode()==TRACK_MODE_PROG; + bool useProgLimit=dontLimitProg ? false : (bool)(track[nextCycleTrack]->getMode() & TRACK_MODE_PROG); motorDriver->checkPowerOverload(useProgLimit, nextCycleTrack); } MotorDriver * TrackManager::getProgDriver() { FOR_EACH_TRACK(t) - if (track[t]->getMode()==TRACK_MODE_PROG) return track[t]; + if (track[t]->getMode() & TRACK_MODE_PROG) return track[t]; return NULL; } @@ -432,7 +457,7 @@ MotorDriver * TrackManager::getProgDriver() { std::vectorTrackManager::getMainDrivers() { std::vector v; FOR_EACH_TRACK(t) - if (track[t]->getMode()==TRACK_MODE_MAIN) v.push_back(track[t]); + if (track[t]->getMode() & TRACK_MODE_MAIN) v.push_back(track[t]); return v; } #endif @@ -453,40 +478,33 @@ void TrackManager::setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byt MotorDriver * driver=track[thistrack]; if (!driver) return; - switch (track[thistrack]->getMode()) { - case TRACK_MODE_MAIN: - if (setProg) break; + TRACK_MODE tm = track[thistrack]->getMode(); + if ( (tm & TRACK_MODE_MAIN) + && !setProg ){ // toggle brake before turning power on - resets overcurrent error // on the Pololu board if brake is wired to ^D2. // XXX see if we can make this conditional driver->setBrake(true); driver->setBrake(false); // DCC runs with brake off - driver->setPower(mode); - break; - case TRACK_MODE_DC: - case TRACK_MODE_DCX: - //DIAG(F("Processing track - %d setProg %d"), thistrack, setProg); - if (setProg || setJoin) break; + driver->setPower(mode); + } else if ( (tm & TRACK_MODE_DC) + && !(setProg || setJoin)){ + //DIAG(F("Processing track - %d setProg %d"), thistrack, setProg); driver->setBrake(true); // DC starts with brake on applyDCSpeed(thistrack); // speed match DCC throttles driver->setPower(mode); - break; - case TRACK_MODE_PROG: - if (!setProg && !setJoin) break; + } else if ( (tm & TRACK_MODE_PROG) + && (setProg || setJoin) ){ driver->setBrake(true); driver->setBrake(false); driver->setPower(mode); - break; - case TRACK_MODE_EXT: + } else if ( (tm & TRACK_MODE_EXT) + || (tm & TRACK_MODE_BOOST)){ driver->setBrake(true); driver->setBrake(false); driver->setPower(mode); - break; - case TRACK_MODE_NONE: - break; - } - - } + } +} void TrackManager::reportPowerChange(Print* stream, byte thistrack) { // This function is for backward JMRI compatibility only @@ -499,7 +517,7 @@ void TrackManager::setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byt POWERMODE TrackManager::getProgPower() { FOR_EACH_TRACK(t) - if (track[t]->getMode()==TRACK_MODE_PROG) + if (track[t]->getMode() & TRACK_MODE_PROG) return track[t]->getPower(); return POWERMODE::OFF; } @@ -544,7 +562,7 @@ void TrackManager::setJoin(bool joined) { #ifdef ARDUINO_ARCH_ESP32 if (joined) { FOR_EACH_TRACK(t) { - if (track[t]->getMode()==TRACK_MODE_PROG) { + if (track[t]->getMode() & TRACK_MODE_PROG) { tempProgTrack = t; setTrackMode(t, TRACK_MODE_MAIN); break; @@ -573,7 +591,7 @@ bool TrackManager::isPowerOn(byte t) { } bool TrackManager::isProg(byte t) { - if (track[t]->getMode()==TRACK_MODE_PROG) + if (track[t]->getMode() & TRACK_MODE_PROG) return true; return false; } diff --git a/TrackManager.h b/TrackManager.h index d197751..d1edca2 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -112,7 +112,7 @@ class TrackManager { static POWERMODE mainPowerGuess; static void applyDCSpeed(byte t); - static int16_t trackDCAddr[MAX_TRACKS]; // dc address if TRACK_MODE_DC or TRACK_MODE_DCX + static int16_t trackDCAddr[MAX_TRACKS]; // dc address if TRACK_MODE_DC #ifdef ARDUINO_ARCH_ESP32 static byte tempProgTrack; // holds the prog track number during join #endif From 1c5f299b0e225e36c799dcc08c6bbb08c97f4adb Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 10 Nov 2023 23:28:41 +0000 Subject: [PATCH 170/218] Fix ESP32 cast issue --- EXRAIL2.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 6a9ee40..9efd98b 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -332,7 +332,7 @@ void RMFT2::setTurntableHiddenState(Turntable * tto) { char RMFT2::getRouteType(int16_t id) { int16_t progCounter=routeLookup->find(id); if (progCounter>=0) { - OPCODE type=GET_OPCODE; + byte type=GET_OPCODE; if (type==OPCODE_ROUTE) return 'R'; if (type==OPCODE_AUTOMATION) return 'A'; } From 6c18226cb592f3244cc314d2a7d4ae280c4d2475 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 10 Nov 2023 23:46:17 +0000 Subject: [PATCH 171/218] Fix non-exrail crash --- MotorDriver.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MotorDriver.cpp b/MotorDriver.cpp index 61e229f..af322b8 100644 --- a/MotorDriver.cpp +++ b/MotorDriver.cpp @@ -676,8 +676,10 @@ void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) { power_sample_overload_wait *= 2; if (power_sample_overload_wait > POWER_SAMPLE_RETRY_MAX) power_sample_overload_wait = POWER_SAMPLE_RETRY_MAX; + #ifdef EXRAIL_ACTIVE DIAG(F("Calling EXRAIL")); RMFT2::powerEvent(trackno, true); // Tell EXRAIL we have an overload + #endif // power on test DIAG(F("TRACK %c POWER RESTORE (after %4M)"), trackno + 'A', mslpc); setPower(POWERMODE::ALERT); From d877fc315ee1ddaedaf463ae883041974f63d36f Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 10 Nov 2023 23:56:41 +0000 Subject: [PATCH 172/218] Fix non-routestate code eliminator --- EXRAIL2Parser.cpp | 20 +++++++++++--------- 1 file changed, 11 insertions(+), 9 deletions(-) diff --git a/EXRAIL2Parser.cpp b/EXRAIL2Parser.cpp index 7670118..7b241d0 100644 --- a/EXRAIL2Parser.cpp +++ b/EXRAIL2Parser.cpp @@ -122,7 +122,7 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16 case 'J': // throttle info commands // This entire code block is compiled out if FEATURE_ROUTESTATE macros not used - if (paramCount<1 || !(compileFeatures & FEATURE_ROUTESTATE)) return; + if (paramCount<1) return; switch(p[0]) case HASH_KEYWORD_A: // returns automations/routes if (paramCount==1) {// @@ -136,14 +136,16 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16 uint16_t id=p[1]; StringFormatter::send(stream,F("\n"), id, getRouteType(id), getRouteDescription(id)); - - // Send any non-default button states or captions - int16_t statePos=routeLookup->findPosition(id); - if (statePos>=0) { - if (routeStateArray[statePos]) - StringFormatter::send(stream,F("\n"), id, routeStateArray[statePos]); - if (routeCaptionArray[statePos]) - StringFormatter::send(stream,F("\n"), id,routeCaptionArray[statePos]); + + if (compileFeatures & FEATURE_ROUTESTATE) { + // Send any non-default button states or captions + int16_t statePos=routeLookup->findPosition(id); + if (statePos>=0) { + if (routeStateArray[statePos]) + StringFormatter::send(stream,F("\n"), id, routeStateArray[statePos]); + if (routeCaptionArray[statePos]) + StringFormatter::send(stream,F("\n"), id,routeCaptionArray[statePos]); + } } opcode=0; return; From 9ce95c07aaeda5ae710455368245fb1199783e1e Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 11 Nov 2023 08:03:59 +0100 Subject: [PATCH 173/218] Booster mode configured by defined booster pin. New mode name output --- TrackManager.cpp | 39 ++++++++++++++++++++++++++++----------- config.example.h | 6 ++++++ 2 files changed, 34 insertions(+), 11 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index 1fffb0b..3b380f1 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -46,7 +46,9 @@ const int16_t HASH_KEYWORD_DCX = 6463; // DC reversed polarity const int16_t HASH_KEYWORD_EXT = 8201; // External DCC signal const int16_t HASH_KEYWORD_A = 65; // parser makes single chars the ascii. const int16_t HASH_KEYWORD_AUTO = -5457; +#ifdef BOOSTER_INPUT const int16_t HASH_KEYWORD_BOOST = 11269; +#endif const int16_t HASH_KEYWORD_INV = 11857; MotorDriver * TrackManager::track[MAX_TRACKS]; @@ -230,15 +232,18 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr //DIAG(F("Track=%c remove ^pin %d"),trackToSet+'A', p.invpin); gpio_reset_pin((gpio_num_t)p.invpin); } +#ifdef BOOSTER_INPUT if (mode & TRACK_MODE_BOOST) { - DIAG(F("Track=%c mode boost pin %d"),trackToSet+'A', p.pin); - pinMode(26, INPUT); // hardcoded XXX + //DIAG(F("Track=%c mode boost pin %d"),trackToSet+'A', p.pin); + pinMode(BOOSTER_INPUT, INPUT); gpio_matrix_in(26, SIG_IN_FUNC228_IDX, false); //pads 224 to 228 available as loopback gpio_matrix_out(p.pin, SIG_IN_FUNC228_IDX, false, false); if (p.invpin != UNUSED_PIN) { gpio_matrix_out(p.invpin, SIG_IN_FUNC228_IDX, true /*inverted*/, false); } - } else if (mode & (TRACK_MODE_MAIN | TRACK_MODE_PROG | TRACK_MODE_DC)) { + } else // elseif clause continues +#endif + if (mode & (TRACK_MODE_MAIN | TRACK_MODE_PROG | TRACK_MODE_DC)) { // gpio_reset_pin may reset to input pinMode(p.pin, OUTPUT); if (p.invpin != UNUSED_PIN) @@ -373,10 +378,10 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) if (params==2 && p[1]==HASH_KEYWORD_EXT) // <= id EXT> return setTrackMode(p[0],TRACK_MODE_EXT); - +#ifdef BOOSTER_INPUT if (params==2 && p[1]==HASH_KEYWORD_BOOST) // <= id BOOST> return setTrackMode(p[0],TRACK_MODE_BOOST); - +#endif if (params==2 && p[1]==HASH_KEYWORD_AUTO) // <= id AUTO> return setTrackMode(p[0], track[p[0]]->getMode() | TRACK_MODE_AUTOINV); @@ -403,8 +408,14 @@ void TrackManager::streamTrackState(Print* stream, byte t) { // else // statestr = (char *)"OFF"; TRACK_MODE tm = track[t]->getMode(); - if (tm & TRACK_MODE_MAIN) - format=F("<= %c MAIN>\n"); + if (tm & TRACK_MODE_MAIN) { + if(tm & TRACK_MODE_AUTOINV) + format=F("<= %c MAIN AUTOINV>\n"); + else if (tm & TRACK_MODE_INV) + format=F("<= %c MAIN INV>\n"); + else + format=F("<= %c MAIN>\n"); + } #ifndef DISABLE_PROG else if (tm & TRACK_MODE_PROG) format=F("<= %c PROG>\n"); @@ -413,13 +424,19 @@ void TrackManager::streamTrackState(Print* stream, byte t) { format=F("<= %c NONE>\n"); else if(tm & TRACK_MODE_EXT) format=F("<= %c EXT>\n"); - else if(tm & TRACK_MODE_BOOST) - format=F("<= %c BOOST>\n"); + else if(tm & TRACK_MODE_BOOST) { + if(tm & TRACK_MODE_AUTOINV) + format=F("<= %c BOOST AUTOINV>\n"); + else if (tm & TRACK_MODE_INV) + format=F("<= %c BOOST INV>\n"); + else + format=F("<= %c BOOST>\n"); + } else if (tm & TRACK_MODE_DC) { if (tm & TRACK_MODE_INV) - format=F("<= %c DCX>\n"); + format=F("<= %c DCX %d>\n"); else - format=F("<= %c DC>\n"); + format=F("<= %c DC %d>\n"); } else format=F("<= %c XXX>\n"); diff --git a/config.example.h b/config.example.h index 0f136f9..de31743 100644 --- a/config.example.h +++ b/config.example.h @@ -266,6 +266,12 @@ The configuration file for DCC-EX Command Station // //#define SERIAL_BT_COMMANDS +// BOOSTER PIN INPUT ON ESP32 +// On ESP32 you have the possibility to define a pin as booster input +// Arduio pin D2 is GPIO 26 on ESPDuino32 +// +//#define BOOSTER_INPUT 26 + // SABERTOOTH // // This is a very special option and only useful if you happen to have a From befcfebec7d2d6021389e37b714ae3f6baffe97d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 11 Nov 2023 08:15:15 +0100 Subject: [PATCH 174/218] version 5.2.0 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index d2a7fd1..867619e 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202310230944Z" +#define GITHUB_SHA "devel-202311110712Z" diff --git a/version.h b/version.h index 242d730..4258cf4 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.1.21" +#define VERSION "5.2.0" +// 5.2.0 - ESP32: Autoreverse and booster mode support // 5.1.21 - EXRAIL invoke multiple ON handlers for same event // 5.1.20 - EXRAIL Tidy and ROUTE_STATE, ROUTE_CAPTION // 5.1.19 - Only flag 2.2.0.0-dev as broken, not 2.2.0.0 From e8b9f80c8cd232cf1736c8740c7a534b776abf03 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Sat, 11 Nov 2023 09:45:28 +0100 Subject: [PATCH 175/218] Reformat reply to <=> --- CommandDistributor.cpp | 4 ++-- CommandDistributor.h | 2 +- TrackManager.cpp | 35 +++++++++++++++-------------------- 3 files changed, 18 insertions(+), 23 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 351a18d..653bbef 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -269,8 +269,8 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) { broadcastReply(type, F("%s"),msg); } -void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr) { - broadcastReply(COMMAND_TYPE, format,trackLetter, dcAddr); +void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, byte pstate, int16_t dcAddr) { + broadcastReply(COMMAND_TYPE, format,trackLetter, pstate, dcAddr); } void CommandDistributor::broadcastRouteState(uint16_t routeId, byte state ) { diff --git a/CommandDistributor.h b/CommandDistributor.h index 83bfbbd..7dfcdf6 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -55,7 +55,7 @@ public : static int16_t retClockTime(); static void broadcastPower(); static void broadcastRaw(clientType type,char * msg); - static void broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr); + static void broadcastTrackState(const FSH* format,byte trackLetter, byte pstate, int16_t dcAddr); template static void broadcastReply(clientType type, Targs... msg); static void forget(byte clientId); static void broadcastRouteState(uint16_t routeId,byte state); diff --git a/TrackManager.cpp b/TrackManager.cpp index 3b380f1..647f07c 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -401,50 +401,45 @@ void TrackManager::streamTrackState(Print* stream, byte t) { // null stream means send to commandDistributor for broadcast if (track[t]==NULL) return; auto format=F(""); -// bool pstate = TrackManager::isPowerOn(t); -// char *statestr; -// if (pstate) -// statestr = (char *)"ON"; -// else -// statestr = (char *)"OFF"; + byte pstate = TrackManager::isPowerOn(t) ? 1 : 0; TRACK_MODE tm = track[t]->getMode(); if (tm & TRACK_MODE_MAIN) { if(tm & TRACK_MODE_AUTOINV) - format=F("<= %c MAIN AUTOINV>\n"); + format=F("<= %c %d MAIN AUTOINV>\n"); else if (tm & TRACK_MODE_INV) - format=F("<= %c MAIN INV>\n"); + format=F("<= %c %d MAIN INV>\n"); else - format=F("<= %c MAIN>\n"); + format=F("<= %c %d MAIN>\n"); } #ifndef DISABLE_PROG else if (tm & TRACK_MODE_PROG) - format=F("<= %c PROG>\n"); + format=F("<= %c %d PROG>\n"); #endif else if (tm & TRACK_MODE_NONE) - format=F("<= %c NONE>\n"); + format=F("<= %c %d NONE>\n"); else if(tm & TRACK_MODE_EXT) - format=F("<= %c EXT>\n"); + format=F("<= %c %d EXT>\n"); else if(tm & TRACK_MODE_BOOST) { if(tm & TRACK_MODE_AUTOINV) - format=F("<= %c BOOST AUTOINV>\n"); + format=F("<= %c %d BOOST AUTOINV>\n"); else if (tm & TRACK_MODE_INV) - format=F("<= %c BOOST INV>\n"); + format=F("<= %c %d BOOST INV>\n"); else - format=F("<= %c BOOST>\n"); + format=F("<= %c %d BOOST>\n"); } else if (tm & TRACK_MODE_DC) { if (tm & TRACK_MODE_INV) - format=F("<= %c DCX %d>\n"); + format=F("<= %c %d DCX %d>\n"); else - format=F("<= %c DC %d>\n"); + format=F("<= %c %d DC %d>\n"); } else - format=F("<= %c XXX>\n"); + format=F("<= %c %d XXX>\n"); if (stream) - StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); + StringFormatter::send(stream,format,'A'+t, pstate, trackDCAddr[t]); else - CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); + CommandDistributor::broadcastTrackState(format,'A'+t, pstate, trackDCAddr[t]); } From 4c89b26c791faca7abfc09903aea93afeb1dae09 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 11 Nov 2023 09:12:08 +0000 Subject: [PATCH 176/218] fix returns automations/routes if (paramCount==1) {// StringFormatter::send(stream, F(" Date: Sat, 11 Nov 2023 17:31:38 +0000 Subject: [PATCH 177/218] Fix returns automations/routes From 86ed8ff8a6aeb45abaf0a25def6d0d3e10815c82 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 13 Nov 2023 17:16:58 +0100 Subject: [PATCH 178/218] remove power state from <=> answer --- CommandDistributor.cpp | 4 ++-- CommandDistributor.h | 2 +- TrackManager.cpp | 31 ++++++++++++++----------------- 3 files changed, 17 insertions(+), 20 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 653bbef..cd9e89e 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -269,8 +269,8 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) { broadcastReply(type, F("%s"),msg); } -void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, byte pstate, int16_t dcAddr) { - broadcastReply(COMMAND_TYPE, format,trackLetter, pstate, dcAddr); +void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr) { + broadcastReply(COMMAND_TYPE, format, trackLetter, dcAddr); } void CommandDistributor::broadcastRouteState(uint16_t routeId, byte state ) { diff --git a/CommandDistributor.h b/CommandDistributor.h index 7dfcdf6..83bfbbd 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -55,7 +55,7 @@ public : static int16_t retClockTime(); static void broadcastPower(); static void broadcastRaw(clientType type,char * msg); - static void broadcastTrackState(const FSH* format,byte trackLetter, byte pstate, int16_t dcAddr); + static void broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr); template static void broadcastReply(clientType type, Targs... msg); static void forget(byte clientId); static void broadcastRouteState(uint16_t routeId,byte state); diff --git a/TrackManager.cpp b/TrackManager.cpp index 647f07c..8cdadb2 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -400,46 +400,43 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) void TrackManager::streamTrackState(Print* stream, byte t) { // null stream means send to commandDistributor for broadcast if (track[t]==NULL) return; - auto format=F(""); - byte pstate = TrackManager::isPowerOn(t) ? 1 : 0; + auto format=F("<= %d XXX>\n"); TRACK_MODE tm = track[t]->getMode(); if (tm & TRACK_MODE_MAIN) { if(tm & TRACK_MODE_AUTOINV) - format=F("<= %c %d MAIN AUTOINV>\n"); + format=F("<= %c MAIN A>\n"); else if (tm & TRACK_MODE_INV) - format=F("<= %c %d MAIN INV>\n"); + format=F("<= %c MAIN I>\n"); else - format=F("<= %c %d MAIN>\n"); + format=F("<= %c MAIN>\n"); } #ifndef DISABLE_PROG else if (tm & TRACK_MODE_PROG) - format=F("<= %c %d PROG>\n"); + format=F("<= %c PROG>\n"); #endif else if (tm & TRACK_MODE_NONE) - format=F("<= %c %d NONE>\n"); + format=F("<= %c NONE>\n"); else if(tm & TRACK_MODE_EXT) - format=F("<= %c %d EXT>\n"); + format=F("<= %c EXT>\n"); else if(tm & TRACK_MODE_BOOST) { if(tm & TRACK_MODE_AUTOINV) - format=F("<= %c %d BOOST AUTOINV>\n"); + format=F("<= %c B A>\n"); else if (tm & TRACK_MODE_INV) - format=F("<= %c %d BOOST INV>\n"); + format=F("<= %c B I>\n"); else - format=F("<= %c %d BOOST>\n"); + format=F("<= %c B>\n"); } else if (tm & TRACK_MODE_DC) { if (tm & TRACK_MODE_INV) - format=F("<= %c %d DCX %d>\n"); + format=F("<= %c DCX %d>\n"); else - format=F("<= %c %d DC %d>\n"); + format=F("<= %c DC %d>\n"); } - else - format=F("<= %c %d XXX>\n"); if (stream) - StringFormatter::send(stream,format,'A'+t, pstate, trackDCAddr[t]); + StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); else - CommandDistributor::broadcastTrackState(format,'A'+t, pstate, trackDCAddr[t]); + CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); } From 582ff890f4e41dafc00c54cebadbfc9a6d37db8f Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 14 Nov 2023 00:05:18 +0100 Subject: [PATCH 179/218] Trackmanager rework for simpler structure --- CommandDistributor.cpp | 5 ++ DCCEXParser.cpp | 147 ++++++++++++----------------------------- EXRAIL2.cpp | 4 +- MotorDriver.h | 3 +- TrackManager.cpp | 115 +++++++++++++++++++------------- TrackManager.h | 15 ++--- 6 files changed, 126 insertions(+), 163 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index cd9e89e..89c8714 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -248,6 +248,11 @@ void CommandDistributor::broadcastLoco(byte slot) { } void CommandDistributor::broadcastPower() { + char pstr[] = "? x"; + for(byte t=0; t<8; t++) + if (TrackManager::getPower(t, pstr)) + broadcastReply(COMMAND_TYPE, F("\n"),pstr); + bool main=TrackManager::getMainPower()==POWERMODE::ON; bool prog=TrackManager::getProgPower()==POWERMODE::ON; bool join=TrackManager::isJoined(); diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 96596c6..3b91d86 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -553,131 +553,66 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case '1': // POWERON <1 [MAIN|PROG|JOIN]> { - bool main=false; - bool prog=false; - bool join=false; - bool singletrack=false; - //byte t=0; - if (params > 1) break; - if (params==0) { // All - main=true; - prog=true; - } - if (params==1) { - if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN> - main=true; + if (params > 1) break; + if (params==0) { // All + TrackManager::setTrackPower(TRACK_MODE_ALL, POWERMODE::ON); + } + if (params==1) { + if (p[0]==HASH_KEYWORD_MAIN) { // <1 MAIN> + TrackManager::setTrackPower(TRACK_MODE_MAIN, POWERMODE::ON); } #ifndef DISABLE_PROG else if (p[0] == HASH_KEYWORD_JOIN) { // <1 JOIN> - main=true; - prog=true; - join=true; + TrackManager::setJoin(true); + TrackManager::setTrackPower(TRACK_MODE_MAIN|TRACK_MODE_PROG, POWERMODE::ON); } else if (p[0]==HASH_KEYWORD_PROG) { // <1 PROG> - prog=true; + TrackManager::setJoin(false); + TrackManager::setTrackPower(TRACK_MODE_PROG, POWERMODE::ON); } #endif - //else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H> - byte t = (p[0] - 'A'); - //DIAG(F("Processing track - %d "), t); - if (TrackManager::isProg(t)) { - main = false; - prog = true; - } - else - { - main=true; - prog=false; - } - singletrack=true; - if (main) TrackManager::setTrackPower(false, false, POWERMODE::ON, t); - if (prog) TrackManager::setTrackPower(true, false, POWERMODE::ON, t); - - StringFormatter::send(stream, F("<1 %c>\n"), t+'A'); - //CommandDistributor::broadcastPower(); - //TrackManager::streamTrackState(NULL,t); - return; + byte t = (p[0] - 'A'); + TrackManager::setTrackPower(POWERMODE::ON, t); + //StringFormatter::send(stream, F("\n"), t+'A'); } - else break; // will reply - } - - if (!singletrack) { - TrackManager::setJoin(join); - if (join) TrackManager::setJoinPower(POWERMODE::ON); - else { - if (main) TrackManager::setMainPower(POWERMODE::ON); - if (prog) TrackManager::setProgPower(POWERMODE::ON); - } - CommandDistributor::broadcastPower(); + } + CommandDistributor::broadcastPower(); + //TrackManager::streamTrackState(NULL,t); - return; - } + return; + } - } - case '0': // POWEROFF <0 [MAIN | PROG] > { - bool main=false; - bool prog=false; - bool singletrack=false; - //byte t=0; - if (params > 1) break; - if (params==0) { // All - main=true; - prog=true; - } - if (params==1) { - if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN> - main=true; - } + if (params > 1) break; + if (params==0) { // All + TrackManager::setJoin(false); + TrackManager::setTrackPower(TRACK_MODE_ALL, POWERMODE::OFF); + } + if (params==1) { + if (p[0]==HASH_KEYWORD_MAIN) { // <0 MAIN> + TrackManager::setJoin(false); + TrackManager::setTrackPower(TRACK_MODE_MAIN, POWERMODE::OFF); + } #ifndef DISABLE_PROG else if (p[0]==HASH_KEYWORD_PROG) { // <0 PROG> - prog=true; + TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off + TrackManager::setTrackPower(TRACK_MODE_PROG, POWERMODE::OFF); } #endif - //else if (p[0] >= 'A' && p[0] <= 'H') { // <1 A-H> - else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H> - byte t = (p[0] - 'A'); - //DIAG(F("Processing track - %d "), t); - if (TrackManager::isProg(t)) { - main = false; - prog = true; - } - else - { - main=true; - prog=false; - } - singletrack=true; - TrackManager::setJoin(false); - if (main) TrackManager::setTrackPower(false, false, POWERMODE::OFF, t); - if (prog) { - TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off - TrackManager::setTrackPower(true, false, POWERMODE::OFF, t); - } - StringFormatter::send(stream, F("<0 %c>\n"), t+'A'); - //CommandDistributor::broadcastPower(); - //TrackManager::streamTrackState(NULL, t); - return; - } - - else break; // will reply + else if (p[0] >= HASH_KEYWORD_A && p[0] <= HASH_KEYWORD_H) { // <1 A-H> + byte t = (p[0] - 'A'); + TrackManager::setJoin(false); + TrackManager::setTrackPower(POWERMODE::OFF, t); + //StringFormatter::send(stream, F("\n"), t+'A'); } - - if (!singletrack) { - TrackManager::setJoin(false); - - if (main) TrackManager::setMainPower(POWERMODE::OFF); - if (prog) { - TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off - TrackManager::setProgPower(POWERMODE::OFF); - } - CommandDistributor::broadcastPower(); - return; - } - } + else break; // will reply + } + CommandDistributor::broadcastPower(); + return; + } case '!': // ESTOP ALL DCC::setThrottle(0,1,1); // this broadcasts speed 1(estop) and sets all reminders to speed 1. diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 8e6a6b5..933650c 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -636,10 +636,10 @@ void RMFT2::loop2() { //byte thistrack=getOperand(1); switch (operand) { case TRACK_POWER_0: - TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), false, POWERMODE::OFF, getOperand(1)); + TrackManager::setTrackPower(POWERMODE::OFF, getOperand(1)); break; case TRACK_POWER_1: - TrackManager::setTrackPower(TrackManager::isProg(getOperand(1)), false, POWERMODE::ON, getOperand(1)); + TrackManager::setTrackPower(POWERMODE::ON, getOperand(1)); break; } diff --git a/MotorDriver.h b/MotorDriver.h index db2ccc5..19bfd13 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -35,7 +35,8 @@ template inline T operator& (T a, T b) { return (T)((int)a & (int)b); } template inline T operator^ (T a, T b) { return (T)((int)a ^ (int)b); } enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4, TRACK_MODE_DC = 8, TRACK_MODE_EXT = 16, TRACK_MODE_BOOST = 32, - TRACK_MODE_INV = 64, TRACK_MODE_DCX = 72, TRACK_MODE_AUTOINV = 128}; + TRACK_MODE_ALL = 62, // only to operate all tracks + TRACK_MODE_INV = 64, TRACK_MODE_DCX = 72 /*DC + INV*/, TRACK_MODE_AUTOINV = 128}; #define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH #define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW diff --git a/TrackManager.cpp b/TrackManager.cpp index 8cdadb2..ff56001 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -471,51 +471,48 @@ std::vectorTrackManager::getMainDrivers() { } #endif -void TrackManager::setPower2(bool setProg,bool setJoin, POWERMODE mode) { - if (!setProg) mainPowerGuess=mode; - FOR_EACH_TRACK(t) { - - TrackManager::setTrackPower(setProg, setJoin, mode, t); - - } - return; -} - -void TrackManager::setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byte thistrack) { - - //DIAG(F("SetTrackPower Processing Track %d"), thistrack); - MotorDriver * driver=track[thistrack]; - if (!driver) return; - - TRACK_MODE tm = track[thistrack]->getMode(); - if ( (tm & TRACK_MODE_MAIN) - && !setProg ){ - // toggle brake before turning power on - resets overcurrent error - // on the Pololu board if brake is wired to ^D2. - // XXX see if we can make this conditional - driver->setBrake(true); - driver->setBrake(false); // DCC runs with brake off - driver->setPower(mode); - } else if ( (tm & TRACK_MODE_DC) - && !(setProg || setJoin)){ - //DIAG(F("Processing track - %d setProg %d"), thistrack, setProg); - driver->setBrake(true); // DC starts with brake on - applyDCSpeed(thistrack); // speed match DCC throttles - driver->setPower(mode); - } else if ( (tm & TRACK_MODE_PROG) - && (setProg || setJoin) ){ - driver->setBrake(true); - driver->setBrake(false); - driver->setPower(mode); - } else if ( (tm & TRACK_MODE_EXT) - || (tm & TRACK_MODE_BOOST)){ - driver->setBrake(true); - driver->setBrake(false); - driver->setPower(mode); +// Set track power for all tracks with this mode +void TrackManager::setTrackPower(TRACK_MODE trackmode, POWERMODE powermode) { + FOR_EACH_TRACK(t) { + MotorDriver *driver=track[t]; + if (trackmode & driver->getMode()) { + if (powermode == POWERMODE::ON) { + if (trackmode & TRACK_MODE_DC) { + driver->setBrake(true); // DC starts with brake on + applyDCSpeed(t); // speed match DCC throttles + } else { + // toggle brake before turning power on - resets overcurrent error + // on the Pololu board if brake is wired to ^D2. + driver->setBrake(true); + driver->setBrake(false); // DCC runs with brake off + } } + driver->setPower(powermode); + } + } } - void TrackManager::reportPowerChange(Print* stream, byte thistrack) { +// Set track power for this track, inependent of mode +void TrackManager::setTrackPower(POWERMODE powermode, byte t) { + MotorDriver *driver=track[t]; + TRACK_MODE trackmode = driver->getMode(); + if (trackmode & TRACK_MODE_DC) { + if (powermode == POWERMODE::ON) { + driver->setBrake(true); // DC starts with brake on + applyDCSpeed(t); // speed match DCC throttles + } + } else { + if (powermode == POWERMODE::ON) { + // toggle brake before turning power on - resets overcurrent error + // on the Pololu board if brake is wired to ^D2. + driver->setBrake(true); + driver->setBrake(false); // DCC runs with brake off + } + } + driver->setPower(powermode); +} + +void TrackManager::reportPowerChange(Print* stream, byte thistrack) { // This function is for backward JMRI compatibility only // It reports the first track only, as main, regardless of track settings. // @@ -524,12 +521,38 @@ void TrackManager::setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byt track[0]->raw2mA(track[0]->getCurrentRaw(false)), maxCurrent, maxCurrent); } +// returns state of the one and only prog track POWERMODE TrackManager::getProgPower() { - FOR_EACH_TRACK(t) - if (track[t]->getMode() & TRACK_MODE_PROG) - return track[t]->getPower(); - return POWERMODE::OFF; + FOR_EACH_TRACK(t) + if (track[t]->getMode() & TRACK_MODE_PROG) + return track[t]->getPower(); // optimize: there is max one prog track + return POWERMODE::OFF; +} + +// returns on if all are on. returns off otherwise +POWERMODE TrackManager::getMainPower() { + POWERMODE result = POWERMODE::OFF; + FOR_EACH_TRACK(t) { + if (track[t]->getMode() & TRACK_MODE_MAIN) { + POWERMODE p = track[t]->getPower(); + if (p == POWERMODE::OFF) + return POWERMODE::OFF; // done and out + if (p == POWERMODE::ON) + result = POWERMODE::ON; + } } + return result; +} + +bool TrackManager::getPower(byte t, char s[]) { + if (track[t]) { + s[0] = track[t]->getPower() == POWERMODE::ON ? '1' : '0'; + s[2] = t + 'A'; + return true; + } + return false; +} + void TrackManager::reportObsoleteCurrent(Print* stream) { // This function is for backward JMRI compatibility only diff --git a/TrackManager.h b/TrackManager.h index d1edca2..644c45a 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -62,23 +62,22 @@ class TrackManager { static void setDCSignal(int16_t cab, byte speedbyte); static MotorDriver * getProgDriver(); #ifdef ARDUINO_ARCH_ESP32 - static std::vectorgetMainDrivers(); + static std::vectorgetMainDrivers(); #endif - static void setPower2(bool progTrack,bool joinTrack,POWERMODE mode); static void setPower(POWERMODE mode) {setMainPower(mode); setProgPower(mode);} - static void setMainPower(POWERMODE mode) {setPower2(false,false,mode);} - static void setProgPower(POWERMODE mode) {setPower2(true,false,mode);} - static void setJoinPower(POWERMODE mode) {setPower2(false,true,mode);} - static void setTrackPower(bool setProg, bool setJoin, POWERMODE mode, byte thistrack); - + static void setTrackPower(POWERMODE mode, byte t); + static void setTrackPower(TRACK_MODE trackmode, POWERMODE powermode); + static void setMainPower(POWERMODE mode) {setTrackPower(TRACK_MODE_MAIN, mode);} + static void setProgPower(POWERMODE mode) {setTrackPower(TRACK_MODE_PROG, mode);} static const int16_t MAX_TRACKS=8; static bool setTrackMode(byte track, TRACK_MODE mode, int16_t DCaddr=0); static bool parseJ(Print * stream, int16_t params, int16_t p[]); static void loop(); - static POWERMODE getMainPower() {return mainPowerGuess;} + static POWERMODE getMainPower(); static POWERMODE getProgPower(); + static bool getPower(byte t, char s[]); static void setJoin(bool join); static bool isJoined() { return progTrackSyncMain;} static void setJoinRelayPin(byte joinRelayPin); From 503378f1bbee6d8bb55d60ae3728a2b60f6a908b Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 14 Nov 2023 00:06:53 +0100 Subject: [PATCH 180/218] version 5.2.1 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 867619e..71069ca 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311110712Z" +#define GITHUB_SHA "devel-202311132306Z" diff --git a/version.h b/version.h index 4258cf4..5640feb 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.0" +#define VERSION "5.2.1" +// 5.2.1 - Trackmanager rework for simpler structure // 5.2.0 - ESP32: Autoreverse and booster mode support // 5.1.21 - EXRAIL invoke multiple ON handlers for same event // 5.1.20 - EXRAIL Tidy and ROUTE_STATE, ROUTE_CAPTION From 763ef8be34743835308ad7eaad31a1d59db8b471 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 14 Nov 2023 11:12:14 +0100 Subject: [PATCH 181/218] prettier MAX_TRACKS --- CommandDistributor.cpp | 2 +- TrackManager.cpp | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 89c8714..4269492 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -249,7 +249,7 @@ void CommandDistributor::broadcastLoco(byte slot) { void CommandDistributor::broadcastPower() { char pstr[] = "? x"; - for(byte t=0; t<8; t++) + for(byte t=0; t\n"),pstr); diff --git a/TrackManager.cpp b/TrackManager.cpp index ff56001..7c1e651 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -526,7 +526,7 @@ POWERMODE TrackManager::getProgPower() { FOR_EACH_TRACK(t) if (track[t]->getMode() & TRACK_MODE_PROG) return track[t]->getPower(); // optimize: there is max one prog track - return POWERMODE::OFF; + return POWERMODE::OFF; } // returns on if all are on. returns off otherwise @@ -545,6 +545,8 @@ POWERMODE TrackManager::getMainPower() { } bool TrackManager::getPower(byte t, char s[]) { + if (t > lastTrack) + return false; if (track[t]) { s[0] = track[t]->getPower() == POWERMODE::ON ? '1' : '0'; s[2] = t + 'A'; From 1af5132e6aca4dea56636ba5df9f2533d538f17a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 14 Nov 2023 11:16:54 +0100 Subject: [PATCH 182/218] version 5.2.1 timestamp --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 71069ca..3f9b1e1 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311132306Z" +#define GITHUB_SHA "devel-202311141013Z" From 566ce1b7f80066d92e1873ad091ef221ccb11d9d Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 14 Nov 2023 19:41:05 +0000 Subject: [PATCH 183/218] Virtual LCD phase 1 --- CommandDistributor.cpp | 49 ++++++++++++++++++++++++++++++++++++++++++ CommandDistributor.h | 9 +++++++- DCCEXParser.cpp | 6 +++++- StringFormatter.cpp | 17 +++++++++++++++ StringFormatter.h | 1 - 5 files changed, 79 insertions(+), 3 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 351a18d..788e4e6 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -105,6 +105,7 @@ void CommandDistributor::parse(byte clientId,byte * buffer, RingStream * stream void CommandDistributor::forget(byte clientId) { if (clients[clientId]==WITHROTTLE_TYPE) WiThrottle::forget(clientId); clients[clientId]=NONE_TYPE; + if (virtualLCDClient==clientId) virtualLCDClient=RingStream::NO_CLIENT; } #endif @@ -280,3 +281,51 @@ void CommandDistributor::broadcastRouteState(uint16_t routeId, byte state ) { void CommandDistributor::broadcastRouteCaption(uint16_t routeId, const FSH* caption ) { broadcastReply(COMMAND_TYPE, F("\n"),routeId,caption); } + +Print * CommandDistributor::getVirtualLCDSerial(byte screen, byte row) { + Print * stream=virtualLCDSerial; + #ifdef CD_HANDLE_RING + rememberVLCDClient=RingStream::NO_CLIENT; + if (!stream && virtualLCDClient!=RingStream::NO_CLIENT) { + // If we are broadcasting from a wifi/eth process we need to complete its output + // before merging broadcasts in the ring, then reinstate it in case + // the process continues to output to its client. + if ((rememberVLCDClient = ring->peekTargetMark()) != RingStream::NO_CLIENT) { + ring->commit(); + } + ring->mark(virtualLCDClient); + stream=ring; + } + #endif + if (stream) StringFormatter::send(stream,F("<@ %d %d \""), screen,row); + return stream; +} + +void CommandDistributor::commitVirtualLCDSerial() { + #ifdef CD_HANDLE_RING + if (virtualLCDClient!=RingStream::NO_CLIENT) { + StringFormatter::send(ring,F("\">\n")); + ring->commit(); + if (rememberVLCDClient!=RingStream::NO_CLIENT) ring->mark(rememberVLCDClient); + return; + } + #endif + StringFormatter::send(virtualLCDSerial,F("\">\n")); +} + +void CommandDistributor::setVirtualLCDSerial(Print * stream) { + #ifdef CD_HANDLE_RING + virtualLCDClient=RingStream::NO_CLIENT; + if (stream && stream->availableForWrite()==RingStream::THIS_IS_A_RINGSTREAM) { + virtualLCDClient=((RingStream *) stream)->peekTargetMark(); + virtualLCDSerial=nullptr; + return; + } + #endif + virtualLCDSerial=stream; +} + +Print* CommandDistributor::virtualLCDSerial=nullptr; +byte CommandDistributor::virtualLCDClient=0xFF; +byte CommandDistributor::rememberVLCDClient=0; + diff --git a/CommandDistributor.h b/CommandDistributor.h index 83bfbbd..359c0fe 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -60,8 +60,15 @@ public : static void forget(byte clientId); static void broadcastRouteState(uint16_t routeId,byte state); static void broadcastRouteCaption(uint16_t routeId,const FSH * caption); - + // Handling code for virtual LCD receiver. + static Print * getVirtualLCDSerial(byte screen, byte row); + static void commitVirtualLCDSerial(); + static void setVirtualLCDSerial(Print * stream); + private: + static Print * virtualLCDSerial; + static byte virtualLCDClient; + static byte rememberVLCDClient; }; #endif diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 96596c6..369d941 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -724,7 +724,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) return; break; #endif - case '=': // TACK MANAGER CONTROL <= [params]> + case '=': // TRACK MANAGER CONTROL <= [params]> if (TrackManager::parseJ(stream, params, p)) return; break; @@ -897,6 +897,10 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case 'L': // LCC interface implemented in EXRAIL parser break; // Will if not intercepted by EXRAIL + case '@': // JMRI saying "give me virtual LCD msgs" + CommandDistributor::setVirtualLCDSerial(stream); + return; + default: //anything else will diagnose and drop out to DIAG(F("Opcode=%c params=%d"), opcode, params); for (int i = 0; i < params; i++) diff --git a/StringFormatter.cpp b/StringFormatter.cpp index b40de1c..218e617 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -19,6 +19,7 @@ #include "StringFormatter.h" #include #include "DisplayInterface.h" +#include "CommandDistributor.h" bool Diag::ACK=false; bool Diag::CMD=false; @@ -45,6 +46,14 @@ void StringFormatter::lcd(byte row, const FSH* input...) { send2(&USB_SERIAL,input,args); send(&USB_SERIAL,F(" *>\n")); + // send to virtual LCD collector (if any) + Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(0,row); + if (virtualLCD) { + va_start(args, input); + send2(virtualLCD,input,args); + CommandDistributor::commitVirtualLCDSerial(); + } + DisplayInterface::setRow(row); va_start(args, input); send2(DisplayInterface::getDisplayHandler(),input,args); @@ -52,6 +61,14 @@ void StringFormatter::lcd(byte row, const FSH* input...) { void StringFormatter::lcd2(uint8_t display, byte row, const FSH* input...) { va_list args; + + // send to virtual LCD collector (if any) + Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(display,row); + if (virtualLCD) { + va_start(args, input); + send2(virtualLCD,input,args); + CommandDistributor::commitVirtualLCDSerial(); + } DisplayInterface::setRow(display, row); va_start(args, input); diff --git a/StringFormatter.h b/StringFormatter.h index 1231d54..25d15e2 100644 --- a/StringFormatter.h +++ b/StringFormatter.h @@ -54,6 +54,5 @@ class StringFormatter private: static void send2(Print * serial, const FSH* input,va_list args); static void printPadded(Print* stream, long value, byte width, bool formatLeft); - }; #endif From 6da3153dd5d1b7dfa218727d64153f4aed572b97 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Wed, 15 Nov 2023 19:29:24 +0000 Subject: [PATCH 184/218] Define scroll rows in config Allow the definition of MAX_CHARACTER_ROWS in config.h --- Display.h | 2 ++ config.example.h | 8 ++++++++ 2 files changed, 10 insertions(+) diff --git a/Display.h b/Display.h index af36d43..467424f 100644 --- a/Display.h +++ b/Display.h @@ -37,7 +37,9 @@ class Display : public DisplayInterface { public: Display(DisplayDevice *deviceDriver); +#if !defined (MAX_CHARACTER_ROWS) static const int MAX_CHARACTER_ROWS = 8; +#endif static const int MAX_CHARACTER_COLS = MAX_MSG_SIZE; static const long DISPLAY_SCROLL_TIME = 3000; // 3 seconds diff --git a/config.example.h b/config.example.h index de31743..9909371 100644 --- a/config.example.h +++ b/config.example.h @@ -167,6 +167,14 @@ The configuration file for DCC-EX Command Station // * #define SCROLLMODE 2 is by row (move up 1 row at a time). #define SCROLLMODE 1 +// In order to avoid wasting memory the current scroll buffer is limited +// to 8 lines. Some users wishing to display additional information +// such as TrackManager power states have requested additional rows aware +// of the warning that this will take extra RAM. if you wish to include additional rows +// uncomment the following #define and set the number of lines you need. +//#define MAX_CHARACTER_ROWS 12 + + ///////////////////////////////////////////////////////////////////////////////////// // DISABLE EEPROM // From b472230b47b86d169a21b26f4566e94c7eb4abf4 Mon Sep 17 00:00:00 2001 From: Colin Murdoch Date: Wed, 15 Nov 2023 19:41:56 +0000 Subject: [PATCH 185/218] Update version.h Updated version.h --- version.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 5640feb..3030981 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.1" +#define VERSION "5.2.2" +// 5.2.2 - Added option to allow MAX_CHARACTER_ROWS to be defined in config.h // 5.2.1 - Trackmanager rework for simpler structure // 5.2.0 - ESP32: Autoreverse and booster mode support // 5.1.21 - EXRAIL invoke multiple ON handlers for same event From 7bd2ba9b418bb2f5768c5e572ee26242780e3353 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 16 Nov 2023 00:27:23 +0100 Subject: [PATCH 186/218] Bugfix: Catch stange input to parser --- DCCEXParser.cpp | 23 +++++++++++++++++------ SerialManager.cpp | 17 +++++++++-------- 2 files changed, 26 insertions(+), 14 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 3b91d86..82a1042 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -210,8 +210,10 @@ int16_t DCCEXParser::splitValues(int16_t result[MAX_COMMAND_PARAMS], const byte case 1: // skipping spaces before a param if (hot == ' ') break; - if (hot == '\0' || hot == '>') - return parameterCount; + if (hot == '\0') + return -1; + if (hot == '>') + return parameterCount; state = 2; continue; @@ -304,14 +306,19 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) #ifndef DISABLE_EEPROM (void)EEPROM; // tell compiler not to warn this is unused #endif + byte params = 0; if (Diag::CMD) DIAG(F("PARSING:%s"), com); int16_t p[MAX_COMMAND_PARAMS]; while (com[0] == '<' || com[0] == ' ') com++; // strip off any number of < or spaces byte opcode = com[0]; - byte params = splitValues(p, com, opcode=='M' || opcode=='P'); - + int16_t splitnum = splitValues(p, com, opcode=='M' || opcode=='P'); + if (splitnum < 0 || splitnum >= MAX_COMMAND_PARAMS) // if arguments are broken, leave but via printing + goto out; + // Because of check above we are now inside byte size + params = splitnum; + if (filterCallback) filterCallback(stream, opcode, params, p); if (filterRMFTCallback && opcode!='\0') @@ -833,14 +840,18 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) break; // Will if not intercepted by EXRAIL default: //anything else will diagnose and drop out to + if (opcode >= ' ' && opcode <= '~') { DIAG(F("Opcode=%c params=%d"), opcode, params); for (int i = 0; i < params; i++) DIAG(F("p[%d]=%d (0x%x)"), i, p[i], p[i]); - break; + } else { + DIAG(F("Unprintable %x"), opcode); + } + break; } // end of opcode switch - // Any fallout here sends an +out:// Any fallout here sends an StringFormatter::send(stream, F("\n")); } diff --git a/SerialManager.cpp b/SerialManager.cpp index bacfceb..88bc7cd 100644 --- a/SerialManager.cpp +++ b/SerialManager.cpp @@ -111,14 +111,15 @@ void SerialManager::loop2() { bufferLength = 0; buffer[0] = '\0'; } - else if (ch == '>') { - buffer[bufferLength] = '\0'; - DCCEXParser::parse(serial, buffer, NULL); - inCommandPayload = false; - break; - } - else if (inCommandPayload) { - if (bufferLength < (COMMAND_BUFFER_SIZE-1)) buffer[bufferLength++] = ch; + else if (inCommandPayload) { + if (bufferLength < (COMMAND_BUFFER_SIZE-1)) + buffer[bufferLength++] = ch; + if (ch == '>') { + buffer[bufferLength] = '\0'; + DCCEXParser::parse(serial, buffer, NULL); + inCommandPayload = false; + break; + } } } From 102d6078a7c290b05eb2d531bae5ab7bbbd37c78 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 16 Nov 2023 08:38:39 +0100 Subject: [PATCH 187/218] version 5.2.3 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 3f9b1e1..2a558fc 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311141013Z" +#define GITHUB_SHA "devel-202311160737Z" diff --git a/version.h b/version.h index 3030981..c14d2b0 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.2" +#define VERSION "5.2.3" +// 5.2.3 - Bugfix: Catch stange input to parser // 5.2.2 - Added option to allow MAX_CHARACTER_ROWS to be defined in config.h // 5.2.1 - Trackmanager rework for simpler structure // 5.2.0 - ESP32: Autoreverse and booster mode support From 2ba5adc8b42bfd5323e09f4bf2b6585321129a8e Mon Sep 17 00:00:00 2001 From: Asbelos Date: Fri, 17 Nov 2023 10:45:36 +0000 Subject: [PATCH 188/218] 5.2.3 @ and ROUTE_DISABLED --- DCCEXParser.cpp | 4 ++++ EXRAIL2.cpp | 3 +++ EXRAIL2.h | 1 + EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 3 +++ StringFormatter.cpp | 15 +++++++++------ version.h | 2 ++ 7 files changed, 24 insertions(+), 6 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 4ceedea..aed185b 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -115,6 +115,7 @@ Once a new OPCODE is decided upon, update this list. #include "DCCTimer.h" #include "EXRAIL2.h" #include "Turntables.h" +#include "version.h" // This macro can't be created easily as a portable function because the // flashlist requires a far pointer for high flash access. @@ -841,6 +842,9 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case '@': // JMRI saying "give me virtual LCD msgs" CommandDistributor::setVirtualLCDSerial(stream); + StringFormatter::send(stream, + F("<@ 0 0 \"DCC-EX v" VERSION "\">\n" + "<@ 0 1 \"Lic GPLv3\">\n")); return; default: //anything else will diagnose and drop out to diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index ccd4d9e..6f0a835 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -932,6 +932,9 @@ void RMFT2::loop2() { case OPCODE_ROUTE_ACTIVE: manageRouteState(operand,1); break; + case OPCODE_ROUTE_DISABLED: + manageRouteState(operand,4); + break; case OPCODE_ROUTE: case OPCODE_AUTOMATION: diff --git a/EXRAIL2.h b/EXRAIL2.h index a4c0e5f..8f777f4 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -69,6 +69,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_LCC,OPCODE_LCCX,OPCODE_ONLCC, OPCODE_ONOVERLOAD, OPCODE_ROUTE_ACTIVE,OPCODE_ROUTE_INACTIVE,OPCODE_ROUTE_HIDDEN, + OPCODE_ROUTE_DISABLED, // OPcodes below this point are skip-nesting IF operations // placed here so that they may be skipped as a group diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index ff8dc8d..ee240ed 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -129,6 +129,7 @@ #undef ROUTE_ACTIVE #undef ROUTE_INACTIVE #undef ROUTE_HIDDEN +#undef ROUTE_DISABLED #undef ROUTE_CAPTION #undef SENDLOCO #undef SEQUENCE @@ -274,6 +275,7 @@ #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) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 3c58699..6024290 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -108,6 +108,8 @@ void exrailHalSetup() { #define ROUTE_INACTIVE(id) | FEATURE_ROUTESTATE #undef ROUTE_HIDDEN #define ROUTE_HIDDEN(id) | FEATURE_ROUTESTATE +#undef ROUTE_DISABLED +#define ROUTE_DISABLED(id) | FEATURE_ROUTESTATE #undef ROUTE_CAPTION #define ROUTE_CAPTION(id,caption) | FEATURE_ROUTESTATE @@ -457,6 +459,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define ROUTE_ACTIVE(id) OPCODE_ROUTE_ACTIVE,V(id), #define ROUTE_INACTIVE(id) OPCODE_ROUTE_INACTIVE,V(id), #define ROUTE_HIDDEN(id) OPCODE_ROUTE_HIDDEN,V(id), +#define ROUTE_DISABLED(id) OPCODE_ROUTE_DISABLED,V(id), #define ROUTE_CAPTION(id,caption) PRINT(caption) #define SENDLOCO(cab,route) OPCODE_SENDLOCO,V(cab),OPCODE_PAD,V(route), #define SEQUENCE(id) OPCODE_SEQUENCE, V(id), diff --git a/StringFormatter.cpp b/StringFormatter.cpp index 218e617..13b5825 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -39,15 +39,18 @@ void StringFormatter::diag( const FSH* input...) { void StringFormatter::lcd(byte row, const FSH* input...) { va_list args; - + Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(0,row); + // Issue the LCD as a diag first - send(&USB_SERIAL,F("<* LCD%d:"),row); - va_start(args, input); - send2(&USB_SERIAL,input,args); - send(&USB_SERIAL,F(" *>\n")); + // Unless the same serial is asking for the virtual @ respomnse + if (virtualLCD!=&USB_SERIAL) { + send(&USB_SERIAL,F("<* LCD%d:"),row); + va_start(args, input); + send2(&USB_SERIAL,input,args); + send(&USB_SERIAL,F(" *>\n")); + } // send to virtual LCD collector (if any) - Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(0,row); if (virtualLCD) { va_start(args, input); send2(virtualLCD,input,args); diff --git a/version.h b/version.h index c14d2b0..57d154a 100644 --- a/version.h +++ b/version.h @@ -4,6 +4,8 @@ #include "StringFormatter.h" #define VERSION "5.2.3" +// 5.2.4 - LCD macro will not do diag if that duplicates @ to same target. +// - Added ROUTE_DISABLED macro in EXRAIL // 5.2.3 - Bugfix: Catch stange input to parser // 5.2.2 - Added option to allow MAX_CHARACTER_ROWS to be defined in config.h // 5.2.1 - Trackmanager rework for simpler structure From 74d11ccb1e9e30f395f18bafeed0f656650e2889 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 20 Nov 2023 09:27:57 +0100 Subject: [PATCH 189/218] Trackmanager: Do not treat TRACK_MODE_ALL as TRACK_MODE_DC --- TrackManager.cpp | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index 7c1e651..2f528bd 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -472,12 +472,13 @@ std::vectorTrackManager::getMainDrivers() { #endif // Set track power for all tracks with this mode -void TrackManager::setTrackPower(TRACK_MODE trackmode, POWERMODE powermode) { +void TrackManager::setTrackPower(TRACK_MODE trackmodeToMatch, POWERMODE powermode) { FOR_EACH_TRACK(t) { - MotorDriver *driver=track[t]; - if (trackmode & driver->getMode()) { + MotorDriver *driver=track[t]; + TRACK_MODE trackmodeOfTrack = driver->getMode(); + if (trackmodeToMatch & trackmodeOfTrack) { if (powermode == POWERMODE::ON) { - if (trackmode & TRACK_MODE_DC) { + if (trackmodeOfTrack & TRACK_MODE_DC) { driver->setBrake(true); // DC starts with brake on applyDCSpeed(t); // speed match DCC throttles } else { From a7096e782c148f0d07dc694612a9048c2818cdb4 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Mon, 20 Nov 2023 09:32:22 +0100 Subject: [PATCH 190/218] version 5.2.5 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 2a558fc..e03b498 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311160737Z" +#define GITHUB_SHA "devel-202311200731Z" diff --git a/version.h b/version.h index 57d154a..e3a71d2 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.3" +#define VERSION "5.2.5" +// 5.2.5 - Trackmanager: Do not treat TRACK_MODE_ALL as TRACK_MODE_DC // 5.2.4 - LCD macro will not do diag if that duplicates @ to same target. // - Added ROUTE_DISABLED macro in EXRAIL // 5.2.3 - Bugfix: Catch stange input to parser From e6f33cfdee7aacc37e8071f3fe4e1659b77d8924 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Nov 2023 11:51:26 +0100 Subject: [PATCH 191/218] Trackmanager broadcast power state on track mode change --- DCCEXParser.cpp | 2 +- TrackManager.cpp | 10 ++++++---- TrackManager.h | 2 +- 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index aed185b..8a81616 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -668,7 +668,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) break; #endif case '=': // TRACK MANAGER CONTROL <= [params]> - if (TrackManager::parseJ(stream, params, p)) + if (TrackManager::parseEqualSign(stream, params, p)) return; break; diff --git a/TrackManager.cpp b/TrackManager.cpp index 2f528bd..58a5c30 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -350,7 +350,7 @@ void TrackManager::applyDCSpeed(byte t) { track[t]->setDCSignal(speedByte); } -bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) +bool TrackManager::parseEqualSign(Print *stream, int16_t params, int16_t p[]) { if (params==0) { // <=> List track assignments @@ -397,8 +397,8 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[]) return false; } +// null stream means send to commandDistributor for broadcast void TrackManager::streamTrackState(Print* stream, byte t) { - // null stream means send to commandDistributor for broadcast if (track[t]==NULL) return; auto format=F("<= %d XXX>\n"); TRACK_MODE tm = track[t]->getMode(); @@ -433,10 +433,12 @@ void TrackManager::streamTrackState(Print* stream, byte t) { format=F("<= %c DC %d>\n"); } - if (stream) + if (stream) { // null stream means send to commandDistributor for broadcast StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); - else + } else { CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); + CommandDistributor::broadcastPower(); + } } diff --git a/TrackManager.h b/TrackManager.h index 644c45a..8092f0a 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -73,7 +73,7 @@ class TrackManager { static const int16_t MAX_TRACKS=8; static bool setTrackMode(byte track, TRACK_MODE mode, int16_t DCaddr=0); - static bool parseJ(Print * stream, int16_t params, int16_t p[]); + static bool parseEqualSign(Print * stream, int16_t params, int16_t p[]); static void loop(); static POWERMODE getMainPower(); static POWERMODE getProgPower(); From 29ea7460621b7948c45703121a131b6ea95662e2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Nov 2023 11:54:43 +0100 Subject: [PATCH 192/218] version 5.2.6 --- GITHUB_SHA.h | 2 +- version.h | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index e03b498..683f81d 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311200731Z" +#define GITHUB_SHA "devel-202311211053Z" diff --git a/version.h b/version.h index e3a71d2..0f26a21 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.5" +#define VERSION "5.2.6" +// 5.2.6 - Trackmanager broadcast power state on track mode change // 5.2.5 - Trackmanager: Do not treat TRACK_MODE_ALL as TRACK_MODE_DC // 5.2.4 - LCD macro will not do diag if that duplicates @ to same target. // - Added ROUTE_DISABLED macro in EXRAIL From 4e1fad48325608888da61a2e30f9068366f0139e Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Nov 2023 15:37:08 +0100 Subject: [PATCH 193/218] Trackmanager consolidate getModeName --- CommandDistributor.cpp | 4 +-- CommandDistributor.h | 2 +- EXRAILMacros.h | 16 +++++------ TrackManager.cpp | 63 ++++++++++++++++++++---------------------- TrackManager.h | 2 +- 5 files changed, 42 insertions(+), 45 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index fa70e0c..1f11e28 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -275,8 +275,8 @@ void CommandDistributor::broadcastRaw(clientType type, char * msg) { broadcastReply(type, F("%s"),msg); } -void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr) { - broadcastReply(COMMAND_TYPE, format, trackLetter, dcAddr); +void CommandDistributor::broadcastTrackState(const FSH* format, byte trackLetter, const FSH *modename, int16_t dcAddr) { + broadcastReply(COMMAND_TYPE, format, trackLetter, modename, dcAddr); } void CommandDistributor::broadcastRouteState(uint16_t routeId, byte state ) { diff --git a/CommandDistributor.h b/CommandDistributor.h index 359c0fe..e4dff5d 100644 --- a/CommandDistributor.h +++ b/CommandDistributor.h @@ -55,7 +55,7 @@ public : static int16_t retClockTime(); static void broadcastPower(); static void broadcastRaw(clientType type,char * msg); - static void broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr); + static void broadcastTrackState(const FSH* format,byte trackLetter, const FSH* modename, int16_t dcAddr); template static void broadcastReply(clientType type, Targs... msg); static void forget(byte clientId); static void broadcastRouteState(uint16_t routeId,byte state); diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 6024290..8edbd2b 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -121,14 +121,14 @@ const byte RMFT2::compileFeatures = 0 #include "EXRAIL2MacroReset.h" #undef ROUTE #define ROUTE(id, description) id, -const int16_t HIGHFLASH RMFT2::routeIdList[]= { +const int16_t HIGHFLASH const RMFT2::routeIdList[]= { #include "myAutomation.h" INT16_MAX}; // Pass 2a create throttle automation list #include "EXRAIL2MacroReset.h" #undef AUTOMATION #define AUTOMATION(id, description) id, -const int16_t HIGHFLASH RMFT2::automationIdList[]= { +const int16_t HIGHFLASH const RMFT2::automationIdList[]= { #include "myAutomation.h" INT16_MAX}; @@ -150,7 +150,7 @@ const FSH * RMFT2::getRouteDescription(int16_t id) { const int StringMacroTracker1=__COUNTER__; #define THRUNGE(msg,mode) \ case (__COUNTER__ - StringMacroTracker1) : {\ - static const char HIGHFLASH thrunge[]=msg;\ + static const char HIGHFLASH const thrunge[]=msg;\ strfar=(uint32_t)GETFARPTR(thrunge);\ tmode=mode;\ break;\ @@ -186,7 +186,7 @@ case (__COUNTER__ - StringMacroTracker1) : {\ #undef LCD #define LCD(id,msg) \ case (__COUNTER__ - StringMacroTracker1) : {\ - static const char HIGHFLASH thrunge[]=msg;\ + static const char HIGHFLASH const thrunge[]=msg;\ strfar=(uint32_t)GETFARPTR(thrunge);\ tmode=thrunge_lcd; \ lcdid=id;\ @@ -195,7 +195,7 @@ case (__COUNTER__ - StringMacroTracker1) : {\ #undef SCREEN #define SCREEN(display,id,msg) \ case (__COUNTER__ - StringMacroTracker1) : {\ - static const char HIGHFLASH thrunge[]=msg;\ + static const char HIGHFLASH const thrunge[]=msg;\ strfar=(uint32_t)GETFARPTR(thrunge);\ tmode=(thrunger)(thrunge_lcd+display); \ lcdid=id;\ @@ -274,7 +274,7 @@ const byte RMFT2::rosterNameCount=0 #include "EXRAIL2MacroReset.h" #undef ROSTER #define ROSTER(cabid,name,funcmap...) cabid, -const int16_t HIGHFLASH RMFT2::rosterIdList[]={ +const int16_t HIGHFLASH const RMFT2::rosterIdList[]={ #include "myAutomation.h" INT16_MAX}; @@ -314,7 +314,7 @@ const FSH * RMFT2::getRosterFunctions(int16_t id) { #undef VIRTUAL_SIGNAL #define VIRTUAL_SIGNAL(id) id,0,0,0, -const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { +const HIGHFLASH int16_t const RMFT2::SignalDefinitions[] = { #include "myAutomation.h" 0,0,0,0 }; @@ -503,7 +503,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; // Build RouteCode const int StringMacroTracker2=__COUNTER__; -const HIGHFLASH byte RMFT2::RouteCode[] = { +const HIGHFLASH byte const RMFT2::RouteCode[] = { #include "myAutomation.h" OPCODE_ENDTASK,0,0,OPCODE_ENDEXRAIL,0,0 }; diff --git a/TrackManager.cpp b/TrackManager.cpp index 58a5c30..a7998f4 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -397,46 +397,58 @@ bool TrackManager::parseEqualSign(Print *stream, int16_t params, int16_t p[]) return false; } -// null stream means send to commandDistributor for broadcast -void TrackManager::streamTrackState(Print* stream, byte t) { - if (track[t]==NULL) return; - auto format=F("<= %d XXX>\n"); - TRACK_MODE tm = track[t]->getMode(); +const FSH* TrackManager::getModeName(TRACK_MODE tm) { + const FSH *modename=F("---"); + if (tm & TRACK_MODE_MAIN) { if(tm & TRACK_MODE_AUTOINV) - format=F("<= %c MAIN A>\n"); + modename=F("MAIN A"); else if (tm & TRACK_MODE_INV) - format=F("<= %c MAIN I>\n"); + modename=F("MAIN I>\n"); else - format=F("<= %c MAIN>\n"); + modename=F("MAIN"); } #ifndef DISABLE_PROG else if (tm & TRACK_MODE_PROG) - format=F("<= %c PROG>\n"); + modename=F("PROG"); #endif else if (tm & TRACK_MODE_NONE) - format=F("<= %c NONE>\n"); + modename=F("NONE"); else if(tm & TRACK_MODE_EXT) - format=F("<= %c EXT>\n"); + modename=F("EXT"); else if(tm & TRACK_MODE_BOOST) { if(tm & TRACK_MODE_AUTOINV) - format=F("<= %c B A>\n"); + modename=F("B A"); else if (tm & TRACK_MODE_INV) - format=F("<= %c B I>\n"); + modename=F("B I"); else - format=F("<= %c B>\n"); + modename=F("B"); } else if (tm & TRACK_MODE_DC) { if (tm & TRACK_MODE_INV) - format=F("<= %c DCX %d>\n"); + modename=F("DCX"); else - format=F("<= %c DC %d>\n"); + modename=F("DC"); } + return modename; +} +// null stream means send to commandDistributor for broadcast +void TrackManager::streamTrackState(Print* stream, byte t) { + const FSH *format; + + if (track[t]==NULL) return; + TRACK_MODE tm = track[t]->getMode(); + if (tm & TRACK_MODE_DC) + format=F("<= %c %S %d>\n"); + else + format=F("<= %c %S>\n"); + + const FSH *modename=getModeName(tm); if (stream) { // null stream means send to commandDistributor for broadcast - StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]); + StringFormatter::send(stream,format,'A'+t, modename, trackDCAddr[t]); } else { - CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]); + CommandDistributor::broadcastTrackState(format,'A'+t, modename, trackDCAddr[t]); CommandDistributor::broadcastPower(); } @@ -641,18 +653,3 @@ int16_t TrackManager::returnDCAddr(byte t) { return (trackDCAddr[t]); } -const char* TrackManager::getModeName(byte Mode) { - - //DIAG(F("PowerMode %d"), Mode); - -switch (Mode) - { - case 1: return "NONE"; - case 2: return "MAIN"; - case 4: return "PROG"; - case 8: return "DC"; - case 16: return "DCX"; - case 32: return "EXT"; - default: return "----"; - } -} diff --git a/TrackManager.h b/TrackManager.h index 8092f0a..f2a357d 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -91,7 +91,7 @@ class TrackManager { static bool isProg(byte t); static byte returnMode(byte t); static int16_t returnDCAddr(byte t); - static const char* getModeName(byte Mode); + static const FSH* getModeName(TRACK_MODE Mode); static int16_t joinRelay; static bool progTrackSyncMain; // true when prog track is a siding switched to main From 263ed18b253faff69227a843ad5fd419c4487027 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Nov 2023 15:37:47 +0100 Subject: [PATCH 194/218] version tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 683f81d..1fc5a39 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311211053Z" +#define GITHUB_SHA "devel-202311211437Z" From e7c4af5d4a64c49ce637c5f7cd65b045e75e5265 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Nov 2023 21:16:20 +0100 Subject: [PATCH 195/218] back out wrong const change --- EXRAILMacros.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 8edbd2b..6024290 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -121,14 +121,14 @@ const byte RMFT2::compileFeatures = 0 #include "EXRAIL2MacroReset.h" #undef ROUTE #define ROUTE(id, description) id, -const int16_t HIGHFLASH const RMFT2::routeIdList[]= { +const int16_t HIGHFLASH RMFT2::routeIdList[]= { #include "myAutomation.h" INT16_MAX}; // Pass 2a create throttle automation list #include "EXRAIL2MacroReset.h" #undef AUTOMATION #define AUTOMATION(id, description) id, -const int16_t HIGHFLASH const RMFT2::automationIdList[]= { +const int16_t HIGHFLASH RMFT2::automationIdList[]= { #include "myAutomation.h" INT16_MAX}; @@ -150,7 +150,7 @@ const FSH * RMFT2::getRouteDescription(int16_t id) { const int StringMacroTracker1=__COUNTER__; #define THRUNGE(msg,mode) \ case (__COUNTER__ - StringMacroTracker1) : {\ - static const char HIGHFLASH const thrunge[]=msg;\ + static const char HIGHFLASH thrunge[]=msg;\ strfar=(uint32_t)GETFARPTR(thrunge);\ tmode=mode;\ break;\ @@ -186,7 +186,7 @@ case (__COUNTER__ - StringMacroTracker1) : {\ #undef LCD #define LCD(id,msg) \ case (__COUNTER__ - StringMacroTracker1) : {\ - static const char HIGHFLASH const thrunge[]=msg;\ + static const char HIGHFLASH thrunge[]=msg;\ strfar=(uint32_t)GETFARPTR(thrunge);\ tmode=thrunge_lcd; \ lcdid=id;\ @@ -195,7 +195,7 @@ case (__COUNTER__ - StringMacroTracker1) : {\ #undef SCREEN #define SCREEN(display,id,msg) \ case (__COUNTER__ - StringMacroTracker1) : {\ - static const char HIGHFLASH const thrunge[]=msg;\ + static const char HIGHFLASH thrunge[]=msg;\ strfar=(uint32_t)GETFARPTR(thrunge);\ tmode=(thrunger)(thrunge_lcd+display); \ lcdid=id;\ @@ -274,7 +274,7 @@ const byte RMFT2::rosterNameCount=0 #include "EXRAIL2MacroReset.h" #undef ROSTER #define ROSTER(cabid,name,funcmap...) cabid, -const int16_t HIGHFLASH const RMFT2::rosterIdList[]={ +const int16_t HIGHFLASH RMFT2::rosterIdList[]={ #include "myAutomation.h" INT16_MAX}; @@ -314,7 +314,7 @@ const FSH * RMFT2::getRosterFunctions(int16_t id) { #undef VIRTUAL_SIGNAL #define VIRTUAL_SIGNAL(id) id,0,0,0, -const HIGHFLASH int16_t const RMFT2::SignalDefinitions[] = { +const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #include "myAutomation.h" 0,0,0,0 }; @@ -503,7 +503,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; // Build RouteCode const int StringMacroTracker2=__COUNTER__; -const HIGHFLASH byte const RMFT2::RouteCode[] = { +const HIGHFLASH byte RMFT2::RouteCode[] = { #include "myAutomation.h" OPCODE_ENDTASK,0,0,OPCODE_ENDEXRAIL,0,0 }; From 2c1b3e0a8fc4f43d0d48527a940de34d30dc9a4a Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Nov 2023 21:16:42 +0100 Subject: [PATCH 196/218] version tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 1fc5a39..3710cad 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311211437Z" +#define GITHUB_SHA "devel-202311212016Z" From 478e9661bb2110b3bbb50c49c6ccb8ec30f565cf Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 21 Nov 2023 21:14:54 +0000 Subject: [PATCH 197/218] EXRAIL ling segment --- EXRAIL2.h | 2 +- EXRAILMacros.h | 16 ++++++++-------- FSH.h | 3 +++ 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/EXRAIL2.h b/EXRAIL2.h index 8f777f4..5249f72 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -203,7 +203,7 @@ private: uint16_t getOperand(byte n); static bool diag; - static const HIGHFLASH byte RouteCode[]; + static const HIGHFLASH3 byte RouteCode[]; static const HIGHFLASH int16_t SignalDefinitions[]; static byte flags[MAX_FLAGS]; static Print * LCCSerial; diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 8edbd2b..9048973 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -121,14 +121,14 @@ const byte RMFT2::compileFeatures = 0 #include "EXRAIL2MacroReset.h" #undef ROUTE #define ROUTE(id, description) id, -const int16_t HIGHFLASH const RMFT2::routeIdList[]= { +const int16_t HIGHFLASH RMFT2::routeIdList[]= { #include "myAutomation.h" INT16_MAX}; // Pass 2a create throttle automation list #include "EXRAIL2MacroReset.h" #undef AUTOMATION #define AUTOMATION(id, description) id, -const int16_t HIGHFLASH const RMFT2::automationIdList[]= { +const int16_t HIGHFLASH RMFT2::automationIdList[]= { #include "myAutomation.h" INT16_MAX}; @@ -150,7 +150,7 @@ const FSH * RMFT2::getRouteDescription(int16_t id) { const int StringMacroTracker1=__COUNTER__; #define THRUNGE(msg,mode) \ case (__COUNTER__ - StringMacroTracker1) : {\ - static const char HIGHFLASH const thrunge[]=msg;\ + static const char HIGHFLASH thrunge[]=msg;\ strfar=(uint32_t)GETFARPTR(thrunge);\ tmode=mode;\ break;\ @@ -186,7 +186,7 @@ case (__COUNTER__ - StringMacroTracker1) : {\ #undef LCD #define LCD(id,msg) \ case (__COUNTER__ - StringMacroTracker1) : {\ - static const char HIGHFLASH const thrunge[]=msg;\ + static const char HIGHFLASH thrunge[]=msg;\ strfar=(uint32_t)GETFARPTR(thrunge);\ tmode=thrunge_lcd; \ lcdid=id;\ @@ -195,7 +195,7 @@ case (__COUNTER__ - StringMacroTracker1) : {\ #undef SCREEN #define SCREEN(display,id,msg) \ case (__COUNTER__ - StringMacroTracker1) : {\ - static const char HIGHFLASH const thrunge[]=msg;\ + static const char HIGHFLASH thrunge[]=msg;\ strfar=(uint32_t)GETFARPTR(thrunge);\ tmode=(thrunger)(thrunge_lcd+display); \ lcdid=id;\ @@ -274,7 +274,7 @@ const byte RMFT2::rosterNameCount=0 #include "EXRAIL2MacroReset.h" #undef ROSTER #define ROSTER(cabid,name,funcmap...) cabid, -const int16_t HIGHFLASH const RMFT2::rosterIdList[]={ +const int16_t HIGHFLASH RMFT2::rosterIdList[]={ #include "myAutomation.h" INT16_MAX}; @@ -314,7 +314,7 @@ const FSH * RMFT2::getRosterFunctions(int16_t id) { #undef VIRTUAL_SIGNAL #define VIRTUAL_SIGNAL(id) id,0,0,0, -const HIGHFLASH int16_t const RMFT2::SignalDefinitions[] = { +const HIGHFLASH int16_t RMFT2::SignalDefinitions[] = { #include "myAutomation.h" 0,0,0,0 }; @@ -503,7 +503,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; // Build RouteCode const int StringMacroTracker2=__COUNTER__; -const HIGHFLASH byte const RMFT2::RouteCode[] = { +const HIGHFLASH3 byte RMFT2::RouteCode[] = { #include "myAutomation.h" OPCODE_ENDTASK,0,0,OPCODE_ENDEXRAIL,0,0 }; diff --git a/FSH.h b/FSH.h index d031935..280d37e 100644 --- a/FSH.h +++ b/FSH.h @@ -56,6 +56,7 @@ typedef __FlashStringHelper FSH; #if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560) // AVR_MEGA memory deliberately placed at end of link may need _far functions #define HIGHFLASH __attribute__((section(".fini2"))) +#define HIGHFLASH3 __attribute__((section(".fini3"))) #define GETFARPTR(data) pgm_get_far_address(data) #define GETHIGHFLASH(data,offset) pgm_read_byte_far(GETFARPTR(data)+offset) #define GETHIGHFLASHW(data,offset) pgm_read_word_far(GETFARPTR(data)+offset) @@ -63,6 +64,7 @@ typedef __FlashStringHelper FSH; // AVR_UNO/NANO runtime does not support _far functions so just use _near equivalent // as there is no progmem above 32kb anyway. #define HIGHFLASH PROGMEM +#define HIGHFLASH3 PROGMEM #define GETFARPTR(data) ((uint32_t)(data)) #define GETHIGHFLASH(data,offset) pgm_read_byte_near(GETFARPTR(data)+(offset)) #define GETHIGHFLASHW(data,offset) pgm_read_word_near(GETFARPTR(data)+(offset)) @@ -80,6 +82,7 @@ typedef __FlashStringHelper FSH; typedef char FSH; #define FLASH #define HIGHFLASH +#define HIGHFLASH3 #define GETFARPTR(data) ((uint32_t)(data)) #define GETFLASH(addr) (*(const byte *)(addr)) #define GETHIGHFLASH(data,offset) (*(const byte *)(GETFARPTR(data)+offset)) From ac4af407aa26b8773d2fc074d641f4dcaf243a15 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Nov 2023 22:47:48 +0100 Subject: [PATCH 198/218] On ESP32, the inversion is already done in HW --- MotorDriver.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/MotorDriver.h b/MotorDriver.h index 19bfd13..07ff93f 100644 --- a/MotorDriver.h +++ b/MotorDriver.h @@ -156,8 +156,10 @@ class MotorDriver { // from outside interrupt void setBrake( bool on, bool interruptContext=false); __attribute__((always_inline)) inline void setSignal( bool high) { +#ifndef ARDUINO_ARCH_ESP32 if (invertPhase) high = !high; +#endif if (trackPWM) { DCCTimer::setPWM(signalPin,high); } From d0df9f3c33d10e30ca54f939758f5c8394b39582 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Nov 2023 22:48:35 +0100 Subject: [PATCH 199/218] version tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 3710cad..d69d7ae 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311212016Z" +#define GITHUB_SHA "devel-202311212148Z" From 4308739c2b7663e47411c6624f626e4087cef268 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Tue, 21 Nov 2023 23:04:05 +0100 Subject: [PATCH 200/218] version 5.2.7 --- version.h | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 0f26a21..b2829c7 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,10 @@ #include "StringFormatter.h" -#define VERSION "5.2.6" +#define VERSION "5.2.7" +// 5.2.7 - Bugfix: EXRAIL ling segment +// - Bugfix: Back out wrongly added const +// - Bugfix ESP32: Do not inverse DCX direction signal twice // 5.2.6 - Trackmanager broadcast power state on track mode change // 5.2.5 - Trackmanager: Do not treat TRACK_MODE_ALL as TRACK_MODE_DC // 5.2.4 - LCD macro will not do diag if that duplicates @ to same target. From 03db06f2ee52bc0838b37f581ae20195d335d4e4 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 22 Nov 2023 10:53:34 +0100 Subject: [PATCH 201/218] Bugfix: Do not turn on track with trackmode NONE --- TrackManager.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index a7998f4..846c163 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -511,12 +511,15 @@ void TrackManager::setTrackPower(TRACK_MODE trackmodeToMatch, POWERMODE powermod void TrackManager::setTrackPower(POWERMODE powermode, byte t) { MotorDriver *driver=track[t]; TRACK_MODE trackmode = driver->getMode(); - if (trackmode & TRACK_MODE_DC) { + if (trackmode & TRACK_MODE_NONE) { + driver->setBrake(true); // Track is unused. Brake is good to have. + powermode = POWERMODE::OFF; // Track is unused. Force it to OFF + } else if (trackmode & TRACK_MODE_DC) { // includes inverted DC (called DCX) if (powermode == POWERMODE::ON) { driver->setBrake(true); // DC starts with brake on applyDCSpeed(t); // speed match DCC throttles } - } else { + } else /* MAIN PROG EXT BOOST */ { if (powermode == POWERMODE::ON) { // toggle brake before turning power on - resets overcurrent error // on the Pololu board if brake is wired to ^D2. From ef47257d67afc8b66f33b0cbbaea10e4cdddb6e7 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Wed, 22 Nov 2023 10:54:01 +0100 Subject: [PATCH 202/218] version tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index d69d7ae..42f43fd 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311212148Z" +#define GITHUB_SHA "devel-202311220953Z" From b478056a9fbbe9b0f45ba49fd2748f080d78f99f Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 23 Nov 2023 09:00:49 +0000 Subject: [PATCH 203/218] Fix @ reporting on startup --- CommandDistributor.cpp | 2 +- CommandStation-EX.ino | 2 +- Display_Implementation.h | 4 +++- myHalKCS.cpp | 1 + 4 files changed, 6 insertions(+), 3 deletions(-) create mode 100644 myHalKCS.cpp diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 1f11e28..aef024b 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -330,7 +330,7 @@ void CommandDistributor::setVirtualLCDSerial(Print * stream) { virtualLCDSerial=stream; } -Print* CommandDistributor::virtualLCDSerial=nullptr; +Print* CommandDistributor::virtualLCDSerial=&USB_SERIAL; byte CommandDistributor::virtualLCDClient=0xFF; byte CommandDistributor::rememberVLCDClient=0; diff --git a/CommandStation-EX.ino b/CommandStation-EX.ino index 77e8f40..205babf 100644 --- a/CommandStation-EX.ino +++ b/CommandStation-EX.ino @@ -87,7 +87,7 @@ void setup() DISPLAY_START ( // This block is still executed for DIAGS if display not in use - LCD(0,F("DCC-EX v%S"),F(VERSION)); + LCD(0,F("DCC-EX v" VERSION)); LCD(1,F("Lic GPLv3")); ); diff --git a/Display_Implementation.h b/Display_Implementation.h index ca19bd7..6a3c995 100644 --- a/Display_Implementation.h +++ b/Display_Implementation.h @@ -54,7 +54,9 @@ xxx; \ t->refresh();} #else - #define DISPLAY_START(xxx) {} + #define DISPLAY_START(xxx) { \ + xxx; \ + } #endif #endif // LCD_Implementation_h diff --git a/myHalKCS.cpp b/myHalKCS.cpp new file mode 100644 index 0000000..a330ae7 --- /dev/null +++ b/myHalKCS.cpp @@ -0,0 +1 @@ +// If you are planning to use multiple mux devices, perhaps it is good to use the 3-position syntax for all. From 784934024ef9a0b7db67fcf831a02688102e021b Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 23 Nov 2023 10:47:43 +0100 Subject: [PATCH 204/218] Bugfix: Do not turn off all tracks on change ; give better power messages --- CommandDistributor.cpp | 53 +++++++++++++++++++++++++++++++++++------- TrackManager.cpp | 16 +++++++------ TrackManager.h | 5 +++- 3 files changed, 57 insertions(+), 17 deletions(-) diff --git a/CommandDistributor.cpp b/CommandDistributor.cpp index 1f11e28..a30fcd3 100644 --- a/CommandDistributor.cpp +++ b/CommandDistributor.cpp @@ -254,21 +254,56 @@ void CommandDistributor::broadcastPower() { if (TrackManager::getPower(t, pstr)) broadcastReply(COMMAND_TYPE, F("\n"),pstr); + byte trackcount=0; + byte oncount=0; + byte offcount=0; + for(byte t=0; t\n"),state,reason); + if (join) { + reason = F("JOIN"); + broadcastReply(COMMAND_TYPE, F("\n"),reason); + } else { + if (main) { + //reason = F("MAIN"); + broadcastReply(COMMAND_TYPE, F("\n")); + } + if (prog) { + //reason = F("PROG"); + broadcastReply(COMMAND_TYPE, F("\n")); + } + } + + if (state != '2') + broadcastReply(COMMAND_TYPE, F("\n"),state); #ifdef CD_HANDLE_RING - broadcastReply(WITHROTTLE_TYPE, F("PPA%c\n"), main?'1':'0'); + // send '1' if all main are on, otherwise global state (which in that case is '0' or '2') + broadcastReply(WITHROTTLE_TYPE, F("PPA%c\n"), main?'1': state); #endif - LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason); + + LCD(2,F("Power %S %S"),state=='1'?F("On"): ( state=='0'? F("Off") : F("SC") ),reason); } void CommandDistributor::broadcastRaw(clientType type, char * msg) { diff --git a/TrackManager.cpp b/TrackManager.cpp index 846c163..ac44c76 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -54,7 +54,6 @@ const int16_t HASH_KEYWORD_INV = 11857; MotorDriver * TrackManager::track[MAX_TRACKS]; int16_t TrackManager::trackDCAddr[MAX_TRACKS]; -POWERMODE TrackManager::mainPowerGuess=POWERMODE::OFF; byte TrackManager::lastTrack=0; bool TrackManager::progTrackSyncMain=false; bool TrackManager::progTrackBoosted=false; @@ -210,6 +209,9 @@ void TrackManager::setDCSignal(int16_t cab, byte speedbyte) { bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr) { if (trackToSet>lastTrack || track[trackToSet]==NULL) return false; + // Remember track mode we came from for later + TRACK_MODE oldmode = track[trackToSet]->getMode(); + //DIAG(F("Track=%c Mode=%d"),trackToSet+'A', mode); // DC tracks require a motorDriver that can set brake! if (mode & TRACK_MODE_DC) { @@ -262,7 +264,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr track[t]->setPower(POWERMODE::OFF); track[t]->setMode(TRACK_MODE_NONE); track[t]->makeProgTrack(false); // revoke prog track special handling - streamTrackState(NULL,t); + streamTrackState(NULL,t); } track[trackToSet]->makeProgTrack(true); // set for prog track special handling } else { @@ -270,7 +272,6 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr } track[trackToSet]->setMode(mode); trackDCAddr[trackToSet]=dcAddr; - streamTrackState(NULL,trackToSet); // When a track is switched, we must clear any side effects of its previous // state, otherwise trains run away or just dont move. @@ -337,10 +338,11 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr applyDCSpeed(trackToSet); } - // Normal running tracks are set to the global power state - track[trackToSet]->setPower( - (mode & (TRACK_MODE_MAIN | TRACK_MODE_DC | TRACK_MODE_EXT | TRACK_MODE_BOOST)) ? - mainPowerGuess : POWERMODE::OFF); + // Turn off power if we changed the mode of this track + if (mode != oldmode) + track[trackToSet]->setPower(POWERMODE::OFF); + streamTrackState(NULL,trackToSet); + //DIAG(F("TrackMode=%d"),mode); return true; } diff --git a/TrackManager.h b/TrackManager.h index f2a357d..12adb4e 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -77,9 +77,13 @@ class TrackManager { static void loop(); static POWERMODE getMainPower(); static POWERMODE getProgPower(); + static inline POWERMODE getPower(byte t) { return track[t]->getPower(); } static bool getPower(byte t, char s[]); static void setJoin(bool join); static bool isJoined() { return progTrackSyncMain;} + static inline bool isActive (byte tr) { + if (tr > lastTrack) return false; + return track[tr]->getMode() & (TRACK_MODE_MAIN|TRACK_MODE_PROG|TRACK_MODE_DC|TRACK_MODE_BOOST|TRACK_MODE_EXT);} static void setJoinRelayPin(byte joinRelayPin); static void sampleCurrent(); static void reportGauges(Print* stream); @@ -108,7 +112,6 @@ class TrackManager { static void addTrack(byte t, MotorDriver* driver); static byte lastTrack; static byte nextCycleTrack; - static POWERMODE mainPowerGuess; static void applyDCSpeed(byte t); static int16_t trackDCAddr[MAX_TRACKS]; // dc address if TRACK_MODE_DC From a16214790ee5cc003204c80864a6e6afe43711b2 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 23 Nov 2023 10:49:15 +0100 Subject: [PATCH 205/218] version 5.2.8 --- 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 42f43fd..28aa3fe 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311220953Z" +#define GITHUB_SHA "devel-202311230948Z" diff --git a/version.h b/version.h index b2829c7..a39bdc3 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.7" +#define VERSION "5.2.8" +// 5.2.8 - Bugfix: Do not turn off all tracks on change +// give better power messages // 5.2.7 - Bugfix: EXRAIL ling segment // - Bugfix: Back out wrongly added const // - Bugfix ESP32: Do not inverse DCX direction signal twice From 2075bc50e827851c2ed8ad5f918c945377f6320f Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 23 Nov 2023 10:41:35 +0000 Subject: [PATCH 206/218] EXRAIL basic stash implementation --- EXRAIL2.cpp | 46 +++++++++++++++++++++++++++++++++++++++++++-- EXRAIL2.h | 4 ++++ EXRAIL2MacroReset.h | 12 ++++++++++-- EXRAILMacros.h | 13 +++++++++++++ myHalKCS.cpp | 1 - 5 files changed, 71 insertions(+), 5 deletions(-) delete mode 100644 myHalKCS.cpp diff --git a/EXRAIL2.cpp b/EXRAIL2.cpp index 6f0a835..f085700 100644 --- a/EXRAIL2.cpp +++ b/EXRAIL2.cpp @@ -86,6 +86,8 @@ LookList * RMFT2::onRotateLookup=NULL; LookList * RMFT2::onOverloadLookup=NULL; byte * RMFT2::routeStateArray=nullptr; const FSH * * RMFT2::routeCaptionArray=nullptr; +int16_t * RMFT2::stashArray=nullptr; +int16_t RMFT2::maxStashId=0; // getOperand instance version, uses progCounter from instance. uint16_t RMFT2::getOperand(byte n) { @@ -232,6 +234,12 @@ if (compileFeatures & FEATURE_SIGNAL) { IODevice::configureInput((VPIN)pin,true); break; } + case OPCODE_STASH: + case OPCODE_CLEAR_STASH: + case OPCODE_PICKUP_STASH: { + maxStashId=max(maxStashId,((int16_t)operand)); + break; + } case OPCODE_ATGTE: case OPCODE_ATLT: @@ -311,8 +319,14 @@ if (compileFeatures & FEATURE_SIGNAL) { } } SKIPOP; // include ENDROUTES opcode - - DIAG(F("EXRAIL %db, fl=%d"),progCounter,MAX_FLAGS); + + if (compileFeatures & FEATURE_STASH) { + // create the stash array from the highest id found + if (maxStashId>0) stashArray=(int16_t*)calloc(maxStashId+1, sizeof(int16_t)); + //TODO check EEPROM and fetch stashArray + } + + DIAG(F("EXRAIL %db, fl=%d, stash=%d"),progCounter,MAX_FLAGS, maxStashArray); // Removed for 4.2.31 new RMFT2(0); // add the startup route diag=saved_diag; @@ -935,6 +949,34 @@ void RMFT2::loop2() { case OPCODE_ROUTE_DISABLED: manageRouteState(operand,4); break; + + case OPCODE_STASH: + if (compileFeatures & FEATURE_STASH) + stashArray[operand] = invert? -loco : loco; + break; + + case OPCODE_CLEAR_STASH: + if (compileFeatures & FEATURE_STASH) + stashArray[operand] = 0; + break; + + case OPCODE_CLEAR_ALL_STASH: + if (compileFeatures & FEATURE_STASH) + for (int i=0;i<=maxStashId;i++) stashArray[operand]=0; + break; + + case OPCODE_PICKUP_STASH: + if (compileFeatures & FEATURE_STASH) { + int16_t x=stashArray[operand]; + if (x>=0) { + loco=x; + invert=false; + break; + } + loco=-x; + invert=true; + } + break; case OPCODE_ROUTE: case OPCODE_AUTOMATION: diff --git a/EXRAIL2.h b/EXRAIL2.h index 5249f72..30a2f45 100644 --- a/EXRAIL2.h +++ b/EXRAIL2.h @@ -70,6 +70,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE, OPCODE_ONOVERLOAD, OPCODE_ROUTE_ACTIVE,OPCODE_ROUTE_INACTIVE,OPCODE_ROUTE_HIDDEN, OPCODE_ROUTE_DISABLED, + OPCODE_STASH,OPCODE_CLEAR_STASH,OPCODE_CLEAR_ALL_STASH,OPCODE_PICKUP_STASH, // OPcodes below this point are skip-nesting IF operations // placed here so that they may be skipped as a group @@ -102,6 +103,7 @@ enum thrunger: byte { static const byte FEATURE_LCC = 0x40; static const byte FEATURE_ROSTER= 0x20; static const byte FEATURE_ROUTESTATE= 0x10; + static const byte FEATURE_STASH = 0x08; // Flag bits for status of hardware and TPL @@ -229,6 +231,8 @@ private: static void manageRouteCaption(uint16_t id, const FSH* caption); static byte * routeStateArray; static const FSH ** routeCaptionArray; + static int16_t * stashArray; + static int16_t maxStashId; // Local variables - exist for each instance/task RMFT2 *next; // loop chain diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index ee240ed..9d23f04 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -39,6 +39,8 @@ #undef AUTOSTART #undef BROADCAST #undef CALL +#undef CLEAR_STASH +#undef CLEAR_ALL_STASH #undef CLOSE #undef DCC_SIGNAL #undef DCC_TURNTABLE @@ -108,6 +110,7 @@ #undef ONCHANGE #undef PARSE #undef PAUSE +#undef PICKUP_STASH #undef PIN_TURNOUT #undef PRINT #ifndef DISABLE_PROG @@ -152,6 +155,7 @@ #undef SIGNALH #undef SPEED #undef START +#undef STASH #undef STOP #undef THROW #undef TT_ADDPOSITION @@ -184,7 +188,9 @@ #define AUTOMATION(id,description) #define AUTOSTART #define BROADCAST(msg) -#define CALL(route) +#define CALL(route) +#define CLEAR_STASH(id) +#define CLEAR_ALL_STASH(id) #define CLOSE(id) #define DCC_SIGNAL(id,add,subaddr) #define DCC_TURNTABLE(id,home,description) @@ -256,6 +262,7 @@ #define PIN_TURNOUT(id,pin,description...) #define PRINT(msg) #define PARSE(msg) +#define PICKUP_STASH(id) #ifndef DISABLE_PROG #define POM(cv,value) #endif @@ -297,7 +304,8 @@ #define SIGNAL(redpin,amberpin,greenpin) #define SIGNALH(redpin,amberpin,greenpin) #define SPEED(speed) -#define START(route) +#define START(route) +#define STASH(id) #define STOP #define THROW(id) #define TT_ADDPOSITION(turntable_id,position,value,angle,description...) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index 0e9842c..ac76efc 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -113,6 +113,15 @@ void exrailHalSetup() { #undef ROUTE_CAPTION #define ROUTE_CAPTION(id,caption) | FEATURE_ROUTESTATE +#undef CLEAR_STASH +#define CLEAR_STASH(id) | FEATURE_STASH +#undef CLEAR_ALL_STASH +#define CLEAR_ALL_STASH | FEATURE_STASH +#undef PICKUP_STASH +#define PICKUP_STASH(id) | FEATURE_STASH +#undef STASH +#define STASH(id) | FEATURE_STASH + const byte RMFT2::compileFeatures = 0 #include "myAutomation.h" ; @@ -353,6 +362,8 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define AUTOSTART OPCODE_AUTOSTART,0,0, #define BROADCAST(msg) PRINT(msg) #define CALL(route) OPCODE_CALL,V(route), +#define CLEAR_STASH(id) OPCODE_CLEAR_STASH,V(id), +#define CLEAR_ALL_STASH OPCODE_CLEAR_ALL_STASH,V(0), #define CLOSE(id) OPCODE_CLOSE,V(id), #ifndef IO_NO_HAL #define DCC_TURNTABLE(id,home,description...) OPCODE_DCCTURNTABLE,V(id),OPCODE_PAD,V(home), @@ -435,6 +446,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id), #define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id), #define PAUSE OPCODE_PAUSE,0,0, +#define PICKUP_STASH(id) OPCODE_PICKUP_STASH,V(id), #define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin), #ifndef DISABLE_PROG #define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value), @@ -482,6 +494,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; #define SIGNALH(redpin,amberpin,greenpin) #define SPEED(speed) OPCODE_SPEED,V(speed), #define START(route) OPCODE_START,V(route), +#define STASH(id) OPCODE_STASH,V(id), #define STOP OPCODE_SPEED,V(0), #define THROW(id) OPCODE_THROW,V(id), #ifndef IO_NO_HAL diff --git a/myHalKCS.cpp b/myHalKCS.cpp deleted file mode 100644 index a330ae7..0000000 --- a/myHalKCS.cpp +++ /dev/null @@ -1 +0,0 @@ -// If you are planning to use multiple mux devices, perhaps it is good to use the 3-position syntax for all. From a5ccb2e29e297d3743f3b8e8a3edcc17fd807842 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 23 Nov 2023 14:15:58 +0000 Subject: [PATCH 207/218] EXRAIL STASH --- DCCEXParser.cpp | 7 +++++++ EXRAIL2.cpp | 2 +- EXRAIL2Parser.cpp | 41 +++++++++++++++++++++++++++++++++++++++-- version.h | 4 +++- 4 files changed, 50 insertions(+), 4 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index 8a81616..abbf54a 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -160,6 +160,7 @@ const int16_t HASH_KEYWORD_C='C'; const int16_t HASH_KEYWORD_G='G'; const int16_t HASH_KEYWORD_H='H'; const int16_t HASH_KEYWORD_I='I'; +const int16_t HASH_KEYWORD_M='M'; const int16_t HASH_KEYWORD_O='O'; const int16_t HASH_KEYWORD_P='P'; const int16_t HASH_KEYWORD_R='R'; @@ -729,6 +730,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) StringFormatter::send(stream, F("\n")); return; + case HASH_KEYWORD_M: // intercepted by EXRAIL + if (params>1) break; // invalid cant do + // requests stash size so say none. + StringFormatter::send(stream,F("\n")); + return; + case HASH_KEYWORD_R: // returns rosters StringFormatter::send(stream, F(" commands to do the following: // - Implement RMFT specific commands/diagnostics @@ -149,7 +151,33 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16 opcode=0; return; } - break; + break; + case HASH_KEYWORD_M: + // NOTE: we only need to handle valid calls here because + // DCCEXParser has to have code to handle the cases where + // exrail isnt involved anyway. + // This entire code block is compiled out if STASH macros not used + if (!(compileFeatures & FEATURE_STASH)) return; + if (paramCount==1) { // + StringFormatter::send(stream,F("\n"),maxStashId); + opcode=0; + break; + } + if (paramCount==2) { // + if (p[1]<=0 || p[1]>maxStashId) break; + StringFormatter::send(stream,F("\n"), + p[1],stashArray[p[1]]); + opcode=0; + break; + } + if (paramCount==3) { // + if (p[1]<=0 || p[1]>maxStashId) break; + stashArray[p[1]]=p[2]; + opcode=0; + break; + } + break; + default: break; } @@ -195,6 +223,15 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) { sigid & SIGNAL_ID_MASK); } } + + if (compileFeatures & FEATURE_STASH) { + for (int i=1;i<=maxStashId;i++) { + if (stashArray[i]) + StringFormatter::send(stream,F("\nSTASH[%d] Loco=%d"), + i, stashArray[i]); + } + } + StringFormatter::send(stream,F(" *>\n")); return true; } diff --git a/version.h b/version.h index a39bdc3..2d89cd0 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.8" +#define VERSION "5.2.9" +// 5.2.9 - Bugfix LCD startup with no LCD, uses <@ +// 5.2.9 - EXRAIL STASH feature // 5.2.8 - Bugfix: Do not turn off all tracks on change // give better power messages // 5.2.7 - Bugfix: EXRAIL ling segment From c8e307db7a63a0a3c16a990e32d6fc1cdab2245d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 23 Nov 2023 22:11:00 +0100 Subject: [PATCH 208/218] remove unused TrackManager::reportPowerChange(...) --- TrackManager.cpp | 9 --------- TrackManager.h | 1 - 2 files changed, 10 deletions(-) diff --git a/TrackManager.cpp b/TrackManager.cpp index ac44c76..4021240 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -532,15 +532,6 @@ void TrackManager::setTrackPower(POWERMODE powermode, byte t) { driver->setPower(powermode); } -void TrackManager::reportPowerChange(Print* stream, byte thistrack) { - // This function is for backward JMRI compatibility only - // It reports the first track only, as main, regardless of track settings. - // - int maxCurrent=track[0]->raw2mA(track[0]->getRawCurrentTripValue()); - StringFormatter::send(stream, F("\n"), - track[0]->raw2mA(track[0]->getCurrentRaw(false)), maxCurrent, maxCurrent); -} - // returns state of the one and only prog track POWERMODE TrackManager::getProgPower() { FOR_EACH_TRACK(t) diff --git a/TrackManager.h b/TrackManager.h index 12adb4e..dd38b72 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -88,7 +88,6 @@ class TrackManager { static void sampleCurrent(); static void reportGauges(Print* stream); static void reportCurrent(Print* stream); - static void reportPowerChange(Print* stream, byte thistrack); static void reportObsoleteCurrent(Print* stream); static void streamTrackState(Print* stream, byte t); static bool isPowerOn(byte t); From 697f228a05d990e028d4085ab1c6d36553d46519 Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 23 Nov 2023 22:14:24 +0100 Subject: [PATCH 209/218] Save progmem with DISABLE_VDPY on Uno --- DCCEXParser.cpp | 4 +++- StringFormatter.cpp | 11 ++++++++--- config.example.h | 12 ++++++++++++ defines.h | 9 ++++----- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/DCCEXParser.cpp b/DCCEXParser.cpp index abbf54a..f3cdfb2 100644 --- a/DCCEXParser.cpp +++ b/DCCEXParser.cpp @@ -847,13 +847,14 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream) case 'L': // LCC interface implemented in EXRAIL parser break; // Will if not intercepted by EXRAIL +#ifndef DISABLE_VDPY case '@': // JMRI saying "give me virtual LCD msgs" CommandDistributor::setVirtualLCDSerial(stream); StringFormatter::send(stream, F("<@ 0 0 \"DCC-EX v" VERSION "\">\n" "<@ 0 1 \"Lic GPLv3\">\n")); return; - +#endif default: //anything else will diagnose and drop out to if (opcode >= ' ' && opcode <= '~') { DIAG(F("Opcode=%c params=%d"), opcode, params); @@ -1064,6 +1065,7 @@ bool DCCEXParser::parseS(Print *stream, int16_t params, int16_t p[]) } bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) { + (void)stream; // arg not used, maybe later? if (params == 0) return false; switch (p[0]) diff --git a/StringFormatter.cpp b/StringFormatter.cpp index 13b5825..9c69877 100644 --- a/StringFormatter.cpp +++ b/StringFormatter.cpp @@ -39,8 +39,11 @@ void StringFormatter::diag( const FSH* input...) { void StringFormatter::lcd(byte row, const FSH* input...) { va_list args; +#ifndef DISABLE_VDPY Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(0,row); - +#else + Print * virtualLCD=NULL; +#endif // Issue the LCD as a diag first // Unless the same serial is asking for the virtual @ respomnse if (virtualLCD!=&USB_SERIAL) { @@ -50,13 +53,14 @@ void StringFormatter::lcd(byte row, const FSH* input...) { send(&USB_SERIAL,F(" *>\n")); } +#ifndef DISABLE_VDPY // send to virtual LCD collector (if any) if (virtualLCD) { va_start(args, input); send2(virtualLCD,input,args); CommandDistributor::commitVirtualLCDSerial(); } - +#endif DisplayInterface::setRow(row); va_start(args, input); send2(DisplayInterface::getDisplayHandler(),input,args); @@ -66,12 +70,14 @@ void StringFormatter::lcd2(uint8_t display, byte row, const FSH* input...) { va_list args; // send to virtual LCD collector (if any) +#ifndef DISABLE_VDPY Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(display,row); if (virtualLCD) { va_start(args, input); send2(virtualLCD,input,args); CommandDistributor::commitVirtualLCDSerial(); } +#endif DisplayInterface::setRow(display, row); va_start(args, input); @@ -250,4 +256,3 @@ void StringFormatter::printHex(Print * stream,uint16_t value) { result[4]='\0'; stream->print(result); } - \ No newline at end of file diff --git a/config.example.h b/config.example.h index 9909371..89d6c1f 100644 --- a/config.example.h +++ b/config.example.h @@ -199,6 +199,18 @@ The configuration file for DCC-EX Command Station // // #define DISABLE_PROG +///////////////////////////////////////////////////////////////////////////////////// +// DISABLE / ENABLE VDPY +// +// The Virtual display "VDPY" feature is by default enabled everywhere +// but on Uno and Nano. If you think you can fit it (for example +// having disabled some of the features above) you can enable it with +// ENABLE_VDPY. You can even disable it on all other CPUs with +// DISABLE_VDPY +// +// #define DISABLE_VDPY +// #define ENABLE_VDPY + ///////////////////////////////////////////////////////////////////////////////////// // REDEFINE WHERE SHORT/LONG ADDR break is. According to NMRA the last short address // is 127 and the first long address is 128. There are manufacturers which have diff --git a/defines.h b/defines.h index e90d7f4..14dd1c5 100644 --- a/defines.h +++ b/defines.h @@ -219,11 +219,10 @@ // The HAL is disabled by default on Nano and Uno platforms, because of limited flash space. // #if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO) - #if defined(DISABLE_DIAG) && defined(DISABLE_EEPROM) && defined(DISABLE_PROG) - #warning you have sacrificed DIAG for HAL - #else - #define IO_NO_HAL - #endif +#define IO_NO_HAL // HAL too big whatever you disable otherwise +#ifndef ENABLE_VDPY +#define DISABLE_VDPY +#endif #endif #if __has_include ( "myAutomation.h") From ebaf1b984ef66084b5534083ef1a81500c57b79d Mon Sep 17 00:00:00 2001 From: Harald Barth Date: Thu, 23 Nov 2023 22:15:03 +0100 Subject: [PATCH 210/218] version tag --- GITHUB_SHA.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 28aa3fe..42d5594 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311230948Z" +#define GITHUB_SHA "devel-202311232114Z" From 96fdbfdc89843a75ec69a1b8ded71161ba1de2e1 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sun, 26 Nov 2023 12:31:41 +0000 Subject: [PATCH 211/218] Trainbrains block occupancy --- IODevice.h | 2 + IO_trainbrains.h | 98 ++++++++++++++++++++++++++++++++++++++++++++++++ version.h | 4 +- 3 files changed, 103 insertions(+), 1 deletion(-) create mode 100644 IO_trainbrains.h diff --git a/IODevice.h b/IODevice.h index 74fe49b..d12fafd 100644 --- a/IODevice.h +++ b/IODevice.h @@ -542,8 +542,10 @@ protected: #include "IO_MCP23017.h" #include "IO_PCF8574.h" #include "IO_PCF8575.h" +#include "IO_PCA9555.h" #include "IO_duinoNodes.h" #include "IO_EXIOExpander.h" +#include "IO_trainbrains.h" #endif // iodevice_h diff --git a/IO_trainbrains.h b/IO_trainbrains.h new file mode 100644 index 0000000..058fe02 --- /dev/null +++ b/IO_trainbrains.h @@ -0,0 +1,98 @@ +/* + * © 2023, Chris Harlow. All rights reserved. + * © 2021, Neil McKechnie. All rights reserved. + * + * This file is part of DCC++EX API + * + * 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 io_trainbrains_h +#define io_trainbrains_h + +#include "IO_GPIOBase.h" +#include "FSH.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////// +/* + * IODevice subclass for trainbrains 3-block occupancy detector. + * For details see http://trainbrains.eu + */ + + enum TrackUnoccupancy +{ + TRACK_UNOCCUPANCY_UNKNOWN = 0, + TRACK_OCCUPIED = 1, + TRACK_UNOCCUPIED = 2 +}; + +class Trainbrains02 : public GPIOBase { +public: + static void create(VPIN vpin, uint8_t nPins, I2CAddress i2cAddress) { + if (checkNoOverlap(vpin, nPins, i2cAddress)) new Trainbrains02(vpin, nPins, i2cAddress); + } + +private: + // Constructor + Trainbrains02(VPIN vpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1) + : GPIOBase((FSH *)F("Trainbrains02"), vpin, nPins, i2cAddress, interruptPin) + { + requestBlock.setRequestParams(_I2CAddress, inputBuffer, sizeof(inputBuffer), + outputBuffer, sizeof(outputBuffer)); + + outputBuffer[0] = (uint8_t)_I2CAddress; // strips away the mux part. + outputBuffer[1] =14; + outputBuffer[2] =1; + outputBuffer[3] =0; // This is the channel updated at each poling call + outputBuffer[4] =0; + outputBuffer[5] =0; + outputBuffer[6] =0; + outputBuffer[7] =0; + outputBuffer[8] =0; + outputBuffer[9] =0; + } + + void _writeGpioPort() override {} + + void _readGpioPort(bool immediate) override { + // cycle channel on device each time + outputBuffer[3]=channelInProgress+1; // 1-origin + channelInProgress++; + if(channelInProgress>=_nPins) channelInProgress=0; + + if (immediate) { + _processCompletion(I2CManager.read(_I2CAddress, inputBuffer, sizeof(inputBuffer), + outputBuffer, sizeof(outputBuffer))); + } else { + // Queue new request + requestBlock.wait(); // Wait for preceding operation to complete + // Issue new request to read GPIO register + I2CManager.queueRequest(&requestBlock); + } + } + + // This function is invoked when an I/O operation on the requestBlock completes. + void _processCompletion(uint8_t status) override { + if (status != I2C_STATUS_OK) inputBuffer[6]=TRACK_UNOCCUPANCY_UNKNOWN; + if (inputBuffer[6] == TRACK_UNOCCUPIED ) _portInputState |= 0x01 < Date: Mon, 27 Nov 2023 08:15:07 +0100 Subject: [PATCH 212/218] Change from TrackManager::returnMode to TrackManager::getMode --- GITHUB_SHA.h | 2 +- TrackManager.cpp | 2 +- TrackManager.h | 2 +- version.h | 3 ++- 4 files changed, 5 insertions(+), 4 deletions(-) diff --git a/GITHUB_SHA.h b/GITHUB_SHA.h index 42d5594..23c6a6e 100644 --- a/GITHUB_SHA.h +++ b/GITHUB_SHA.h @@ -1 +1 @@ -#define GITHUB_SHA "devel-202311232114Z" +#define GITHUB_SHA "devel-202311270714Z" diff --git a/TrackManager.cpp b/TrackManager.cpp index 4021240..7d2b36b 100644 --- a/TrackManager.cpp +++ b/TrackManager.cpp @@ -641,7 +641,7 @@ bool TrackManager::isProg(byte t) { return false; } -byte TrackManager::returnMode(byte t) { +TRACK_MODE TrackManager::getMode(byte t) { return (track[t]->getMode()); } diff --git a/TrackManager.h b/TrackManager.h index dd38b72..6310030 100644 --- a/TrackManager.h +++ b/TrackManager.h @@ -92,7 +92,7 @@ class TrackManager { static void streamTrackState(Print* stream, byte t); static bool isPowerOn(byte t); static bool isProg(byte t); - static byte returnMode(byte t); + static TRACK_MODE getMode(byte t); static int16_t returnDCAddr(byte t); static const FSH* getModeName(TRACK_MODE Mode); diff --git a/version.h b/version.h index 3937937..e8a5e81 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.10" +#define VERSION "5.2.11" +// 5.2.11 - Change from TrackManager::returnMode to TrackManager::getMode // 5.2.10 - Include trainbrains.eu block unoccupancy driver // - include IO_PCA9555 // 5.2.9 - Bugfix LCD startup with no LCD, uses <@ From 07fd4bc309c333e9fccc98f4287ec1866a8cd0aa Mon Sep 17 00:00:00 2001 From: Asbelos Date: Mon, 27 Nov 2023 16:49:02 +0000 Subject: [PATCH 213/218] Window --- DCC.cpp | 2 +- DCCWaveform.cpp | 67 +++++++++++++++++++++++++++++++------------------ DCCWaveform.h | 4 ++- 3 files changed, 46 insertions(+), 27 deletions(-) diff --git a/DCC.cpp b/DCC.cpp index 30fcf5f..e576812 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -595,7 +595,7 @@ void DCC::loop() { void DCC::issueReminders() { // if the main track transmitter still has a pending packet, skip this time around. - if ( DCCWaveform::mainTrack.getPacketPending()) return; + if (!DCCWaveform::mainTrack.isReminderWindowOpen()) return; // Move to next loco slot. If occupied, send a reminder. int reg = lastLocoReminder+1; if (reg > highestUsedReg) reg = 0; // Go to start of table diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 4a99997..0e9667a 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -106,6 +106,7 @@ void DCCWaveform::interruptHandler() { DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) { isMainTrack = isMain; packetPending = false; + reminderWindowOpen = false; memcpy(transmitPacket, idlePacket, sizeof(idlePacket)); state = WAVE_START; // The +1 below is to allow the preamble generator to create the stop bit @@ -127,9 +128,15 @@ void DCCWaveform::interrupt2() { if (remainingPreambles > 0 ) { state=WAVE_MID_1; // switch state to trigger LOW on next interrupt remainingPreambles--; + + // As we get to the end of the preambles, open the reminder window. + // This delays any reminder insertion until the last moment so + // that the reminder doesn't block a more urgent packet. + reminderWindowOpen=transmitRepeats==0 && remainingPreambles<4 && remainingPreambles>1; + if (remainingPreambles==1) promotePendingPacket(); // Update free memory diagnostic as we don't have anything else to do this time. // Allow for checkAck and its called functions using 22 bytes more. - DCCTimer::updateMinimumFreeMemoryISR(22); + else DCCTimer::updateMinimumFreeMemoryISR(22); return; } @@ -148,30 +155,9 @@ void DCCWaveform::interrupt2() { if (bytes_sent >= transmitLength) { // end of transmission buffer... repeat or switch to next message bytes_sent = 0; + // preamble for next packet will start... remainingPreambles = requiredPreambles; - - if (transmitRepeats > 0) { - transmitRepeats--; } - else if (packetPending) { - // Copy pending packet to transmit packet - // a fixed length memcpy is faster than a variable length loop for these small lengths - // for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b]; - memcpy( transmitPacket, pendingPacket, sizeof(pendingPacket)); - - transmitLength = pendingLength; - transmitRepeats = pendingRepeats; - packetPending = false; - clearResets(); - } - else { - // Fortunately reset and idle packets are the same length - memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket)); - transmitLength = sizeof(idlePacket); - transmitRepeats = 0; - if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!) - } - } } } #pragma GCC pop_options @@ -193,8 +179,39 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea packetPending = true; clearResets(); } -bool DCCWaveform::getPacketPending() { - return packetPending; + +bool DCCWaveform::isReminderWindowOpen() { + return reminderWindowOpen && ! packetPending; +} + +void DCCWaveform::promotePendingPacket() { + // fill the transmission packet from the pending packet + + // Just keep going if repeating + if (transmitRepeats > 0) { + transmitRepeats--; + return; + } + + if (packetPending) { + // Copy pending packet to transmit packet + // a fixed length memcpy is faster than a variable length loop for these small lengths + // for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b]; + memcpy( transmitPacket, pendingPacket, sizeof(pendingPacket)); + + transmitLength = pendingLength; + transmitRepeats = pendingRepeats; + packetPending = false; + clearResets(); + return; + } + + // nothing to do, just send idles or resets + // Fortunately reset and idle packets are the same length + memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket)); + transmitLength = sizeof(idlePacket); + transmitRepeats = 0; + if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!) } #endif diff --git a/DCCWaveform.h b/DCCWaveform.h index 1dad1b2..2202b53 100644 --- a/DCCWaveform.h +++ b/DCCWaveform.h @@ -76,11 +76,13 @@ class DCCWaveform { }; #endif void schedulePacket(const byte buffer[], byte byteCount, byte repeats); - bool getPacketPending(); + bool isReminderWindowOpen(); + void promotePendingPacket(); private: #ifndef ARDUINO_ARCH_ESP32 volatile bool packetPending; + volatile bool reminderWindowOpen; volatile byte sentResetsSincePacket; #else volatile uint32_t resetPacketBase; From 3f4099520aaa75c56f7bdbc3a222f094acc4210e Mon Sep 17 00:00:00 2001 From: Asbelos Date: Tue, 28 Nov 2023 19:57:14 +0000 Subject: [PATCH 214/218] ESP32 update for reminders --- DCCWaveform.cpp | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/DCCWaveform.cpp b/DCCWaveform.cpp index 0e9667a..93b21a2 100644 --- a/DCCWaveform.cpp +++ b/DCCWaveform.cpp @@ -283,15 +283,15 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea } } -bool DCCWaveform::getPacketPending() { +bool DCCWaveform::isReminderWindowOpen() { if(isMainTrack) { if (rmtMainChannel == NULL) - return true; - return rmtMainChannel->busy(); + return false; + return !rmtMainChannel->busy(); } else { if (rmtProgChannel == NULL) - return true; - return rmtProgChannel->busy(); + return false; + return !rmtProgChannel->busy(); } } void IRAM_ATTR DCCWaveform::loop() { From 753567427ed99de4bd7a1016826c1439d79062d4 Mon Sep 17 00:00:00 2001 From: pmantoine Date: Thu, 30 Nov 2023 14:38:16 +0800 Subject: [PATCH 215/218] ESP32 LCD messages, STM32 minor updates --- DCCTimerSTM32.cpp | 15 ++++++++++----- WifiESP32.cpp | 12 +++++++++--- version.h | 5 ++++- 3 files changed, 23 insertions(+), 9 deletions(-) diff --git a/DCCTimerSTM32.cpp b/DCCTimerSTM32.cpp index f2d51ff..f24adc2 100644 --- a/DCCTimerSTM32.cpp +++ b/DCCTimerSTM32.cpp @@ -50,11 +50,16 @@ HardwareSerial Serial6(PA12, PA11); // Rx=PA12, Tx=PA11 -- CN10 pins 12 and 14 // via the debugger on the Nucleo-64. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc. // On the F446RE, Serial3 and Serial5 are easy to use: HardwareSerial Serial3(PC11, PC10); // Rx=PC11, Tx=PC10 -- USART3 - F446RE -HardwareSerial Serial5(PD2, PC12); // Rx=PC7, Tx=PC6 -- UART5 - F446RE +HardwareSerial Serial5(PD2, PC12); // Rx=PD2, Tx=PC12 -- UART5 - F446RE // On the F446RE, Serial4 and Serial6 also use pins we can't readily map while using the Arduino pins -#elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE) +#elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F446ZE) || \ + defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F439ZI) // Nucleo-144 boards don't have Serial1 defined by default HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6 +HardwareSerial Serial5(PD2, PC12); // Rx=PD2, Tx=PC12 -- UART5 +#if !defined(ARDUINO_NUCLEO_F412ZG) + HardwareSerial Serial2(PD6, PD5); // Rx=PD6, Tx=PD5 -- UART5 +#endif // Serial3 is defined to use USART3 by default, but is in fact used as the diag console // via the debugger on the Nucleo-144. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc. #else @@ -215,9 +220,9 @@ void DCCTimer::clearPWM() { } void DCCTimer::getSimulatedMacAddress(byte mac[6]) { - volatile uint32_t *serno1 = (volatile uint32_t *)0x1FFF7A10; - volatile uint32_t *serno2 = (volatile uint32_t *)0x1FFF7A14; - // volatile uint32_t *serno3 = (volatile uint32_t *)0x1FFF7A18; + volatile uint32_t *serno1 = (volatile uint32_t *)UID_BASE; + volatile uint32_t *serno2 = (volatile uint32_t *)UID_BASE+4; + // volatile uint32_t *serno3 = (volatile uint32_t *)UID_BASE+8; volatile uint32_t m1 = *serno1; volatile uint32_t m2 = *serno2; diff --git a/WifiESP32.cpp b/WifiESP32.cpp index 28a15fe..f0a857f 100644 --- a/WifiESP32.cpp +++ b/WifiESP32.cpp @@ -163,7 +163,9 @@ bool WifiESP::setup(const char *SSid, delay(500); } if (WiFi.status() == WL_CONNECTED) { - DIAG(F("Wifi STA IP %s"),WiFi.localIP().toString().c_str()); + // DIAG(F("Wifi STA IP %s"),WiFi.localIP().toString().c_str()); + DIAG(F("Wifi in STA mode")); + LCD(7, F("IP: %s"), WiFi.softAPIP().toString().c_str()); wifiUp = true; } else { DIAG(F("Could not connect to Wifi SSID %s"),SSid); @@ -209,8 +211,12 @@ bool WifiESP::setup(const char *SSid, if (WiFi.softAP(strSSID.c_str(), havePassword ? password : strPass.c_str(), channel, false, 8)) { - DIAG(F("Wifi AP SSID %s PASS %s"),strSSID.c_str(),havePassword ? password : strPass.c_str()); - DIAG(F("Wifi AP IP %s"),WiFi.softAPIP().toString().c_str()); + // DIAG(F("Wifi AP SSID %s PASS %s"),strSSID.c_str(),havePassword ? password : strPass.c_str()); + DIAG(F("Wifi in AP mode")); + LCD(5, F("Wifi: %s"), strSSID.c_str()); + LCD(6, F("PASS: %s"),havePassword ? password : strPass.c_str()); + // DIAG(F("Wifi AP IP %s"),WiFi.softAPIP().toString().c_str()); + LCD(7, F("IP: %s"),WiFi.softAPIP().toString().c_str()); wifiUp = true; APmode = true; } else { diff --git a/version.h b/version.h index e8a5e81..a0cb16a 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,10 @@ #include "StringFormatter.h" -#define VERSION "5.2.11" +#define VERSION "5.2.12" +// 5.2.12 - ESP32 add AP mode LCD messages with SSID/PW for +// - STM32 change to UID_BASE constants in DCCTimerSTM32 rather than raw hex addresses for UID registers +// - STM32 extra UART/USARTs for larger Nucleo models // 5.2.11 - Change from TrackManager::returnMode to TrackManager::getMode // 5.2.10 - Include trainbrains.eu block unoccupancy driver // - include IO_PCA9555 From 763c9d8ae6d2d46fe09cbaff4f4ac82bef94496a Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 30 Nov 2023 11:32:39 +0000 Subject: [PATCH 216/218] STEALTH --- EXRAIL2MacroReset.h | 2 ++ EXRAILMacros.h | 3 +++ version.h | 3 ++- 3 files changed, 7 insertions(+), 1 deletion(-) diff --git a/EXRAIL2MacroReset.h b/EXRAIL2MacroReset.h index 9d23f04..7811a0d 100644 --- a/EXRAIL2MacroReset.h +++ b/EXRAIL2MacroReset.h @@ -156,6 +156,7 @@ #undef SPEED #undef START #undef STASH +#undef STEALTH #undef STOP #undef THROW #undef TT_ADDPOSITION @@ -306,6 +307,7 @@ #define SPEED(speed) #define START(route) #define STASH(id) +#define STEALTH(code...) #define STOP #define THROW(id) #define TT_ADDPOSITION(turntable_id,position,value,angle,description...) diff --git a/EXRAILMacros.h b/EXRAILMacros.h index ac76efc..f79693d 100644 --- a/EXRAILMacros.h +++ b/EXRAILMacros.h @@ -210,6 +210,8 @@ case (__COUNTER__ - StringMacroTracker1) : {\ lcdid=id;\ break;\ } +#undef STEALTH +#define STEALTH(code...) case (__COUNTER__ - StringMacroTracker1) : {code} return; #undef WITHROTTLE #define WITHROTTLE(msg) THRUNGE(msg,thrunge_withrottle) @@ -422,6 +424,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup]; OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF), #define LCD(id,msg) PRINT(msg) #define SCREEN(display,id,msg) PRINT(msg) +#define STEALTH(code...) PRINT(dummy) #define LCN(msg) PRINT(msg) #define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0), #define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr), diff --git a/version.h b/version.h index a0cb16a..0304500 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,8 @@ #include "StringFormatter.h" -#define VERSION "5.2.12" +#define VERSION "5.2.13" +// 5.2.13 - EXRAIL STEALTH // 5.2.12 - ESP32 add AP mode LCD messages with SSID/PW for // - STM32 change to UID_BASE constants in DCCTimerSTM32 rather than raw hex addresses for UID registers // - STM32 extra UART/USARTs for larger Nucleo models From a69017f8bbd71aa09e1c8bd53f6b44fe8d62d677 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 30 Nov 2023 19:48:02 +0000 Subject: [PATCH 217/218] Optional DISABLE_FUNCTION_REMINDERS --- DCC.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/DCC.cpp b/DCC.cpp index e576812..60c07df 100644 --- a/DCC.cpp +++ b/DCC.cpp @@ -620,14 +620,23 @@ bool DCC::issueReminder(int reg) { case 1: // remind function group 1 (F0-F4) if (flags & FN_GROUP_1) setFunctionInternal(loco,0, 128 | ((functions>>1)& 0x0F) | ((functions & 0x01)<<4)); // 100D DDDD +#ifdef DISABLE_FUNCTION_REMINDERS + flags&= ~FN_GROUP_1; // dont send them again +#endif break; case 2: // remind function group 2 F5-F8 if (flags & FN_GROUP_2) setFunctionInternal(loco,0, 176 | ((functions>>5)& 0x0F)); // 1011 DDDD +#ifdef DISABLE_FUNCTION_REMINDERS + flags&= ~FN_GROUP_2; // dont send them again +#endif break; case 3: // remind function group 3 F9-F12 if (flags & FN_GROUP_3) setFunctionInternal(loco,0, 160 | ((functions>>9)& 0x0F)); // 1010 DDDD +#ifdef DISABLE_FUNCTION_REMINDERS + flags&= ~FN_GROUP_3; // dont send them again +#endif break; case 4: // remind function group 4 F13-F20 if (flags & FN_GROUP_4) From 08f0a2b37de4297e045f7a2597c1b6786242b077 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 30 Nov 2023 19:56:58 +0000 Subject: [PATCH 218/218] 5.2.13 --- version.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/version.h b/version.h index 0304500..a6265d8 100644 --- a/version.h +++ b/version.h @@ -3,7 +3,9 @@ #include "StringFormatter.h" -#define VERSION "5.2.13" +#define VERSION "5.2.14" +// 5.2.14 - Reminder window DCC packet optimization +// - Optional #define DISABLE_FUNCTION_REMINDERS // 5.2.13 - EXRAIL STEALTH // 5.2.12 - ESP32 add AP mode LCD messages with SSID/PW for // - STM32 change to UID_BASE constants in DCCTimerSTM32 rather than raw hex addresses for UID registers