1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2024-12-24 13:21:23 +01:00

Merge branch 'master' into progasmain

Conflicts:
	WiThrottle.cpp
This commit is contained in:
Harald Barth 2020-07-30 17:14:25 +02:00
commit 11395dde2b
8 changed files with 180 additions and 111 deletions

101
AnalogReadFast.h Normal file
View File

@ -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 <https://www.gnu.org/licenses/>.
*/
#ifndef COMMANDSTATION_DCC_ANALOGREADFAST_H_
#define COMMANDSTATION_DCC_ANALOGREADFAST_H_
#include <Arduino.h>
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_

View File

@ -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 <F cab func 1|0>
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 <! 3 4 99>
if (paramCount==0) DCC::forgetAllLocos();
else for (int i=0;i<paramCount;i++) DCC::forgetLoco(p[i]);
opcode=0; // tell parser to ignore this command as we have done it already
break;
case '$': // Diagnose parser <$....>
DIAG(F("$ paramCount=%d\n"),paramCount);
for (int i=0;i<paramCount;i++) DIAG(F("p[%d]=%d (0x%x)\n"),i,p[i],p[i]);
opcode=0; // Normal parser wont understand $,
break;
default: // drop through and parser will use the command unaltered.
break;
}
@ -92,8 +87,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 +116,7 @@ void loop() {
// Responsibility 3: Optionally handle any incoming WiFi traffic
#ifdef WIFI
WifiInterface::loop(Serial2);
WifiInterface::loop(Serial1);
#endif
// Your additional code

View File

@ -29,8 +29,12 @@
const char VERSION[] PROGMEM ="99.666";
// These keywords are used in the <1> 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("<p%c>"),opcode);
return;
}
if (p[0]==HASH_KEYWORD_MAIN) {
switch (p[0]) {
case HASH_KEYWORD_MAIN:
DCCWaveform::mainTrack.setPowerMode(mode);
StringFormatter::send(stream,F("<p%c MAIN>"),opcode);
return;
}
if (p[0]==HASH_KEYWORD_PROG) {
case HASH_KEYWORD_PROG:
DCCWaveform::progTrack.setPowerMode(mode);
StringFormatter::send(stream,F("<p%c PROG>"),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("<p1 JOIN>"),opcode);
}
else StringFormatter::send(stream,F("<p0>"));
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 <X>
StringFormatter::send(stream,F("<# %d>"), MAX_LOCOS);
return;
case 'F': // New command to call the new Loco Function API <F cab func 1|0>
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 <X>
DIAG(F("\nOpcode=%c params=%d\n"),opcode,params);
for (int i=0;i<params;i++) DIAG(F("p[%d]=%d (0x%x)\n"),i,p[i],p[i]);
break;
} // end of opcode switch

View File

@ -19,7 +19,7 @@
#include <Arduino.h>
//#include <TimerOne.h> // use IDE menu Tools..Manage Libraries to locate and install TimerOne
#include <ArduinoTimers.h> // 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"

View File

@ -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);

View File

@ -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);

View File

@ -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;
}

View File

@ -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