1
0
mirror of https://github.com/DCC-EX/CommandStation-EX.git synced 2025-07-31 11:23:44 +02:00

Compare commits

..

76 Commits

Author SHA1 Message Date
Harald Barth
fcbd5f96ab versioncomment 2024-01-14 22:47:03 +01:00
Harald Barth
4b486900ad remove preamble doubling and try to hit right preamble bit for cutout 2024-01-14 22:45:43 +01:00
habazut
8bd6403cd1 Merge pull request #391 from Arq/arq-RailCom
RailCom cutout for MAIN and PROG track
2024-01-14 19:43:22 +01:00
Arkadiusz Hahn
65b9079337 RailCom cutout for MAIN and PROG track 2024-01-14 16:14:19 +01:00
Harald Barth
818e05b425 version 5.0.8 2024-01-10 08:37:54 +01:00
Harald Barth
c5168f030f Do not crash on turnouts without description 2024-01-10 08:25:34 +01:00
Harald Barth
387ea019bd version 5.0.7 2023-11-06 22:11:56 +01:00
Harald Barth
a981f83bb9 Only flag 2.2.0.0-dev as broken, not 2.2.0.0 2023-11-06 22:11:31 +01:00
Asbelos
749a859db5 Bugfix TURNOUTL 2023-11-01 20:13:05 +00:00
Harald Barth
659c58b307 version 5.0.5 2023-10-28 19:20:33 +02:00
Harald Barth
0b9ec7460b Bugfix version detection logic and better message 2023-10-28 19:18:59 +02:00
Asbelos
8b8e9e4919 clean result from invalid <JR n> 2023-10-12 11:07:05 +01:00
Asbelos
bef4b2ec35 fix <JR> default roster 2023-10-09 18:09:48 +01:00
Harald Barth
9333beda49 correct return when requesting D RAM 2023-09-24 20:54:17 +02:00
Harald Barth
46289fa78c Check bad AT firmware version 2023-09-14 09:05:23 +02:00
Harald Barth
b3cafd126e sample file corrections 2023-08-30 23:26:20 +02:00
Harald Barth
c55fa9f9d2 version number update 2023-08-25 19:08:58 +02:00
Harald Barth
210d96a3e3 Bugfix: ESP32 30ms off time 2023-08-25 19:07:57 +02:00
Harald Barth
42f3c7c128 version number update 2023-08-24 10:05:31 +02:00
Harald Barth
6cd7002e91 Bugfix: execute 30ms off time before rejoin 2023-08-24 10:03:29 +02:00
peteGSX
085762e800 Add OPCODE list to DCCEXParser.cpp 2023-08-18 18:52:34 +10:00
Harald Barth
2db2b0ecc6 Committing a SHA 2023-08-07 20:27:22 +02:00
Harald Barth
fd58a749ef Committing a SHA 2023-08-07 20:25:14 +02:00
Harald Barth
3bddf4dfd1 Make 4.2.69 the 5.0.0 release 2023-08-07 19:45:45 +02:00
Harald Barth
e0e965f81c Merge branch 'master' into devel 2023-08-07 19:41:00 +02:00
Harald Barth
f2be3aeac3 Make <!> work in DC mode 2023-08-04 14:45:05 +02:00
Harald Barth
a74d85e895 Rename track mode OFF to NONE 2023-08-02 10:00:43 +02:00
Fred
2eb0f48994 Update .gitignore
Updated .gitignore from the devel branch and used "my@.cpp" instead of listing the individual files so we can ignore anything that starts with "my"
2023-06-29 14:24:51 -04:00
Harald Barth
99521f8a3f Support DCC-EX shield 2023-05-20 17:35:09 +02:00
mstevetodd
fcf05206b4 Merge pull request #333 from mstevetodd/master
Fix: turnout state should be 2/4, not T2/T4
2023-04-25 16:06:10 -04:00
stevet
cc3aba1feb Update WiThrottle.cpp
Fix: turnout state should be 2/4, not T2/T4
2023-04-25 16:02:42 -04:00
Fred
91d36ae909 Update ThrottleAssists.md 2023-03-03 21:59:18 -05:00
Fred
98af5c45ed Update ThrottleAssists.md 2023-03-03 21:46:07 -05:00
Fred
d3eceb6d6c Update ThrottleAssists.md 2023-03-03 21:41:22 -05:00
Fred
79eaaa85fa Update ThrottleAssists.md
Fixing formatting
2023-03-03 21:35:13 -05:00
Fred
0f5b8adb6b Update ThrottleAssists.md 2023-03-03 21:26:46 -05:00
Fred
da8faa808b Update ThrottleAssists.md 2023-03-03 21:07:58 -05:00
Harald Barth
7311f2ce64 LCN bugfix 2023-02-12 20:38:03 +01:00
Harald Barth
7e4f9eb0e1 jT answer should contain empty string 2023-01-29 11:33:28 +01:00
Harald Barth
1f5eafbcca Bugfix for issue #299 TurnoutDescription NULL 2023-01-29 11:32:54 +01:00
peteGSX
7e16ec7088 Fix support request issue template 2022-11-05 05:17:03 +10:00
Harald Barth
912646f8ff Merge branch 'master' of https://github.com/DCC-EX/CommandStation-EX into HEAD 2022-11-04 15:41:05 +01:00
Harald Barth
dd309a3705 Ethernet init order 2022-11-04 15:39:35 +01:00
peteGSX
5376c9f410 Update project workflow for forks 2022-11-04 06:54:49 +10:00
peteGSX
b1d110ecbf Fix project workflow 2022-11-03 14:06:43 +10:00
Fred
5b7801ca6c Update version.h 2022-10-28 14:05:35 -04:00
Fred
aca9c9c941 Update release_notes.md 2022-10-28 10:52:12 -04:00
Fred
6f94cd71ab Update release_notes_v4.1.2.md 2022-10-28 10:50:35 -04:00
Fred
1827a11f83 Update release_notes_v4.1.1.md 2022-10-28 10:49:32 -04:00
Fred
0023ce3356 Create release_notes_v4.1.2.md 2022-10-28 10:48:40 -04:00
Fred
7b9f3ae08d Update release_notes.md 2022-10-28 10:39:13 -04:00
Fred
5e50731a78 Update version.h
Fix version number in notes from 4.2.1 to 4.1.2
2022-10-28 10:28:54 -04:00
Harald Barth
df6c511d1d Fix for W5100 ethernet shield which does not report as the W5200 or W5500 2022-10-28 13:24:12 +02:00
peteGSX
4bfd4b1a12 Add templates and project workflow (#258)
* Add templates and project workflow

* Fixed template typos
2022-10-26 19:34:13 -04:00
Fred
4a3f3d0f34 Update release_notes_v4.1.1.md 2022-10-23 08:22:00 -04:00
Fred
f0d1909d9f Update release_notes.md 2022-10-23 08:21:36 -04:00
Fred
daf6799ac1 Update release_notes.md 2022-10-22 18:10:42 -04:00
Kcsmith0708
b7a010f904 Verion.h 4.1.1 (#263)
Edited & Reformatted
 verify then ready for release
2022-10-22 18:01:37 -04:00
Kcsmith0708
d1518b8af0 Update Release_notes_v4.1.1.md (#264)
* Update release_notes_v4.1.1.md

Edited </RED > etc., commands and added KILLALL function to EXRAIL list

* Update release_notes_v4.1.1.md

added <t cab> back in

* Update release_notes_v4.1.1.md

fixed < t cab>  so it would display
2022-10-22 18:00:21 -04:00
Fred
39a85903ce Update release_notes.md 2022-10-21 20:04:05 -04:00
Fred
d72474cd8f Update release_notes_v4.1.1.md 2022-10-21 20:03:00 -04:00
Kcsmith0708
941e74beaf Realese Document Edit & Enhancements (#262)
* Realese Document Edit & Enhancements

Edited Intro and Rearranged EXRAIL Enhancements

* Update release_notes_v4.1.1.md

edited indents

* Update release_notes_v4.1.1.md

Fomating
2022-10-21 13:45:54 -04:00
Fred
e618b91900 Update version.h 2022-10-21 11:57:55 -04:00
Fred
dcab5a0e72 Create release_notes_v4.1.1.md 2022-10-20 16:25:10 -04:00
Fred
1901d9547e Update release_notes.md 2022-10-20 16:23:13 -04:00
Fred
7388d14bab Update release_notes.md 2022-10-20 16:08:40 -04:00
Harald Barth
2da28ad2db version 2022-09-18 22:23:18 +02:00
Harald Barth
06bd80438e new version 2022-09-13 22:46:43 +02:00
Harald Barth
cd15eed005 EX-RAIL bugfix: Could not read long loco addrs 2022-09-13 22:43:31 +02:00
Harald Barth
23d0158804 simplify EthernetInterface::setup, make code shorter and format according to our overall style 2022-09-05 22:19:18 +02:00
habazut
2e9e614ad5 Merge pull request #256 from bcsanches/master
Keep Ethernet singleton "alive" until connection is established.
2022-09-05 20:34:11 +02:00
Bruno Crivelari Sanches
64b1de08be Detects when ethernet cable is connected and is disconnected, also correctly handles EthernetServer tead down on such situations 2022-09-05 14:23:54 -03:00
Bruno Crivelari Sanches
34c3d10767 Keep Ethernet singleton "alive" until connection is established. 2022-09-03 17:16:33 -03:00
Harald Barth
f2eb64fd21 make service start to be outside the DONT_TOUCH_WIFI_CONF area 2022-07-31 23:07:19 +02:00
Harald Barth
a80b16acba HH not supported 2022-06-21 19:46:59 +02:00
Harald Barth
b1f5e9f48c Initial version 2022-06-21 15:04:45 +02:00
14 changed files with 379 additions and 113 deletions

View File

@@ -351,7 +351,7 @@ void DCCACK::callback(int value) {
switch (callbackState) {
case AFTER_READ:
if (ackManagerRejoin && autoPowerOff) {
if (ackManagerRejoin && !autoPowerOff) {
progDriver->setPower(POWERMODE::OFF);
callbackStart=millis();
callbackState=WAITING_30;

View File

@@ -25,6 +25,79 @@
* You should have received a copy of the GNU General Public License
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
*/
/*
List of single character OPCODEs in use for reference.
When determining a new OPCODE for a new feature, refer to this list as the source of truth.
Once a new OPCODE is decided upon, update this list.
Character, Usage
/, |EX-R| interactive commands
-, Remove from reminder table
=, |TM| configuration
!, Emergency stop
@, Reserved for future use - LCD messages to JMRI
#, Request number of supported cabs/locos; heartbeat
+, WiFi AT commands
?, Reserved for future use
0, Track power off
1, Track power on
a, DCC accessory control
A,
b, Write CV bit on main
B, Write CV bit
c, Request current command
C,
d,
D, Diagnostic commands
e, Erase EEPROM
E, Store configuration in EEPROM
f, Loco decoder function control (deprecated)
F, Loco decoder function control
g,
G,
h,
H, Turnout state broadcast
i, Reserved for future use - Turntable object broadcast
I, Reserved for future use - Turntable object command and control
j, Throttle responses
J, Throttle queries
k, Reserved for future use - Potentially Railcom
K, Reserved for future use - Potentially Railcom
l, Loco speedbyte/function map broadcast
L,
m,
M, Write DCC packet
n,
N,
o,
O, Output broadcast
p, Broadcast power state
P, Write DCC packet
q, Sensor deactivated
Q, Sensor activated
r, Broadcast address read on programming track
R, Read CVs
s, Display status
S, Sensor configuration
t, Cab/loco update command
T, Turnout configuration/control
u, Reserved for user commands
U, Reserved for user commands
v,
V, Verify CVs
w, Write CV on main
W, Write CV
x,
X, Invalid command
y,
Y, Output broadcast
z,
Z, Output configuration/control
*/
#include "StringFormatter.h"
#include "DCCEXParser.h"
#include "DCC.h"
@@ -48,7 +121,7 @@
for (int16_t i=0;;i+=sizeof(flashList[0])) { \
int16_t value=GETHIGHFLASHW(flashList,i); \
if (value==INT16_MAX) break; \
if (value != 0) StringFormatter::send(stream,F(" %d"),value); \
StringFormatter::send(stream,F(" %d"),value); \
}
@@ -656,11 +729,15 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
SENDFLASHLIST(stream,RMFT2::rosterIdList)
}
else {
const FSH * functionNames= RMFT2::getRosterFunctions(id);
StringFormatter::send(stream,F(" %d \"%S\" \"%S\""),
id, RMFT2::getRosterName(id),
functionNames == NULL ? RMFT2::getRosterFunctions(0) : functionNames);
}
auto rosterName= RMFT2::getRosterName(id);
if (!rosterName) rosterName=F("");
auto functionNames= RMFT2::getRosterFunctions(id);
if (!functionNames) functionNames=RMFT2::getRosterFunctions(0);
if (!functionNames) functionNames=F("");
StringFormatter::send(stream,F(" %d \"%S\" \"%S\""),
id, rosterName, functionNames);
}
#endif
StringFormatter::send(stream, F(">\n"));
return;
@@ -912,7 +989,7 @@ bool DCCEXParser::parseD(Print *stream, int16_t params, int16_t p[])
case HASH_KEYWORD_RAM: // <D RAM>
StringFormatter::send(stream, F("Free memory=%d\n"), DCCTimer::getMinimumFreeMemory());
break;
return true;
#ifndef DISABLE_PROG
case HASH_KEYWORD_ACK: // <D ACK ON/OFF> <D ACK [LIMIT|MIN|MAX|RETRY] Value>

View File

@@ -1,4 +1,5 @@
/*
* @ 2024 Arkadiusz Hahn
* © 2021 Neil McKechnie
* © 2021 Mike S
* © 2021 Fred Decker
@@ -35,6 +36,8 @@
DCCWaveform DCCWaveform::mainTrack(PREAMBLE_BITS_MAIN, true);
DCCWaveform DCCWaveform::progTrack(PREAMBLE_BITS_PROG, false);
bool DCCWaveform::supportsRailcom=false;
bool DCCWaveform::useRailcom=false;
// This bitmask has 9 entries as each byte is trasmitted as a zero + 8 bits.
const byte bitMask[] = {0x00, 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01};
@@ -62,6 +65,20 @@ const bool signalTransform[]={
/* WAVE_PENDING (should not happen) -> */ LOW};
void DCCWaveform::begin() {
// supportsRailcom depends on hardware capability
supportsRailcom = TrackManager::isRailcomCapable();
// useRailcom is user switchable at run time.
useRailcom=supportsRailcom;
if (useRailcom) {
DIAG(F("Railcom is enabled"));
} else {
DIAG(F("Railcom is disabled"));
}
TrackManager::setCutout(false,false);
TrackManager::setPROGCutout(false,false);
DCCTimer::begin(DCCWaveform::interruptHandler);
}
@@ -69,13 +86,28 @@ void DCCWaveform::loop() {
// empty placemarker in case ESP32 needs something here
}
bool DCCWaveform::setUseRailcom(bool on) {
if (!supportsRailcom) return false;
useRailcom=on;
if (!on) {
// turn off any existing cutout
TrackManager::setCutout(false);
TrackManager::setPROGCutout(false);
}
return true;
}
#pragma GCC push_options
#pragma GCC optimize ("-O3")
void DCCWaveform::interruptHandler() {
// call the timer edge sensitive actions for progtrack and maintrack
// member functions would be cleaner but have more overhead
byte sigMain=signalTransform[mainTrack.state];
byte sigProg=TrackManager::progTrackSyncMain? sigMain : signalTransform[progTrack.state];
byte sigMain= signalTransform[mainTrack.state];
byte sigProg=TrackManager::progTrackSyncMain? sigMain : signalTransform[progTrack.state];
// Set the signal state for both tracks
TrackManager::setDCCSignal(sigMain);
@@ -84,15 +116,26 @@ void DCCWaveform::interruptHandler() {
// Refresh the values in the ADCee object buffering the values of the ADC HW
ADCee::scan();
// WAVE_START is at start of bit where we need to find
// out if this is an railcom start or stop time
if (useRailcom) {
if (mainTrack.state==WAVE_MID_1) mainTrack.railcom2(true);
if (mainTrack.state==WAVE_START) mainTrack.railcom2(false);
if (progTrack.state==WAVE_MID_1) progTrack.railcom2(true);
if (progTrack.state==WAVE_START) progTrack.railcom2(false);
}
// Move on in the state engine
mainTrack.state=stateTransform[mainTrack.state];
progTrack.state=stateTransform[progTrack.state];
// WAVE_PENDING means we dont yet know what the next bit is
if (mainTrack.state==WAVE_PENDING) mainTrack.interrupt2();
if (progTrack.state==WAVE_PENDING) progTrack.interrupt2();
else DCCACK::checkAck(progTrack.getResets());
if (mainTrack.state==WAVE_PENDING) mainTrack.interrupt2();
if (progTrack.state==WAVE_PENDING) {
progTrack.interrupt2();
} else {
DCCACK::checkAck(progTrack.getResets());
}
}
#pragma GCC pop_options
@@ -110,11 +153,39 @@ DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
state = WAVE_START;
// The +1 below is to allow the preamble generator to create the stop bit
// for the previous packet.
requiredPreambles = preambleBits+1;
requiredPreambles = preambleBits+1;
//requiredPreambles <<=1; // double the number of preamble wave halves
remainingPreambles=0;
bytes_sent = 0;
bits_sent = 0;
}
#pragma GCC push_options
#pragma GCC optimize ("-O3")
void DCCWaveform::railcom2(bool starting) {
bool cutout;
if (starting && remainingPreambles==(requiredPreambles-2)) {
cutout=true;
} else if (!starting && remainingPreambles==(requiredPreambles-5)) {
cutout=false;
} else {
return; // neither start or end of cutout, do nothing
}
if (isMainTrack) {
if (TrackManager::progTrackSyncMain) {// we are main track and synced so we take care of prog track as well
TrackManager::setPROGCutout(cutout,true);
}
TrackManager::setCutout(cutout,true);
} else {
if (!TrackManager::progTrackSyncMain) {// we are prog track and not synced so we take care of ourselves
TrackManager::setPROGCutout(cutout,true);
}
}
}
#pragma GCC pop_options
#pragma GCC push_options
@@ -127,19 +198,20 @@ void DCCWaveform::interrupt2() {
if (remainingPreambles > 0 ) {
state=WAVE_MID_1; // switch state to trigger LOW on next interrupt
remainingPreambles--;
// Update free memory diagnostic as we don't have anything else to do this time.
// Allow for checkAck and its called functions using 22 bytes more.
DCCTimer::updateMinimumFreeMemoryISR(22);
return;
}
// Wave has gone HIGH but what happens next depends on the bit to be transmitted
// beware OF 9-BIT MASK generating a zero to start each byte
state=(transmitPacket[bytes_sent] & bitMask[bits_sent])? WAVE_MID_1 : WAVE_HIGH_0;
bits_sent++;
// If this is the last bit of a byte, prepare for the next byte
if (bits_sent == 9) { // zero followed by 8 bits of a byte
//end of Byte
bits_sent = 0;
@@ -149,30 +221,30 @@ void DCCWaveform::interrupt2() {
// end of transmission buffer... repeat or switch to next message
bytes_sent = 0;
remainingPreambles = requiredPreambles;
if (transmitRepeats > 0) {
transmitRepeats--;
transmitRepeats--;
}
else if (packetPending) {
// Copy pending packet to transmit packet
// a fixed length memcpy is faster than a variable length loop for these small lengths
// for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b];
memcpy( transmitPacket, pendingPacket, sizeof(pendingPacket));
// Copy pending packet to transmit packet
// a fixed length memcpy is faster than a variable length loop for these small lengths
// for (int b = 0; b < pendingLength; b++) transmitPacket[b] = pendingPacket[b];
memcpy( transmitPacket, pendingPacket, sizeof(pendingPacket));
transmitLength = pendingLength;
transmitRepeats = pendingRepeats;
packetPending = false;
clearResets();
transmitLength = pendingLength;
transmitRepeats = pendingRepeats;
packetPending = false;
clearResets();
}
else {
// Fortunately reset and idle packets are the same length
memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket));
transmitLength = sizeof(idlePacket);
transmitRepeats = 0;
if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!)
// Fortunately reset and idle packets are the same length
memcpy( transmitPacket, isMainTrack ? idlePacket : resetPacket, sizeof(idlePacket));
transmitLength = sizeof(idlePacket);
transmitRepeats = 0;
if (getResets() < 250) sentResetsSincePacket++; // only place to increment (private!)
}
}
}
}
}
#pragma GCC pop_options

View File

@@ -33,9 +33,9 @@
// Number of preamble bits.
const int PREAMBLE_BITS_MAIN = 16;
const int PREAMBLE_BITS_PROG = 22;
const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload size WITHOUT checksum.
const int PREAMBLE_BITS_MAIN = 16;
const int PREAMBLE_BITS_PROG = 22;
const byte MAX_PACKET_SIZE = 5; // NMRA standard extended packets, payload size WITHOUT checksum.
// The WAVE_STATE enum is deliberately numbered because a change of order would be catastrophic
@@ -52,6 +52,11 @@ class DCCWaveform {
static void loop();
static DCCWaveform mainTrack;
static DCCWaveform progTrack;
static bool supportsRailcom;
static bool useRailcom;
static bool setUseRailcom(bool on);
inline void clearRepeats() { transmitRepeats=0; }
#ifndef ARDUINO_ARCH_ESP32
inline void clearResets() { sentResetsSincePacket=0; }
@@ -87,6 +92,7 @@ class DCCWaveform {
#endif
static void interruptHandler();
void interrupt2();
void railcom2(bool starting);
bool isMainTrack;
// Transmission controller

View File

@@ -259,8 +259,9 @@ LookList* RMFT2::LookListLoader(OPCODE op1, OPCODE op2, OPCODE op3) {
}
void RMFT2::setTurnoutHiddenState(Turnout * t) {
// turnout descriptions are in low flash F strings
t->setHidden(GETFLASH(getTurnoutDescription(t->getId()))==0x01);
// turnout descriptions are in low flash F strings
const FSH *desc = getTurnoutDescription(t->getId());
if (desc) t->setHidden(GETFLASH(desc)==0x01);
}
char RMFT2::getRouteType(int16_t id) {

View File

@@ -172,6 +172,8 @@ void RMFT2::printMessage(uint16_t id) {
#include "EXRAIL2MacroReset.h"
#undef TURNOUT
#define TURNOUT(id,addr,subaddr,description...) O_DESC(id,description)
#undef TURNOUTL
#define TURNOUTL(id,addr,description...) O_DESC(id,description)
#undef PIN_TURNOUT
#define PIN_TURNOUT(id,pin,description...) O_DESC(id,description)
#undef SERVO_TURNOUT

View File

@@ -1 +1 @@
#define GITHUB_SHA "devel-202308012308Z"
#define GITHUB_SHA "railcomtests-202401142146Z"

View File

@@ -1,4 +1,4 @@
/*
/* @ 2024 Arkadiusz Hahn
* © 2022-2023 Paul M Antoine
* © 2021 Mike S
* © 2021 Fred Decker
@@ -32,6 +32,7 @@ unsigned long MotorDriver::globalOverloadStart = 0;
volatile portreg_t shadowPORTA;
volatile portreg_t shadowPORTB;
volatile portreg_t shadowPORTC;
volatile portreg_t shadowPORTH;
MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, int16_t brake_pin,
byte current_pin, float sense_factor, unsigned int trip_milliamps, int16_t fault_pin) {
@@ -52,17 +53,17 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
fastSignalPin.shadowinout = NULL;
if (HAVE_PORTA(fastSignalPin.inout == &PORTA)) {
DIAG(F("Found PORTA pin %d"),signalPin);
DIAG(F("Found SignalPin PORTA pin %d"),signalPin);
fastSignalPin.shadowinout = fastSignalPin.inout;
fastSignalPin.inout = &shadowPORTA;
}
if (HAVE_PORTB(fastSignalPin.inout == &PORTB)) {
DIAG(F("Found PORTB pin %d"),signalPin);
DIAG(F("Found SignalPin PORTB pin %d"),signalPin);
fastSignalPin.shadowinout = fastSignalPin.inout;
fastSignalPin.inout = &shadowPORTB;
}
if (HAVE_PORTC(fastSignalPin.inout == &PORTC)) {
DIAG(F("Found PORTC pin %d"),signalPin);
DIAG(F("Found SignalPin PORTC pin %d"),signalPin);
fastSignalPin.shadowinout = fastSignalPin.inout;
fastSignalPin.inout = &shadowPORTC;
}
@@ -75,24 +76,24 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
fastSignalPin2.shadowinout = NULL;
if (HAVE_PORTA(fastSignalPin2.inout == &PORTA)) {
DIAG(F("Found PORTA pin %d"),signalPin2);
DIAG(F("Found SignalPin2 PORTA pin %d"),signalPin2);
fastSignalPin2.shadowinout = fastSignalPin2.inout;
fastSignalPin2.inout = &shadowPORTA;
}
if (HAVE_PORTB(fastSignalPin2.inout == &PORTB)) {
DIAG(F("Found PORTB pin %d"),signalPin2);
DIAG(F("Found SignalPin2 PORTB pin %d"),signalPin2);
fastSignalPin2.shadowinout = fastSignalPin2.inout;
fastSignalPin2.inout = &shadowPORTB;
}
if (HAVE_PORTC(fastSignalPin2.inout == &PORTC)) {
DIAG(F("Found PORTC pin %d"),signalPin2);
DIAG(F("Found SignalPin2 PORTC pin %d"),signalPin2);
fastSignalPin2.shadowinout = fastSignalPin2.inout;
fastSignalPin2.inout = &shadowPORTC;
}
}
else dualSignal=false;
if (brake_pin!=UNUSED_PIN){
if (brake_pin!=UNUSED_PIN) {
invertBrake=brake_pin < 0;
if (invertBrake)
brake_pin = 0-brake_pin;
@@ -102,6 +103,31 @@ MotorDriver::MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, i
getFastPin(F("BRAKE"),brakePin,fastBrakePin);
// if brake is used for railcom cutout we need to do PORTX register trick here as well
pinMode(brakePin, OUTPUT);
fastBrakePin.shadowinout = NULL;
//DIAG(F("Found BrakePin %d "), brake_pin);
if (HAVE_PORTA(fastBrakePin.inout == &PORTA)) {
DIAG(F("Found BrakePin PORTA pin %d"),brakePin);
fastBrakePin.shadowinout = fastBrakePin.inout;
fastBrakePin.inout = &shadowPORTA;
}
if (HAVE_PORTB(fastBrakePin.inout == &PORTB)) {
DIAG(F("Found BrakePin PORTB pin %d"),brakePin);
fastBrakePin.shadowinout = fastBrakePin.inout;
fastBrakePin.inout = &shadowPORTB;
}
if (HAVE_PORTC(fastBrakePin.inout == &PORTC)) {
DIAG(F("Found BrakePin PORTC pin %d"),brakePin);
fastBrakePin.shadowinout = fastBrakePin.inout;
fastBrakePin.inout = &shadowPORTC;
}
if (HAVE_PORTH(fastBrakePin.inout == &PORTH)) {
DIAG(F("Found BrakePin PORTH pin %d"),brakePin);
fastBrakePin.shadowinout = fastBrakePin.inout;
fastBrakePin.inout = &shadowPORTH;
}
setBrake(true); // start with brake on in case we hace DC stuff going on
} else {
brakePin=UNUSED_PIN;
@@ -170,6 +196,9 @@ bool MotorDriver::isPWMCapable() {
return (!dualSignal) && DCCTimer::isPWMPin(signalPin);
}
bool MotorDriver::isRailcomCapable() {
return (!dualSignal) && (brakePin!=UNUSED_PIN);
}
void MotorDriver::setPower(POWERMODE mode) {
if (powerMode == mode) return;
@@ -195,23 +224,6 @@ void MotorDriver::setPower(POWERMODE mode) {
powerMode=mode;
}
// setBrake applies brake if on == true. So to get
// voltage from the motor bride one needs to do a
// setBrake(false).
// If the brakePin is negative that means the sense
// of the brake pin on the motor bridge is inverted
// (HIGH == release brake) and setBrake does
// compensate for that.
//
void MotorDriver::setBrake(bool on, bool interruptContext) {
if (brakePin == UNUSED_PIN) return;
if (!interruptContext) {noInterrupts();}
if (on ^ invertBrake)
setHIGH(fastBrakePin);
else
setLOW(fastBrakePin);
if (!interruptContext) {interrupts();}
}
bool MotorDriver::canMeasureCurrent() {
return currentPin!=UNUSED_PIN;
@@ -455,7 +467,7 @@ void MotorDriver::getFastPin(const FSH* type,int pin, bool input, FASTPIN & res
result.inout = portOutputRegister(port);
result.maskHIGH = digitalPinToBitMask(pin);
result.maskLOW = ~result.maskHIGH;
// DIAG(F(" port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH);
//DIAG(F("MotorDriver::getFastPin port=0x%x, inoutpin=0x%x, isinput=%d, mask=0x%x"),port, result.inout,input,result.maskHIGH);
}
///////////////////////////////////////////////////////////////////////////////////////////

View File

@@ -28,7 +28,7 @@
#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,
enum TRACK_MODE : byte {TRACK_MODE_NONE = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG = 4,
TRACK_MODE_DC = 8, TRACK_MODE_DCX = 16, TRACK_MODE_EXT = 32};
#define setHIGH(fastpin) *fastpin.inout |= fastpin.maskHIGH
@@ -43,6 +43,7 @@ enum TRACK_MODE : byte {TRACK_MODE_OFF = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG
#define HAVE_PORTA(X) X
#define HAVE_PORTB(X) X
#define HAVE_PORTC(X) X
#define HAVE_PORTH(X) X
#endif
#if defined(ARDUINO_AVR_UNO)
#define HAVE_PORTB(X) X
@@ -74,7 +75,9 @@ enum TRACK_MODE : byte {TRACK_MODE_OFF = 1, TRACK_MODE_MAIN = 2, TRACK_MODE_PROG
#ifndef HAVE_PORTC
#define HAVE_PORTC(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0
#endif
#ifndef HAVE_PORTH
#define HAVE_PORTH(X) byte TOKENPASTE2(Unique_, __LINE__) __attribute__((unused)) =0
#endif
// Virtualised Motor shield 1-track hardware Interface
#ifndef UNUSED_PIN // sync define with the one in MotorDrivers.h
@@ -110,6 +113,7 @@ struct FASTPIN {
extern volatile portreg_t shadowPORTA;
extern volatile portreg_t shadowPORTB;
extern volatile portreg_t shadowPORTC;
extern volatile portreg_t shadowPORTH;
enum class POWERMODE : byte { OFF, ON, OVERLOAD, ALERT };
@@ -118,35 +122,59 @@ class MotorDriver {
MotorDriver(int16_t power_pin, byte signal_pin, byte signal_pin2, int16_t brake_pin,
byte current_pin, float senseFactor, unsigned int tripMilliamps, int16_t fault_pin);
void setPower( POWERMODE mode);
POWERMODE getPower() { return powerMode;}
// as the port registers can be shadowed to get syncronized DCC signals
// we need to take care of that and we have to turn off interrupts if
// we setSignal() or setBrake() or setPower() during that time as
// otherwise the call from interrupt context can undo whatever we do
// from outside interrupt
void setBrake( bool on, bool interruptContext=false);
__attribute__((always_inline)) inline void setSignal( bool high) {
// setBrake applies brake if on == true. So to get
// voltage from the motor bride one needs to do a
// setBrake(false).
// If the brakePin is negative that means the sense
// of the brake pin on the motor bridge is inverted
// (HIGH == release brake) and setBrake does
// compensate for that.
__attribute__((always_inline)) inline void setBrake(bool on, bool interruptContext=false) {
if (brakePin == UNUSED_PIN) return;
if (!interruptContext) {noInterrupts();}
if (on ^ invertBrake) {
setHIGH(fastBrakePin);
} else {
setLOW(fastBrakePin);
}
if (!interruptContext) {interrupts();}
};
__attribute__((always_inline)) inline void setSignal( bool high) {
if (trackPWM) {
DCCTimer::setPWM(signalPin,high);
}
else {
if (high) {
setHIGH(fastSignalPin);
if (dualSignal) setLOW(fastSignalPin2);
}
else {
setLOW(fastSignalPin);
if (dualSignal) setHIGH(fastSignalPin2);
}
DCCTimer::setPWM(signalPin,high);
} else {
if (high) {
setHIGH(fastSignalPin);
if (dualSignal) setLOW(fastSignalPin2);
} else {
setLOW(fastSignalPin);
if (dualSignal) setHIGH(fastSignalPin2);
}
}
};
inline void enableSignal(bool on) {
if (on)
pinMode(signalPin, OUTPUT);
else
pinMode(signalPin, INPUT);
if (on) {
pinMode(signalPin, OUTPUT);
} else {
pinMode(signalPin, INPUT);
}
};
inline pinpair getSignalPin() { return pinpair(signalPin,signalPin2); };
void setDCSignal(byte speedByte);
void throttleInrush(bool on);
@@ -178,6 +206,7 @@ class MotorDriver {
return rawCurrentTripValue;
}
bool isPWMCapable();
bool isRailcomCapable();
bool canMeasureCurrent();
bool trackPWM = false; // this track uses PWM timer to generate the DCC waveform
bool commonFaultPin = false; // This is a stupid motor shield which has only a common fault pin for both outputs
@@ -219,7 +248,7 @@ class MotorDriver {
bool isProgTrack = false; // tells us if this is a prog track
void getFastPin(const FSH* type,int pin, bool input, 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) {
@@ -290,7 +319,7 @@ class MotorDriver {
static const int TRIP_CURRENT_PROG=250;
unsigned long power_sample_overload_wait = POWER_SAMPLE_OVERLOAD_WAIT;
unsigned int power_good_counter = 0;
TRACK_MODE trackMode = TRACK_MODE_OFF; // we assume off at startup
TRACK_MODE trackMode = TRACK_MODE_NONE; // we assume track not assigned at startup
};
#endif

View File

@@ -1,4 +1,4 @@
/*
/* @ 2024 Arkadiusz Hahn
* © 2022 Chris Harlow
* © 2022 Harald Barth
* All rights reserved.
@@ -38,6 +38,7 @@ const int16_t HASH_KEYWORD_PROG = -29718;
#endif
const int16_t HASH_KEYWORD_MAIN = 11339;
const int16_t HASH_KEYWORD_OFF = 22479;
const int16_t HASH_KEYWORD_NONE = -26550;
const int16_t HASH_KEYWORD_DC = 2183;
const int16_t HASH_KEYWORD_DCX = 6463; // DC reversed polarity
const int16_t HASH_KEYWORD_EXT = 8201; // External DCC signal
@@ -52,7 +53,7 @@ bool TrackManager::progTrackSyncMain=false;
bool TrackManager::progTrackBoosted=false;
int16_t TrackManager::joinRelay=UNUSED_PIN;
#ifdef ARDUINO_ARCH_ESP32
byte TrackManager::tempProgTrack=MAX_TRACKS+1;
byte TrackManager::tempProgTrack=MAX_TRACKS+1; // MAX_TRACKS+1 is the unused flag
#endif
#ifdef ANALOG_READ_INTERRUPT
@@ -126,10 +127,10 @@ void TrackManager::Setup(const FSH * shieldname,
FOR_EACH_TRACK(t) {
for (byte s=t+1;s<=lastTrack;s++) {
if (track[t]->getFaultPin() != UNUSED_PIN &&
track[t]->getFaultPin() == track[s]->getFaultPin()) {
track[t]->setCommonFaultPin();
track[s]->setCommonFaultPin();
DIAG(F("Common Fault pin tracks %c and %c"), t+'A', s+'A');
track[t]->getFaultPin() == track[s]->getFaultPin()) {
track[t]->setCommonFaultPin();
track[s]->setCommonFaultPin();
DIAG(F("Common Fault pin tracks %c and %c"), t+'A', s+'A');
}
}
}
@@ -139,10 +140,10 @@ void TrackManager::Setup(const FSH * shieldname,
void TrackManager::addTrack(byte t, MotorDriver* driver) {
track[t]=driver;
if (driver) {
track[t]->setPower(POWERMODE::OFF);
track[t]->setMode(TRACK_MODE_OFF);
track[t]->setTrackLetter('A'+t);
lastTrack=t;
track[t]->setPower(POWERMODE::OFF);
track[t]->setMode(TRACK_MODE_NONE);
track[t]->setTrackLetter('A'+t);
lastTrack=t;
}
}
@@ -158,10 +159,41 @@ void TrackManager::setDCCSignal( bool on) {
HAVE_PORTC(PORTC=shadowPORTC);
}
void TrackManager::setCutout( bool on) {
(void) on;
// TODO Cutout needs fake ports as well
// TODO APPLY_BY_MODE(TRACK_MODE_MAIN,setCutout(on));
// setCutout() for MAIN track
void TrackManager::setCutout( bool on,bool interruptContext) {
//(void) on; // avoid compiler warning -Wunused
// Cutout needs fake ports as well
HAVE_PORTA(shadowPORTA=PORTA);
HAVE_PORTB(shadowPORTB=PORTB);
HAVE_PORTC(shadowPORTC=PORTC);
HAVE_PORTH(shadowPORTH=PORTH);
APPLY_BY_MODE(TRACK_MODE_MAIN,setBrake(on,interruptContext));
HAVE_PORTA(PORTA=shadowPORTA);
HAVE_PORTB(PORTB=shadowPORTB);
HAVE_PORTC(PORTC=shadowPORTC);
HAVE_PORTH(PORTH=shadowPORTH);
}
void TrackManager::setPROGCutout( bool on,bool interruptContext) {
HAVE_PORTA(shadowPORTA=PORTA);
HAVE_PORTB(shadowPORTB=PORTB);
HAVE_PORTC(shadowPORTC=PORTC);
HAVE_PORTH(shadowPORTH=PORTH);
APPLY_BY_MODE(TRACK_MODE_PROG,setBrake(on,interruptContext));
HAVE_PORTA(PORTA=shadowPORTA);
HAVE_PORTB(PORTB=shadowPORTB);
HAVE_PORTC(PORTC=shadowPORTC);
HAVE_PORTH(PORTH=shadowPORTH);
}
// true when there is any railcom capable MAIN track
bool TrackManager::isRailcomCapable() {
FOR_EACH_TRACK(t) {
if((track[t]->getMode()==TRACK_MODE_MAIN) && (track[t]->isRailcomCapable())){
return true;
}
}
return false;
}
// setPROGSignal(), called from interrupt context
@@ -181,7 +213,7 @@ void TrackManager::setPROGSignal( bool on) {
// with interrupts turned off around the critical section
void TrackManager::setDCSignal(int16_t cab, byte speedbyte) {
FOR_EACH_TRACK(t) {
if (trackDCAddr[t]!=cab) continue;
if (trackDCAddr[t]!=cab && cab != 0) continue;
if (track[t]->getMode()==TRACK_MODE_DC) track[t]->setDCSignal(speedbyte);
else if (track[t]->getMode()==TRACK_MODE_DCX) track[t]->setDCSignal(speedbyte ^ 128);
}
@@ -224,7 +256,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
FOR_EACH_TRACK(t)
if (track[t]->getMode()==TRACK_MODE_PROG && t != trackToSet) {
track[t]->setPower(POWERMODE::OFF);
track[t]->setMode(TRACK_MODE_OFF);
track[t]->setMode(TRACK_MODE_NONE);
track[t]->makeProgTrack(false); // revoke prog track special handling
streamTrackState(NULL,t);
}
@@ -332,8 +364,8 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[])
return setTrackMode(p[0],TRACK_MODE_PROG);
#endif
if (params==2 && p[1]==HASH_KEYWORD_OFF) // <= id OFF>
return setTrackMode(p[0],TRACK_MODE_OFF);
if (params==2 && (p[1]==HASH_KEYWORD_OFF || p[1]==HASH_KEYWORD_NONE)) // <= id OFF> <= id NONE>
return setTrackMode(p[0],TRACK_MODE_NONE);
if (params==2 && p[1]==HASH_KEYWORD_EXT) // <= id EXT>
return setTrackMode(p[0],TRACK_MODE_EXT);
@@ -360,8 +392,8 @@ void TrackManager::streamTrackState(Print* stream, byte t) {
format=F("<= %c PROG>\n");
break;
#endif
case TRACK_MODE_OFF:
format=F("<= %c OFF>\n");
case TRACK_MODE_NONE:
format=F("<= %c NONE>\n");
break;
case TRACK_MODE_EXT:
format=F("<= %c EXT>\n");
@@ -443,7 +475,7 @@ void TrackManager::setPower2(bool setProg,POWERMODE mode) {
driver->setBrake(false);
driver->setPower(mode);
break;
case TRACK_MODE_OFF:
case TRACK_MODE_NONE:
break;
}
}
@@ -504,7 +536,12 @@ void TrackManager::setJoin(bool joined) {
}
} else {
if (tempProgTrack != MAX_TRACKS+1) {
// as setTrackMode with TRACK_MODE_PROG defaults to
// power off, we will take the current power state
// of our track and then preserve that state.
POWERMODE tPTmode = track[tempProgTrack]->getPower(); //get current power status of this track
setTrackMode(tempProgTrack, TRACK_MODE_PROG);
track[tempProgTrack]->setPower(tPTmode); //set track status as it was before
tempProgTrack = MAX_TRACKS+1;
}
}

View File

@@ -51,9 +51,11 @@ class TrackManager {
);
static void setDCCSignal( bool on);
static void setCutout( bool on);
static void setPROGSignal( bool on);
static void setDCSignal(int16_t cab, byte speedbyte);
static void setCutout( bool on,bool interruptContext=false);
static void setPROGCutout( bool on,bool interruptContext=false);
static bool isRailcomCapable();
static MotorDriver * getProgDriver();
#ifdef ARDUINO_ARCH_ESP32
static std::vector<MotorDriver *>getMainDrivers();

View File

@@ -200,7 +200,23 @@ wifiSerialState WifiInterface::setup2(const FSH* SSid, const FSH* password,
// Display the AT version information
StringFormatter::send(wifiStream, F("AT+GMR\r\n"));
checkForOK(2000, true, false); // Makes this visible on the console
if (checkForOK(2000, F("AT version:"), true, false)) {
char version[] = "0.0.0.0-xxx";
for (int i=0; i<11;i++) {
while(!wifiStream->available());
version[i]=wifiStream->read();
StringFormatter::printEscape(version[i]);
}
if ((version[0] == '0') ||
(version[0] == '2' && version[2] == '0') ||
(version[0] == '2' && version[2] == '2' && version[4] == '0' && version[6] == '0'
&& version[7] == '-' && version[8] == 'd' && version[9] == 'e' && version[10] == 'v')) {
DIAG(F("You need to up/downgrade the ESP firmware"));
SSid = F("UPDATE_ESP_FIRMWARE");
forceAP = true;
}
}
checkForOK(2000, true, false);
#ifdef DONT_TOUCH_WIFI_CONF
DIAG(F("DONT_TOUCH_WIFI_CONF was set: Using existing config"));

View File

@@ -24,6 +24,7 @@
//#include "IO_TouchKeypad.h // Touch keypad with 16 keys
//#include "IO_EXTurntable.h" // Turntable-EX turntable controller
//#include "IO_EXFastClock.h" // FastClock driver
//#include "IO_PCA9555.h" // 16-bit I/O expander (NXP & Texas Instruments).
//==========================================================================
// The function halSetup() is invoked from CS if it exists within the build.
@@ -51,7 +52,7 @@ void halSetup() {
// Create a 20x4 LCD display device as display number 2
// (line 0 is written by EX-RAIL 'SCREEN(2, 0, "text")').
// HALDisplay<LiquidCrystal>(2, 0x27, 20, 4);
// HALDisplay<LiquidCrystal>::create(2, 0x27, 20, 4);
//=======================================================================

View File

@@ -3,7 +3,18 @@
#include "StringFormatter.h"
#define VERSION "4.2.67"
#define VERSION "5.0.8"
// 5.0.8 - Bugfix: Do not crash on turnouts without description
// 5.0.7 - Only flag 2.2.0.0-dev as broken, not 2.2.0.0
// 5.0.6 - Bugfix lost TURNOUTL description
// 5.0.5 - Bugfix version detection logic and better message
// 5.0.4 - Bugfix: <JR> misses default roster.
// 5.0.3 - Check bad AT firmware version
// 5.0.2 - Bugfix: ESP32 30ms off time
// 5.0.1 - Bugfix: execute 30ms off time before rejoin
// 5.0.0 - Make 4.2.69 the 5.0.0 release
// 4.2.69 - Bugfix: Make <!> work in DC mode
// 4.2.68 - Rename track mode OFF to NONE
// 4.2.67 - AVR: Pin specific timer register seting
// - Protect Uno user from choosing DC(X)
// - More Nucleo variant defines