From 7af5effba97b3f6a5b279e23192a985e3acb7f7d Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 25 Jul 2020 11:00:04 +0100 Subject: [PATCH 1/6] AnalogReadFast Version copied from CommandStation --- AnalogReadFast.h | 101 ++++++++++++++++++++++++++++++++++++++++ Hardware.cpp | 2 +- avdweb_AnalogReadFast.h | 55 ---------------------- 3 files changed, 102 insertions(+), 56 deletions(-) create mode 100644 AnalogReadFast.h delete mode 100644 avdweb_AnalogReadFast.h diff --git a/AnalogReadFast.h b/AnalogReadFast.h new file mode 100644 index 0000000..3cd6041 --- /dev/null +++ b/AnalogReadFast.h @@ -0,0 +1,101 @@ +/* + * AnalogReadFast.h + * + * Copyright (C) 2016 Albert van Dalen http://www.avdweb.nl + * + * This file is part of CommandStation. + * + * CommandStation 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. + * + * CommandStation 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 COMMANDSTATION_DCC_ANALOGREADFAST_H_ +#define COMMANDSTATION_DCC_ANALOGREADFAST_H_ + +#include + +int inline analogReadFast(uint8_t ADCpin); + +#if defined(ARDUINO_ARCH_SAMD) +int inline analogReadFast(uint8_t ADCpin) +{ ADC->CTRLA.bit.ENABLE = 0; // disable ADC + while( ADC->STATUS.bit.SYNCBUSY == 1 ); // wait for synchronization + + int CTRLBoriginal = ADC->CTRLB.reg; + int AVGCTRLoriginal = ADC->AVGCTRL.reg; + int SAMPCTRLoriginal = ADC->SAMPCTRL.reg; + + ADC->CTRLB.reg &= 0b1111100011111111; // mask PRESCALER bits + ADC->CTRLB.reg |= ADC_CTRLB_PRESCALER_DIV64; // divide Clock by 64 + ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1 | // take 1 sample + ADC_AVGCTRL_ADJRES(0x00ul); // adjusting result by 0 + ADC->SAMPCTRL.reg = 0x00; // sampling Time Length = 0 + + ADC->CTRLA.bit.ENABLE = 1; // enable ADC + while(ADC->STATUS.bit.SYNCBUSY == 1); // wait for synchronization + + int adc = analogRead(ADCpin); + + ADC->CTRLB.reg = CTRLBoriginal; + ADC->AVGCTRL.reg = AVGCTRLoriginal; + ADC->SAMPCTRL.reg = SAMPCTRLoriginal; + + return adc; +} + +#elif defined(ARDUINO_ARCH_SAMC) + +int inline analogReadFast(uint8_t ADCpin) +{ + Adc* ADC; + if ( (g_APinDescription[ADCpin].ulPeripheralAttribute & PER_ATTR_ADC_MASK) == PER_ATTR_ADC_STD ) { + ADC = ADC0; + } else { + ADC = ADC1; + } + + ADC->CTRLA.bit.ENABLE = 0; // disable ADC + while( ADC->SYNCBUSY.bit.ENABLE == 1 ); // wait for synchronization + + int CTRLBoriginal = ADC->CTRLB.reg; + int AVGCTRLoriginal = ADC->AVGCTRL.reg; + int SAMPCTRLoriginal = ADC->SAMPCTRL.reg; + + ADC->CTRLB.reg &= 0b1111100011111111; // mask PRESCALER bits + ADC->CTRLB.reg |= ADC_CTRLB_PRESCALER_DIV64; // divide Clock by 64 + ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1 | // take 1 sample + ADC_AVGCTRL_ADJRES(0x00ul); // adjusting result by 0 + ADC->SAMPCTRL.reg = 0x00; // sampling Time Length = 0 + + ADC->CTRLA.bit.ENABLE = 1; // enable ADC + while(ADC->SYNCBUSY.bit.ENABLE == 1); // wait for synchronization + + int adc = analogRead(ADCpin); + + ADC->CTRLB.reg = CTRLBoriginal; + ADC->AVGCTRL.reg = AVGCTRLoriginal; + ADC->SAMPCTRL.reg = SAMPCTRLoriginal; + + return adc; +} + +#else +int inline analogReadFast(uint8_t ADCpin) +{ byte ADCSRAoriginal = ADCSRA; + ADCSRA = (ADCSRA & B11111000) | 4; + int adc = analogRead(ADCpin); + ADCSRA = ADCSRAoriginal; + return adc; +} +#endif +#endif // COMMANDSTATION_DCC_ANALOGREADFAST_H_ \ No newline at end of file diff --git a/Hardware.cpp b/Hardware.cpp index 91e475d..65cebeb 100644 --- a/Hardware.cpp +++ b/Hardware.cpp @@ -19,7 +19,7 @@ #include //#include // use IDE menu Tools..Manage Libraries to locate and install TimerOne #include // use IDE menu Tools..Manage Libraries to locate and install TimerOne -#include "avdweb_AnalogReadFast.h" +#include "AnalogReadFast.h" #include "Hardware.h" #include "Config.h" #include "DIAG.h" diff --git a/avdweb_AnalogReadFast.h b/avdweb_AnalogReadFast.h deleted file mode 100644 index ae68432..0000000 --- a/avdweb_AnalogReadFast.h +++ /dev/null @@ -1,55 +0,0 @@ -/* -Copyright (C) 2016 Albert van Dalen http://www.avdweb.nl -This program 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. -This program 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 at http://www.gnu.org/licenses . - -AUTHOR: Albert van Dalen -WEBSITE: http://www.avdweb.nl/arduino/libraries/fast-10-bit-adc.html -UPDATE: https://www.avdweb.nl/arduino/adc-dac/fast-10-bit-adc -HISTORY: -1.0.0 7-3-2018 analogReadFast for SAMD21 (10/12bit) and AVR (10bit) -*/ - -#ifndef analogReadFast_H -#define analogReadFast_H - -int inline analogReadFast(byte ADCpin); - -#if defined(__arm__) -int inline analogReadFast(byte ADCpin) // inline library functions must be in header -{ ADC->CTRLA.bit.ENABLE = 0; // disable ADC - while( ADC->STATUS.bit.SYNCBUSY == 1 ); // wait for synchronization - - int CTRLBoriginal = ADC->CTRLB.reg; - int AVGCTRLoriginal = ADC->AVGCTRL.reg; - int SAMPCTRLoriginal = ADC->SAMPCTRL.reg; - - ADC->CTRLB.reg &= 0b1111100011111111; // mask PRESCALER bits - ADC->CTRLB.reg |= ADC_CTRLB_PRESCALER_DIV64; // divide Clock by 64 - ADC->AVGCTRL.reg = ADC_AVGCTRL_SAMPLENUM_1 | // take 1 sample - ADC_AVGCTRL_ADJRES(0x00ul); // adjusting result by 0 - ADC->SAMPCTRL.reg = 0x00; // sampling Time Length = 0 - - ADC->CTRLA.bit.ENABLE = 1; // enable ADC - while(ADC->STATUS.bit.SYNCBUSY == 1); // wait for synchronization - - int adc = analogRead(ADCpin); - - ADC->CTRLB.reg = CTRLBoriginal; - ADC->AVGCTRL.reg = AVGCTRLoriginal; - ADC->SAMPCTRL.reg = SAMPCTRLoriginal; - - return adc; -} -#else -int inline analogReadFast(byte ADCpin) -{ byte ADCSRAoriginal = ADCSRA; - ADCSRA = (ADCSRA & B11111000) | 4; - int adc = analogRead(ADCpin); - ADCSRA = ADCSRAoriginal; - return adc; -} -#endif -#endif From 95a5f7b9a4e26e04bb7242ea0f0399af48167c94 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 25 Jul 2020 14:40:59 +0100 Subject: [PATCH 2/6] Revert "Merge branch 'thingietest'" This reverts commit 79f552ab5f56d408654ce3e14cecc53804b1c2be, reversing changes made to 7af5effba97b3f6a5b279e23192a985e3acb7f7d. --- CVReader.ino | 6 +++--- WifiInterface.cpp | 26 +++++++------------------- 2 files changed, 10 insertions(+), 22 deletions(-) diff --git a/CVReader.ino b/CVReader.ino index 39ef57d..1abe39c 100644 --- a/CVReader.ino +++ b/CVReader.ino @@ -92,8 +92,8 @@ void setup() { // setup(serial, F(router name), F(password) , port) // #ifdef WIFI - Serial2.begin(115200); - WifiInterface::setup(Serial2, F("BTHub5-M6PT"), F("49de8d4862"),F("DCCEX"),F("CVReader"),3532); // (3532 is 0xDCC decimal... ) + Serial1.begin(115200); + WifiInterface::setup(Serial1, F("BTHub5-M6PT"), F("49de8d4862"),F("DCCEX"),F("CVReader"),3532); // (3532 is 0xDCC decimal... ) #endif // This is just for demonstration purposes @@ -121,7 +121,7 @@ void loop() { // Responsibility 3: Optionally handle any incoming WiFi traffic #ifdef WIFI - WifiInterface::loop(Serial2); + WifiInterface::loop(Serial1); #endif // Your additional code diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 21963bc..34efe1b 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -23,7 +23,6 @@ #include "WiThrottle.h" #include "HTTPParser.h" const char PROGMEM READY_SEARCH[] ="\r\nready\r\n"; -const char PROGMEM WIFIOK_SEARCH[] ="\r\nWIFI GOT IPready\r\n"; const char PROGMEM OK_SEARCH[] ="\r\nOK\r\n"; const char PROGMEM END_DETAIL_SEARCH[] ="@ 1000"; const char PROGMEM PROMPT_SEARCH[] =">"; @@ -57,28 +56,20 @@ bool WifiInterface::setup2(Stream & wifiStream, const __FlashStringHelper* SSid, //checkForOK(wifiStream,5000,END_DETAIL_SEARCH,true); // Show startup but ignore unreadable upto ready checkForOK(wifiStream,5000,READY_SEARCH,true); -// StringFormatter::send(wifiStream,F("AT+CWMODE=3\r\n")); // configure as server or access point -// checkForOK(wifiStream,1000,OK_SEARCH,true); // Not always OK, sometimes "no change" + StringFormatter::send(wifiStream,F("AT+CWMODE=3\r\n")); // configure as server or access point + checkForOK(wifiStream,1000,OK_SEARCH,true); // Not always OK, sometimes "no change" -// StringFormatter::send(wifiStream, F("AT+CWHOSTNAME=\"%S\"\r\n"), hostname); // Set Host name for Wifi Client -// checkForOK(wifiStream,2000, OK_SEARCH, true); + StringFormatter::send(wifiStream, F("AT+CWHOSTNAME=\"%S\"\r\n"), hostname); // Set Host name for Wifi Client + checkForOK(wifiStream,2000, OK_SEARCH, true); // Older ES versions have AT+CWJAP, newer ones have AT+CWJAP_CUR and AT+CWHOSTNAME -// StringFormatter::send(wifiStream,F("AT+CWJAP=\"%S\",\"%S\"\r\n"),SSid,password); -// if (!checkForOK(wifiStream,20000,OK_SEARCH,true)) { -// StringFormatter::send(wifiStream,F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"),SSid,password); -// if (!checkForOK(wifiStream,20000,OK_SEARCH,true)) return false; -// } - - checkForOK(wifiStream,5000,WIFIOK_SEARCH,true); - - StringFormatter::send(wifiStream,F("AT+CWJAP?\r\n"),SSid,password); + StringFormatter::send(wifiStream,F("AT+CWJAP=\"%S\",\"%S\"\r\n"),SSid,password); if (!checkForOK(wifiStream,20000,OK_SEARCH,true)) { - StringFormatter::send(wifiStream,F("AT+CWJAP_CUR?\r\n"),SSid,password); + StringFormatter::send(wifiStream,F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"),SSid,password); if (!checkForOK(wifiStream,20000,OK_SEARCH,true)) return false; } - + StringFormatter::send(wifiStream,F("AT+CIFSR\r\n")); // get ip address //192.168.4.1 if (!checkForOK(wifiStream,10000,OK_SEARCH,true)) return false; @@ -89,9 +80,6 @@ bool WifiInterface::setup2(Stream & wifiStream, const __FlashStringHelper* SSid, StringFormatter::send(wifiStream,F("AT+CIPSERVER=1,%d\r\n"),port); // turn on server on port 80 if (!checkForOK(wifiStream,10000,OK_SEARCH,true)) return false; - StringFormatter::send(wifiStream,F("AT+CIPRECVMODE=0\r\n"),port); // turn on server on port 80 - if (!checkForOK(wifiStream,10000,OK_SEARCH,true)) return false; - // StringFormatter::send(wifiStream, F("AT+MDNS=1,\"%S.local\",\"%S.local\",%d\r\n"), hostname, servername, port); // Setup mDNS for Server // if (!checkForOK(wifiStream,5000, OK_SEARCH, true)) return false; (void)servername; // not currently in use From 32b507248d9393e3d742f4bd02fcf0e6d2ef08bc Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 25 Jul 2020 15:16:06 +0100 Subject: [PATCH 3/6] Update WifiInterface.cpp Merge newer AT commands and freveal AT version at startup --- WifiInterface.cpp | 42 +++++++++++++++++++++++++++--------------- 1 file changed, 27 insertions(+), 15 deletions(-) diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 34efe1b..460630a 100644 --- a/WifiInterface.cpp +++ b/WifiInterface.cpp @@ -53,22 +53,37 @@ bool WifiInterface::setup2(Stream & wifiStream, const __FlashStringHelper* SSid, delay(1000); StringFormatter::send(wifiStream,F("AT+RST\r\n")); // reset module - //checkForOK(wifiStream,5000,END_DETAIL_SEARCH,true); // Show startup but ignore unreadable upto ready - checkForOK(wifiStream,5000,READY_SEARCH,true); - + checkForOK(wifiStream,5000,READY_SEARCH,false); // generally not interesting to DCC + + StringFormatter::send(wifiStream,F("AT+GMR\r\n")); // request AT version + checkForOK(wifiStream,2000,OK_SEARCH,true); // Makes this visible on the console + StringFormatter::send(wifiStream,F("AT+CWMODE=3\r\n")); // configure as server or access point checkForOK(wifiStream,1000,OK_SEARCH,true); // Not always OK, sometimes "no change" - StringFormatter::send(wifiStream, F("AT+CWHOSTNAME=\"%S\"\r\n"), hostname); // Set Host name for Wifi Client - checkForOK(wifiStream,2000, OK_SEARCH, true); - - // Older ES versions have AT+CWJAP, newer ones have AT+CWJAP_CUR and AT+CWHOSTNAME - StringFormatter::send(wifiStream,F("AT+CWJAP=\"%S\",\"%S\"\r\n"),SSid,password); - if (!checkForOK(wifiStream,20000,OK_SEARCH,true)) { - StringFormatter::send(wifiStream,F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"),SSid,password); - if (!checkForOK(wifiStream,20000,OK_SEARCH,true)) return false; + StringFormatter::send(wifiStream,F("AT+CWJAP?\r\n")); + if (checkForOK(wifiStream,2000,OK_SEARCH,true)) { + // early version supports CWJAP + StringFormatter::send(wifiStream,F("AT+CWJAP=\"%S\",\"%S\"\r\n"),SSid,password); + checkForOK(wifiStream,20000,OK_SEARCH,true); // can ignore failure as AP mode may still be ok } + else { + // later version supports CWJAP_CUR + StringFormatter::send(wifiStream, F("AT+CWHOSTNAME=\"%S\"\r\n"), hostname); // Set Host name for Wifi Client + checkForOK(wifiStream,2000, OK_SEARCH, true); // dont care if not supported + + StringFormatter::send(wifiStream,F("AT+CWJAP_CUR=\"%S\",\"%S\"\r\n"),SSid,password); + checkForOK(wifiStream,20000,OK_SEARCH,true); // can ignore failure as AP mode may still be ok + + StringFormatter::send(wifiStream,F("AT+CIPRECVMODE=0\r\n"),port); // make sure transfer mode is correct + checkForOK(wifiStream,2000,OK_SEARCH,true); + + // StringFormatter::send(wifiStream, F("AT+MDNS=1,\"%S.local\",\"%S.local\",%d\r\n"), hostname, servername, port); // Setup mDNS for Server + // if (!checkForOK(wifiStream,5000, OK_SEARCH, true)) return false; + (void)servername; // avoid compiler warning from commented out AT_MDNS above + + } StringFormatter::send(wifiStream,F("AT+CIFSR\r\n")); // get ip address //192.168.4.1 if (!checkForOK(wifiStream,10000,OK_SEARCH,true)) return false; @@ -77,12 +92,9 @@ bool WifiInterface::setup2(Stream & wifiStream, const __FlashStringHelper* SSid, StringFormatter::send(wifiStream,F("AT+CIPMUX=1\r\n")); // configure for multiple connections if (!checkForOK(wifiStream,10000,OK_SEARCH,true)) return false; - StringFormatter::send(wifiStream,F("AT+CIPSERVER=1,%d\r\n"),port); // turn on server on port 80 + StringFormatter::send(wifiStream,F("AT+CIPSERVER=1,%d\r\n"),port); // turn on server on port if (!checkForOK(wifiStream,10000,OK_SEARCH,true)) return false; - // StringFormatter::send(wifiStream, F("AT+MDNS=1,\"%S.local\",\"%S.local\",%d\r\n"), hostname, servername, port); // Setup mDNS for Server - // if (!checkForOK(wifiStream,5000, OK_SEARCH, true)) return false; - (void)servername; // not currently in use return true; } From 3aff6230a296d1ec7a17d987d3b02238b19c2db0 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Sat, 25 Jul 2020 17:33:39 +0100 Subject: [PATCH 4/6] Reject unknown turnouts --- WiThrottle.cpp | 15 +++++++++------ 1 file changed, 9 insertions(+), 6 deletions(-) diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 0310cb9..22502f4 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -110,7 +110,7 @@ void WiThrottle::parse(Print & stream, byte * cmdx) { if (Turnout::firstTurnout) { StringFormatter::send(stream,F("PTL")); for(Turnout *tt=Turnout::firstTurnout;tt!=NULL;tt=tt->nextTurnout){ - StringFormatter::send(stream,F("]\\[DT%d}|{T%d}|{%d"), tt->data.id, tt->data.id, (bool)(tt->data.tStatus & STATUS_ACTIVE)); + StringFormatter::send(stream,F("]\\[%d}|{T%d}|{%d"), tt->data.id, tt->data.id, (bool)(tt->data.tStatus & STATUS_ACTIVE)); } StringFormatter::send(stream,F("\n")); } @@ -131,17 +131,20 @@ void WiThrottle::parse(Print & stream, byte * cmdx) { StringFormatter::send(stream, F("PPA%c\n"),cmd[3]); } else if (cmd[1]=='T' && cmd[2]=='A') { // PTA accessory toggle - // TODO... if we are given an address that is not a known Turnout... - // should we create one or just send the DCC message. - int id=getInt(cmd+6); + int id=getInt(cmd+4); bool newstate=false; + Turnout * tt=Turnout::get(id); + if (!tt) { + StringFormatter::send(stream, F("HMTurnout %d Unknown\n"),id); + break; + } switch (cmd[3]) { case 'T': newstate=true; break; case 'C': newstate=false; break; case '2': newstate=!Turnout::isActive(id); } - Turnout::activate(id,newstate); - StringFormatter::send(stream, F("PTA%cDT%d\n"),newstate?'4':'2',id ); + tt->activate(newstate); + StringFormatter::send(stream, F("PTA%c%d\n"),newstate?'4':'2',id ); } break; case 'N': // Heartbeat (2) From 45863a043fc12a426741574b7cccf39ca8c86f5e Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 30 Jul 2020 15:31:31 +0100 Subject: [PATCH 5/6] Withrottle Heartbeat Dont allow millisecond delays to triigger heartbeat missed --- WiThrottle.cpp | 4 ++-- WiThrottle.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/WiThrottle.cpp b/WiThrottle.cpp index 22502f4..b72cc43 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -103,7 +103,7 @@ void WiThrottle::parse(Print & stream, byte * cmdx) { StringFormatter::send(stream,F("VN2.0\nHTDCC++EX\nRL0\nPPA%x\n"),DCCWaveform::mainTrack.getPowerMode()==POWERMODE::ON); if (annotateLeftRight) StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[Left}|{2]\\[Right}|{4\n")); else StringFormatter::send(stream,F("PTT]\\[Turnouts}|{Turnout]\\[Closed}|{2]\\[Thrown}|{4\n")); - StringFormatter::send(stream,F("*10\n")); + StringFormatter::send(stream,F("*%d\n"),HEARTBEAT_TIMEOUT/2); break; case 1: // second call... send the turnout table if we have one callState++; @@ -148,7 +148,7 @@ void WiThrottle::parse(Print & stream, byte * cmdx) { } break; case 'N': // Heartbeat (2) - StringFormatter::send(stream, F("*10\n")); // 10 second timeout + StringFormatter::send(stream, F("*%d\n"),HEARTBEAT_TIMEOUT/2); // 5 second timeout break; case 'M': // multithrottle multithrottle(stream, cmd); diff --git a/WiThrottle.h b/WiThrottle.h index b0f29f9..1e64a29 100644 --- a/WiThrottle.h +++ b/WiThrottle.h @@ -36,7 +36,7 @@ class WiThrottle { ~WiThrottle(); static const int MAX_MY_LOCO=10; - static const int HEARTBEAT_TIMEOUT=10; + static const int HEARTBEAT_TIMEOUT=10;// Timeout after 10 seconds, heartbeat at 5 static WiThrottle* firstThrottle; static int getInt(byte * cmd); static int getLocoId(byte * cmd); From a14b9ef8bfa565310aa279b1cf36115de2ae3421 Mon Sep 17 00:00:00 2001 From: Asbelos Date: Thu, 30 Jul 2020 15:34:56 +0100 Subject: [PATCH 6/6] <1 JOIN> and other commands <1 JOIN> for prog track siding to set locio function individually in Example filter to forget locos <$ .. or any unknown char> to deebug --- CVReader.ino | 13 ++++--------- DCCEXParser.cpp | 40 ++++++++++++++++++++++++++++++++-------- 2 files changed, 36 insertions(+), 17 deletions(-) diff --git a/CVReader.ino b/CVReader.ino index 1abe39c..267b833 100644 --- a/CVReader.ino +++ b/CVReader.ino @@ -38,16 +38,11 @@ void myFilter(Print & stream, byte & opcode, byte & paramCount, int p[]) { (void)stream; // avoid compiler warning if we don't access this parameter switch (opcode) { - case 'F': // Invent new command to call the new Loco Function API - DIAG(F("Setting loco %d F%d %S"),p[0],p[1],p[2]?F("ON"):F("OFF")); - DCC::setFn(p[0],p[1],p[2]==1); - opcode=0; // tell parser to ignore this command + case '!': // Create a bespoke new command to clear all loco reminders or specific locos e.g + if (paramCount==0) DCC::forgetAllLocos(); + else for (int i=0;i - DIAG(F("$ paramCount=%d\n"),paramCount); - for (int i=0;i command. The number is what you get if you use the keyword as a parameter. +// To discover new keyword numbers , use the <$ YOURKEYWORD> command const int HASH_KEYWORD_PROG=-29718; const int HASH_KEYWORD_MAIN=11339; +const int HASH_KEYWORD_JOIN=-30750; + int DCCEXParser::stashP[MAX_PARAMS]; bool DCCEXParser::stashBusy; @@ -202,23 +206,35 @@ void DCCEXParser::parse(Print & stream, const byte *com, bool blocking) { if (params>1) break; { POWERMODE mode= opcode=='1'?POWERMODE::ON:POWERMODE::OFF; + DCC::setProgTrackSyncMain(false); // Only <1 JOIN> will set this on, all others set it off if (params==0) { DCCWaveform::mainTrack.setPowerMode(mode); DCCWaveform::progTrack.setPowerMode(mode); StringFormatter::send(stream,F(""),opcode); return; } - if (p[0]==HASH_KEYWORD_MAIN) { + switch (p[0]) { + case HASH_KEYWORD_MAIN: DCCWaveform::mainTrack.setPowerMode(mode); StringFormatter::send(stream,F(""),opcode); return; - } - if (p[0]==HASH_KEYWORD_PROG) { + + case HASH_KEYWORD_PROG: DCCWaveform::progTrack.setPowerMode(mode); StringFormatter::send(stream,F(""),opcode); return; + case HASH_KEYWORD_JOIN: + DCCWaveform::mainTrack.setPowerMode(mode); + DCCWaveform::progTrack.setPowerMode(mode); + if (mode==POWERMODE::ON) { + DCC::setProgTrackSyncMain(true); + StringFormatter::send(stream,F(""),opcode); + } + else StringFormatter::send(stream,F("")); + return; + } - DIAG(F("keyword hash=%d\n"),p[0]); + DIAG(F("\nUnexpected keyword hash=%d\n"),p[0]); break; } return; @@ -258,10 +274,18 @@ void DCCEXParser::parse(Print & stream, const byte *com, bool blocking) { return; case '#': // NUMBER OF LOCOSLOTS <#> - StringFormatter::send(stream,F("<# %d>"), MAX_LOCOS); - return; - - default: //anything else will drop out to + StringFormatter::send(stream,F("<# %d>"), MAX_LOCOS); + return; + + case 'F': // New command to call the new Loco Function API + DIAG(F("Setting loco %d F%d %S"),p[0],p[1],p[2]?F("ON"):F("OFF")); + DCC::setFn(p[0],p[1],p[2]==1); + return; + + + default: //anything else will diagnose and drop out to + DIAG(F("\nOpcode=%c params=%d\n"),opcode,params); + for (int i=0;i