mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-07-30 02:43:45 +02:00
Compare commits
31 Commits
v4.2.60
...
devel-matt
Author | SHA1 | Date | |
---|---|---|---|
|
2950ef010a | ||
|
c2eb5f23b4 | ||
|
94648ead28 | ||
|
ec0499e9da | ||
|
9b75026eef | ||
|
6036ff9b15 | ||
|
6476a7aac2 | ||
|
0edf34bfe2 | ||
|
aa1f25fc72 | ||
|
b44bebc1c6 | ||
|
1a17cdb62f | ||
|
7ce1618a9c | ||
|
4192c1f5a3 | ||
|
c2fcdddd1f | ||
|
f19db3aa5c | ||
|
e6a40e622c | ||
|
b3251e89d7 | ||
|
ae2bbbf668 | ||
|
96a46f36c2 | ||
|
10c59028e1 | ||
|
ab1356d070 | ||
|
70d4c016ef | ||
|
efe96d1d84 | ||
|
5d17f247de | ||
|
7c41ec7c25 | ||
|
9c5e48c3d5 | ||
|
1bdb05a471 | ||
|
c2fa76c76a | ||
|
35fd912c60 | ||
|
dfba6c6fc1 | ||
|
f3cb263aaa |
@@ -30,6 +30,7 @@
|
|||||||
* © 2021 Neil McKechnie
|
* © 2021 Neil McKechnie
|
||||||
* © 2020-2021 Chris Harlow, Harald Barth, David Cutting,
|
* © 2020-2021 Chris Harlow, Harald Barth, David Cutting,
|
||||||
* Fred Decker, Gregor Baues, Anthony W - Dayton
|
* Fred Decker, Gregor Baues, Anthony W - Dayton
|
||||||
|
* © 2023 Nathan Kellenicki
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
@@ -95,11 +96,11 @@ void setup()
|
|||||||
// Start Ethernet if it exists
|
// Start Ethernet if it exists
|
||||||
#ifndef ARDUINO_ARCH_ESP32
|
#ifndef ARDUINO_ARCH_ESP32
|
||||||
#if WIFI_ON
|
#if WIFI_ON
|
||||||
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL);
|
WifiInterface::setup(WIFI_SERIAL_LINK_SPEED, F(WIFI_SSID), F(WIFI_PASSWORD), F(WIFI_HOSTNAME), IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
|
||||||
#endif // WIFI_ON
|
#endif // WIFI_ON
|
||||||
#else
|
#else
|
||||||
// ESP32 needs wifi on always
|
// ESP32 needs wifi on always
|
||||||
WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL);
|
WifiESP::setup(WIFI_SSID, WIFI_PASSWORD, WIFI_HOSTNAME, IP_PORT, WIFI_CHANNEL, WIFI_FORCE_AP);
|
||||||
#endif // ARDUINO_ARCH_ESP32
|
#endif // ARDUINO_ARCH_ESP32
|
||||||
|
|
||||||
#if ETHERNET_ON
|
#if ETHERNET_ON
|
||||||
|
@@ -487,9 +487,9 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
#endif
|
#endif
|
||||||
else break; // will reply <X>
|
else break; // will reply <X>
|
||||||
}
|
}
|
||||||
|
TrackManager::setJoin(join);
|
||||||
if (main) TrackManager::setMainPower(POWERMODE::ON);
|
if (main) TrackManager::setMainPower(POWERMODE::ON);
|
||||||
if (prog) TrackManager::setProgPower(POWERMODE::ON);
|
if (prog) TrackManager::setProgPower(POWERMODE::ON);
|
||||||
TrackManager::setJoin(join);
|
|
||||||
|
|
||||||
CommandDistributor::broadcastPower();
|
CommandDistributor::broadcastPower();
|
||||||
return;
|
return;
|
||||||
@@ -516,12 +516,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
else break; // will reply <X>
|
else break; // will reply <X>
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TrackManager::setJoin(false);
|
||||||
if (main) TrackManager::setMainPower(POWERMODE::OFF);
|
if (main) TrackManager::setMainPower(POWERMODE::OFF);
|
||||||
if (prog) {
|
if (prog) {
|
||||||
TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off
|
TrackManager::progTrackBoosted=false; // Prog track boost mode will not outlive prog track off
|
||||||
TrackManager::setProgPower(POWERMODE::OFF);
|
TrackManager::setProgPower(POWERMODE::OFF);
|
||||||
}
|
}
|
||||||
TrackManager::setJoin(false);
|
|
||||||
|
|
||||||
CommandDistributor::broadcastPower();
|
CommandDistributor::broadcastPower();
|
||||||
return;
|
return;
|
||||||
|
@@ -194,8 +194,10 @@ int RMTChannel::RMTfillData(const byte buffer[], byte byteCount, byte repeatCoun
|
|||||||
setDCCBit1(data + bitcounter-1); // overwrite previous zero bit with one bit
|
setDCCBit1(data + bitcounter-1); // overwrite previous zero bit with one bit
|
||||||
setEOT(data + bitcounter++); // EOT marker
|
setEOT(data + bitcounter++); // EOT marker
|
||||||
dataLen = bitcounter;
|
dataLen = bitcounter;
|
||||||
|
noInterrupts(); // keep dataReady and dataRepeat consistnet to each other
|
||||||
dataReady = true;
|
dataReady = true;
|
||||||
dataRepeat = repeatCount+1; // repeatCount of 0 means send once
|
dataRepeat = repeatCount+1; // repeatCount of 0 means send once
|
||||||
|
interrupts();
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -212,6 +214,8 @@ void IRAM_ATTR RMTChannel::RMTinterrupt() {
|
|||||||
if (dataReady) { // if we have new data, fill while preamble is running
|
if (dataReady) { // if we have new data, fill while preamble is running
|
||||||
rmt_fill_tx_items(channel, data, dataLen, preambleLen-1);
|
rmt_fill_tx_items(channel, data, dataLen, preambleLen-1);
|
||||||
dataReady = false;
|
dataReady = false;
|
||||||
|
if (dataRepeat == 0) // all data should go out at least once
|
||||||
|
DIAG(F("Channel %d DCC signal lost data"), channel);
|
||||||
}
|
}
|
||||||
if (dataRepeat > 0) // if a repeat count was specified, work on that
|
if (dataRepeat > 0) // if a repeat count was specified, work on that
|
||||||
dataRepeat--;
|
dataRepeat--;
|
||||||
|
@@ -180,8 +180,8 @@ void DCCTimer::DCCEXanalogWrite(uint8_t pin, int value) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
pin_to_channel[pin] = --cnt_channel;
|
pin_to_channel[pin] = --cnt_channel;
|
||||||
ledcAttachPin(pin, cnt_channel);
|
|
||||||
ledcSetup(cnt_channel, 1000, 8);
|
ledcSetup(cnt_channel, 1000, 8);
|
||||||
|
ledcAttachPin(pin, cnt_channel);
|
||||||
} else {
|
} else {
|
||||||
ledcAttachPin(pin, pin_to_channel[pin]);
|
ledcAttachPin(pin, pin_to_channel[pin]);
|
||||||
}
|
}
|
||||||
|
@@ -247,6 +247,9 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea
|
|||||||
pendingPacket[byteCount] = checksum;
|
pendingPacket[byteCount] = checksum;
|
||||||
pendingLength = byteCount + 1;
|
pendingLength = byteCount + 1;
|
||||||
pendingRepeats = repeats;
|
pendingRepeats = repeats;
|
||||||
|
// DIAG repeated commands (accesories)
|
||||||
|
// if (pendingRepeats > 0)
|
||||||
|
// DIAG(F("Repeats=%d on %s track"), pendingRepeats, isMainTrack ? "MAIN" : "PROG");
|
||||||
// The resets will be zero not only now but as well repeats packets into the future
|
// The resets will be zero not only now but as well repeats packets into the future
|
||||||
clearResets(repeats+1);
|
clearResets(repeats+1);
|
||||||
{
|
{
|
||||||
|
@@ -1 +1 @@
|
|||||||
#define GITHUB_SHA "devel-202306222129Z"
|
#define GITHUB_SHA "devel-202307172316Z"
|
||||||
|
@@ -50,12 +50,12 @@ EXTurntable::EXTurntable(VPIN firstVpin, int nPins, I2CAddress I2CAddress) {
|
|||||||
// Initialisation of EXTurntable
|
// Initialisation of EXTurntable
|
||||||
void EXTurntable::_begin() {
|
void EXTurntable::_begin() {
|
||||||
I2CManager.begin();
|
I2CManager.begin();
|
||||||
I2CManager.setClock(1000000);
|
|
||||||
if (I2CManager.exists(_I2CAddress)) {
|
if (I2CManager.exists(_I2CAddress)) {
|
||||||
#ifdef DIAG_IO
|
#ifdef DIAG_IO
|
||||||
_display();
|
_display();
|
||||||
#endif
|
#endif
|
||||||
} else {
|
} else {
|
||||||
|
DIAG(F("EX-Turntable I2C:%s device not found"), _I2CAddress.toString());
|
||||||
_deviceState = DEVSTATE_FAILED;
|
_deviceState = DEVSTATE_FAILED;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -134,12 +134,13 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Device specific read function
|
// Return the position sent by the rotary encoder software
|
||||||
int _readAnalogue(VPIN vpin) override {
|
int _readAnalogue(VPIN vpin) override {
|
||||||
if (_deviceState == DEVSTATE_FAILED) return 0;
|
if (_deviceState == DEVSTATE_FAILED) return 0;
|
||||||
return _position;
|
return _position;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send the feedback value to the rotary encoder software
|
||||||
void _write(VPIN vpin, int value) override {
|
void _write(VPIN vpin, int value) override {
|
||||||
if (vpin == _firstVpin + 1) {
|
if (vpin == _firstVpin + 1) {
|
||||||
if (value != 0) value = 0x01;
|
if (value != 0) value = 0x01;
|
||||||
@@ -148,9 +149,12 @@ private:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Send a position update to the rotary encoder software
|
||||||
|
// To be valid, must be 0 to 255, and different to the current position
|
||||||
|
// If the current position is the same, it was initiated by the rotary encoder
|
||||||
void _writeAnalogue(VPIN vpin, int position, uint8_t profile, uint16_t duration) override {
|
void _writeAnalogue(VPIN vpin, int position, uint8_t profile, uint16_t duration) override {
|
||||||
if (vpin == _firstVpin + 2) {
|
if (vpin == _firstVpin + 2) {
|
||||||
if (position >= 0 && position <= 255) {
|
if (position >= 0 && position <= 255 && position != _position) {
|
||||||
byte newPosition = position & 0xFF;
|
byte newPosition = position & 0xFF;
|
||||||
byte _positionBuffer[2] = {RE_MOVE, newPosition};
|
byte _positionBuffer[2] = {RE_MOVE, newPosition};
|
||||||
I2CManager.write(_I2CAddress, _positionBuffer, 2);
|
I2CManager.write(_I2CAddress, _positionBuffer, 2);
|
||||||
|
285
MotorDriver.cpp
285
MotorDriver.cpp
@@ -135,7 +135,11 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
|
|||||||
// float calculations or libraray code.
|
// float calculations or libraray code.
|
||||||
senseFactorInternal=sense_factor * senseScale;
|
senseFactorInternal=sense_factor * senseScale;
|
||||||
tripMilliamps=trip_milliamps;
|
tripMilliamps=trip_milliamps;
|
||||||
rawCurrentTripValue=mA2raw(trip_milliamps);
|
#ifdef MAX_CURRENT
|
||||||
|
if (MAX_CURRENT > 0 && MAX_CURRENT < tripMilliamps)
|
||||||
|
tripMilliamps = MAX_CURRENT;
|
||||||
|
#endif
|
||||||
|
rawCurrentTripValue=mA2raw(tripMilliamps);
|
||||||
|
|
||||||
if (rawCurrentTripValue + senseOffset > ADCee::ADCmax()) {
|
if (rawCurrentTripValue + senseOffset > ADCee::ADCmax()) {
|
||||||
// This would mean that the values obtained from the ADC never
|
// This would mean that the values obtained from the ADC never
|
||||||
@@ -169,7 +173,11 @@ bool MotorDriver::isPWMCapable() {
|
|||||||
|
|
||||||
void MotorDriver::setPower(POWERMODE mode) {
|
void MotorDriver::setPower(POWERMODE mode) {
|
||||||
if (powerMode == mode) return;
|
if (powerMode == mode) return;
|
||||||
bool on=mode==POWERMODE::ON;
|
//DIAG(F("Track %c POWERMODE=%d"), trackLetter, (int)mode);
|
||||||
|
lastPowerChange[(int)mode] = micros();
|
||||||
|
if (mode == POWERMODE::OVERLOAD)
|
||||||
|
globalOverloadStart = lastPowerChange[(int)mode];
|
||||||
|
bool on=(mode==POWERMODE::ON || mode ==POWERMODE::ALERT);
|
||||||
if (on) {
|
if (on) {
|
||||||
// when switching a track On, we need to check the crrentOffset with the pin OFF
|
// when switching a track On, we need to check the crrentOffset with the pin OFF
|
||||||
if (powerMode==POWERMODE::OFF && currentPin!=UNUSED_PIN) {
|
if (powerMode==POWERMODE::OFF && currentPin!=UNUSED_PIN) {
|
||||||
@@ -209,8 +217,8 @@ bool MotorDriver::canMeasureCurrent() {
|
|||||||
return currentPin!=UNUSED_PIN;
|
return currentPin!=UNUSED_PIN;
|
||||||
}
|
}
|
||||||
/*
|
/*
|
||||||
* Return the current reading as pin reading 0 to 1023. If the fault
|
* Return the current reading as pin reading 0 to max resolution (1024 or 4096).
|
||||||
* pin is activated return a negative current to show active fault pin.
|
* If the fault pin is activated return a negative current to show active fault pin.
|
||||||
* As there is no -0, cheat a little and return -1 in that case.
|
* As there is no -0, cheat a little and return -1 in that case.
|
||||||
*
|
*
|
||||||
* senseOffset handles the case where a shield returns values above or below
|
* senseOffset handles the case where a shield returns values above or below
|
||||||
@@ -339,7 +347,35 @@ void MotorDriver::setDCSignal(byte speedcode) {
|
|||||||
interrupts();
|
interrupts();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
void MotorDriver::throttleInrush(bool on) {
|
||||||
|
if (brakePin == UNUSED_PIN)
|
||||||
|
return;
|
||||||
|
if ( !(trackMode & (TRACK_MODE_MAIN | TRACK_MODE_PROG | TRACK_MODE_EXT)))
|
||||||
|
return;
|
||||||
|
byte duty = on ? 208 : 0;
|
||||||
|
if (invertBrake)
|
||||||
|
duty = 255-duty;
|
||||||
|
#if defined(ARDUINO_ARCH_ESP32)
|
||||||
|
if(on) {
|
||||||
|
DCCTimer::DCCEXanalogWrite(brakePin,duty);
|
||||||
|
DCCTimer::DCCEXanalogWriteFrequency(brakePin, 62500);
|
||||||
|
} else {
|
||||||
|
ledcDetachPin(brakePin);
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
if(on){
|
||||||
|
#if defined(ARDUINO_AVR_UNO)
|
||||||
|
TCCR2B = (TCCR2B & B11111000) | B00000001; // div 1 is max
|
||||||
|
#endif
|
||||||
|
#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560)
|
||||||
|
TCCR2B = (TCCR2B & B11111000) | B00000001; // div 1 is max
|
||||||
|
TCCR4B = (TCCR4B & B11111000) | B00000001; // div 1 is max
|
||||||
|
TCCR5B = (TCCR5B & B11111000) | B00000001; // div 1 is max
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
analogWrite(brakePin,duty);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
unsigned int MotorDriver::raw2mA( int raw) {
|
unsigned int MotorDriver::raw2mA( int raw) {
|
||||||
//DIAG(F("%d = %d * %d / %d"), (int32_t)raw * senseFactorInternal / senseScale, raw, senseFactorInternal, senseScale);
|
//DIAG(F("%d = %d * %d / %d"), (int32_t)raw * senseFactorInternal / senseScale, raw, senseFactorInternal, senseScale);
|
||||||
return (int32_t)raw * senseFactorInternal / senseScale;
|
return (int32_t)raw * senseFactorInternal / senseScale;
|
||||||
@@ -368,112 +404,169 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res
|
|||||||
// DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH);
|
// DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// checkPowerOverload(useProgLimit, trackno)
|
||||||
|
// bool useProgLimit: Trackmanager knows if this track is in prog mode or in main mode
|
||||||
|
// byte trackno: trackmanager knows it's number (could be skipped?)
|
||||||
|
//
|
||||||
|
// Short ciruit handling strategy:
|
||||||
|
//
|
||||||
|
// There are the following power states: ON ALERT OVERLOAD OFF
|
||||||
|
// OFF state is only changed to/from manually. Power is on
|
||||||
|
// during ON and ALERT. Power is off during OVERLOAD and OFF.
|
||||||
|
// The overload mechanism changes between the other states like
|
||||||
|
//
|
||||||
|
// ON -1-> ALERT -2-> OVERLOAD -3-> ALERT -4-> ON
|
||||||
|
// or
|
||||||
|
// ON -1-> ALERT -4-> ON
|
||||||
|
//
|
||||||
|
// Times are in class MotorDriver (MotorDriver.h).
|
||||||
|
//
|
||||||
|
// 1. ON to ALERT:
|
||||||
|
// Transition on fault pin condition or current overload
|
||||||
|
//
|
||||||
|
// 2. ALERT to OVERLOAD:
|
||||||
|
// Transition happens if different timeouts have elapsed.
|
||||||
|
// If only the fault pin is active, timeout is
|
||||||
|
// POWER_SAMPLE_IGNORE_FAULT_LOW (100ms)
|
||||||
|
// If only overcurrent is detected, timeout is
|
||||||
|
// POWER_SAMPLE_IGNORE_CURRENT (100ms)
|
||||||
|
// If fault pin and overcurrent are active, timeout is
|
||||||
|
// POWER_SAMPLE_IGNORE_FAULT_HIGH (5ms)
|
||||||
|
// Transition to OVERLOAD turns off power to the affected
|
||||||
|
// output (unless fault pins are shared)
|
||||||
|
// If the transition conditions are not fullfilled,
|
||||||
|
// transition according to 4 is tested.
|
||||||
|
//
|
||||||
|
// 3. OVERLOAD to ALERT
|
||||||
|
// Transiton happens when timeout has elapsed, timeout
|
||||||
|
// is named power_sample_overload_wait. It is started
|
||||||
|
// at POWER_SAMPLE_OVERLOAD_WAIT (40ms) at first entry
|
||||||
|
// to OVERLOAD and then increased by a factor of 2
|
||||||
|
// at further entries to the OVERLOAD condition. This
|
||||||
|
// happens until POWER_SAMPLE_RETRY_MAX (10sec) is reached.
|
||||||
|
// power_sample_overload_wait is reset by a poweroff or
|
||||||
|
// a POWER_SAMPLE_ALL_GOOD (5sec) period during ON.
|
||||||
|
// After timeout power is turned on again and state
|
||||||
|
// goes back to ALERT.
|
||||||
|
//
|
||||||
|
// 4. ALERT to ON
|
||||||
|
// Transition happens by watching the current and fault pin
|
||||||
|
// samples during POWER_SAMPLE_ALERT_GOOD (20ms) time. If
|
||||||
|
// values have been good during that time, transition is
|
||||||
|
// made back to ON. Note that even if state is back to ON,
|
||||||
|
// the power_sample_overload_wait time is first reset
|
||||||
|
// later (see above).
|
||||||
|
//
|
||||||
|
// The time keeping is handled by timestamps lastPowerChange[]
|
||||||
|
// which are set by each power change and by lastBadSample which
|
||||||
|
// keeps track if conditions during ALERT have been good enough
|
||||||
|
// to go back to ON. The time differences are calculated by
|
||||||
|
// microsSinceLastPowerChange().
|
||||||
|
//
|
||||||
|
|
||||||
void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) {
|
void MotorDriver::checkPowerOverload(bool useProgLimit, byte trackno) {
|
||||||
int tripValue= useProgLimit?progTripValue:getRawCurrentTripValue();
|
|
||||||
|
|
||||||
switch (powerMode) {
|
switch (powerMode) {
|
||||||
case POWERMODE::OFF:
|
|
||||||
if (overloadNow) {
|
case POWERMODE::OFF: {
|
||||||
// reset overload condition as we have just turned off power
|
lastPowerMode = POWERMODE::OFF;
|
||||||
// DIAG(F("OVERLOAD POFF OFF"));
|
power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
|
||||||
overloadNow=false;
|
break;
|
||||||
setLastPowerChange();
|
}
|
||||||
}
|
|
||||||
if (microsSinceLastPowerChange() > POWER_SAMPLE_ALL_GOOD) {
|
case POWERMODE::ON: {
|
||||||
power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
|
lastPowerMode = POWERMODE::ON;
|
||||||
}
|
bool cF = checkFault();
|
||||||
break;
|
bool cC = checkCurrent(useProgLimit);
|
||||||
case POWERMODE::ON:
|
if(cF || cC ) {
|
||||||
// Check current
|
if (cC) {
|
||||||
lastCurrent=getCurrentRaw();
|
unsigned int mA=raw2mA(lastCurrent);
|
||||||
if (lastCurrent < 0) {
|
DIAG(F("TRACK %c ALERT %s %dmA"), trackno + 'A',
|
||||||
// We have a fault pin condition to take care of
|
cF ? "FAULT" : "",
|
||||||
if (!overloadNow) {
|
mA);
|
||||||
// turn on overload condition as fault pin has gone active
|
|
||||||
// DIAG(F("OVERLOAD FPIN ON"));
|
|
||||||
overloadNow=true;
|
|
||||||
setLastPowerChangeOverload();
|
|
||||||
}
|
|
||||||
lastCurrent = -lastCurrent;
|
|
||||||
{
|
|
||||||
if (lastCurrent < tripValue) {
|
|
||||||
if (power_sample_overload_wait <= (POWER_SAMPLE_OVERLOAD_WAIT * 10) && // almost virgin
|
|
||||||
microsSinceLastPowerChange() < POWER_SAMPLE_IGNORE_FAULT_LOW) {
|
|
||||||
// Ignore 50ms fault pin if no current
|
|
||||||
DIAG(F("TRACK %c FAULT PIN (50ms ignore)"), trackno + 'A');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
lastCurrent = tripValue; // exaggerate so condition below (*) is true
|
|
||||||
} else {
|
|
||||||
if (power_sample_overload_wait <= POWER_SAMPLE_OVERLOAD_WAIT && // virgin
|
|
||||||
microsSinceLastPowerChange() < POWER_SAMPLE_IGNORE_FAULT_HIGH) {
|
|
||||||
// Ignore 5ms fault pin if we see current
|
|
||||||
DIAG(F("TRACK %c FAULT PIN (5ms ignore)"), trackno + 'A');
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DIAG(F("TRACK %c FAULT PIN"), trackno + 'A');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// // //
|
|
||||||
// above we looked at fault pin, below we look at current
|
|
||||||
// // //
|
|
||||||
if (lastCurrent < tripValue) { // see above (*)
|
|
||||||
if (overloadNow) {
|
|
||||||
// current is below trip value, turn off overload condition
|
|
||||||
// DIAG(F("OVERLOAD PON OFF"));
|
|
||||||
overloadNow=false;
|
|
||||||
setLastPowerChange();
|
|
||||||
}
|
|
||||||
if (microsSinceLastPowerChange() > POWER_SAMPLE_ALL_GOOD) {
|
|
||||||
power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
|
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
// too much current
|
DIAG(F("TRACK %c ALERT FAULT"), trackno + 'A');
|
||||||
if (!overloadNow) {
|
|
||||||
// current is over trip value, turn on overload condition
|
|
||||||
// DIAG(F("OVERLOAD PON ON"));
|
|
||||||
overloadNow=true;
|
|
||||||
setLastPowerChange();
|
|
||||||
}
|
|
||||||
unsigned long uSecs = microsSinceLastPowerChange();
|
|
||||||
if (power_sample_overload_wait > POWER_SAMPLE_OVERLOAD_WAIT || // not virgin
|
|
||||||
uSecs > POWER_SAMPLE_OFF_DELAY) {
|
|
||||||
// Overload has existed longer than delay (typ. 10ms)
|
|
||||||
setPower(POWERMODE::OVERLOAD);
|
|
||||||
if (overloadNow) {
|
|
||||||
// the setPower just turned off, so overload is now gone
|
|
||||||
// DIAG(F("OVERLOAD PON OFF"));
|
|
||||||
overloadNow=false;
|
|
||||||
setLastPowerChangeOverload();
|
|
||||||
}
|
|
||||||
unsigned int mA=raw2mA(lastCurrent);
|
|
||||||
unsigned int maxmA=raw2mA(tripValue);
|
|
||||||
DIAG(F("TRACK %c POWER OVERLOAD %4dmA (max %4dmA) detected after %4M. Pause %4M"),
|
|
||||||
trackno + 'A', mA, maxmA, uSecs, power_sample_overload_wait);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
setPower(POWERMODE::ALERT);
|
||||||
break;
|
break;
|
||||||
case POWERMODE::OVERLOAD:
|
}
|
||||||
{
|
// all well
|
||||||
// Try setting it back on after the OVERLOAD_WAIT
|
if (microsSinceLastPowerChange(POWERMODE::ON) > POWER_SAMPLE_ALL_GOOD) {
|
||||||
unsigned long mslpc = (commonFaultPin ? (micros() - globalOverloadStart) : microsSinceLastPowerChange());
|
power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case POWERMODE::ALERT: {
|
||||||
|
// set local flags that handle how much is output to diag (do not output duplicates)
|
||||||
|
bool notFromOverload = (lastPowerMode != POWERMODE::OVERLOAD);
|
||||||
|
bool newPowerMode = (powerMode != lastPowerMode);
|
||||||
|
unsigned long now = micros();
|
||||||
|
if (newPowerMode)
|
||||||
|
lastBadSample = now;
|
||||||
|
lastPowerMode = POWERMODE::ALERT;
|
||||||
|
// check how long we have been in this state
|
||||||
|
unsigned long mslpc = microsSinceLastPowerChange(POWERMODE::ALERT);
|
||||||
|
if(checkFault()) {
|
||||||
|
throttleInrush(true);
|
||||||
|
lastBadSample = now;
|
||||||
|
unsigned long timeout = checkCurrent(useProgLimit) ? POWER_SAMPLE_IGNORE_FAULT_HIGH : POWER_SAMPLE_IGNORE_FAULT_LOW;
|
||||||
|
if ( mslpc < timeout) {
|
||||||
|
if (newPowerMode)
|
||||||
|
DIAG(F("TRACK %c FAULT PIN (%M ignore)"), trackno + 'A', timeout);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
DIAG(F("TRACK %c FAULT PIN detected after %4M. Pause %4M)"), trackno + 'A', mslpc, power_sample_overload_wait);
|
||||||
|
throttleInrush(false);
|
||||||
|
setPower(POWERMODE::OVERLOAD);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (checkCurrent(useProgLimit)) {
|
||||||
|
lastBadSample = now;
|
||||||
|
if (mslpc < POWER_SAMPLE_IGNORE_CURRENT) {
|
||||||
|
if (newPowerMode) {
|
||||||
|
unsigned int mA=raw2mA(lastCurrent);
|
||||||
|
DIAG(F("TRACK %c CURRENT (%M ignore) %dmA"), trackno + 'A', POWER_SAMPLE_IGNORE_CURRENT, mA);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
unsigned int mA=raw2mA(lastCurrent);
|
||||||
|
unsigned int maxmA=raw2mA(tripValue);
|
||||||
|
DIAG(F("TRACK %c POWER OVERLOAD %4dmA (max %4dmA) detected after %4M. Pause %4M"),
|
||||||
|
trackno + 'A', mA, maxmA, mslpc, power_sample_overload_wait);
|
||||||
|
throttleInrush(false);
|
||||||
|
setPower(POWERMODE::OVERLOAD);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
// all well
|
||||||
|
unsigned long goodtime = micros() - lastBadSample;
|
||||||
|
if (goodtime > POWER_SAMPLE_ALERT_GOOD) {
|
||||||
|
if (true || notFromOverload) { // we did a RESTORE message XXX
|
||||||
|
unsigned int mA=raw2mA(lastCurrent);
|
||||||
|
DIAG(F("TRACK %c NORMAL (after %M/%M) %dmA"), trackno + 'A', goodtime, mslpc, mA);
|
||||||
|
}
|
||||||
|
throttleInrush(false);
|
||||||
|
setPower(POWERMODE::ON);
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case POWERMODE::OVERLOAD: {
|
||||||
|
lastPowerMode = POWERMODE::OVERLOAD;
|
||||||
|
unsigned long mslpc = (commonFaultPin ? (micros() - globalOverloadStart) : microsSinceLastPowerChange(POWERMODE::OVERLOAD));
|
||||||
if (mslpc > power_sample_overload_wait) {
|
if (mslpc > power_sample_overload_wait) {
|
||||||
// adjust next wait time
|
// adjust next wait time
|
||||||
power_sample_overload_wait *= 2;
|
power_sample_overload_wait *= 2;
|
||||||
if (power_sample_overload_wait > POWER_SAMPLE_RETRY_MAX)
|
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;
|
||||||
// power on test
|
// power on test
|
||||||
setPower(POWERMODE::ON);
|
|
||||||
// here we change power but not the overloadNow as that was
|
|
||||||
// already changed to false when we entered POWERMODE::OVERLOAD
|
|
||||||
// so we need to set the lastPowerChange anyway.
|
|
||||||
overloadNow=false;
|
|
||||||
setLastPowerChange();
|
|
||||||
DIAG(F("TRACK %c POWER RESTORE (after %4M)"), trackno + 'A', mslpc);
|
DIAG(F("TRACK %c POWER RESTORE (after %4M)"), trackno + 'A', mslpc);
|
||||||
|
setPower(POWERMODE::ALERT);
|
||||||
}
|
}
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
break;
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
@@ -27,6 +27,10 @@
|
|||||||
#include "IODevice.h"
|
#include "IODevice.h"
|
||||||
#include "DCCTimer.h"
|
#include "DCCTimer.h"
|
||||||
|
|
||||||
|
// use powers of two so we can do logical and/or on the track modes in if clauses.
|
||||||
|
enum TRACK_MODE : byte {TRACK_MODE_OFF = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4,
|
||||||
|
TRACK_MODE_DC = 8, TRACK_MODE_DCX = 16, TRACK_MODE_EXT = 32};
|
||||||
|
|
||||||
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
|
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
|
||||||
#define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW
|
#define setLOW(fastpin) *fastpin.inout &= fastpin.maskLOW
|
||||||
#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH)
|
#define isHIGH(fastpin) (*fastpin.inout & fastpin.maskHIGH)
|
||||||
@@ -107,7 +111,7 @@ extern volatile portreg_t shadowPORTA;
|
|||||||
extern volatile portreg_t shadowPORTB;
|
extern volatile portreg_t shadowPORTB;
|
||||||
extern volatile portreg_t shadowPORTC;
|
extern volatile portreg_t shadowPORTC;
|
||||||
|
|
||||||
enum class POWERMODE : byte { OFF, ON, OVERLOAD };
|
enum class POWERMODE : byte { OFF, ON, OVERLOAD, ALERT };
|
||||||
|
|
||||||
class MotorDriver {
|
class MotorDriver {
|
||||||
public:
|
public:
|
||||||
@@ -145,6 +149,7 @@ class MotorDriver {
|
|||||||
};
|
};
|
||||||
inline pinpair getSignalPin() { return pinpair(signalPin,signalPin2); };
|
inline pinpair getSignalPin() { return pinpair(signalPin,signalPin2); };
|
||||||
void setDCSignal(byte speedByte);
|
void setDCSignal(byte speedByte);
|
||||||
|
void throttleInrush(bool on);
|
||||||
inline void detachDCSignal() {
|
inline void detachDCSignal() {
|
||||||
#if defined(__arm__)
|
#if defined(__arm__)
|
||||||
pinMode(brakePin, OUTPUT);
|
pinMode(brakePin, OUTPUT);
|
||||||
@@ -192,35 +197,43 @@ class MotorDriver {
|
|||||||
// this returns how much time has passed since the last power change. If it
|
// this returns how much time has passed since the last power change. If it
|
||||||
// was really long ago (approx > 52min) advance counter approx 35 min so that
|
// was really long ago (approx > 52min) advance counter approx 35 min so that
|
||||||
// we are at 18 minutes again. Times for 32 bit unsigned long.
|
// we are at 18 minutes again. Times for 32 bit unsigned long.
|
||||||
inline unsigned long microsSinceLastPowerChange() {
|
inline unsigned long microsSinceLastPowerChange(POWERMODE mode) {
|
||||||
unsigned long now = micros();
|
unsigned long now = micros();
|
||||||
unsigned long diff = now - lastPowerChange;
|
unsigned long diff = now - lastPowerChange[(int)mode];
|
||||||
if (diff > (1UL << (7 *sizeof(unsigned long)))) // 2^(4*7)us = 268.4 seconds
|
if (diff > (1UL << (7 *sizeof(unsigned long)))) // 2^(4*7)us = 268.4 seconds
|
||||||
lastPowerChange = now - 30000000UL; // 30 seconds ago
|
lastPowerChange[(int)mode] = now - 30000000UL; // 30 seconds ago
|
||||||
return diff;
|
return diff;
|
||||||
};
|
};
|
||||||
inline void setLastPowerChange() {
|
|
||||||
lastPowerChange = micros();
|
|
||||||
};
|
|
||||||
// as setLastPowerChange but sets the global timestamp as well which
|
|
||||||
// is only used to sync power restore in case of common Fault pin.
|
|
||||||
inline void setLastPowerChangeOverload() {
|
|
||||||
if (commonFaultPin)
|
|
||||||
globalOverloadStart = lastPowerChange = micros();
|
|
||||||
else
|
|
||||||
setLastPowerChange();
|
|
||||||
};
|
|
||||||
#ifdef ANALOG_READ_INTERRUPT
|
#ifdef ANALOG_READ_INTERRUPT
|
||||||
bool sampleCurrentFromHW();
|
bool sampleCurrentFromHW();
|
||||||
void startCurrentFromHW();
|
void startCurrentFromHW();
|
||||||
#endif
|
#endif
|
||||||
|
inline void setMode(TRACK_MODE m) {
|
||||||
|
trackMode = m;
|
||||||
|
};
|
||||||
|
inline TRACK_MODE getMode() {
|
||||||
|
return trackMode;
|
||||||
|
};
|
||||||
private:
|
private:
|
||||||
char trackLetter = '?';
|
char trackLetter = '?';
|
||||||
bool isProgTrack = false; // tells us if this is a prog track
|
bool isProgTrack = false; // tells us if this is a prog track
|
||||||
void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result);
|
void getFastPin(const FSH* type,int pin, bool input, FASTPIN & result);
|
||||||
void getFastPin(const FSH* type,int pin, FASTPIN & result) {
|
inline void getFastPin(const FSH* type,int pin, FASTPIN & result) {
|
||||||
getFastPin(type, pin, 0, result);
|
getFastPin(type, pin, 0, result);
|
||||||
}
|
};
|
||||||
|
// side effect sets lastCurrent and tripValue
|
||||||
|
inline bool checkCurrent(bool useProgLimit) {
|
||||||
|
tripValue= useProgLimit?progTripValue:getRawCurrentTripValue();
|
||||||
|
lastCurrent = getCurrentRaw();
|
||||||
|
if (lastCurrent < 0)
|
||||||
|
lastCurrent = -lastCurrent;
|
||||||
|
return lastCurrent >= tripValue;
|
||||||
|
};
|
||||||
|
// side effect sets lastCurrent
|
||||||
|
inline bool checkFault() {
|
||||||
|
lastCurrent = getCurrentRaw();
|
||||||
|
return lastCurrent < 0;
|
||||||
|
};
|
||||||
VPIN powerPin;
|
VPIN powerPin;
|
||||||
byte signalPin, signalPin2, currentPin, faultPin, brakePin;
|
byte signalPin, signalPin2, currentPin, faultPin, brakePin;
|
||||||
FASTPIN fastSignalPin, fastSignalPin2, fastBrakePin,fastFaultPin;
|
FASTPIN fastSignalPin, fastSignalPin2, fastBrakePin,fastFaultPin;
|
||||||
@@ -241,12 +254,14 @@ class MotorDriver {
|
|||||||
int rawCurrentTripValue;
|
int rawCurrentTripValue;
|
||||||
// current sampling
|
// current sampling
|
||||||
POWERMODE powerMode;
|
POWERMODE powerMode;
|
||||||
bool overloadNow = false;
|
POWERMODE lastPowerMode;
|
||||||
unsigned long lastPowerChange; // timestamp in microseconds
|
unsigned long lastPowerChange[4]; // timestamp in microseconds
|
||||||
|
unsigned long lastBadSample; // timestamp in microseconds
|
||||||
// used to sync restore time when common Fault pin detected
|
// used to sync restore time when common Fault pin detected
|
||||||
static unsigned long globalOverloadStart; // timestamp in microseconds
|
static unsigned long globalOverloadStart; // timestamp in microseconds
|
||||||
int progTripValue;
|
int progTripValue;
|
||||||
int lastCurrent;
|
int lastCurrent; //temp value
|
||||||
|
int tripValue; //temp value
|
||||||
#ifdef ANALOG_READ_INTERRUPT
|
#ifdef ANALOG_READ_INTERRUPT
|
||||||
volatile unsigned long sampleCurrentTimestamp;
|
volatile unsigned long sampleCurrentTimestamp;
|
||||||
volatile uint16_t sampleCurrent;
|
volatile uint16_t sampleCurrent;
|
||||||
@@ -256,15 +271,17 @@ class MotorDriver {
|
|||||||
|
|
||||||
// Times for overload management. Unit: microseconds.
|
// Times for overload management. Unit: microseconds.
|
||||||
// Base for wait time until power is turned on again
|
// Base for wait time until power is turned on again
|
||||||
static const unsigned long POWER_SAMPLE_OVERLOAD_WAIT = 100UL;
|
static const unsigned long POWER_SAMPLE_OVERLOAD_WAIT = 40000UL;
|
||||||
// Time after we consider all faults old and forgotten
|
// Time after we consider all faults old and forgotten
|
||||||
static const unsigned long POWER_SAMPLE_ALL_GOOD = 5000000UL;
|
static const unsigned long POWER_SAMPLE_ALL_GOOD = 5000000UL;
|
||||||
|
// Time after which we consider a ALERT over
|
||||||
|
static const unsigned long POWER_SAMPLE_ALERT_GOOD = 20000UL;
|
||||||
// How long to ignore fault pin if current is under limit
|
// How long to ignore fault pin if current is under limit
|
||||||
static const unsigned long POWER_SAMPLE_IGNORE_FAULT_LOW = 50000UL;
|
static const unsigned long POWER_SAMPLE_IGNORE_FAULT_LOW = 100000UL;
|
||||||
// How long to ignore fault pin if current is higher than limit
|
// How long to ignore fault pin if current is higher than limit
|
||||||
static const unsigned long POWER_SAMPLE_IGNORE_FAULT_HIGH = 5000UL;
|
static const unsigned long POWER_SAMPLE_IGNORE_FAULT_HIGH = 5000UL;
|
||||||
// How long to wait between overcurrent and turning off
|
// How long to wait between overcurrent and turning off
|
||||||
static const unsigned long POWER_SAMPLE_OFF_DELAY = 10000UL;
|
static const unsigned long POWER_SAMPLE_IGNORE_CURRENT = 100000UL;
|
||||||
// Upper limit for retry period
|
// Upper limit for retry period
|
||||||
static const unsigned long POWER_SAMPLE_RETRY_MAX = 10000000UL;
|
static const unsigned long POWER_SAMPLE_RETRY_MAX = 10000000UL;
|
||||||
|
|
||||||
@@ -273,6 +290,7 @@ class MotorDriver {
|
|||||||
static const int TRIP_CURRENT_PROG=250;
|
static const int TRIP_CURRENT_PROG=250;
|
||||||
unsigned long power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
|
unsigned long power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
|
||||||
unsigned int power_good_counter = 0;
|
unsigned int power_good_counter = 0;
|
||||||
|
TRACK_MODE trackMode = TRACK_MODE_OFF; // we assume off at startup
|
||||||
|
|
||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
|
@@ -31,7 +31,7 @@
|
|||||||
|
|
||||||
#define APPLY_BY_MODE(findmode,function) \
|
#define APPLY_BY_MODE(findmode,function) \
|
||||||
FOR_EACH_TRACK(t) \
|
FOR_EACH_TRACK(t) \
|
||||||
if (trackMode[t]==findmode) \
|
if (track[t]->getMode()==findmode) \
|
||||||
track[t]->function;
|
track[t]->function;
|
||||||
#ifndef DISABLE_PROG
|
#ifndef DISABLE_PROG
|
||||||
const int16_t HASH_KEYWORD_PROG = -29718;
|
const int16_t HASH_KEYWORD_PROG = -29718;
|
||||||
@@ -44,7 +44,6 @@ 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_A = 65; // parser makes single chars the ascii.
|
||||||
|
|
||||||
MotorDriver * TrackManager::track[MAX_TRACKS];
|
MotorDriver * TrackManager::track[MAX_TRACKS];
|
||||||
TRACK_MODE TrackManager::trackMode[MAX_TRACKS];
|
|
||||||
int16_t TrackManager::trackDCAddr[MAX_TRACKS];
|
int16_t TrackManager::trackDCAddr[MAX_TRACKS];
|
||||||
|
|
||||||
POWERMODE TrackManager::mainPowerGuess=POWERMODE::OFF;
|
POWERMODE TrackManager::mainPowerGuess=POWERMODE::OFF;
|
||||||
@@ -74,7 +73,7 @@ void TrackManager::sampleCurrent() {
|
|||||||
waiting = false;
|
waiting = false;
|
||||||
tr++;
|
tr++;
|
||||||
if (tr > lastTrack) tr = 0;
|
if (tr > lastTrack) tr = 0;
|
||||||
if (lastTrack < 2 || trackMode[tr] & TRACK_MODE_PROG) {
|
if (lastTrack < 2 || track[tr]->getMode() & TRACK_MODE_PROG) {
|
||||||
return; // We could continue but for prog track we
|
return; // We could continue but for prog track we
|
||||||
// rather do it in next interrupt beacuse
|
// rather do it in next interrupt beacuse
|
||||||
// that gives us well defined sampling point.
|
// that gives us well defined sampling point.
|
||||||
@@ -85,7 +84,7 @@ void TrackManager::sampleCurrent() {
|
|||||||
if (!waiting) {
|
if (!waiting) {
|
||||||
// look for a valid track to sample or until we are around
|
// look for a valid track to sample or until we are around
|
||||||
while (true) {
|
while (true) {
|
||||||
if (trackMode[tr] & ( 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_DCX|TRACK_MODE_EXT )) {
|
||||||
track[tr]->startCurrentFromHW();
|
track[tr]->startCurrentFromHW();
|
||||||
// for scope debug track[1]->setBrake(1);
|
// for scope debug track[1]->setBrake(1);
|
||||||
waiting = true;
|
waiting = true;
|
||||||
@@ -138,10 +137,10 @@ void TrackManager::Setup(const FSH * shieldname,
|
|||||||
}
|
}
|
||||||
|
|
||||||
void TrackManager::addTrack(byte t, MotorDriver* driver) {
|
void TrackManager::addTrack(byte t, MotorDriver* driver) {
|
||||||
trackMode[t]=TRACK_MODE_OFF;
|
|
||||||
track[t]=driver;
|
track[t]=driver;
|
||||||
if (driver) {
|
if (driver) {
|
||||||
track[t]->setPower(POWERMODE::OFF);
|
track[t]->setPower(POWERMODE::OFF);
|
||||||
|
track[t]->setMode(TRACK_MODE_OFF);
|
||||||
track[t]->setTrackLetter('A'+t);
|
track[t]->setTrackLetter('A'+t);
|
||||||
lastTrack=t;
|
lastTrack=t;
|
||||||
}
|
}
|
||||||
@@ -183,15 +182,15 @@ void TrackManager::setPROGSignal( bool on) {
|
|||||||
void TrackManager::setDCSignal(int16_t cab, byte speedbyte) {
|
void TrackManager::setDCSignal(int16_t cab, byte speedbyte) {
|
||||||
FOR_EACH_TRACK(t) {
|
FOR_EACH_TRACK(t) {
|
||||||
if (trackDCAddr[t]!=cab) continue;
|
if (trackDCAddr[t]!=cab) continue;
|
||||||
if (trackMode[t]==TRACK_MODE_DC) track[t]->setDCSignal(speedbyte);
|
if (track[t]->getMode()==TRACK_MODE_DC) track[t]->setDCSignal(speedbyte);
|
||||||
else if (trackMode[t]==TRACK_MODE_DCX) track[t]->setDCSignal(speedbyte ^ 128);
|
else if (track[t]->getMode()==TRACK_MODE_DCX) track[t]->setDCSignal(speedbyte ^ 128);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr) {
|
bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr) {
|
||||||
if (trackToSet>lastTrack || track[trackToSet]==NULL) return false;
|
if (trackToSet>lastTrack || track[trackToSet]==NULL) return false;
|
||||||
|
|
||||||
//DIAG(F("Track=%c"),trackToSet+'A');
|
DIAG(F("Track=%c Mode=%d"),trackToSet+'A', mode);
|
||||||
// DC tracks require a motorDriver that can set brake!
|
// DC tracks require a motorDriver that can set brake!
|
||||||
if ((mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX)
|
if ((mode==TRACK_MODE_DC || mode==TRACK_MODE_DCX)
|
||||||
&& !track[trackToSet]->brakeCanPWM()) {
|
&& !track[trackToSet]->brakeCanPWM()) {
|
||||||
@@ -218,9 +217,9 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
|
|||||||
#endif
|
#endif
|
||||||
// only allow 1 track to be prog
|
// only allow 1 track to be prog
|
||||||
FOR_EACH_TRACK(t)
|
FOR_EACH_TRACK(t)
|
||||||
if (trackMode[t]==TRACK_MODE_PROG && t != trackToSet) {
|
if (track[t]->getMode()==TRACK_MODE_PROG && t != trackToSet) {
|
||||||
track[t]->setPower(POWERMODE::OFF);
|
track[t]->setPower(POWERMODE::OFF);
|
||||||
trackMode[t]=TRACK_MODE_OFF;
|
track[t]->setMode(TRACK_MODE_OFF);
|
||||||
track[t]->makeProgTrack(false); // revoke prog track special handling
|
track[t]->makeProgTrack(false); // revoke prog track special handling
|
||||||
streamTrackState(NULL,t);
|
streamTrackState(NULL,t);
|
||||||
}
|
}
|
||||||
@@ -228,7 +227,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
|
|||||||
} else {
|
} else {
|
||||||
track[trackToSet]->makeProgTrack(false); // only the prog track knows it's type
|
track[trackToSet]->makeProgTrack(false); // only the prog track knows it's type
|
||||||
}
|
}
|
||||||
trackMode[trackToSet]=mode;
|
track[trackToSet]->setMode(mode);
|
||||||
trackDCAddr[trackToSet]=dcAddr;
|
trackDCAddr[trackToSet]=dcAddr;
|
||||||
streamTrackState(NULL,trackToSet);
|
streamTrackState(NULL,trackToSet);
|
||||||
|
|
||||||
@@ -255,7 +254,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
|
|||||||
// DC tracks must not have the DCC PWM switched on
|
// DC tracks must not have the DCC PWM switched on
|
||||||
// so we globally turn it off if one of the PWM
|
// so we globally turn it off if one of the PWM
|
||||||
// capable tracks is now DC or DCX.
|
// capable tracks is now DC or DCX.
|
||||||
if (trackMode[t]==TRACK_MODE_DC || trackMode[t]==TRACK_MODE_DCX) {
|
if (track[t]->getMode()==TRACK_MODE_DC || track[t]->getMode()==TRACK_MODE_DCX) {
|
||||||
if (track[t]->isPWMCapable()) {
|
if (track[t]->isPWMCapable()) {
|
||||||
canDo=false; // this track is capable but can not run PWM
|
canDo=false; // this track is capable but can not run PWM
|
||||||
break; // in this mode, so abort and prevent globally below
|
break; // in this mode, so abort and prevent globally below
|
||||||
@@ -263,7 +262,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
|
|||||||
track[t]->trackPWM=false; // this track sure can not run with PWM
|
track[t]->trackPWM=false; // this track sure can not run with PWM
|
||||||
//DIAG(F("Track %c trackPWM 0 (not capable)"), t+'A');
|
//DIAG(F("Track %c trackPWM 0 (not capable)"), t+'A');
|
||||||
}
|
}
|
||||||
} else if (trackMode[t]==TRACK_MODE_MAIN || trackMode[t]==TRACK_MODE_PROG) {
|
} else if (track[t]->getMode()==TRACK_MODE_MAIN || track[t]->getMode()==TRACK_MODE_PROG) {
|
||||||
track[t]->trackPWM = track[t]->isPWMCapable(); // trackPWM is still a guess here
|
track[t]->trackPWM = track[t]->isPWMCapable(); // trackPWM is still a guess here
|
||||||
//DIAG(F("Track %c trackPWM %d"), t+'A', track[t]->trackPWM);
|
//DIAG(F("Track %c trackPWM %d"), t+'A', track[t]->trackPWM);
|
||||||
canDo &= track[t]->trackPWM;
|
canDo &= track[t]->trackPWM;
|
||||||
@@ -301,7 +300,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
|
|||||||
|
|
||||||
void TrackManager::applyDCSpeed(byte t) {
|
void TrackManager::applyDCSpeed(byte t) {
|
||||||
uint8_t speedByte=DCC::getThrottleSpeedByte(trackDCAddr[t]);
|
uint8_t speedByte=DCC::getThrottleSpeedByte(trackDCAddr[t]);
|
||||||
if (trackMode[t]==TRACK_MODE_DCX)
|
if (track[t]->getMode()==TRACK_MODE_DCX)
|
||||||
speedByte = speedByte ^ 128; // reverse direction bit
|
speedByte = speedByte ^ 128; // reverse direction bit
|
||||||
track[t]->setDCSignal(speedByte);
|
track[t]->setDCSignal(speedByte);
|
||||||
}
|
}
|
||||||
@@ -347,7 +346,7 @@ void TrackManager::streamTrackState(Print* stream, byte t) {
|
|||||||
// null stream means send to commandDistributor for broadcast
|
// null stream means send to commandDistributor for broadcast
|
||||||
if (track[t]==NULL) return;
|
if (track[t]==NULL) return;
|
||||||
auto format=F("");
|
auto format=F("");
|
||||||
switch(trackMode[t]) {
|
switch(track[t]->getMode()) {
|
||||||
case TRACK_MODE_MAIN:
|
case TRACK_MODE_MAIN:
|
||||||
format=F("<= %c MAIN>\n");
|
format=F("<= %c MAIN>\n");
|
||||||
break;
|
break;
|
||||||
@@ -387,13 +386,13 @@ void TrackManager::loop() {
|
|||||||
if (nextCycleTrack>lastTrack) nextCycleTrack=0;
|
if (nextCycleTrack>lastTrack) nextCycleTrack=0;
|
||||||
if (track[nextCycleTrack]==NULL) return;
|
if (track[nextCycleTrack]==NULL) return;
|
||||||
MotorDriver * motorDriver=track[nextCycleTrack];
|
MotorDriver * motorDriver=track[nextCycleTrack];
|
||||||
bool useProgLimit=dontLimitProg? false: trackMode[nextCycleTrack]==TRACK_MODE_PROG;
|
bool useProgLimit=dontLimitProg? false: track[nextCycleTrack]->getMode()==TRACK_MODE_PROG;
|
||||||
motorDriver->checkPowerOverload(useProgLimit, nextCycleTrack);
|
motorDriver->checkPowerOverload(useProgLimit, nextCycleTrack);
|
||||||
}
|
}
|
||||||
|
|
||||||
MotorDriver * TrackManager::getProgDriver() {
|
MotorDriver * TrackManager::getProgDriver() {
|
||||||
FOR_EACH_TRACK(t)
|
FOR_EACH_TRACK(t)
|
||||||
if (trackMode[t]==TRACK_MODE_PROG) return track[t];
|
if (track[t]->getMode()==TRACK_MODE_PROG) return track[t];
|
||||||
return NULL;
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -401,7 +400,7 @@ MotorDriver * TrackManager::getProgDriver() {
|
|||||||
std::vector<MotorDriver *>TrackManager::getMainDrivers() {
|
std::vector<MotorDriver *>TrackManager::getMainDrivers() {
|
||||||
std::vector<MotorDriver *> v;
|
std::vector<MotorDriver *> v;
|
||||||
FOR_EACH_TRACK(t)
|
FOR_EACH_TRACK(t)
|
||||||
if (trackMode[t]==TRACK_MODE_MAIN) v.push_back(track[t]);
|
if (track[t]->getMode()==TRACK_MODE_MAIN) v.push_back(track[t]);
|
||||||
return v;
|
return v;
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
@@ -411,7 +410,7 @@ void TrackManager::setPower2(bool setProg,POWERMODE mode) {
|
|||||||
FOR_EACH_TRACK(t) {
|
FOR_EACH_TRACK(t) {
|
||||||
MotorDriver * driver=track[t];
|
MotorDriver * driver=track[t];
|
||||||
if (!driver) continue;
|
if (!driver) continue;
|
||||||
switch (trackMode[t]) {
|
switch (track[t]->getMode()) {
|
||||||
case TRACK_MODE_MAIN:
|
case TRACK_MODE_MAIN:
|
||||||
if (setProg) break;
|
if (setProg) break;
|
||||||
// toggle brake before turning power on - resets overcurrent error
|
// toggle brake before turning power on - resets overcurrent error
|
||||||
@@ -447,8 +446,8 @@ void TrackManager::setPower2(bool setProg,POWERMODE mode) {
|
|||||||
|
|
||||||
POWERMODE TrackManager::getProgPower() {
|
POWERMODE TrackManager::getProgPower() {
|
||||||
FOR_EACH_TRACK(t)
|
FOR_EACH_TRACK(t)
|
||||||
if (trackMode[t]==TRACK_MODE_PROG)
|
if (track[t]->getMode()==TRACK_MODE_PROG)
|
||||||
return track[t]->getPower();
|
return track[t]->getPower();
|
||||||
return POWERMODE::OFF;
|
return POWERMODE::OFF;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -492,7 +491,7 @@ void TrackManager::setJoin(bool joined) {
|
|||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
if (joined) {
|
if (joined) {
|
||||||
FOR_EACH_TRACK(t) {
|
FOR_EACH_TRACK(t) {
|
||||||
if (trackMode[t]==TRACK_MODE_PROG) {
|
if (track[t]->getMode()==TRACK_MODE_PROG) {
|
||||||
tempProgTrack = t;
|
tempProgTrack = t;
|
||||||
setTrackMode(t, TRACK_MODE_MAIN);
|
setTrackMode(t, TRACK_MODE_MAIN);
|
||||||
break;
|
break;
|
||||||
|
@@ -27,10 +27,6 @@
|
|||||||
#include "MotorDriver.h"
|
#include "MotorDriver.h"
|
||||||
// Virtualised Motor shield multi-track hardware Interface
|
// Virtualised Motor shield multi-track hardware Interface
|
||||||
|
|
||||||
// use powers of two so we can do logical and/or on the track modes in if clauses.
|
|
||||||
enum TRACK_MODE : byte {TRACK_MODE_OFF = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4,
|
|
||||||
TRACK_MODE_DC = 8, TRACK_MODE_DCX = 16, TRACK_MODE_EXT = 32};
|
|
||||||
|
|
||||||
// These constants help EXRAIL macros say SET_TRACK(2,mode) OR SET_TRACK(C,mode) etc.
|
// These constants help EXRAIL macros say SET_TRACK(2,mode) OR SET_TRACK(C,mode) etc.
|
||||||
const byte TRACK_NUMBER_0=0, TRACK_NUMBER_A=0;
|
const byte TRACK_NUMBER_0=0, TRACK_NUMBER_A=0;
|
||||||
const byte TRACK_NUMBER_1=1, TRACK_NUMBER_B=1;
|
const byte TRACK_NUMBER_1=1, TRACK_NUMBER_B=1;
|
||||||
@@ -100,7 +96,6 @@ class TrackManager {
|
|||||||
static POWERMODE mainPowerGuess;
|
static POWERMODE mainPowerGuess;
|
||||||
static void applyDCSpeed(byte t);
|
static void applyDCSpeed(byte t);
|
||||||
|
|
||||||
static TRACK_MODE trackMode[MAX_TRACKS];
|
|
||||||
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 or TRACK_MODE_DCX
|
||||||
#ifdef ARDUINO_ARCH_ESP32
|
#ifdef ARDUINO_ARCH_ESP32
|
||||||
static byte tempProgTrack; // holds the prog track number during join
|
static byte tempProgTrack; // holds the prog track number during join
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
© 2021, Harald Barth.
|
© 2023 Paul M. Antoine
|
||||||
|
© 2021 Harald Barth
|
||||||
|
© 2023 Nathan Kellenicki
|
||||||
|
|
||||||
This file is part of CommandStation-EX
|
This file is part of CommandStation-EX
|
||||||
|
|
||||||
@@ -106,11 +108,18 @@ void wifiLoop(void *){
|
|||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
char asciitolower(char in) {
|
||||||
|
if (in <= 'Z' && in >= 'A')
|
||||||
|
return in - ('Z' - 'z');
|
||||||
|
return in;
|
||||||
|
}
|
||||||
|
|
||||||
bool WifiESP::setup(const char *SSid,
|
bool WifiESP::setup(const char *SSid,
|
||||||
const char *password,
|
const char *password,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
int port,
|
int port,
|
||||||
const byte channel) {
|
const byte channel,
|
||||||
|
const bool forceAP) {
|
||||||
bool havePassword = true;
|
bool havePassword = true;
|
||||||
bool haveSSID = true;
|
bool haveSSID = true;
|
||||||
bool wifiUp = false;
|
bool wifiUp = false;
|
||||||
@@ -138,7 +147,8 @@ bool WifiESP::setup(const char *SSid,
|
|||||||
if (strncmp(yourNetwork, password, 13) == 0 || strncmp("", password, 13) == 0)
|
if (strncmp(yourNetwork, password, 13) == 0 || strncmp("", password, 13) == 0)
|
||||||
havePassword = false;
|
havePassword = false;
|
||||||
|
|
||||||
if (haveSSID && havePassword) {
|
if (haveSSID && havePassword && !forceAP) {
|
||||||
|
WiFi.setHostname(hostname); // Strangely does not work unless we do it HERE!
|
||||||
WiFi.mode(WIFI_STA);
|
WiFi.mode(WIFI_STA);
|
||||||
#ifdef SERIAL_BT_COMMANDS
|
#ifdef SERIAL_BT_COMMANDS
|
||||||
WiFi.setSleep(true);
|
WiFi.setSleep(true);
|
||||||
@@ -175,16 +185,20 @@ bool WifiESP::setup(const char *SSid,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (!haveSSID) {
|
if (!haveSSID || forceAP) {
|
||||||
// prepare all strings
|
// prepare all strings
|
||||||
String strSSID("DCCEX_");
|
String strSSID(forceAP ? SSid : "DCCEX_");
|
||||||
String strPass("PASS_");
|
String strPass(forceAP ? password : "PASS_");
|
||||||
String strMac = WiFi.macAddress();
|
if (!forceAP) {
|
||||||
strMac.remove(0,9);
|
String strMac = WiFi.macAddress();
|
||||||
strMac.replace(":","");
|
strMac.remove(0,9);
|
||||||
strMac.replace(":","");
|
strMac.replace(":","");
|
||||||
strSSID.concat(strMac);
|
strMac.replace(":","");
|
||||||
strPass.concat(strMac);
|
// convert mac addr hex chars to lower case to be compatible with AT software
|
||||||
|
std::transform(strMac.begin(), strMac.end(), strMac.begin(), asciitolower);
|
||||||
|
strSSID.concat(strMac);
|
||||||
|
strPass.concat(strMac);
|
||||||
|
}
|
||||||
|
|
||||||
WiFi.mode(WIFI_AP);
|
WiFi.mode(WIFI_AP);
|
||||||
#ifdef SERIAL_BT_COMMANDS
|
#ifdef SERIAL_BT_COMMANDS
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
/*
|
/*
|
||||||
* © 2021, Harald Barth.
|
* © 2021 Harald Barth
|
||||||
|
* © 2023 Nathan Kellenicki
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
*
|
*
|
||||||
@@ -31,7 +32,8 @@ public:
|
|||||||
const char *wifiPassword,
|
const char *wifiPassword,
|
||||||
const char *hostname,
|
const char *hostname,
|
||||||
const int port,
|
const int port,
|
||||||
const byte channel);
|
const byte channel,
|
||||||
|
const bool forceAP);
|
||||||
static void loop();
|
static void loop();
|
||||||
private:
|
private:
|
||||||
};
|
};
|
||||||
|
@@ -2,6 +2,7 @@
|
|||||||
* © 2021 Fred Decker
|
* © 2021 Fred Decker
|
||||||
* © 2020-2022 Harald Barth
|
* © 2020-2022 Harald Barth
|
||||||
* © 2020-2022 Chris Harlow
|
* © 2020-2022 Chris Harlow
|
||||||
|
* © 2023 Nathan Kellenicki
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
@@ -83,7 +84,8 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||||||
const FSH *wifiPassword,
|
const FSH *wifiPassword,
|
||||||
const FSH *hostname,
|
const FSH *hostname,
|
||||||
const int port,
|
const int port,
|
||||||
const byte channel) {
|
const byte channel,
|
||||||
|
const bool forceAP) {
|
||||||
|
|
||||||
wifiSerialState wifiUp = WIFI_NOAT;
|
wifiSerialState wifiUp = WIFI_NOAT;
|
||||||
|
|
||||||
@@ -95,12 +97,13 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||||||
(void) hostname;
|
(void) hostname;
|
||||||
(void) port;
|
(void) port;
|
||||||
(void) channel;
|
(void) channel;
|
||||||
|
(void) forceAP;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// See if the WiFi is attached to the first serial port
|
// See if the WiFi is attached to the first serial port
|
||||||
#if NUM_SERIAL > 0 && !defined(SERIAL1_COMMANDS)
|
#if NUM_SERIAL > 0 && !defined(SERIAL1_COMMANDS)
|
||||||
SERIAL1.begin(serial_link_speed);
|
SERIAL1.begin(serial_link_speed);
|
||||||
wifiUp = setup(SERIAL1, wifiESSID, wifiPassword, hostname, port, channel);
|
wifiUp = setup(SERIAL1, wifiESSID, wifiPassword, hostname, port, channel, forceAP);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Other serials are tried, depending on hardware.
|
// Other serials are tried, depending on hardware.
|
||||||
@@ -110,7 +113,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||||||
if (wifiUp == WIFI_NOAT)
|
if (wifiUp == WIFI_NOAT)
|
||||||
{
|
{
|
||||||
Serial2.begin(serial_link_speed);
|
Serial2.begin(serial_link_speed);
|
||||||
wifiUp = setup(Serial2, wifiESSID, wifiPassword, hostname, port, channel);
|
wifiUp = setup(Serial2, wifiESSID, wifiPassword, hostname, port, channel, forceAP);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
#endif
|
#endif
|
||||||
@@ -121,7 +124,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||||||
if (wifiUp == WIFI_NOAT)
|
if (wifiUp == WIFI_NOAT)
|
||||||
{
|
{
|
||||||
SERIAL3.begin(serial_link_speed);
|
SERIAL3.begin(serial_link_speed);
|
||||||
wifiUp = setup(SERIAL3, wifiESSID, wifiPassword, hostname, port, channel);
|
wifiUp = setup(SERIAL3, wifiESSID, wifiPassword, hostname, port, channel, forceAP);
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -139,7 +142,7 @@ bool WifiInterface::setup(long serial_link_speed,
|
|||||||
}
|
}
|
||||||
|
|
||||||
wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, const FSH* password,
|
wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, const FSH* password,
|
||||||
const FSH* hostname, int port, byte channel) {
|
const FSH* hostname, int port, byte channel, bool forceAP) {
|
||||||
wifiSerialState wifiState;
|
wifiSerialState wifiState;
|
||||||
static uint8_t ntry = 0;
|
static uint8_t ntry = 0;
|
||||||
ntry++;
|
ntry++;
|
||||||
@@ -148,7 +151,7 @@ wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, con
|
|||||||
|
|
||||||
DIAG(F("++ Wifi Setup Try %d ++"), ntry);
|
DIAG(F("++ Wifi Setup Try %d ++"), ntry);
|
||||||
|
|
||||||
wifiState = setup2( SSid, password, hostname, port, channel);
|
wifiState = setup2( SSid, password, hostname, port, channel, forceAP);
|
||||||
|
|
||||||
if (wifiState == WIFI_NOAT) {
|
if (wifiState == WIFI_NOAT) {
|
||||||
LCD(4, F("WiFi no AT chip"));
|
LCD(4, F("WiFi no AT chip"));
|
||||||
@@ -172,7 +175,7 @@ wifiSerialState WifiInterface::setup(Stream & setupStream, const FSH* SSid, con
|
|||||||
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
#pragma GCC diagnostic ignored "-Wunused-parameter"
|
||||||
#endif
|
#endif
|
||||||
wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
|
wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
|
||||||
const FSH* hostname, int port, byte channel) {
|
const FSH* hostname, int port, byte channel, bool forceAP) {
|
||||||
bool ipOK = false;
|
bool ipOK = false;
|
||||||
bool oldCmd = false;
|
bool oldCmd = false;
|
||||||
|
|
||||||
@@ -225,7 +228,7 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
|
|||||||
if (!checkForOK(1000, F("0.0.0.0"), true,false))
|
if (!checkForOK(1000, F("0.0.0.0"), true,false))
|
||||||
ipOK = true;
|
ipOK = true;
|
||||||
}
|
}
|
||||||
} else {
|
} else if (!forceAP) {
|
||||||
// SSID was configured, so we assume station (client) mode.
|
// SSID was configured, so we assume station (client) mode.
|
||||||
if (oldCmd) {
|
if (oldCmd) {
|
||||||
// AT command early version supports CWJAP/CWSAP
|
// AT command early version supports CWJAP/CWSAP
|
||||||
@@ -285,14 +288,19 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
|
|||||||
|
|
||||||
i=0;
|
i=0;
|
||||||
do {
|
do {
|
||||||
if (STRNCMP_P(yourNetwork, (const char*)password, 13) == 0) {
|
if (!forceAP) {
|
||||||
// unconfigured
|
if (STRNCMP_P(yourNetwork, (const char*)password, 13) == 0) {
|
||||||
StringFormatter::send(wifiStream, F("AT+CWSAP%s=\"DCCEX_%s\",\"PASS_%s\",%d,4\r\n"),
|
// unconfigured
|
||||||
oldCmd ? "" : "_CUR", macTail, macTail, channel);
|
StringFormatter::send(wifiStream, F("AT+CWSAP%s=\"DCCEX_%s\",\"PASS_%s\",%d,4\r\n"),
|
||||||
|
oldCmd ? "" : "_CUR", macTail, macTail, channel);
|
||||||
|
} else {
|
||||||
|
// password configured by user
|
||||||
|
StringFormatter::send(wifiStream, F("AT+CWSAP%s=\"DCCEX_%s\",\"%S\",%d,4\r\n"), oldCmd ? "" : "_CUR",
|
||||||
|
macTail, password, channel);
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// password configured by user
|
StringFormatter::send(wifiStream, F("AT+CWSAP%s=\"%S\",\"%S\",%d,4\r\n"),
|
||||||
StringFormatter::send(wifiStream, F("AT+CWSAP%s=\"DCCEX_%s\",\"%S\",%d,4\r\n"), oldCmd ? "" : "_CUR",
|
oldCmd ? "" : "_CUR", SSid, password, channel);
|
||||||
macTail, password, channel);
|
|
||||||
}
|
}
|
||||||
} while (!checkForOK(WIFI_CONNECT_TIMEOUT, true) && i++<2); // do twice if necessary but ignore failure as AP mode may still be ok
|
} while (!checkForOK(WIFI_CONNECT_TIMEOUT, true) && i++<2); // do twice if necessary but ignore failure as AP mode may still be ok
|
||||||
if (i >= 2)
|
if (i >= 2)
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
/*
|
/*
|
||||||
* © 2020-2021 Chris Harlow
|
* © 2020-2021 Chris Harlow
|
||||||
* © 2020, Harald Barth.
|
* © 2020, Harald Barth.
|
||||||
|
* © 2023 Nathan Kellenicki
|
||||||
* All rights reserved.
|
* All rights reserved.
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
@@ -36,17 +37,18 @@ public:
|
|||||||
const FSH *wifiPassword,
|
const FSH *wifiPassword,
|
||||||
const FSH *hostname,
|
const FSH *hostname,
|
||||||
const int port,
|
const int port,
|
||||||
const byte channel);
|
const byte channel,
|
||||||
|
const bool forceAP);
|
||||||
static void loop();
|
static void loop();
|
||||||
static void ATCommand(HardwareSerial * stream,const byte *command);
|
static void ATCommand(HardwareSerial * stream,const byte *command);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static wifiSerialState setup(Stream &setupStream, const FSH *SSSid, const FSH *password,
|
static wifiSerialState setup(Stream &setupStream, const FSH *SSSid, const FSH *password,
|
||||||
const FSH *hostname, int port, byte channel);
|
const FSH *hostname, int port, byte channel, bool forceAP);
|
||||||
static Stream *wifiStream;
|
static Stream *wifiStream;
|
||||||
static DCCEXParser parser;
|
static DCCEXParser parser;
|
||||||
static wifiSerialState setup2(const FSH *SSSid, const FSH *password,
|
static wifiSerialState setup2(const FSH *SSSid, const FSH *password,
|
||||||
const FSH *hostname, int port, byte channel);
|
const FSH *hostname, int port, byte channel, bool forceAP);
|
||||||
static bool checkForOK(const unsigned int timeout, bool echo, bool escapeEcho = true);
|
static bool checkForOK(const unsigned int timeout, bool echo, bool escapeEcho = true);
|
||||||
static bool checkForOK(const unsigned int timeout, const FSH *waitfor, bool echo, bool escapeEcho = true);
|
static bool checkForOK(const unsigned int timeout, const FSH *waitfor, bool echo, bool escapeEcho = true);
|
||||||
static bool connected;
|
static bool connected;
|
||||||
|
@@ -4,6 +4,7 @@
|
|||||||
* © 2020-2023 Harald Barth
|
* © 2020-2023 Harald Barth
|
||||||
* © 2020-2021 Fred Decker
|
* © 2020-2021 Fred Decker
|
||||||
* © 2020-2021 Chris Harlow
|
* © 2020-2021 Chris Harlow
|
||||||
|
* © 2023 Nathan Kellenicki
|
||||||
*
|
*
|
||||||
* This file is part of CommandStation-EX
|
* This file is part of CommandStation-EX
|
||||||
*
|
*
|
||||||
@@ -57,6 +58,21 @@ The configuration file for DCC-EX Command Station
|
|||||||
// +-----------------------v
|
// +-----------------------v
|
||||||
//
|
//
|
||||||
#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD
|
#define MOTOR_SHIELD_TYPE STANDARD_MOTOR_SHIELD
|
||||||
|
//
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
//
|
||||||
|
// If you want to restrict the maximum current LOWER than what your
|
||||||
|
// motor shield can provide, you can do that here. For example if you
|
||||||
|
// have a motor shield that can provide 5A and your power supply can
|
||||||
|
// only provide 2.5A then you should restict the maximum current to
|
||||||
|
// 2.25A (90% of 2.5A) so that DCC-EX does shut off the track before
|
||||||
|
// your PS does shut DCC-EX. MAX_CURRENT is in mA so for this example
|
||||||
|
// it would be 2250, adjust the number according to your PS. If your
|
||||||
|
// PS has a higher rating than your motor shield you do not need this.
|
||||||
|
// You can use this as well if you are cautious and your trains do not
|
||||||
|
// need full current.
|
||||||
|
// #define MAX_CURRENT 2250
|
||||||
|
//
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
// The IP port to talk to a WIFI or Ethernet shield.
|
// The IP port to talk to a WIFI or Ethernet shield.
|
||||||
@@ -108,6 +124,11 @@ The configuration file for DCC-EX Command Station
|
|||||||
// this line exists or not. If you need to use an alternate channel (we recommend
|
// this line exists or not. If you need to use an alternate channel (we recommend
|
||||||
// using only 1,6, or 11) you may change it here.
|
// using only 1,6, or 11) you may change it here.
|
||||||
#define WIFI_CHANNEL 1
|
#define WIFI_CHANNEL 1
|
||||||
|
//
|
||||||
|
// WIFI_FORCE_AP: If you'd like to specify your own WIFI_SSID in AP mode, set this
|
||||||
|
// true. Otherwise it is assumed that you'd like to connect to an existing network
|
||||||
|
// with that SSID.
|
||||||
|
#define WIFI_FORCE_AP false
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
//
|
//
|
||||||
|
11
defines.h
11
defines.h
@@ -182,6 +182,15 @@
|
|||||||
#define WIFI_ON false
|
#define WIFI_ON false
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef WIFI_FORCE_AP
|
||||||
|
#define WIFI_FORCE_AP false
|
||||||
|
#else
|
||||||
|
#if WIFI_FORCE_AP==true || WIFI_FORCE_AP==false
|
||||||
|
#else
|
||||||
|
#error WIFI_FORCE_AP needs to be true or false
|
||||||
|
#endif
|
||||||
|
#endif
|
||||||
|
|
||||||
#if ENABLE_ETHERNET
|
#if ENABLE_ETHERNET
|
||||||
#if defined(HAS_ENOUGH_MEMORY)
|
#if defined(HAS_ENOUGH_MEMORY)
|
||||||
#define ETHERNET_ON true
|
#define ETHERNET_ON true
|
||||||
@@ -205,7 +214,7 @@
|
|||||||
#define WIFI_SERIAL_LINK_SPEED 115200
|
#define WIFI_SERIAL_LINK_SPEED 115200
|
||||||
|
|
||||||
#if __has_include ( "myAutomation.h")
|
#if __has_include ( "myAutomation.h")
|
||||||
#if defined(HAS_ENOUGH_MEMORY) || defined(DISABLE_EEPROM)
|
#if defined(HAS_ENOUGH_MEMORY) || defined(DISABLE_EEPROM) || defined(DISABLE_PROG)
|
||||||
#define EXRAIL_ACTIVE
|
#define EXRAIL_ACTIVE
|
||||||
#else
|
#else
|
||||||
#define EXRAIL_WARNING
|
#define EXRAIL_WARNING
|
||||||
|
23
installer.sh
23
installer.sh
@@ -1,7 +1,7 @@
|
|||||||
#!/bin/bash
|
#!/bin/bash
|
||||||
|
|
||||||
#
|
#
|
||||||
# © 2022 Harald Barth
|
# © 2022,2023 Harald Barth
|
||||||
#
|
#
|
||||||
# This file is part of CommandStation-EX
|
# This file is part of CommandStation-EX
|
||||||
#
|
#
|
||||||
@@ -29,14 +29,33 @@ ACLI="./bin/arduino-cli"
|
|||||||
|
|
||||||
function need () {
|
function need () {
|
||||||
type -p $1 > /dev/null && return
|
type -p $1 > /dev/null && return
|
||||||
|
dpkg -l $1 2>&1 | egrep ^ii >/dev/null && return
|
||||||
sudo apt-get install $1
|
sudo apt-get install $1
|
||||||
type -p $1 > /dev/null && return
|
type -p $1 > /dev/null && return
|
||||||
echo "Could not install $1, abort"
|
echo "Could not install $1, abort"
|
||||||
exit 255
|
exit 255
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
need git
|
need git
|
||||||
|
|
||||||
|
if cat /etc/issue | egrep '^Raspbian' 2>&1 >/dev/null ; then
|
||||||
|
# we are on a raspi where we do not support graphical
|
||||||
|
unset DISPLAY
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ x$DISPLAY != x ] ; then
|
||||||
|
# we have DISPLAY, do the graphic thing
|
||||||
|
need python3-tk
|
||||||
|
need python3.8-venv
|
||||||
|
mkdir -p ~/ex-installer/venv
|
||||||
|
python3 -m venv ~/ex-installer/venv
|
||||||
|
cd ~/ex-installer/venv || exit 255
|
||||||
|
source ./bin/activate
|
||||||
|
git clone https://github.com/DCC-EX/EX-Installer
|
||||||
|
cd EX-Installer || exit 255
|
||||||
|
pip3 install -r requirements.txt
|
||||||
|
exec python3 -m ex_installer
|
||||||
|
fi
|
||||||
if test -d `basename "$DCCEXGITURL"` ; then
|
if test -d `basename "$DCCEXGITURL"` ; then
|
||||||
: assume we are almost there
|
: assume we are almost there
|
||||||
cd `basename "$DCCEXGITURL"` || exit 255
|
cd `basename "$DCCEXGITURL"` || exit 255
|
||||||
|
10
version.h
10
version.h
@@ -3,8 +3,14 @@
|
|||||||
|
|
||||||
#include "StringFormatter.h"
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
|
#define VERSION "4.2.65"
|
||||||
#define VERSION "4.2.60"
|
// 4.2.65 - new config WIFI_FORCE_AP option
|
||||||
|
// 4.2.63 - completely new overcurrent detection
|
||||||
|
// - ESP32 protect from race in RMT code
|
||||||
|
// 4.2.62 - Update IO_RotaryEncoder.h to ignore sending current position
|
||||||
|
// - Update IO_EXTurntable.h to remove forced I2C clock speed
|
||||||
|
// - Show device offline if EX-Turntable not connected
|
||||||
|
// 4.2.61 - MAX_CURRENT restriction (caps motor shield value)
|
||||||
// 4.2.60 - Add mDNS capability to ESP32 for autodiscovery
|
// 4.2.60 - Add mDNS capability to ESP32 for autodiscovery
|
||||||
// 4.2.59 - Fix: AP SSID was DCC_ instead of DCCEX_
|
// 4.2.59 - Fix: AP SSID was DCC_ instead of DCCEX_
|
||||||
// 4.2.58 - Start motordriver as soon as possible but without waveform
|
// 4.2.58 - Start motordriver as soon as possible but without waveform
|
||||||
|
Reference in New Issue
Block a user