mirror of
https://github.com/DCC-EX/CommandStation-EX.git
synced 2025-07-31 03:13:45 +02:00
Compare commits
37 Commits
v5.2.5-Dev
...
v5.2.16-De
Author | SHA1 | Date | |
---|---|---|---|
|
1398cf1999 | ||
|
797028b223 | ||
|
1881d4c9ad | ||
|
18116a391c | ||
|
a1accec79a | ||
|
08f0a2b37d | ||
|
6637ea6fe7 | ||
|
a69017f8bb | ||
|
763c9d8ae6 | ||
|
753567427e | ||
|
3f4099520a | ||
|
07fd4bc309 | ||
|
1f05ef42d2 | ||
|
96fdbfdc89 | ||
|
ebaf1b984e | ||
|
697f228a05 | ||
|
c8e307db7a | ||
|
a5ccb2e29e | ||
|
42e2e69f5f | ||
|
2075bc50e8 | ||
|
a16214790e | ||
|
784934024e | ||
|
b478056a9f | ||
|
ef47257d67 | ||
|
03db06f2ee | ||
|
4308739c2b | ||
|
0cfea3e1a5 | ||
|
d0df9f3c33 | ||
|
ac4af407aa | ||
|
a236a205fe | ||
|
478e9661bb | ||
|
2c1b3e0a8f | ||
|
e7c4af5d4a | ||
|
263ed18b25 | ||
|
4e1fad4832 | ||
|
29ea746062 | ||
|
e6f33cfdee |
@@ -254,29 +254,64 @@ void CommandDistributor::broadcastPower() {
|
|||||||
if (TrackManager::getPower(t, pstr))
|
if (TrackManager::getPower(t, pstr))
|
||||||
broadcastReply(COMMAND_TYPE, F("<p%s>\n"),pstr);
|
broadcastReply(COMMAND_TYPE, F("<p%s>\n"),pstr);
|
||||||
|
|
||||||
|
byte trackcount=0;
|
||||||
|
byte oncount=0;
|
||||||
|
byte offcount=0;
|
||||||
|
for(byte t=0; t<TrackManager::MAX_TRACKS; t++) {
|
||||||
|
if (TrackManager::isActive(t)) {
|
||||||
|
trackcount++;
|
||||||
|
// do not call getPower(t) unless isActive(t)!
|
||||||
|
if (TrackManager::getPower(t) == POWERMODE::ON)
|
||||||
|
oncount++;
|
||||||
|
else
|
||||||
|
offcount++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//DIAG(F("t=%d on=%d off=%d"), trackcount, oncount, offcount);
|
||||||
|
|
||||||
|
char state='2';
|
||||||
|
if (oncount==0 || offcount == trackcount)
|
||||||
|
state = '0';
|
||||||
|
else if (oncount == trackcount) {
|
||||||
|
state = '1';
|
||||||
|
}
|
||||||
|
|
||||||
|
// additional info about MAIN, PROG and JOIN
|
||||||
bool main=TrackManager::getMainPower()==POWERMODE::ON;
|
bool main=TrackManager::getMainPower()==POWERMODE::ON;
|
||||||
bool prog=TrackManager::getProgPower()==POWERMODE::ON;
|
bool prog=TrackManager::getProgPower()==POWERMODE::ON;
|
||||||
bool join=TrackManager::isJoined();
|
bool join=TrackManager::isJoined();
|
||||||
|
//DIAG(F("m=%d p=%d j=%d"), main, prog, join);
|
||||||
const FSH * reason=F("");
|
const FSH * reason=F("");
|
||||||
char state='1';
|
if (join) {
|
||||||
if (main && prog && join) reason=F(" JOIN");
|
reason = F(" JOIN"); // with space at start so we can append without space
|
||||||
else if (main && prog);
|
broadcastReply(COMMAND_TYPE, F("<p1 %S>\n"),reason);
|
||||||
else if (main) reason=F(" MAIN");
|
} else {
|
||||||
else if (prog) reason=F(" PROG");
|
if (main) {
|
||||||
else state='0';
|
//reason = F("MAIN");
|
||||||
broadcastReply(COMMAND_TYPE, F("<p%c%S>\n"),state,reason);
|
broadcastReply(COMMAND_TYPE, F("<p1 MAIN>\n"));
|
||||||
|
}
|
||||||
|
if (prog) {
|
||||||
|
//reason = F("PROG");
|
||||||
|
broadcastReply(COMMAND_TYPE, F("<p1 PROG>\n"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (state != '2')
|
||||||
|
broadcastReply(COMMAND_TYPE, F("<p%c>\n"),state);
|
||||||
#ifdef CD_HANDLE_RING
|
#ifdef CD_HANDLE_RING
|
||||||
broadcastReply(WITHROTTLE_TYPE, F("PPA%c\n"), main?'1':'0');
|
// send '1' if all main are on, otherwise global state (which in that case is '0' or '2')
|
||||||
|
broadcastReply(WITHROTTLE_TYPE, F("PPA%c\n"), main?'1': state);
|
||||||
#endif
|
#endif
|
||||||
LCD(2,F("Power %S%S"),state=='1'?F("On"):F("Off"),reason);
|
|
||||||
|
LCD(2,F("Power %S%S"),state=='1'?F("On"): ( state=='0'? F("Off") : F("SC") ),reason);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandDistributor::broadcastRaw(clientType type, char * msg) {
|
void CommandDistributor::broadcastRaw(clientType type, char * msg) {
|
||||||
broadcastReply(type, F("%s"),msg);
|
broadcastReply(type, F("%s"),msg);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandDistributor::broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr) {
|
void CommandDistributor::broadcastTrackState(const FSH* format, byte trackLetter, const FSH *modename, int16_t dcAddr) {
|
||||||
broadcastReply(COMMAND_TYPE, format, trackLetter, dcAddr);
|
broadcastReply(COMMAND_TYPE, format, trackLetter, modename, dcAddr);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CommandDistributor::broadcastRouteState(uint16_t routeId, byte state ) {
|
void CommandDistributor::broadcastRouteState(uint16_t routeId, byte state ) {
|
||||||
@@ -330,7 +365,7 @@ void CommandDistributor::setVirtualLCDSerial(Print * stream) {
|
|||||||
virtualLCDSerial=stream;
|
virtualLCDSerial=stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
Print* CommandDistributor::virtualLCDSerial=nullptr;
|
Print* CommandDistributor::virtualLCDSerial=&USB_SERIAL;
|
||||||
byte CommandDistributor::virtualLCDClient=0xFF;
|
byte CommandDistributor::virtualLCDClient=0xFF;
|
||||||
byte CommandDistributor::rememberVLCDClient=0;
|
byte CommandDistributor::rememberVLCDClient=0;
|
||||||
|
|
||||||
|
@@ -55,7 +55,7 @@ public :
|
|||||||
static int16_t retClockTime();
|
static int16_t retClockTime();
|
||||||
static void broadcastPower();
|
static void broadcastPower();
|
||||||
static void broadcastRaw(clientType type,char * msg);
|
static void broadcastRaw(clientType type,char * msg);
|
||||||
static void broadcastTrackState(const FSH* format,byte trackLetter, int16_t dcAddr);
|
static void broadcastTrackState(const FSH* format,byte trackLetter, const FSH* modename, int16_t dcAddr);
|
||||||
template<typename... Targs> static void broadcastReply(clientType type, Targs... msg);
|
template<typename... Targs> static void broadcastReply(clientType type, Targs... msg);
|
||||||
static void forget(byte clientId);
|
static void forget(byte clientId);
|
||||||
static void broadcastRouteState(uint16_t routeId,byte state);
|
static void broadcastRouteState(uint16_t routeId,byte state);
|
||||||
|
@@ -87,7 +87,7 @@ void setup()
|
|||||||
|
|
||||||
DISPLAY_START (
|
DISPLAY_START (
|
||||||
// This block is still executed for DIAGS if display not in use
|
// This block is still executed for DIAGS if display not in use
|
||||||
LCD(0,F("DCC-EX v%S"),F(VERSION));
|
LCD(0,F("DCC-EX v" VERSION));
|
||||||
LCD(1,F("Lic GPLv3"));
|
LCD(1,F("Lic GPLv3"));
|
||||||
);
|
);
|
||||||
|
|
||||||
|
31
DCC.cpp
31
DCC.cpp
@@ -122,7 +122,7 @@ void DCC::setThrottle2( uint16_t cab, byte speedCode) {
|
|||||||
DCCWaveform::mainTrack.schedulePacket(b, nB, 0);
|
DCCWaveform::mainTrack.schedulePacket(b, nB, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
void DCC::setFunctionInternal(int cab, byte byte1, byte byte2) {
|
void DCC::setFunctionInternal(int cab, byte byte1, byte byte2, byte count) {
|
||||||
// DIAG(F("setFunctionInternal %d %x %x"),cab,byte1,byte2);
|
// DIAG(F("setFunctionInternal %d %x %x"),cab,byte1,byte2);
|
||||||
byte b[4];
|
byte b[4];
|
||||||
byte nB = 0;
|
byte nB = 0;
|
||||||
@@ -133,7 +133,7 @@ void DCC::setFunctionInternal(int cab, byte byte1, byte byte2) {
|
|||||||
if (byte1!=0) b[nB++] = byte1;
|
if (byte1!=0) b[nB++] = byte1;
|
||||||
b[nB++] = byte2;
|
b[nB++] = byte2;
|
||||||
|
|
||||||
DCCWaveform::mainTrack.schedulePacket(b, nB, 0);
|
DCCWaveform::mainTrack.schedulePacket(b, nB, count);
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns speed steps 0 to 127 (1 == emergency stop)
|
// returns speed steps 0 to 127 (1 == emergency stop)
|
||||||
@@ -595,7 +595,7 @@ void DCC::loop() {
|
|||||||
|
|
||||||
void DCC::issueReminders() {
|
void DCC::issueReminders() {
|
||||||
// if the main track transmitter still has a pending packet, skip this time around.
|
// if the main track transmitter still has a pending packet, skip this time around.
|
||||||
if ( DCCWaveform::mainTrack.getPacketPending()) return;
|
if (!DCCWaveform::mainTrack.isReminderWindowOpen()) return;
|
||||||
// Move to next loco slot. If occupied, send a reminder.
|
// Move to next loco slot. If occupied, send a reminder.
|
||||||
int reg = lastLocoReminder+1;
|
int reg = lastLocoReminder+1;
|
||||||
if (reg > highestUsedReg) reg = 0; // Go to start of table
|
if (reg > highestUsedReg) reg = 0; // Go to start of table
|
||||||
@@ -619,24 +619,39 @@ bool DCC::issueReminder(int reg) {
|
|||||||
break;
|
break;
|
||||||
case 1: // remind function group 1 (F0-F4)
|
case 1: // remind function group 1 (F0-F4)
|
||||||
if (flags & FN_GROUP_1)
|
if (flags & FN_GROUP_1)
|
||||||
setFunctionInternal(loco,0, 128 | ((functions>>1)& 0x0F) | ((functions & 0x01)<<4)); // 100D DDDD
|
#ifndef DISABLE_FUNCTION_REMINDERS
|
||||||
|
setFunctionInternal(loco,0, 128 | ((functions>>1)& 0x0F) | ((functions & 0x01)<<4),0); // 100D DDDD
|
||||||
|
#else
|
||||||
|
setFunctionInternal(loco,0, 128 | ((functions>>1)& 0x0F) | ((functions & 0x01)<<4),2);
|
||||||
|
flags&= ~FN_GROUP_1; // dont send them again
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case 2: // remind function group 2 F5-F8
|
case 2: // remind function group 2 F5-F8
|
||||||
if (flags & FN_GROUP_2)
|
if (flags & FN_GROUP_2)
|
||||||
setFunctionInternal(loco,0, 176 | ((functions>>5)& 0x0F)); // 1011 DDDD
|
#ifndef DISABLE_FUNCTION_REMINDERS
|
||||||
|
setFunctionInternal(loco,0, 176 | ((functions>>5)& 0x0F),0); // 1011 DDDD
|
||||||
|
#else
|
||||||
|
setFunctionInternal(loco,0, 176 | ((functions>>5)& 0x0F),2);
|
||||||
|
flags&= ~FN_GROUP_2; // dont send them again
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case 3: // remind function group 3 F9-F12
|
case 3: // remind function group 3 F9-F12
|
||||||
if (flags & FN_GROUP_3)
|
if (flags & FN_GROUP_3)
|
||||||
setFunctionInternal(loco,0, 160 | ((functions>>9)& 0x0F)); // 1010 DDDD
|
#ifndef DISABLE_FUNCTION_REMINDERS
|
||||||
|
setFunctionInternal(loco,0, 160 | ((functions>>9)& 0x0F),0); // 1010 DDDD
|
||||||
|
#else
|
||||||
|
setFunctionInternal(loco,0, 160 | ((functions>>9)& 0x0F),2);
|
||||||
|
flags&= ~FN_GROUP_3; // dont send them again
|
||||||
|
#endif
|
||||||
break;
|
break;
|
||||||
case 4: // remind function group 4 F13-F20
|
case 4: // remind function group 4 F13-F20
|
||||||
if (flags & FN_GROUP_4)
|
if (flags & FN_GROUP_4)
|
||||||
setFunctionInternal(loco,222, ((functions>>13)& 0xFF));
|
setFunctionInternal(loco,222, ((functions>>13)& 0xFF),2);
|
||||||
flags&= ~FN_GROUP_4; // dont send them again
|
flags&= ~FN_GROUP_4; // dont send them again
|
||||||
break;
|
break;
|
||||||
case 5: // remind function group 5 F21-F28
|
case 5: // remind function group 5 F21-F28
|
||||||
if (flags & FN_GROUP_5)
|
if (flags & FN_GROUP_5)
|
||||||
setFunctionInternal(loco,223, ((functions>>21)& 0xFF));
|
setFunctionInternal(loco,223, ((functions>>21)& 0xFF),2);
|
||||||
flags&= ~FN_GROUP_5; // dont send them again
|
flags&= ~FN_GROUP_5; // dont send them again
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
2
DCC.h
2
DCC.h
@@ -109,7 +109,7 @@ private:
|
|||||||
static byte loopStatus;
|
static byte loopStatus;
|
||||||
static void setThrottle2(uint16_t cab, uint8_t speedCode);
|
static void setThrottle2(uint16_t cab, uint8_t speedCode);
|
||||||
static void updateLocoReminder(int loco, byte speedCode);
|
static void updateLocoReminder(int loco, byte speedCode);
|
||||||
static void setFunctionInternal(int cab, byte fByte, byte eByte);
|
static void setFunctionInternal(int cab, byte fByte, byte eByte, byte count);
|
||||||
static bool issueReminder(int reg);
|
static bool issueReminder(int reg);
|
||||||
static int lastLocoReminder;
|
static int lastLocoReminder;
|
||||||
static int highestUsedReg;
|
static int highestUsedReg;
|
||||||
|
@@ -160,6 +160,7 @@ const int16_t HASH_KEYWORD_C='C';
|
|||||||
const int16_t HASH_KEYWORD_G='G';
|
const int16_t HASH_KEYWORD_G='G';
|
||||||
const int16_t HASH_KEYWORD_H='H';
|
const int16_t HASH_KEYWORD_H='H';
|
||||||
const int16_t HASH_KEYWORD_I='I';
|
const int16_t HASH_KEYWORD_I='I';
|
||||||
|
const int16_t HASH_KEYWORD_M='M';
|
||||||
const int16_t HASH_KEYWORD_O='O';
|
const int16_t HASH_KEYWORD_O='O';
|
||||||
const int16_t HASH_KEYWORD_P='P';
|
const int16_t HASH_KEYWORD_P='P';
|
||||||
const int16_t HASH_KEYWORD_R='R';
|
const int16_t HASH_KEYWORD_R='R';
|
||||||
@@ -586,7 +587,6 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
}
|
}
|
||||||
else break; // will reply <X>
|
else break; // will reply <X>
|
||||||
}
|
}
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
//TrackManager::streamTrackState(NULL,t);
|
//TrackManager::streamTrackState(NULL,t);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
@@ -618,7 +618,6 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
}
|
}
|
||||||
else break; // will reply <X>
|
else break; // will reply <X>
|
||||||
}
|
}
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -668,7 +667,7 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
break;
|
break;
|
||||||
#endif
|
#endif
|
||||||
case '=': // TRACK MANAGER CONTROL <= [params]>
|
case '=': // TRACK MANAGER CONTROL <= [params]>
|
||||||
if (TrackManager::parseJ(stream, params, p))
|
if (TrackManager::parseEqualSign(stream, params, p))
|
||||||
return;
|
return;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -729,6 +728,12 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
StringFormatter::send(stream, F("<jA>\n"));
|
StringFormatter::send(stream, F("<jA>\n"));
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
case HASH_KEYWORD_M: // <JM> intercepted by EXRAIL
|
||||||
|
if (params>1) break; // invalid cant do
|
||||||
|
// <JM> requests stash size so say none.
|
||||||
|
StringFormatter::send(stream,F("<jM 0>\n"));
|
||||||
|
return;
|
||||||
|
|
||||||
case HASH_KEYWORD_R: // <JR> returns rosters
|
case HASH_KEYWORD_R: // <JR> returns rosters
|
||||||
StringFormatter::send(stream, F("<jR"));
|
StringFormatter::send(stream, F("<jR"));
|
||||||
#ifdef EXRAIL_ACTIVE
|
#ifdef EXRAIL_ACTIVE
|
||||||
@@ -840,13 +845,14 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
|
|||||||
case 'L': // LCC interface implemented in EXRAIL parser
|
case 'L': // LCC interface implemented in EXRAIL parser
|
||||||
break; // Will <X> if not intercepted by EXRAIL
|
break; // Will <X> if not intercepted by EXRAIL
|
||||||
|
|
||||||
|
#ifndef DISABLE_VDPY
|
||||||
case '@': // JMRI saying "give me virtual LCD msgs"
|
case '@': // JMRI saying "give me virtual LCD msgs"
|
||||||
CommandDistributor::setVirtualLCDSerial(stream);
|
CommandDistributor::setVirtualLCDSerial(stream);
|
||||||
StringFormatter::send(stream,
|
StringFormatter::send(stream,
|
||||||
F("<@ 0 0 \"DCC-EX v" VERSION "\">\n"
|
F("<@ 0 0 \"DCC-EX v" VERSION "\">\n"
|
||||||
"<@ 0 1 \"Lic GPLv3\">\n"));
|
"<@ 0 1 \"Lic GPLv3\">\n"));
|
||||||
return;
|
return;
|
||||||
|
#endif
|
||||||
default: //anything else will diagnose and drop out to <X>
|
default: //anything else will diagnose and drop out to <X>
|
||||||
if (opcode >= ' ' && opcode <= '~') {
|
if (opcode >= ' ' && opcode <= '~') {
|
||||||
DIAG(F("Opcode=%c params=%d"), opcode, params);
|
DIAG(F("Opcode=%c params=%d"), opcode, params);
|
||||||
@@ -1057,6 +1063,7 @@ bool DCCEXParser::parseS(Print *stream, int16_t params, int16_t p[])
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) {
|
bool DCCEXParser::parseC(Print *stream, int16_t params, int16_t p[]) {
|
||||||
|
(void)stream; // arg not used, maybe later?
|
||||||
if (params == 0)
|
if (params == 0)
|
||||||
return false;
|
return false;
|
||||||
switch (p[0])
|
switch (p[0])
|
||||||
|
@@ -50,11 +50,16 @@ HardwareSerial Serial6(PA12, PA11); // Rx=PA12, Tx=PA11 -- CN10 pins 12 and 14
|
|||||||
// via the debugger on the Nucleo-64. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc.
|
// via the debugger on the Nucleo-64. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc.
|
||||||
// On the F446RE, Serial3 and Serial5 are easy to use:
|
// On the F446RE, Serial3 and Serial5 are easy to use:
|
||||||
HardwareSerial Serial3(PC11, PC10); // Rx=PC11, Tx=PC10 -- USART3 - F446RE
|
HardwareSerial Serial3(PC11, PC10); // Rx=PC11, Tx=PC10 -- USART3 - F446RE
|
||||||
HardwareSerial Serial5(PD2, PC12); // Rx=PC7, Tx=PC6 -- UART5 - F446RE
|
HardwareSerial Serial5(PD2, PC12); // Rx=PD2, Tx=PC12 -- UART5 - F446RE
|
||||||
// On the F446RE, Serial4 and Serial6 also use pins we can't readily map while using the Arduino pins
|
// On the F446RE, Serial4 and Serial6 also use pins we can't readily map while using the Arduino pins
|
||||||
#elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F446ZE)
|
#elif defined(ARDUINO_NUCLEO_F412ZG) || defined(ARDUINO_NUCLEO_F413ZH) || defined(ARDUINO_NUCLEO_F446ZE) || \
|
||||||
|
defined(ARDUINO_NUCLEO_F429ZI) || defined(ARDUINO_NUCLEO_F439ZI)
|
||||||
// Nucleo-144 boards don't have Serial1 defined by default
|
// Nucleo-144 boards don't have Serial1 defined by default
|
||||||
HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6
|
HardwareSerial Serial6(PG9, PG14); // Rx=PG9, Tx=PG14 -- USART6
|
||||||
|
HardwareSerial Serial5(PD2, PC12); // Rx=PD2, Tx=PC12 -- UART5
|
||||||
|
#if !defined(ARDUINO_NUCLEO_F412ZG)
|
||||||
|
HardwareSerial Serial2(PD6, PD5); // Rx=PD6, Tx=PD5 -- UART5
|
||||||
|
#endif
|
||||||
// Serial3 is defined to use USART3 by default, but is in fact used as the diag console
|
// Serial3 is defined to use USART3 by default, but is in fact used as the diag console
|
||||||
// via the debugger on the Nucleo-144. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc.
|
// via the debugger on the Nucleo-144. It is therefore unavailable for other DCC-EX uses like WiFi, DFPlayer, etc.
|
||||||
#else
|
#else
|
||||||
@@ -215,9 +220,9 @@ void DCCTimer::clearPWM() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
|
void DCCTimer::getSimulatedMacAddress(byte mac[6]) {
|
||||||
volatile uint32_t *serno1 = (volatile uint32_t *)0x1FFF7A10;
|
volatile uint32_t *serno1 = (volatile uint32_t *)UID_BASE;
|
||||||
volatile uint32_t *serno2 = (volatile uint32_t *)0x1FFF7A14;
|
volatile uint32_t *serno2 = (volatile uint32_t *)UID_BASE+4;
|
||||||
// volatile uint32_t *serno3 = (volatile uint32_t *)0x1FFF7A18;
|
// volatile uint32_t *serno3 = (volatile uint32_t *)UID_BASE+8;
|
||||||
|
|
||||||
volatile uint32_t m1 = *serno1;
|
volatile uint32_t m1 = *serno1;
|
||||||
volatile uint32_t m2 = *serno2;
|
volatile uint32_t m2 = *serno2;
|
||||||
|
@@ -106,6 +106,7 @@ void DCCWaveform::interruptHandler() {
|
|||||||
DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
|
DCCWaveform::DCCWaveform( byte preambleBits, bool isMain) {
|
||||||
isMainTrack = isMain;
|
isMainTrack = isMain;
|
||||||
packetPending = false;
|
packetPending = false;
|
||||||
|
reminderWindowOpen = false;
|
||||||
memcpy(transmitPacket, idlePacket, sizeof(idlePacket));
|
memcpy(transmitPacket, idlePacket, sizeof(idlePacket));
|
||||||
state = WAVE_START;
|
state = WAVE_START;
|
||||||
// The +1 below is to allow the preamble generator to create the stop bit
|
// The +1 below is to allow the preamble generator to create the stop bit
|
||||||
@@ -127,9 +128,15 @@ void DCCWaveform::interrupt2() {
|
|||||||
if (remainingPreambles > 0 ) {
|
if (remainingPreambles > 0 ) {
|
||||||
state=WAVE_MID_1; // switch state to trigger LOW on next interrupt
|
state=WAVE_MID_1; // switch state to trigger LOW on next interrupt
|
||||||
remainingPreambles--;
|
remainingPreambles--;
|
||||||
|
|
||||||
|
// As we get to the end of the preambles, open the reminder window.
|
||||||
|
// This delays any reminder insertion until the last moment so
|
||||||
|
// that the reminder doesn't block a more urgent packet.
|
||||||
|
reminderWindowOpen=transmitRepeats==0 && remainingPreambles<4 && remainingPreambles>1;
|
||||||
|
if (remainingPreambles==1) promotePendingPacket();
|
||||||
// Update free memory diagnostic as we don't have anything else to do this time.
|
// 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.
|
// Allow for checkAck and its called functions using 22 bytes more.
|
||||||
DCCTimer::updateMinimumFreeMemoryISR(22);
|
else DCCTimer::updateMinimumFreeMemoryISR(22);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -148,30 +155,9 @@ void DCCWaveform::interrupt2() {
|
|||||||
if (bytes_sent >= transmitLength) {
|
if (bytes_sent >= transmitLength) {
|
||||||
// end of transmission buffer... repeat or switch to next message
|
// end of transmission buffer... repeat or switch to next message
|
||||||
bytes_sent = 0;
|
bytes_sent = 0;
|
||||||
|
// preamble for next packet will start...
|
||||||
remainingPreambles = requiredPreambles;
|
remainingPreambles = requiredPreambles;
|
||||||
|
|
||||||
if (transmitRepeats > 0) {
|
|
||||||
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));
|
|
||||||
|
|
||||||
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!)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
#pragma GCC pop_options
|
#pragma GCC pop_options
|
||||||
@@ -193,8 +179,39 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea
|
|||||||
packetPending = true;
|
packetPending = true;
|
||||||
clearResets();
|
clearResets();
|
||||||
}
|
}
|
||||||
bool DCCWaveform::getPacketPending() {
|
|
||||||
return packetPending;
|
bool DCCWaveform::isReminderWindowOpen() {
|
||||||
|
return reminderWindowOpen && ! packetPending;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DCCWaveform::promotePendingPacket() {
|
||||||
|
// fill the transmission packet from the pending packet
|
||||||
|
|
||||||
|
// Just keep going if repeating
|
||||||
|
if (transmitRepeats > 0) {
|
||||||
|
transmitRepeats--;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
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));
|
||||||
|
|
||||||
|
transmitLength = pendingLength;
|
||||||
|
transmitRepeats = pendingRepeats;
|
||||||
|
packetPending = false;
|
||||||
|
clearResets();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// nothing to do, just send idles or resets
|
||||||
|
// 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!)
|
||||||
}
|
}
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
@@ -266,15 +283,15 @@ void DCCWaveform::schedulePacket(const byte buffer[], byte byteCount, byte repea
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool DCCWaveform::getPacketPending() {
|
bool DCCWaveform::isReminderWindowOpen() {
|
||||||
if(isMainTrack) {
|
if(isMainTrack) {
|
||||||
if (rmtMainChannel == NULL)
|
if (rmtMainChannel == NULL)
|
||||||
return true;
|
return false;
|
||||||
return rmtMainChannel->busy();
|
return !rmtMainChannel->busy();
|
||||||
} else {
|
} else {
|
||||||
if (rmtProgChannel == NULL)
|
if (rmtProgChannel == NULL)
|
||||||
return true;
|
return false;
|
||||||
return rmtProgChannel->busy();
|
return !rmtProgChannel->busy();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
void IRAM_ATTR DCCWaveform::loop() {
|
void IRAM_ATTR DCCWaveform::loop() {
|
||||||
|
@@ -76,11 +76,13 @@ class DCCWaveform {
|
|||||||
};
|
};
|
||||||
#endif
|
#endif
|
||||||
void schedulePacket(const byte buffer[], byte byteCount, byte repeats);
|
void schedulePacket(const byte buffer[], byte byteCount, byte repeats);
|
||||||
bool getPacketPending();
|
bool isReminderWindowOpen();
|
||||||
|
void promotePendingPacket();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#ifndef ARDUINO_ARCH_ESP32
|
#ifndef ARDUINO_ARCH_ESP32
|
||||||
volatile bool packetPending;
|
volatile bool packetPending;
|
||||||
|
volatile bool reminderWindowOpen;
|
||||||
volatile byte sentResetsSincePacket;
|
volatile byte sentResetsSincePacket;
|
||||||
#else
|
#else
|
||||||
volatile uint32_t resetPacketBase;
|
volatile uint32_t resetPacketBase;
|
||||||
|
@@ -54,7 +54,9 @@
|
|||||||
xxx; \
|
xxx; \
|
||||||
t->refresh();}
|
t->refresh();}
|
||||||
#else
|
#else
|
||||||
#define DISPLAY_START(xxx) {}
|
#define DISPLAY_START(xxx) { \
|
||||||
|
xxx; \
|
||||||
|
}
|
||||||
|
|
||||||
#endif
|
#endif
|
||||||
#endif // LCD_Implementation_h
|
#endif // LCD_Implementation_h
|
||||||
|
51
EXRAIL2.cpp
51
EXRAIL2.cpp
@@ -86,6 +86,8 @@ LookList * RMFT2::onRotateLookup=NULL;
|
|||||||
LookList * RMFT2::onOverloadLookup=NULL;
|
LookList * RMFT2::onOverloadLookup=NULL;
|
||||||
byte * RMFT2::routeStateArray=nullptr;
|
byte * RMFT2::routeStateArray=nullptr;
|
||||||
const FSH * * RMFT2::routeCaptionArray=nullptr;
|
const FSH * * RMFT2::routeCaptionArray=nullptr;
|
||||||
|
int16_t * RMFT2::stashArray=nullptr;
|
||||||
|
int16_t RMFT2::maxStashId=0;
|
||||||
|
|
||||||
// getOperand instance version, uses progCounter from instance.
|
// getOperand instance version, uses progCounter from instance.
|
||||||
uint16_t RMFT2::getOperand(byte n) {
|
uint16_t RMFT2::getOperand(byte n) {
|
||||||
@@ -232,6 +234,12 @@ if (compileFeatures & FEATURE_SIGNAL) {
|
|||||||
IODevice::configureInput((VPIN)pin,true);
|
IODevice::configureInput((VPIN)pin,true);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
case OPCODE_STASH:
|
||||||
|
case OPCODE_CLEAR_STASH:
|
||||||
|
case OPCODE_PICKUP_STASH: {
|
||||||
|
maxStashId=max(maxStashId,((int16_t)operand));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
case OPCODE_ATGTE:
|
case OPCODE_ATGTE:
|
||||||
case OPCODE_ATLT:
|
case OPCODE_ATLT:
|
||||||
@@ -311,8 +319,14 @@ if (compileFeatures & FEATURE_SIGNAL) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
SKIPOP; // include ENDROUTES opcode
|
SKIPOP; // include ENDROUTES opcode
|
||||||
|
|
||||||
DIAG(F("EXRAIL %db, fl=%d"),progCounter,MAX_FLAGS);
|
if (compileFeatures & FEATURE_STASH) {
|
||||||
|
// create the stash array from the highest id found
|
||||||
|
if (maxStashId>0) stashArray=(int16_t*)calloc(maxStashId+1, sizeof(int16_t));
|
||||||
|
//TODO check EEPROM and fetch stashArray
|
||||||
|
}
|
||||||
|
|
||||||
|
DIAG(F("EXRAIL %db, fl=%d, stash=%d"),progCounter,MAX_FLAGS, maxStashId);
|
||||||
|
|
||||||
// Removed for 4.2.31 new RMFT2(0); // add the startup route
|
// Removed for 4.2.31 new RMFT2(0); // add the startup route
|
||||||
diag=saved_diag;
|
diag=saved_diag;
|
||||||
@@ -400,7 +414,6 @@ void RMFT2::driveLoco(byte speed) {
|
|||||||
power on appropriate track if DC or main if dcc
|
power on appropriate track if DC or main if dcc
|
||||||
if (TrackManager::getMainPowerMode()==POWERMODE::OFF) {
|
if (TrackManager::getMainPowerMode()==POWERMODE::OFF) {
|
||||||
TrackManager::setMainPower(POWERMODE::ON);
|
TrackManager::setMainPower(POWERMODE::ON);
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
}
|
}
|
||||||
**********/
|
**********/
|
||||||
|
|
||||||
@@ -628,7 +641,6 @@ void RMFT2::loop2() {
|
|||||||
case OPCODE_POWEROFF:
|
case OPCODE_POWEROFF:
|
||||||
TrackManager::setPower(POWERMODE::OFF);
|
TrackManager::setPower(POWERMODE::OFF);
|
||||||
TrackManager::setJoin(false);
|
TrackManager::setJoin(false);
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPCODE_SET_POWER:
|
case OPCODE_SET_POWER:
|
||||||
@@ -823,12 +835,10 @@ void RMFT2::loop2() {
|
|||||||
case OPCODE_JOIN:
|
case OPCODE_JOIN:
|
||||||
TrackManager::setPower(POWERMODE::ON);
|
TrackManager::setPower(POWERMODE::ON);
|
||||||
TrackManager::setJoin(true);
|
TrackManager::setJoin(true);
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPCODE_UNJOIN:
|
case OPCODE_UNJOIN:
|
||||||
TrackManager::setJoin(false);
|
TrackManager::setJoin(false);
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPCODE_READ_LOCO1: // READ_LOCO is implemented as 2 separate opcodes
|
case OPCODE_READ_LOCO1: // READ_LOCO is implemented as 2 separate opcodes
|
||||||
@@ -856,7 +866,6 @@ void RMFT2::loop2() {
|
|||||||
case OPCODE_POWERON:
|
case OPCODE_POWERON:
|
||||||
TrackManager::setMainPower(POWERMODE::ON);
|
TrackManager::setMainPower(POWERMODE::ON);
|
||||||
TrackManager::setJoin(false);
|
TrackManager::setJoin(false);
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OPCODE_START:
|
case OPCODE_START:
|
||||||
@@ -935,6 +944,34 @@ void RMFT2::loop2() {
|
|||||||
case OPCODE_ROUTE_DISABLED:
|
case OPCODE_ROUTE_DISABLED:
|
||||||
manageRouteState(operand,4);
|
manageRouteState(operand,4);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case OPCODE_STASH:
|
||||||
|
if (compileFeatures & FEATURE_STASH)
|
||||||
|
stashArray[operand] = invert? -loco : loco;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPCODE_CLEAR_STASH:
|
||||||
|
if (compileFeatures & FEATURE_STASH)
|
||||||
|
stashArray[operand] = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPCODE_CLEAR_ALL_STASH:
|
||||||
|
if (compileFeatures & FEATURE_STASH)
|
||||||
|
for (int i=0;i<=maxStashId;i++) stashArray[operand]=0;
|
||||||
|
break;
|
||||||
|
|
||||||
|
case OPCODE_PICKUP_STASH:
|
||||||
|
if (compileFeatures & FEATURE_STASH) {
|
||||||
|
int16_t x=stashArray[operand];
|
||||||
|
if (x>=0) {
|
||||||
|
loco=x;
|
||||||
|
invert=false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
loco=-x;
|
||||||
|
invert=true;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
case OPCODE_ROUTE:
|
case OPCODE_ROUTE:
|
||||||
case OPCODE_AUTOMATION:
|
case OPCODE_AUTOMATION:
|
||||||
|
@@ -70,6 +70,7 @@ enum OPCODE : byte {OPCODE_THROW,OPCODE_CLOSE,
|
|||||||
OPCODE_ONOVERLOAD,
|
OPCODE_ONOVERLOAD,
|
||||||
OPCODE_ROUTE_ACTIVE,OPCODE_ROUTE_INACTIVE,OPCODE_ROUTE_HIDDEN,
|
OPCODE_ROUTE_ACTIVE,OPCODE_ROUTE_INACTIVE,OPCODE_ROUTE_HIDDEN,
|
||||||
OPCODE_ROUTE_DISABLED,
|
OPCODE_ROUTE_DISABLED,
|
||||||
|
OPCODE_STASH,OPCODE_CLEAR_STASH,OPCODE_CLEAR_ALL_STASH,OPCODE_PICKUP_STASH,
|
||||||
|
|
||||||
// OPcodes below this point are skip-nesting IF operations
|
// OPcodes below this point are skip-nesting IF operations
|
||||||
// placed here so that they may be skipped as a group
|
// placed here so that they may be skipped as a group
|
||||||
@@ -102,6 +103,7 @@ enum thrunger: byte {
|
|||||||
static const byte FEATURE_LCC = 0x40;
|
static const byte FEATURE_LCC = 0x40;
|
||||||
static const byte FEATURE_ROSTER= 0x20;
|
static const byte FEATURE_ROSTER= 0x20;
|
||||||
static const byte FEATURE_ROUTESTATE= 0x10;
|
static const byte FEATURE_ROUTESTATE= 0x10;
|
||||||
|
static const byte FEATURE_STASH = 0x08;
|
||||||
|
|
||||||
|
|
||||||
// Flag bits for status of hardware and TPL
|
// Flag bits for status of hardware and TPL
|
||||||
@@ -203,7 +205,7 @@ private:
|
|||||||
uint16_t getOperand(byte n);
|
uint16_t getOperand(byte n);
|
||||||
|
|
||||||
static bool diag;
|
static bool diag;
|
||||||
static const HIGHFLASH byte RouteCode[];
|
static const HIGHFLASH3 byte RouteCode[];
|
||||||
static const HIGHFLASH int16_t SignalDefinitions[];
|
static const HIGHFLASH int16_t SignalDefinitions[];
|
||||||
static byte flags[MAX_FLAGS];
|
static byte flags[MAX_FLAGS];
|
||||||
static Print * LCCSerial;
|
static Print * LCCSerial;
|
||||||
@@ -229,6 +231,8 @@ private:
|
|||||||
static void manageRouteCaption(uint16_t id, const FSH* caption);
|
static void manageRouteCaption(uint16_t id, const FSH* caption);
|
||||||
static byte * routeStateArray;
|
static byte * routeStateArray;
|
||||||
static const FSH ** routeCaptionArray;
|
static const FSH ** routeCaptionArray;
|
||||||
|
static int16_t * stashArray;
|
||||||
|
static int16_t maxStashId;
|
||||||
|
|
||||||
// Local variables - exist for each instance/task
|
// Local variables - exist for each instance/task
|
||||||
RMFT2 *next; // loop chain
|
RMFT2 *next; // loop chain
|
||||||
|
@@ -39,6 +39,8 @@
|
|||||||
#undef AUTOSTART
|
#undef AUTOSTART
|
||||||
#undef BROADCAST
|
#undef BROADCAST
|
||||||
#undef CALL
|
#undef CALL
|
||||||
|
#undef CLEAR_STASH
|
||||||
|
#undef CLEAR_ALL_STASH
|
||||||
#undef CLOSE
|
#undef CLOSE
|
||||||
#undef DCC_SIGNAL
|
#undef DCC_SIGNAL
|
||||||
#undef DCC_TURNTABLE
|
#undef DCC_TURNTABLE
|
||||||
@@ -108,6 +110,7 @@
|
|||||||
#undef ONCHANGE
|
#undef ONCHANGE
|
||||||
#undef PARSE
|
#undef PARSE
|
||||||
#undef PAUSE
|
#undef PAUSE
|
||||||
|
#undef PICKUP_STASH
|
||||||
#undef PIN_TURNOUT
|
#undef PIN_TURNOUT
|
||||||
#undef PRINT
|
#undef PRINT
|
||||||
#ifndef DISABLE_PROG
|
#ifndef DISABLE_PROG
|
||||||
@@ -152,6 +155,8 @@
|
|||||||
#undef SIGNALH
|
#undef SIGNALH
|
||||||
#undef SPEED
|
#undef SPEED
|
||||||
#undef START
|
#undef START
|
||||||
|
#undef STASH
|
||||||
|
#undef STEALTH
|
||||||
#undef STOP
|
#undef STOP
|
||||||
#undef THROW
|
#undef THROW
|
||||||
#undef TT_ADDPOSITION
|
#undef TT_ADDPOSITION
|
||||||
@@ -184,7 +189,9 @@
|
|||||||
#define AUTOMATION(id,description)
|
#define AUTOMATION(id,description)
|
||||||
#define AUTOSTART
|
#define AUTOSTART
|
||||||
#define BROADCAST(msg)
|
#define BROADCAST(msg)
|
||||||
#define CALL(route)
|
#define CALL(route)
|
||||||
|
#define CLEAR_STASH(id)
|
||||||
|
#define CLEAR_ALL_STASH(id)
|
||||||
#define CLOSE(id)
|
#define CLOSE(id)
|
||||||
#define DCC_SIGNAL(id,add,subaddr)
|
#define DCC_SIGNAL(id,add,subaddr)
|
||||||
#define DCC_TURNTABLE(id,home,description)
|
#define DCC_TURNTABLE(id,home,description)
|
||||||
@@ -256,6 +263,7 @@
|
|||||||
#define PIN_TURNOUT(id,pin,description...)
|
#define PIN_TURNOUT(id,pin,description...)
|
||||||
#define PRINT(msg)
|
#define PRINT(msg)
|
||||||
#define PARSE(msg)
|
#define PARSE(msg)
|
||||||
|
#define PICKUP_STASH(id)
|
||||||
#ifndef DISABLE_PROG
|
#ifndef DISABLE_PROG
|
||||||
#define POM(cv,value)
|
#define POM(cv,value)
|
||||||
#endif
|
#endif
|
||||||
@@ -297,7 +305,9 @@
|
|||||||
#define SIGNAL(redpin,amberpin,greenpin)
|
#define SIGNAL(redpin,amberpin,greenpin)
|
||||||
#define SIGNALH(redpin,amberpin,greenpin)
|
#define SIGNALH(redpin,amberpin,greenpin)
|
||||||
#define SPEED(speed)
|
#define SPEED(speed)
|
||||||
#define START(route)
|
#define START(route)
|
||||||
|
#define STASH(id)
|
||||||
|
#define STEALTH(code...)
|
||||||
#define STOP
|
#define STOP
|
||||||
#define THROW(id)
|
#define THROW(id)
|
||||||
#define TT_ADDPOSITION(turntable_id,position,value,angle,description...)
|
#define TT_ADDPOSITION(turntable_id,position,value,angle,description...)
|
||||||
|
@@ -44,7 +44,9 @@ const int16_t HASH_KEYWORD_ROUTES=-3702;
|
|||||||
const int16_t HASH_KEYWORD_RED=26099;
|
const int16_t HASH_KEYWORD_RED=26099;
|
||||||
const int16_t HASH_KEYWORD_AMBER=18713;
|
const int16_t HASH_KEYWORD_AMBER=18713;
|
||||||
const int16_t HASH_KEYWORD_GREEN=-31493;
|
const int16_t HASH_KEYWORD_GREEN=-31493;
|
||||||
const int16_t HASH_KEYWORD_A='A';
|
const int16_t HASH_KEYWORD_A='A';
|
||||||
|
const int16_t HASH_KEYWORD_M='M';
|
||||||
|
|
||||||
|
|
||||||
// This filter intercepts <> commands to do the following:
|
// This filter intercepts <> commands to do the following:
|
||||||
// - Implement RMFT specific commands/diagnostics
|
// - Implement RMFT specific commands/diagnostics
|
||||||
@@ -149,7 +151,33 @@ void RMFT2::ComandFilter(Print * stream, byte & opcode, byte & paramCount, int16
|
|||||||
opcode=0;
|
opcode=0;
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
case HASH_KEYWORD_M:
|
||||||
|
// NOTE: we only need to handle valid calls here because
|
||||||
|
// DCCEXParser has to have code to handle the <J<> cases where
|
||||||
|
// exrail isnt involved anyway.
|
||||||
|
// This entire code block is compiled out if STASH macros not used
|
||||||
|
if (!(compileFeatures & FEATURE_STASH)) return;
|
||||||
|
if (paramCount==1) { // <JM>
|
||||||
|
StringFormatter::send(stream,F("<jM %d>\n"),maxStashId);
|
||||||
|
opcode=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (paramCount==2) { // <JM id>
|
||||||
|
if (p[1]<=0 || p[1]>maxStashId) break;
|
||||||
|
StringFormatter::send(stream,F("<jM %d %d>\n"),
|
||||||
|
p[1],stashArray[p[1]]);
|
||||||
|
opcode=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
if (paramCount==3) { // <JM id cab>
|
||||||
|
if (p[1]<=0 || p[1]>maxStashId) break;
|
||||||
|
stashArray[p[1]]=p[2];
|
||||||
|
opcode=0;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
@@ -195,6 +223,15 @@ bool RMFT2::parseSlash(Print * stream, byte & paramCount, int16_t p[]) {
|
|||||||
sigid & SIGNAL_ID_MASK);
|
sigid & SIGNAL_ID_MASK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (compileFeatures & FEATURE_STASH) {
|
||||||
|
for (int i=1;i<=maxStashId;i++) {
|
||||||
|
if (stashArray[i])
|
||||||
|
StringFormatter::send(stream,F("\nSTASH[%d] Loco=%d"),
|
||||||
|
i, stashArray[i]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
StringFormatter::send(stream,F(" *>\n"));
|
StringFormatter::send(stream,F(" *>\n"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
@@ -113,6 +113,15 @@ void exrailHalSetup() {
|
|||||||
#undef ROUTE_CAPTION
|
#undef ROUTE_CAPTION
|
||||||
#define ROUTE_CAPTION(id,caption) | FEATURE_ROUTESTATE
|
#define ROUTE_CAPTION(id,caption) | FEATURE_ROUTESTATE
|
||||||
|
|
||||||
|
#undef CLEAR_STASH
|
||||||
|
#define CLEAR_STASH(id) | FEATURE_STASH
|
||||||
|
#undef CLEAR_ALL_STASH
|
||||||
|
#define CLEAR_ALL_STASH | FEATURE_STASH
|
||||||
|
#undef PICKUP_STASH
|
||||||
|
#define PICKUP_STASH(id) | FEATURE_STASH
|
||||||
|
#undef STASH
|
||||||
|
#define STASH(id) | FEATURE_STASH
|
||||||
|
|
||||||
const byte RMFT2::compileFeatures = 0
|
const byte RMFT2::compileFeatures = 0
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
;
|
;
|
||||||
@@ -121,7 +130,7 @@ const byte RMFT2::compileFeatures = 0
|
|||||||
#include "EXRAIL2MacroReset.h"
|
#include "EXRAIL2MacroReset.h"
|
||||||
#undef ROUTE
|
#undef ROUTE
|
||||||
#define ROUTE(id, description) id,
|
#define ROUTE(id, description) id,
|
||||||
const int16_t HIGHFLASH RMFT2::routeIdList[]= {
|
const int16_t HIGHFLASH RMFT2::routeIdList[]= {
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
INT16_MAX};
|
INT16_MAX};
|
||||||
// Pass 2a create throttle automation list
|
// Pass 2a create throttle automation list
|
||||||
@@ -201,6 +210,8 @@ case (__COUNTER__ - StringMacroTracker1) : {\
|
|||||||
lcdid=id;\
|
lcdid=id;\
|
||||||
break;\
|
break;\
|
||||||
}
|
}
|
||||||
|
#undef STEALTH
|
||||||
|
#define STEALTH(code...) case (__COUNTER__ - StringMacroTracker1) : {code} return;
|
||||||
#undef WITHROTTLE
|
#undef WITHROTTLE
|
||||||
#define WITHROTTLE(msg) THRUNGE(msg,thrunge_withrottle)
|
#define WITHROTTLE(msg) THRUNGE(msg,thrunge_withrottle)
|
||||||
|
|
||||||
@@ -353,6 +364,8 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
|||||||
#define AUTOSTART OPCODE_AUTOSTART,0,0,
|
#define AUTOSTART OPCODE_AUTOSTART,0,0,
|
||||||
#define BROADCAST(msg) PRINT(msg)
|
#define BROADCAST(msg) PRINT(msg)
|
||||||
#define CALL(route) OPCODE_CALL,V(route),
|
#define CALL(route) OPCODE_CALL,V(route),
|
||||||
|
#define CLEAR_STASH(id) OPCODE_CLEAR_STASH,V(id),
|
||||||
|
#define CLEAR_ALL_STASH OPCODE_CLEAR_ALL_STASH,V(0),
|
||||||
#define CLOSE(id) OPCODE_CLOSE,V(id),
|
#define CLOSE(id) OPCODE_CLOSE,V(id),
|
||||||
#ifndef IO_NO_HAL
|
#ifndef IO_NO_HAL
|
||||||
#define DCC_TURNTABLE(id,home,description...) OPCODE_DCCTURNTABLE,V(id),OPCODE_PAD,V(home),
|
#define DCC_TURNTABLE(id,home,description...) OPCODE_DCCTURNTABLE,V(id),OPCODE_PAD,V(home),
|
||||||
@@ -411,6 +424,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
|||||||
OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF),
|
OPCODE_PAD,V((((uint64_t)sender)>>0)&0xFFFF),
|
||||||
#define LCD(id,msg) PRINT(msg)
|
#define LCD(id,msg) PRINT(msg)
|
||||||
#define SCREEN(display,id,msg) PRINT(msg)
|
#define SCREEN(display,id,msg) PRINT(msg)
|
||||||
|
#define STEALTH(code...) PRINT(dummy)
|
||||||
#define LCN(msg) PRINT(msg)
|
#define LCN(msg) PRINT(msg)
|
||||||
#define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0),
|
#define MOVETT(id,steps,activity) OPCODE_SERVO,V(id),OPCODE_PAD,V(steps),OPCODE_PAD,V(EXTurntable::activity),OPCODE_PAD,V(0),
|
||||||
#define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr),
|
#define ONACTIVATE(addr,subaddr) OPCODE_ONACTIVATE,V(addr<<2|subaddr),
|
||||||
@@ -435,6 +449,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
|||||||
#define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id),
|
#define ONTHROW(turnout_id) OPCODE_ONTHROW,V(turnout_id),
|
||||||
#define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id),
|
#define ONCHANGE(sensor_id) OPCODE_ONCHANGE,V(sensor_id),
|
||||||
#define PAUSE OPCODE_PAUSE,0,0,
|
#define PAUSE OPCODE_PAUSE,0,0,
|
||||||
|
#define PICKUP_STASH(id) OPCODE_PICKUP_STASH,V(id),
|
||||||
#define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin),
|
#define PIN_TURNOUT(id,pin,description...) OPCODE_PINTURNOUT,V(id),OPCODE_PAD,V(pin),
|
||||||
#ifndef DISABLE_PROG
|
#ifndef DISABLE_PROG
|
||||||
#define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value),
|
#define POM(cv,value) OPCODE_POM,V(cv),OPCODE_PAD,V(value),
|
||||||
@@ -482,6 +497,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
|||||||
#define SIGNALH(redpin,amberpin,greenpin)
|
#define SIGNALH(redpin,amberpin,greenpin)
|
||||||
#define SPEED(speed) OPCODE_SPEED,V(speed),
|
#define SPEED(speed) OPCODE_SPEED,V(speed),
|
||||||
#define START(route) OPCODE_START,V(route),
|
#define START(route) OPCODE_START,V(route),
|
||||||
|
#define STASH(id) OPCODE_STASH,V(id),
|
||||||
#define STOP OPCODE_SPEED,V(0),
|
#define STOP OPCODE_SPEED,V(0),
|
||||||
#define THROW(id) OPCODE_THROW,V(id),
|
#define THROW(id) OPCODE_THROW,V(id),
|
||||||
#ifndef IO_NO_HAL
|
#ifndef IO_NO_HAL
|
||||||
@@ -503,7 +519,7 @@ int RMFT2::onLCCLookup[RMFT2::countLCCLookup];
|
|||||||
|
|
||||||
// Build RouteCode
|
// Build RouteCode
|
||||||
const int StringMacroTracker2=__COUNTER__;
|
const int StringMacroTracker2=__COUNTER__;
|
||||||
const HIGHFLASH byte RMFT2::RouteCode[] = {
|
const HIGHFLASH3 byte RMFT2::RouteCode[] = {
|
||||||
#include "myAutomation.h"
|
#include "myAutomation.h"
|
||||||
OPCODE_ENDTASK,0,0,OPCODE_ENDEXRAIL,0,0 };
|
OPCODE_ENDTASK,0,0,OPCODE_ENDEXRAIL,0,0 };
|
||||||
|
|
||||||
|
3
FSH.h
3
FSH.h
@@ -56,6 +56,7 @@ typedef __FlashStringHelper FSH;
|
|||||||
#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560)
|
#if defined(ARDUINO_AVR_MEGA) || defined(ARDUINO_AVR_MEGA2560)
|
||||||
// AVR_MEGA memory deliberately placed at end of link may need _far functions
|
// AVR_MEGA memory deliberately placed at end of link may need _far functions
|
||||||
#define HIGHFLASH __attribute__((section(".fini2")))
|
#define HIGHFLASH __attribute__((section(".fini2")))
|
||||||
|
#define HIGHFLASH3 __attribute__((section(".fini3")))
|
||||||
#define GETFARPTR(data) pgm_get_far_address(data)
|
#define GETFARPTR(data) pgm_get_far_address(data)
|
||||||
#define GETHIGHFLASH(data,offset) pgm_read_byte_far(GETFARPTR(data)+offset)
|
#define GETHIGHFLASH(data,offset) pgm_read_byte_far(GETFARPTR(data)+offset)
|
||||||
#define GETHIGHFLASHW(data,offset) pgm_read_word_far(GETFARPTR(data)+offset)
|
#define GETHIGHFLASHW(data,offset) pgm_read_word_far(GETFARPTR(data)+offset)
|
||||||
@@ -63,6 +64,7 @@ typedef __FlashStringHelper FSH;
|
|||||||
// AVR_UNO/NANO runtime does not support _far functions so just use _near equivalent
|
// AVR_UNO/NANO runtime does not support _far functions so just use _near equivalent
|
||||||
// as there is no progmem above 32kb anyway.
|
// as there is no progmem above 32kb anyway.
|
||||||
#define HIGHFLASH PROGMEM
|
#define HIGHFLASH PROGMEM
|
||||||
|
#define HIGHFLASH3 PROGMEM
|
||||||
#define GETFARPTR(data) ((uint32_t)(data))
|
#define GETFARPTR(data) ((uint32_t)(data))
|
||||||
#define GETHIGHFLASH(data,offset) pgm_read_byte_near(GETFARPTR(data)+(offset))
|
#define GETHIGHFLASH(data,offset) pgm_read_byte_near(GETFARPTR(data)+(offset))
|
||||||
#define GETHIGHFLASHW(data,offset) pgm_read_word_near(GETFARPTR(data)+(offset))
|
#define GETHIGHFLASHW(data,offset) pgm_read_word_near(GETFARPTR(data)+(offset))
|
||||||
@@ -80,6 +82,7 @@ typedef __FlashStringHelper FSH;
|
|||||||
typedef char FSH;
|
typedef char FSH;
|
||||||
#define FLASH
|
#define FLASH
|
||||||
#define HIGHFLASH
|
#define HIGHFLASH
|
||||||
|
#define HIGHFLASH3
|
||||||
#define GETFARPTR(data) ((uint32_t)(data))
|
#define GETFARPTR(data) ((uint32_t)(data))
|
||||||
#define GETFLASH(addr) (*(const byte *)(addr))
|
#define GETFLASH(addr) (*(const byte *)(addr))
|
||||||
#define GETHIGHFLASH(data,offset) (*(const byte *)(GETFARPTR(data)+offset))
|
#define GETHIGHFLASH(data,offset) (*(const byte *)(GETFARPTR(data)+offset))
|
||||||
|
@@ -1 +1 @@
|
|||||||
#define GITHUB_SHA "devel-202311200731Z"
|
#define GITHUB_SHA "devel-202312131041Z"
|
||||||
|
@@ -542,8 +542,10 @@ protected:
|
|||||||
#include "IO_MCP23017.h"
|
#include "IO_MCP23017.h"
|
||||||
#include "IO_PCF8574.h"
|
#include "IO_PCF8574.h"
|
||||||
#include "IO_PCF8575.h"
|
#include "IO_PCF8575.h"
|
||||||
|
#include "IO_PCA9555.h"
|
||||||
#include "IO_duinoNodes.h"
|
#include "IO_duinoNodes.h"
|
||||||
#include "IO_EXIOExpander.h"
|
#include "IO_EXIOExpander.h"
|
||||||
|
#include "IO_trainbrains.h"
|
||||||
|
|
||||||
|
|
||||||
#endif // iodevice_h
|
#endif // iodevice_h
|
||||||
|
@@ -22,13 +22,10 @@
|
|||||||
* This device driver will configure the device on startup, along with
|
* This device driver will configure the device on startup, along with
|
||||||
* interacting with the device for all input/output duties.
|
* interacting with the device for all input/output duties.
|
||||||
*
|
*
|
||||||
* To create EX-IOExpander devices, these are defined in myHal.cpp:
|
* To create EX-IOExpander devices, these are defined in myAutomation.h:
|
||||||
* (Note the device driver is included by default)
|
* (Note the device driver is included by default)
|
||||||
*
|
*
|
||||||
* void halSetup() {
|
* HAL(EXIOExpander,800,18,0x65)
|
||||||
* // EXIOExpander::create(vpin, num_vpins, i2c_address);
|
|
||||||
* EXIOExpander::create(800, 18, 0x65);
|
|
||||||
* }
|
|
||||||
*
|
*
|
||||||
* All pins on an EX-IOExpander device are allocated according to the pin map for the specific
|
* All pins on an EX-IOExpander device are allocated according to the pin map for the specific
|
||||||
* device in use. There is no way for the device driver to sanity check pins are used for the
|
* device in use. There is no way for the device driver to sanity check pins are used for the
|
||||||
@@ -98,25 +95,30 @@ private:
|
|||||||
_numAnaloguePins = receiveBuffer[2];
|
_numAnaloguePins = receiveBuffer[2];
|
||||||
|
|
||||||
// See if we already have suitable buffers assigned
|
// See if we already have suitable buffers assigned
|
||||||
size_t digitalBytesNeeded = (_numDigitalPins + 7) / 8;
|
if (_numDigitalPins>0) {
|
||||||
if (_digitalPinBytes < digitalBytesNeeded) {
|
size_t digitalBytesNeeded = (_numDigitalPins + 7) / 8;
|
||||||
// Not enough space, free any existing buffer and allocate a new one
|
if (_digitalPinBytes < digitalBytesNeeded) {
|
||||||
if (_digitalPinBytes > 0) free(_digitalInputStates);
|
// Not enough space, free any existing buffer and allocate a new one
|
||||||
_digitalInputStates = (byte*) calloc(_digitalPinBytes, 1);
|
if (_digitalPinBytes > 0) free(_digitalInputStates);
|
||||||
_digitalPinBytes = digitalBytesNeeded;
|
_digitalInputStates = (byte*) calloc(_digitalPinBytes, 1);
|
||||||
}
|
_digitalPinBytes = digitalBytesNeeded;
|
||||||
size_t analogueBytesNeeded = _numAnaloguePins * 2;
|
}
|
||||||
if (_analoguePinBytes < analogueBytesNeeded) {
|
}
|
||||||
// Free any existing buffers and allocate new ones.
|
|
||||||
if (_analoguePinBytes > 0) {
|
if (_numAnaloguePins>0) {
|
||||||
free(_analogueInputBuffer);
|
size_t analogueBytesNeeded = _numAnaloguePins * 2;
|
||||||
free(_analogueInputStates);
|
if (_analoguePinBytes < analogueBytesNeeded) {
|
||||||
free(_analoguePinMap);
|
// Free any existing buffers and allocate new ones.
|
||||||
|
if (_analoguePinBytes > 0) {
|
||||||
|
free(_analogueInputBuffer);
|
||||||
|
free(_analogueInputStates);
|
||||||
|
free(_analoguePinMap);
|
||||||
|
}
|
||||||
|
_analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1);
|
||||||
|
_analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1);
|
||||||
|
_analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1);
|
||||||
|
_analoguePinBytes = analogueBytesNeeded;
|
||||||
}
|
}
|
||||||
_analogueInputStates = (uint8_t*) calloc(analogueBytesNeeded, 1);
|
|
||||||
_analogueInputBuffer = (uint8_t*) calloc(analogueBytesNeeded, 1);
|
|
||||||
_analoguePinMap = (uint8_t*) calloc(_numAnaloguePins, 1);
|
|
||||||
_analoguePinBytes = analogueBytesNeeded;
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
DIAG(F("EX-IOExpander I2C:%s ERROR configuring device"), _I2CAddress.toString());
|
DIAG(F("EX-IOExpander I2C:%s ERROR configuring device"), _I2CAddress.toString());
|
||||||
@@ -124,8 +126,8 @@ private:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// We now need to retrieve the analogue pin map
|
// We now need to retrieve the analogue pin map if there are analogue pins
|
||||||
if (status == I2C_STATUS_OK) {
|
if (status == I2C_STATUS_OK && _numAnaloguePins>0) {
|
||||||
commandBuffer[0] = EXIOINITA;
|
commandBuffer[0] = EXIOINITA;
|
||||||
status = I2CManager.read(_I2CAddress, _analoguePinMap, _numAnaloguePins, commandBuffer, 1);
|
status = I2CManager.read(_I2CAddress, _analoguePinMap, _numAnaloguePins, commandBuffer, 1);
|
||||||
}
|
}
|
||||||
@@ -239,7 +241,7 @@ private:
|
|||||||
|
|
||||||
// If we're not doing anything now, check to see if a new input transfer is due.
|
// If we're not doing anything now, check to see if a new input transfer is due.
|
||||||
if (_readState == RDS_IDLE) {
|
if (_readState == RDS_IDLE) {
|
||||||
if (currentMicros - _lastDigitalRead > _digitalRefresh) { // Delay for digital read refresh
|
if (currentMicros - _lastDigitalRead > _digitalRefresh && _numDigitalPins>0) { // Delay for digital read refresh
|
||||||
// Issue new read request for digital states. As the request is non-blocking, the buffer has to
|
// Issue new read request for digital states. As the request is non-blocking, the buffer has to
|
||||||
// be allocated from heap (object state).
|
// be allocated from heap (object state).
|
||||||
_readCommandBuffer[0] = EXIORDD;
|
_readCommandBuffer[0] = EXIORDD;
|
||||||
@@ -247,7 +249,7 @@ private:
|
|||||||
// non-blocking read
|
// non-blocking read
|
||||||
_lastDigitalRead = currentMicros;
|
_lastDigitalRead = currentMicros;
|
||||||
_readState = RDS_DIGITAL;
|
_readState = RDS_DIGITAL;
|
||||||
} else if (currentMicros - _lastAnalogueRead > _analogueRefresh) { // Delay for analogue read refresh
|
} else if (currentMicros - _lastAnalogueRead > _analogueRefresh && _numAnaloguePins>0) { // Delay for analogue read refresh
|
||||||
// Issue new read for analogue input states
|
// Issue new read for analogue input states
|
||||||
_readCommandBuffer[0] = EXIORDAN;
|
_readCommandBuffer[0] = EXIORDAN;
|
||||||
I2CManager.read(_I2CAddress, _analogueInputBuffer,
|
I2CManager.read(_I2CAddress, _analogueInputBuffer,
|
||||||
|
98
IO_trainbrains.h
Normal file
98
IO_trainbrains.h
Normal file
@@ -0,0 +1,98 @@
|
|||||||
|
/*
|
||||||
|
* © 2023, Chris Harlow. All rights reserved.
|
||||||
|
* © 2021, Neil McKechnie. All rights reserved.
|
||||||
|
*
|
||||||
|
* This file is part of DCC++EX API
|
||||||
|
*
|
||||||
|
* This is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* It is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with CommandStation. If not, see <https://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef io_trainbrains_h
|
||||||
|
#define io_trainbrains_h
|
||||||
|
|
||||||
|
#include "IO_GPIOBase.h"
|
||||||
|
#include "FSH.h"
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
/*
|
||||||
|
* IODevice subclass for trainbrains 3-block occupancy detector.
|
||||||
|
* For details see http://trainbrains.eu
|
||||||
|
*/
|
||||||
|
|
||||||
|
enum TrackUnoccupancy
|
||||||
|
{
|
||||||
|
TRACK_UNOCCUPANCY_UNKNOWN = 0,
|
||||||
|
TRACK_OCCUPIED = 1,
|
||||||
|
TRACK_UNOCCUPIED = 2
|
||||||
|
};
|
||||||
|
|
||||||
|
class Trainbrains02 : public GPIOBase<uint16_t> {
|
||||||
|
public:
|
||||||
|
static void create(VPIN vpin, uint8_t nPins, I2CAddress i2cAddress) {
|
||||||
|
if (checkNoOverlap(vpin, nPins, i2cAddress)) new Trainbrains02(vpin, nPins, i2cAddress);
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Constructor
|
||||||
|
Trainbrains02(VPIN vpin, uint8_t nPins, I2CAddress i2cAddress, int interruptPin=-1)
|
||||||
|
: GPIOBase<uint16_t>((FSH *)F("Trainbrains02"), vpin, nPins, i2cAddress, interruptPin)
|
||||||
|
{
|
||||||
|
requestBlock.setRequestParams(_I2CAddress, inputBuffer, sizeof(inputBuffer),
|
||||||
|
outputBuffer, sizeof(outputBuffer));
|
||||||
|
|
||||||
|
outputBuffer[0] = (uint8_t)_I2CAddress; // strips away the mux part.
|
||||||
|
outputBuffer[1] =14;
|
||||||
|
outputBuffer[2] =1;
|
||||||
|
outputBuffer[3] =0; // This is the channel updated at each poling call
|
||||||
|
outputBuffer[4] =0;
|
||||||
|
outputBuffer[5] =0;
|
||||||
|
outputBuffer[6] =0;
|
||||||
|
outputBuffer[7] =0;
|
||||||
|
outputBuffer[8] =0;
|
||||||
|
outputBuffer[9] =0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void _writeGpioPort() override {}
|
||||||
|
|
||||||
|
void _readGpioPort(bool immediate) override {
|
||||||
|
// cycle channel on device each time
|
||||||
|
outputBuffer[3]=channelInProgress+1; // 1-origin
|
||||||
|
channelInProgress++;
|
||||||
|
if(channelInProgress>=_nPins) channelInProgress=0;
|
||||||
|
|
||||||
|
if (immediate) {
|
||||||
|
_processCompletion(I2CManager.read(_I2CAddress, inputBuffer, sizeof(inputBuffer),
|
||||||
|
outputBuffer, sizeof(outputBuffer)));
|
||||||
|
} else {
|
||||||
|
// Queue new request
|
||||||
|
requestBlock.wait(); // Wait for preceding operation to complete
|
||||||
|
// Issue new request to read GPIO register
|
||||||
|
I2CManager.queueRequest(&requestBlock);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// This function is invoked when an I/O operation on the requestBlock completes.
|
||||||
|
void _processCompletion(uint8_t status) override {
|
||||||
|
if (status != I2C_STATUS_OK) inputBuffer[6]=TRACK_UNOCCUPANCY_UNKNOWN;
|
||||||
|
if (inputBuffer[6] == TRACK_UNOCCUPIED ) _portInputState |= 0x01 <<channelInProgress;
|
||||||
|
else _portInputState &= ~(0x01 <<channelInProgress);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t channelInProgress=0;
|
||||||
|
uint8_t outputBuffer[10];
|
||||||
|
uint8_t inputBuffer[10];
|
||||||
|
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif
|
@@ -156,8 +156,10 @@ class MotorDriver {
|
|||||||
// from outside interrupt
|
// from outside interrupt
|
||||||
void setBrake( bool on, bool interruptContext=false);
|
void setBrake( bool on, bool interruptContext=false);
|
||||||
__attribute__((always_inline)) inline void setSignal( bool high) {
|
__attribute__((always_inline)) inline void setSignal( bool high) {
|
||||||
|
#ifndef ARDUINO_ARCH_ESP32
|
||||||
if (invertPhase)
|
if (invertPhase)
|
||||||
high = !high;
|
high = !high;
|
||||||
|
#endif
|
||||||
if (trackPWM) {
|
if (trackPWM) {
|
||||||
DCCTimer::setPWM(signalPin,high);
|
DCCTimer::setPWM(signalPin,high);
|
||||||
}
|
}
|
||||||
|
@@ -39,8 +39,11 @@ void StringFormatter::diag( const FSH* input...) {
|
|||||||
|
|
||||||
void StringFormatter::lcd(byte row, const FSH* input...) {
|
void StringFormatter::lcd(byte row, const FSH* input...) {
|
||||||
va_list args;
|
va_list args;
|
||||||
|
#ifndef DISABLE_VDPY
|
||||||
Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(0,row);
|
Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(0,row);
|
||||||
|
#else
|
||||||
|
Print * virtualLCD=NULL;
|
||||||
|
#endif
|
||||||
// Issue the LCD as a diag first
|
// Issue the LCD as a diag first
|
||||||
// Unless the same serial is asking for the virtual @ respomnse
|
// Unless the same serial is asking for the virtual @ respomnse
|
||||||
if (virtualLCD!=&USB_SERIAL) {
|
if (virtualLCD!=&USB_SERIAL) {
|
||||||
@@ -50,13 +53,14 @@ void StringFormatter::lcd(byte row, const FSH* input...) {
|
|||||||
send(&USB_SERIAL,F(" *>\n"));
|
send(&USB_SERIAL,F(" *>\n"));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#ifndef DISABLE_VDPY
|
||||||
// send to virtual LCD collector (if any)
|
// send to virtual LCD collector (if any)
|
||||||
if (virtualLCD) {
|
if (virtualLCD) {
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
send2(virtualLCD,input,args);
|
send2(virtualLCD,input,args);
|
||||||
CommandDistributor::commitVirtualLCDSerial();
|
CommandDistributor::commitVirtualLCDSerial();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
DisplayInterface::setRow(row);
|
DisplayInterface::setRow(row);
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
send2(DisplayInterface::getDisplayHandler(),input,args);
|
send2(DisplayInterface::getDisplayHandler(),input,args);
|
||||||
@@ -66,12 +70,14 @@ void StringFormatter::lcd2(uint8_t display, byte row, const FSH* input...) {
|
|||||||
va_list args;
|
va_list args;
|
||||||
|
|
||||||
// send to virtual LCD collector (if any)
|
// send to virtual LCD collector (if any)
|
||||||
|
#ifndef DISABLE_VDPY
|
||||||
Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(display,row);
|
Print * virtualLCD=CommandDistributor::getVirtualLCDSerial(display,row);
|
||||||
if (virtualLCD) {
|
if (virtualLCD) {
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
send2(virtualLCD,input,args);
|
send2(virtualLCD,input,args);
|
||||||
CommandDistributor::commitVirtualLCDSerial();
|
CommandDistributor::commitVirtualLCDSerial();
|
||||||
}
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
DisplayInterface::setRow(display, row);
|
DisplayInterface::setRow(display, row);
|
||||||
va_start(args, input);
|
va_start(args, input);
|
||||||
@@ -250,4 +256,3 @@ void StringFormatter::printHex(Print * stream,uint16_t value) {
|
|||||||
result[4]='\0';
|
result[4]='\0';
|
||||||
stream->print(result);
|
stream->print(result);
|
||||||
}
|
}
|
||||||
|
|
111
TrackManager.cpp
111
TrackManager.cpp
@@ -54,7 +54,6 @@ const int16_t HASH_KEYWORD_INV = 11857;
|
|||||||
MotorDriver * TrackManager::track[MAX_TRACKS];
|
MotorDriver * TrackManager::track[MAX_TRACKS];
|
||||||
int16_t TrackManager::trackDCAddr[MAX_TRACKS];
|
int16_t TrackManager::trackDCAddr[MAX_TRACKS];
|
||||||
|
|
||||||
POWERMODE TrackManager::mainPowerGuess=POWERMODE::OFF;
|
|
||||||
byte TrackManager::lastTrack=0;
|
byte TrackManager::lastTrack=0;
|
||||||
bool TrackManager::progTrackSyncMain=false;
|
bool TrackManager::progTrackSyncMain=false;
|
||||||
bool TrackManager::progTrackBoosted=false;
|
bool TrackManager::progTrackBoosted=false;
|
||||||
@@ -210,6 +209,9 @@ void TrackManager::setDCSignal(int16_t cab, byte speedbyte) {
|
|||||||
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;
|
||||||
|
|
||||||
|
// Remember track mode we came from for later
|
||||||
|
TRACK_MODE oldmode = track[trackToSet]->getMode();
|
||||||
|
|
||||||
//DIAG(F("Track=%c Mode=%d"),trackToSet+'A', mode);
|
//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) {
|
if (mode & TRACK_MODE_DC) {
|
||||||
@@ -262,7 +264,7 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
|
|||||||
track[t]->setPower(POWERMODE::OFF);
|
track[t]->setPower(POWERMODE::OFF);
|
||||||
track[t]->setMode(TRACK_MODE_NONE);
|
track[t]->setMode(TRACK_MODE_NONE);
|
||||||
track[t]->makeProgTrack(false); // revoke prog track special handling
|
track[t]->makeProgTrack(false); // revoke prog track special handling
|
||||||
streamTrackState(NULL,t);
|
streamTrackState(NULL,t);
|
||||||
}
|
}
|
||||||
track[trackToSet]->makeProgTrack(true); // set for prog track special handling
|
track[trackToSet]->makeProgTrack(true); // set for prog track special handling
|
||||||
} else {
|
} else {
|
||||||
@@ -270,7 +272,6 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
|
|||||||
}
|
}
|
||||||
track[trackToSet]->setMode(mode);
|
track[trackToSet]->setMode(mode);
|
||||||
trackDCAddr[trackToSet]=dcAddr;
|
trackDCAddr[trackToSet]=dcAddr;
|
||||||
streamTrackState(NULL,trackToSet);
|
|
||||||
|
|
||||||
// When a track is switched, we must clear any side effects of its previous
|
// When a track is switched, we must clear any side effects of its previous
|
||||||
// state, otherwise trains run away or just dont move.
|
// state, otherwise trains run away or just dont move.
|
||||||
@@ -337,10 +338,11 @@ bool TrackManager::setTrackMode(byte trackToSet, TRACK_MODE mode, int16_t dcAddr
|
|||||||
applyDCSpeed(trackToSet);
|
applyDCSpeed(trackToSet);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Normal running tracks are set to the global power state
|
// Turn off power if we changed the mode of this track
|
||||||
track[trackToSet]->setPower(
|
if (mode != oldmode)
|
||||||
(mode & (TRACK_MODE_MAIN | TRACK_MODE_DC | TRACK_MODE_EXT | TRACK_MODE_BOOST)) ?
|
track[trackToSet]->setPower(POWERMODE::OFF);
|
||||||
mainPowerGuess : POWERMODE::OFF);
|
streamTrackState(NULL,trackToSet);
|
||||||
|
|
||||||
//DIAG(F("TrackMode=%d"),mode);
|
//DIAG(F("TrackMode=%d"),mode);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
@@ -350,7 +352,7 @@ void TrackManager::applyDCSpeed(byte t) {
|
|||||||
track[t]->setDCSignal(speedByte);
|
track[t]->setDCSignal(speedByte);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[])
|
bool TrackManager::parseEqualSign(Print *stream, int16_t params, int16_t p[])
|
||||||
{
|
{
|
||||||
|
|
||||||
if (params==0) { // <=> List track assignments
|
if (params==0) { // <=> List track assignments
|
||||||
@@ -397,46 +399,60 @@ bool TrackManager::parseJ(Print *stream, int16_t params, int16_t p[])
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
void TrackManager::streamTrackState(Print* stream, byte t) {
|
const FSH* TrackManager::getModeName(TRACK_MODE tm) {
|
||||||
// null stream means send to commandDistributor for broadcast
|
const FSH *modename=F("---");
|
||||||
if (track[t]==NULL) return;
|
|
||||||
auto format=F("<= %d XXX>\n");
|
|
||||||
TRACK_MODE tm = track[t]->getMode();
|
|
||||||
if (tm & TRACK_MODE_MAIN) {
|
if (tm & TRACK_MODE_MAIN) {
|
||||||
if(tm & TRACK_MODE_AUTOINV)
|
if(tm & TRACK_MODE_AUTOINV)
|
||||||
format=F("<= %c MAIN A>\n");
|
modename=F("MAIN A");
|
||||||
else if (tm & TRACK_MODE_INV)
|
else if (tm & TRACK_MODE_INV)
|
||||||
format=F("<= %c MAIN I>\n");
|
modename=F("MAIN I>\n");
|
||||||
else
|
else
|
||||||
format=F("<= %c MAIN>\n");
|
modename=F("MAIN");
|
||||||
}
|
}
|
||||||
#ifndef DISABLE_PROG
|
#ifndef DISABLE_PROG
|
||||||
else if (tm & TRACK_MODE_PROG)
|
else if (tm & TRACK_MODE_PROG)
|
||||||
format=F("<= %c PROG>\n");
|
modename=F("PROG");
|
||||||
#endif
|
#endif
|
||||||
else if (tm & TRACK_MODE_NONE)
|
else if (tm & TRACK_MODE_NONE)
|
||||||
format=F("<= %c NONE>\n");
|
modename=F("NONE");
|
||||||
else if(tm & TRACK_MODE_EXT)
|
else if(tm & TRACK_MODE_EXT)
|
||||||
format=F("<= %c EXT>\n");
|
modename=F("EXT");
|
||||||
else if(tm & TRACK_MODE_BOOST) {
|
else if(tm & TRACK_MODE_BOOST) {
|
||||||
if(tm & TRACK_MODE_AUTOINV)
|
if(tm & TRACK_MODE_AUTOINV)
|
||||||
format=F("<= %c B A>\n");
|
modename=F("B A");
|
||||||
else if (tm & TRACK_MODE_INV)
|
else if (tm & TRACK_MODE_INV)
|
||||||
format=F("<= %c B I>\n");
|
modename=F("B I");
|
||||||
else
|
else
|
||||||
format=F("<= %c B>\n");
|
modename=F("B");
|
||||||
}
|
}
|
||||||
else if (tm & TRACK_MODE_DC) {
|
else if (tm & TRACK_MODE_DC) {
|
||||||
if (tm & TRACK_MODE_INV)
|
if (tm & TRACK_MODE_INV)
|
||||||
format=F("<= %c DCX %d>\n");
|
modename=F("DCX");
|
||||||
else
|
else
|
||||||
format=F("<= %c DC %d>\n");
|
modename=F("DC");
|
||||||
}
|
}
|
||||||
|
return modename;
|
||||||
|
}
|
||||||
|
|
||||||
if (stream)
|
// null stream means send to commandDistributor for broadcast
|
||||||
StringFormatter::send(stream,format,'A'+t, trackDCAddr[t]);
|
void TrackManager::streamTrackState(Print* stream, byte t) {
|
||||||
|
const FSH *format;
|
||||||
|
|
||||||
|
if (track[t]==NULL) return;
|
||||||
|
TRACK_MODE tm = track[t]->getMode();
|
||||||
|
if (tm & TRACK_MODE_DC)
|
||||||
|
format=F("<= %c %S %d>\n");
|
||||||
else
|
else
|
||||||
CommandDistributor::broadcastTrackState(format,'A'+t, trackDCAddr[t]);
|
format=F("<= %c %S>\n");
|
||||||
|
|
||||||
|
const FSH *modename=getModeName(tm);
|
||||||
|
if (stream) { // null stream means send to commandDistributor for broadcast
|
||||||
|
StringFormatter::send(stream,format,'A'+t, modename, trackDCAddr[t]);
|
||||||
|
} else {
|
||||||
|
CommandDistributor::broadcastTrackState(format,'A'+t, modename, trackDCAddr[t]);
|
||||||
|
CommandDistributor::broadcastPower();
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -473,10 +489,13 @@ std::vector<MotorDriver *>TrackManager::getMainDrivers() {
|
|||||||
|
|
||||||
// Set track power for all tracks with this mode
|
// Set track power for all tracks with this mode
|
||||||
void TrackManager::setTrackPower(TRACK_MODE trackmodeToMatch, POWERMODE powermode) {
|
void TrackManager::setTrackPower(TRACK_MODE trackmodeToMatch, POWERMODE powermode) {
|
||||||
|
bool didChange=false;
|
||||||
FOR_EACH_TRACK(t) {
|
FOR_EACH_TRACK(t) {
|
||||||
MotorDriver *driver=track[t];
|
MotorDriver *driver=track[t];
|
||||||
TRACK_MODE trackmodeOfTrack = driver->getMode();
|
TRACK_MODE trackmodeOfTrack = driver->getMode();
|
||||||
if (trackmodeToMatch & trackmodeOfTrack) {
|
if (trackmodeToMatch & trackmodeOfTrack) {
|
||||||
|
if (powermode != driver->getPower())
|
||||||
|
didChange=true;
|
||||||
if (powermode == POWERMODE::ON) {
|
if (powermode == POWERMODE::ON) {
|
||||||
if (trackmodeOfTrack & TRACK_MODE_DC) {
|
if (trackmodeOfTrack & TRACK_MODE_DC) {
|
||||||
driver->setBrake(true); // DC starts with brake on
|
driver->setBrake(true); // DC starts with brake on
|
||||||
@@ -491,18 +510,24 @@ void TrackManager::setTrackPower(TRACK_MODE trackmodeToMatch, POWERMODE powermod
|
|||||||
driver->setPower(powermode);
|
driver->setPower(powermode);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
if (didChange)
|
||||||
|
CommandDistributor::broadcastPower();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set track power for this track, inependent of mode
|
// Set track power for this track, inependent of mode
|
||||||
void TrackManager::setTrackPower(POWERMODE powermode, byte t) {
|
void TrackManager::setTrackPower(POWERMODE powermode, byte t) {
|
||||||
MotorDriver *driver=track[t];
|
MotorDriver *driver=track[t];
|
||||||
TRACK_MODE trackmode = driver->getMode();
|
TRACK_MODE trackmode = driver->getMode();
|
||||||
if (trackmode & TRACK_MODE_DC) {
|
POWERMODE oldpower = driver->getPower();
|
||||||
|
if (trackmode & TRACK_MODE_NONE) {
|
||||||
|
driver->setBrake(true); // Track is unused. Brake is good to have.
|
||||||
|
powermode = POWERMODE::OFF; // Track is unused. Force it to OFF
|
||||||
|
} else if (trackmode & TRACK_MODE_DC) { // includes inverted DC (called DCX)
|
||||||
if (powermode == POWERMODE::ON) {
|
if (powermode == POWERMODE::ON) {
|
||||||
driver->setBrake(true); // DC starts with brake on
|
driver->setBrake(true); // DC starts with brake on
|
||||||
applyDCSpeed(t); // speed match DCC throttles
|
applyDCSpeed(t); // speed match DCC throttles
|
||||||
}
|
}
|
||||||
} else {
|
} else /* MAIN PROG EXT BOOST */ {
|
||||||
if (powermode == POWERMODE::ON) {
|
if (powermode == POWERMODE::ON) {
|
||||||
// toggle brake before turning power on - resets overcurrent error
|
// toggle brake before turning power on - resets overcurrent error
|
||||||
// on the Pololu board if brake is wired to ^D2.
|
// on the Pololu board if brake is wired to ^D2.
|
||||||
@@ -511,15 +536,8 @@ void TrackManager::setTrackPower(POWERMODE powermode, byte t) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
driver->setPower(powermode);
|
driver->setPower(powermode);
|
||||||
}
|
if (oldpower != driver->getPower())
|
||||||
|
CommandDistributor::broadcastPower();
|
||||||
void TrackManager::reportPowerChange(Print* stream, byte thistrack) {
|
|
||||||
// This function is for backward JMRI compatibility only
|
|
||||||
// It reports the first track only, as main, regardless of track settings.
|
|
||||||
// <c MeterName value C/V unit min max res warn>
|
|
||||||
int maxCurrent=track[0]->raw2mA(track[0]->getRawCurrentTripValue());
|
|
||||||
StringFormatter::send(stream, F("<c CurrentMAIN %d C Milli 0 %d 1 %d>\n"),
|
|
||||||
track[0]->raw2mA(track[0]->getCurrentRaw(false)), maxCurrent, maxCurrent);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// returns state of the one and only prog track
|
// returns state of the one and only prog track
|
||||||
@@ -631,7 +649,7 @@ bool TrackManager::isProg(byte t) {
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
byte TrackManager::returnMode(byte t) {
|
TRACK_MODE TrackManager::getMode(byte t) {
|
||||||
return (track[t]->getMode());
|
return (track[t]->getMode());
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -639,18 +657,3 @@ int16_t TrackManager::returnDCAddr(byte t) {
|
|||||||
return (trackDCAddr[t]);
|
return (trackDCAddr[t]);
|
||||||
}
|
}
|
||||||
|
|
||||||
const char* TrackManager::getModeName(byte Mode) {
|
|
||||||
|
|
||||||
//DIAG(F("PowerMode %d"), Mode);
|
|
||||||
|
|
||||||
switch (Mode)
|
|
||||||
{
|
|
||||||
case 1: return "NONE";
|
|
||||||
case 2: return "MAIN";
|
|
||||||
case 4: return "PROG";
|
|
||||||
case 8: return "DC";
|
|
||||||
case 16: return "DCX";
|
|
||||||
case 32: return "EXT";
|
|
||||||
default: return "----";
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@@ -73,25 +73,28 @@ class TrackManager {
|
|||||||
|
|
||||||
static const int16_t MAX_TRACKS=8;
|
static const int16_t MAX_TRACKS=8;
|
||||||
static bool setTrackMode(byte track, TRACK_MODE mode, int16_t DCaddr=0);
|
static bool setTrackMode(byte track, TRACK_MODE mode, int16_t DCaddr=0);
|
||||||
static bool parseJ(Print * stream, int16_t params, int16_t p[]);
|
static bool parseEqualSign(Print * stream, int16_t params, int16_t p[]);
|
||||||
static void loop();
|
static void loop();
|
||||||
static POWERMODE getMainPower();
|
static POWERMODE getMainPower();
|
||||||
static POWERMODE getProgPower();
|
static POWERMODE getProgPower();
|
||||||
|
static inline POWERMODE getPower(byte t) { return track[t]->getPower(); }
|
||||||
static bool getPower(byte t, char s[]);
|
static bool getPower(byte t, char s[]);
|
||||||
static void setJoin(bool join);
|
static void setJoin(bool join);
|
||||||
static bool isJoined() { return progTrackSyncMain;}
|
static bool isJoined() { return progTrackSyncMain;}
|
||||||
|
static inline bool isActive (byte tr) {
|
||||||
|
if (tr > lastTrack) return false;
|
||||||
|
return track[tr]->getMode() & (TRACK_MODE_MAIN|TRACK_MODE_PROG|TRACK_MODE_DC|TRACK_MODE_BOOST|TRACK_MODE_EXT);}
|
||||||
static void setJoinRelayPin(byte joinRelayPin);
|
static void setJoinRelayPin(byte joinRelayPin);
|
||||||
static void sampleCurrent();
|
static void sampleCurrent();
|
||||||
static void reportGauges(Print* stream);
|
static void reportGauges(Print* stream);
|
||||||
static void reportCurrent(Print* stream);
|
static void reportCurrent(Print* stream);
|
||||||
static void reportPowerChange(Print* stream, byte thistrack);
|
|
||||||
static void reportObsoleteCurrent(Print* stream);
|
static void reportObsoleteCurrent(Print* stream);
|
||||||
static void streamTrackState(Print* stream, byte t);
|
static void streamTrackState(Print* stream, byte t);
|
||||||
static bool isPowerOn(byte t);
|
static bool isPowerOn(byte t);
|
||||||
static bool isProg(byte t);
|
static bool isProg(byte t);
|
||||||
static byte returnMode(byte t);
|
static TRACK_MODE getMode(byte t);
|
||||||
static int16_t returnDCAddr(byte t);
|
static int16_t returnDCAddr(byte t);
|
||||||
static const char* getModeName(byte Mode);
|
static const FSH* getModeName(TRACK_MODE Mode);
|
||||||
|
|
||||||
static int16_t joinRelay;
|
static int16_t joinRelay;
|
||||||
static bool progTrackSyncMain; // true when prog track is a siding switched to main
|
static bool progTrackSyncMain; // true when prog track is a siding switched to main
|
||||||
@@ -108,7 +111,6 @@ class TrackManager {
|
|||||||
static void addTrack(byte t, MotorDriver* driver);
|
static void addTrack(byte t, MotorDriver* driver);
|
||||||
static byte lastTrack;
|
static byte lastTrack;
|
||||||
static byte nextCycleTrack;
|
static byte nextCycleTrack;
|
||||||
static POWERMODE mainPowerGuess;
|
|
||||||
static void applyDCSpeed(byte t);
|
static void applyDCSpeed(byte t);
|
||||||
|
|
||||||
static int16_t trackDCAddr[MAX_TRACKS]; // dc address if TRACK_MODE_DC
|
static int16_t trackDCAddr[MAX_TRACKS]; // dc address if TRACK_MODE_DC
|
||||||
|
@@ -150,7 +150,6 @@ void WiThrottle::parse(RingStream * stream, byte * cmdx) {
|
|||||||
DCCWaveform::progTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF);
|
DCCWaveform::progTrack.setPowerMode(cmd[3]=='1'?POWERMODE::ON:POWERMODE::OFF);
|
||||||
*/
|
*/
|
||||||
|
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
}
|
}
|
||||||
#if defined(EXRAIL_ACTIVE)
|
#if defined(EXRAIL_ACTIVE)
|
||||||
else if (cmd[1]=='R' && cmd[2]=='A' && cmd[3]=='2' ) { // Route activate
|
else if (cmd[1]=='R' && cmd[2]=='A' && cmd[3]=='2' ) { // Route activate
|
||||||
@@ -496,7 +495,6 @@ void WiThrottle::getLocoCallback(int16_t locoid) {
|
|||||||
TrackManager::setJoin(true); // <1 JOIN> so we can drive loco away
|
TrackManager::setJoin(true); // <1 JOIN> so we can drive loco away
|
||||||
DIAG(F("LocoCallback commit success"));
|
DIAG(F("LocoCallback commit success"));
|
||||||
stashStream->commit();
|
stashStream->commit();
|
||||||
CommandDistributor::broadcastPower();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void WiThrottle::sendIntro(Print* stream) {
|
void WiThrottle::sendIntro(Print* stream) {
|
||||||
|
@@ -163,7 +163,9 @@ bool WifiESP::setup(const char *SSid,
|
|||||||
delay(500);
|
delay(500);
|
||||||
}
|
}
|
||||||
if (WiFi.status() == WL_CONNECTED) {
|
if (WiFi.status() == WL_CONNECTED) {
|
||||||
DIAG(F("Wifi STA IP %s"),WiFi.localIP().toString().c_str());
|
// DIAG(F("Wifi STA IP %s"),WiFi.localIP().toString().c_str());
|
||||||
|
DIAG(F("Wifi in STA mode"));
|
||||||
|
LCD(7, F("IP: %s"), WiFi.softAPIP().toString().c_str());
|
||||||
wifiUp = true;
|
wifiUp = true;
|
||||||
} else {
|
} else {
|
||||||
DIAG(F("Could not connect to Wifi SSID %s"),SSid);
|
DIAG(F("Could not connect to Wifi SSID %s"),SSid);
|
||||||
@@ -209,8 +211,12 @@ bool WifiESP::setup(const char *SSid,
|
|||||||
if (WiFi.softAP(strSSID.c_str(),
|
if (WiFi.softAP(strSSID.c_str(),
|
||||||
havePassword ? password : strPass.c_str(),
|
havePassword ? password : strPass.c_str(),
|
||||||
channel, false, 8)) {
|
channel, false, 8)) {
|
||||||
DIAG(F("Wifi AP SSID %s PASS %s"),strSSID.c_str(),havePassword ? password : strPass.c_str());
|
// DIAG(F("Wifi AP SSID %s PASS %s"),strSSID.c_str(),havePassword ? password : strPass.c_str());
|
||||||
DIAG(F("Wifi AP IP %s"),WiFi.softAPIP().toString().c_str());
|
DIAG(F("Wifi in AP mode"));
|
||||||
|
LCD(5, F("Wifi: %s"), strSSID.c_str());
|
||||||
|
LCD(6, F("PASS: %s"),havePassword ? password : strPass.c_str());
|
||||||
|
// DIAG(F("Wifi AP IP %s"),WiFi.softAPIP().toString().c_str());
|
||||||
|
LCD(7, F("IP: %s"),WiFi.softAPIP().toString().c_str());
|
||||||
wifiUp = true;
|
wifiUp = true;
|
||||||
APmode = true;
|
APmode = true;
|
||||||
} else {
|
} else {
|
||||||
|
@@ -199,6 +199,18 @@ The configuration file for DCC-EX Command Station
|
|||||||
//
|
//
|
||||||
// #define DISABLE_PROG
|
// #define DISABLE_PROG
|
||||||
|
|
||||||
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
|
// DISABLE / ENABLE VDPY
|
||||||
|
//
|
||||||
|
// The Virtual display "VDPY" feature is by default enabled everywhere
|
||||||
|
// but on Uno and Nano. If you think you can fit it (for example
|
||||||
|
// having disabled some of the features above) you can enable it with
|
||||||
|
// ENABLE_VDPY. You can even disable it on all other CPUs with
|
||||||
|
// DISABLE_VDPY
|
||||||
|
//
|
||||||
|
// #define DISABLE_VDPY
|
||||||
|
// #define ENABLE_VDPY
|
||||||
|
|
||||||
/////////////////////////////////////////////////////////////////////////////////////
|
/////////////////////////////////////////////////////////////////////////////////////
|
||||||
// REDEFINE WHERE SHORT/LONG ADDR break is. According to NMRA the last short address
|
// REDEFINE WHERE SHORT/LONG ADDR break is. According to NMRA the last short address
|
||||||
// is 127 and the first long address is 128. There are manufacturers which have
|
// is 127 and the first long address is 128. There are manufacturers which have
|
||||||
|
@@ -219,11 +219,10 @@
|
|||||||
// The HAL is disabled by default on Nano and Uno platforms, because of limited flash space.
|
// The HAL is disabled by default on Nano and Uno platforms, because of limited flash space.
|
||||||
//
|
//
|
||||||
#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO)
|
#if defined(ARDUINO_AVR_NANO) || defined(ARDUINO_AVR_UNO)
|
||||||
#if defined(DISABLE_DIAG) && defined(DISABLE_EEPROM) && defined(DISABLE_PROG)
|
#define IO_NO_HAL // HAL too big whatever you disable otherwise
|
||||||
#warning you have sacrificed DIAG for HAL
|
#ifndef ENABLE_VDPY
|
||||||
#else
|
#define DISABLE_VDPY
|
||||||
#define IO_NO_HAL
|
#endif
|
||||||
#endif
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
#if __has_include ( "myAutomation.h")
|
#if __has_include ( "myAutomation.h")
|
||||||
|
22
version.h
22
version.h
@@ -3,7 +3,27 @@
|
|||||||
|
|
||||||
#include "StringFormatter.h"
|
#include "StringFormatter.h"
|
||||||
|
|
||||||
#define VERSION "5.2.5"
|
#define VERSION "5.2.16"
|
||||||
|
// 5.2.16 - Bugfix to allow for devices using the EX-IOExpander protocol to have no analogue or no digital pins
|
||||||
|
// 5.2.15 - move call to CommandDistributor::broadcastPower() into the TrackManager::setTrackPower(*) functions
|
||||||
|
// - add repeats to function packets that are not reminded in accordance with accessory packets
|
||||||
|
// 5.2.14 - Reminder window DCC packet optimization
|
||||||
|
// - Optional #define DISABLE_FUNCTION_REMINDERS
|
||||||
|
// 5.2.13 - EXRAIL STEALTH
|
||||||
|
// 5.2.12 - ESP32 add AP mode LCD messages with SSID/PW for
|
||||||
|
// - STM32 change to UID_BASE constants in DCCTimerSTM32 rather than raw hex addresses for UID registers
|
||||||
|
// - STM32 extra UART/USARTs for larger Nucleo models
|
||||||
|
// 5.2.11 - Change from TrackManager::returnMode to TrackManager::getMode
|
||||||
|
// 5.2.10 - Include trainbrains.eu block unoccupancy driver
|
||||||
|
// - include IO_PCA9555
|
||||||
|
// 5.2.9 - Bugfix LCD startup with no LCD, uses <@
|
||||||
|
// 5.2.9 - EXRAIL STASH feature
|
||||||
|
// 5.2.8 - Bugfix: Do not turn off all tracks on change
|
||||||
|
// give better power messages
|
||||||
|
// 5.2.7 - Bugfix: EXRAIL ling segment
|
||||||
|
// - Bugfix: Back out wrongly added const
|
||||||
|
// - Bugfix ESP32: Do not inverse DCX direction signal twice
|
||||||
|
// 5.2.6 - Trackmanager broadcast power state on track mode change
|
||||||
// 5.2.5 - Trackmanager: Do not treat TRACK_MODE_ALL as TRACK_MODE_DC
|
// 5.2.5 - Trackmanager: Do not treat TRACK_MODE_ALL as TRACK_MODE_DC
|
||||||
// 5.2.4 - LCD macro will not do diag if that duplicates @ to same target.
|
// 5.2.4 - LCD macro will not do diag if that duplicates @ to same target.
|
||||||
// - Added ROUTE_DISABLED macro in EXRAIL
|
// - Added ROUTE_DISABLED macro in EXRAIL
|
||||||
|
Reference in New Issue
Block a user