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/CVReader.ino b/CVReader.ino index 39ef57d..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 //#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/WiThrottle.cpp b/WiThrottle.cpp index 3a97e74..3974b54 100644 --- a/WiThrottle.cpp +++ b/WiThrottle.cpp @@ -103,14 +103,14 @@ 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++; 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,26 +131,30 @@ 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+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); } + // If turnout does not exist, create it if (Turnout::activate(id,newstate) == false) { int addr = ((id - 1) / 4) + 1; int subaddr = (id - 1) % 4; Turnout::create(id,addr,subaddr); Turnout::activate(id,newstate); } - StringFormatter::send(stream, F("PTA%cDT%d\n"),newstate?'4':'2',id ); + StringFormatter::send(stream, F("PTA%c%d\n"),newstate?'4':'2',id ); } 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); diff --git a/WifiInterface.cpp b/WifiInterface.cpp index 21963bc..460630a 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[] =">"; @@ -54,31 +53,38 @@ 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); - -// 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); + 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" // 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); - if (!checkForOK(wifiStream,20000,OK_SEARCH,true)) { - StringFormatter::send(wifiStream,F("AT+CWJAP_CUR?\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; @@ -86,15 +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+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 return true; } 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