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

Compare commits

...

9 Commits

Author SHA1 Message Date
Harald Barth
dad84aed6f version update 2024-03-24 16:45:06 +01:00
Harald Barth
66d59c6a8a Allow no shield 2024-03-24 16:41:47 +01:00
Harald Barth
814d1045ba Z21: report the (unchanged) loco mode back after every attempted change 2024-03-24 16:34:26 +01:00
Harald Barth
bc8ab26b30 Add turnout functionality to Z21 2024-03-24 15:51:40 +01:00
Harald Barth
54e1bdb878 Fix index of DB[] to comply with Roco documentation 2024-03-24 12:18:28 +01:00
Harald Barth
e6dde5a11c add READ_REGISTER name to Z21 parser 2024-03-24 09:33:44 +01:00
Harald Barth
16362c14cc Functions for DC frequency: Use func up to F31 part 2 2024-03-17 06:11:58 +01:00
Harald Barth
900aa254c6 Support up to 31 functions and clean up genration of the LAN_X_HEADER_LOCO_INFO message 2024-03-11 15:46:12 +01:00
Harald Barth
d303831773 Functions for DC frequency: Use func up to F31 2024-03-11 15:09:37 +01:00
11 changed files with 170 additions and 113 deletions

View File

@ -209,9 +209,7 @@ int16_t CommandDistributor::retClockTime() {
void CommandDistributor::broadcastLoco(byte slot) { void CommandDistributor::broadcastLoco(byte slot) {
DCC::LOCO * sp=&DCC::speedTable[slot]; DCC::LOCO * sp=&DCC::speedTable[slot];
uint32_t func = sp->functions; broadcastReply(COMMAND_TYPE, F("<l %d %d %d %l>\n"), sp->loco,slot,sp->speedCode,sp->functions);
func = func & 0x1fffffff; // mask out bits 0-28
broadcastReply(COMMAND_TYPE, F("<l %d %d %d %l>\n"), sp->loco,slot,sp->speedCode,func);
#ifdef SABERTOOTH #ifdef SABERTOOTH
if (Serial2 && sp->loco == SABERTOOTH) { if (Serial2 && sp->loco == SABERTOOTH) {
static uint8_t rampingmode = 0; static uint8_t rampingmode = 0;

View File

@ -219,7 +219,8 @@ bool DCC::setFn( int cab, int16_t functionNumber, bool on) {
} else { } else {
speedTable[reg].functions &= ~funcmask; speedTable[reg].functions &= ~funcmask;
} }
if (speedTable[reg].functions != previous && functionNumber <= 28) { if (speedTable[reg].functions != previous) {
if (functionNumber <= 28)
updateGroupflags(speedTable[reg].groupFlags, functionNumber); updateGroupflags(speedTable[reg].groupFlags, functionNumber);
CommandDistributor::broadcastLoco(reg); CommandDistributor::broadcastLoco(reg);
} }
@ -235,14 +236,14 @@ void DCC::changeFn( int cab, int16_t functionNumber) {
speedTable[reg].functions ^= funcmask; speedTable[reg].functions ^= funcmask;
if (functionNumber <= 28) { if (functionNumber <= 28) {
updateGroupflags(speedTable[reg].groupFlags, functionNumber); updateGroupflags(speedTable[reg].groupFlags, functionNumber);
CommandDistributor::broadcastLoco(reg);
} }
CommandDistributor::broadcastLoco(reg);
} }
// Report function state (used from withrottle protocol) // Report function state (used from withrottle protocol)
// returns 0 false, 1 true or -1 for do not know // returns 0 false, 1 true or -1 for do not know
int8_t DCC::getFn( int cab, int16_t functionNumber) { int8_t DCC::getFn( int cab, int16_t functionNumber) {
if (cab<=0 || functionNumber>28) if (cab<=0 || functionNumber>31)
return -1; // unknown return -1; // unknown
int reg = lookupSpeedTable(cab); int reg = lookupSpeedTable(cab);
if (reg<0) if (reg<0)

View File

@ -283,25 +283,22 @@ void DCCEXParser::parseOne(Print *stream, byte *com, RingStream * ringStream)
return; // filterCallback asked us to ignore return; // filterCallback asked us to ignore
case 't': // THROTTLE <t [REGISTER] CAB SPEED DIRECTION> case 't': // THROTTLE <t [REGISTER] CAB SPEED DIRECTION>
{ {
if (params==1) { // <t cab> display state int16_t cab;
int16_t tspeed;
int16_t direction;
if (params==1) { // <t cab> display state
int16_t slot=DCC::lookupSpeedTable(p[0],false); int16_t slot=DCC::lookupSpeedTable(p[0],false);
if (slot>=0) { if (slot>=0)
DCC::LOCO * sp=&DCC::speedTable[slot]; CommandDistributor::broadcastLoco(slot);
StringFormatter::send(stream,F("<l %d %d %d %l>\n"),
sp->loco,slot,sp->speedCode,sp->functions);
}
else // send dummy state speed 0 fwd no functions. else // send dummy state speed 0 fwd no functions.
StringFormatter::send(stream,F("<l %d -1 128 0>\n"),p[0]); StringFormatter::send(stream,F("<l %d -1 128 0>\n"),p[0]);
return; return;
} }
int16_t cab;
int16_t tspeed;
int16_t direction;
if (params == 4) if (params == 4)
{ // <t REGISTER CAB SPEED DIRECTION> { // <t REGISTER CAB SPEED DIRECTION>
// ignore register p[0]
cab = p[1]; cab = p[1];
tspeed = p[2]; tspeed = p[2];
direction = p[3]; direction = p[3];

View File

@ -1 +1 @@
#define GITHUB_SHA "devel-z21-202403110817Z" #define GITHUB_SHA "devel-z21-202403241544Z"

View File

@ -1,7 +1,7 @@
/* /*
* © 2022-2023 Paul M. Antoine * © 2022-2023 Paul M. Antoine
* © 2021 Fred Decker * © 2021 Fred Decker
* © 2020-2023 Harald Barth * © 2020-2024 Harald Barth
* (c) 2020 Chris Harlow. All rights reserved. * (c) 2020 Chris Harlow. All rights reserved.
* (c) 2021 Fred Decker. All rights reserved. * (c) 2021 Fred Decker. All rights reserved.
* (c) 2020 Harald Barth. All rights reserved. * (c) 2020 Harald Barth. All rights reserved.
@ -57,6 +57,10 @@
// of the brake pin on the motor bridge is inverted // of the brake pin on the motor bridge is inverted
// (HIGH == release brake) // (HIGH == release brake)
// You can have a CS wihout any possibility to do any track signal.
// That's strange but possible.
#define NO_SHIELD F("No shield at all")
// Arduino STANDARD Motor Shield, used on different architectures: // Arduino STANDARD Motor Shield, used on different architectures:
#if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32) #if defined(ARDUINO_ARCH_SAMD) || defined(ARDUINO_ARCH_STM32)

View File

@ -1,6 +1,6 @@
/* /*
* © 2022 Chris Harlow * © 2022 Chris Harlow
* © 2022,2023 Harald Barth * © 2022-2024 Harald Barth
* © 2023 Colin Murdoch * © 2023 Colin Murdoch
* All rights reserved. * All rights reserved.
* *
@ -41,7 +41,7 @@
MotorDriver * TrackManager::track[MAX_TRACKS]; MotorDriver * TrackManager::track[MAX_TRACKS];
int16_t TrackManager::trackDCAddr[MAX_TRACKS]; int16_t TrackManager::trackDCAddr[MAX_TRACKS];
byte TrackManager::lastTrack=0; int8_t TrackManager::lastTrack=-1;
bool TrackManager::progTrackSyncMain=false; bool TrackManager::progTrackSyncMain=false;
bool TrackManager::progTrackBoosted=false; bool TrackManager::progTrackBoosted=false;
int16_t TrackManager::joinRelay=UNUSED_PIN; int16_t TrackManager::joinRelay=UNUSED_PIN;
@ -499,6 +499,10 @@ void TrackManager::setTrackPower(TRACK_MODE trackmodeToMatch, POWERMODE powermod
// 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];
if (driver == NULL) { // track is not defined at all
DIAG(F("Error: Track %c does not exist"), t+'A');
return;
}
TRACK_MODE trackmode = driver->getMode(); TRACK_MODE trackmode = driver->getMode();
POWERMODE oldpower = driver->getPower(); POWERMODE oldpower = driver->getPower();
if (trackmode & TRACK_MODE_NONE) { if (trackmode & TRACK_MODE_NONE) {

View File

@ -1,6 +1,6 @@
/* /*
* © 2022 Chris Harlow * © 2022 Chris Harlow
* © 2022 Harald Barth * © 2022-2024 Harald Barth
* © 2023 Colin Murdoch * © 2023 Colin Murdoch
* *
* All rights reserved. * All rights reserved.
@ -46,7 +46,7 @@ const byte TRACK_POWER_1=1, TRACK_POWER_ON=1;
class TrackManager { class TrackManager {
public: public:
static void Setup(const FSH * shieldName, static void Setup(const FSH * shieldName,
MotorDriver * track0, MotorDriver * track0=NULL,
MotorDriver * track1=NULL, MotorDriver * track1=NULL,
MotorDriver * track2=NULL, MotorDriver * track2=NULL,
MotorDriver * track3=NULL, MotorDriver * track3=NULL,
@ -108,7 +108,7 @@ class TrackManager {
private: private:
static void addTrack(byte t, MotorDriver* driver); static void addTrack(byte t, MotorDriver* driver);
static byte lastTrack; static int8_t lastTrack;
static byte nextCycleTrack; static byte nextCycleTrack;
static void applyDCSpeed(byte t); static void applyDCSpeed(byte t);

View File

@ -571,7 +571,7 @@ void WiThrottle::sendRoutes(Print* stream) {
void WiThrottle::sendFunctions(Print* stream, byte loco) { void WiThrottle::sendFunctions(Print* stream, byte loco) {
int16_t locoid=myLocos[loco].cab; int16_t locoid=myLocos[loco].cab;
int fkeys=29; int fkeys=32; // upper limit (send functions 0 to 31)
myLocos[loco].functionToggles=1<<2; // F2 (HORN) is a non-toggle myLocos[loco].functionToggles=1<<2; // F2 (HORN) is a non-toggle
#ifdef EXRAIL_ACTIVE #ifdef EXRAIL_ACTIVE

View File

@ -1,6 +1,6 @@
/* /*
* © 2023 Thierry Paris / Locoduino * © 2023 Thierry Paris / Locoduino
* © 2023 Harald Barth * © 2023,2024 Harald Barth
* All rights reserved. * All rights reserved.
* *
* This file is part of CommandStation-EX * This file is part of CommandStation-EX
@ -385,50 +385,44 @@ void Z21Throttle::notifyLocoInfo(byte inMSB, byte inLSB) {
Z21Throttle::replyBuffer[3] = DCC::getThrottleSpeed(locoAddress); // RVVVVVVV R = forward VVVVVVV = speed Z21Throttle::replyBuffer[3] = DCC::getThrottleSpeed(locoAddress); // RVVVVVVV R = forward VVVVVVV = speed
if (DCC::getThrottleDirection(locoAddress)) bitSet(Z21Throttle::replyBuffer[3], 7); if (DCC::getThrottleDirection(locoAddress)) bitSet(Z21Throttle::replyBuffer[3], 7);
Z21Throttle::replyBuffer[4] = B00000000; // 0DSLFGHJ D = double traction S = Smartsearch L = F0 F = F4 G = F3 H = F2 J = F1 uint32_t functionMap = DCC::getFunctionMap(locoAddress);
if (DCC::getFn(locoAddress, 0)) bitSet(Z21Throttle::replyBuffer[4], 4);
if (DCC::getFn(locoAddress, 1)) bitSet(Z21Throttle::replyBuffer[4], 0);
if (DCC::getFn(locoAddress, 2)) bitSet(Z21Throttle::replyBuffer[4], 1);
if (DCC::getFn(locoAddress, 3)) bitSet(Z21Throttle::replyBuffer[4], 2);
if (DCC::getFn(locoAddress, 4)) bitSet(Z21Throttle::replyBuffer[4], 3);
Z21Throttle::replyBuffer[5] = B00000000; // function F5 to F12 F5 is bit0 // Byte 4: 0DSLFGHJ
if (DCC::getFn(locoAddress, 5)) bitSet(Z21Throttle::replyBuffer[5], 0); // D = double traction S = Smartsearch L = F0 F = F4 G = F3 H = F2 J = F1
if (DCC::getFn(locoAddress, 6)) bitSet(Z21Throttle::replyBuffer[5], 1); Z21Throttle::replyBuffer[4] = (functionMap >> 1) & 0xF; // function F1 to F5
if (DCC::getFn(locoAddress, 7)) bitSet(Z21Throttle::replyBuffer[5], 2); if (functionMap & 1) // set F0 (Light)
if (DCC::getFn(locoAddress, 8)) bitSet(Z21Throttle::replyBuffer[5], 3); Z21Throttle::replyBuffer[4] += 16;
if (DCC::getFn(locoAddress, 9)) bitSet(Z21Throttle::replyBuffer[5], 4); functionMap >>=5; // shift out the 5 bits which are not needed any more
if (DCC::getFn(locoAddress, 10)) bitSet(Z21Throttle::replyBuffer[5],5);
if (DCC::getFn(locoAddress, 11)) bitSet(Z21Throttle::replyBuffer[5],6);
if (DCC::getFn(locoAddress, 12)) bitSet(Z21Throttle::replyBuffer[5],7);
Z21Throttle::replyBuffer[6] = B00000000; // function F13 to F20 F13 is bit0 Z21Throttle::replyBuffer[5] = functionMap & 0xFF; // function F5 to F12; F5 is bit0
if (DCC::getFn(locoAddress, 13)) bitSet(Z21Throttle::replyBuffer[6], 0); functionMap >>=8; // shift out 8 more
if (DCC::getFn(locoAddress, 14)) bitSet(Z21Throttle::replyBuffer[6], 1); Z21Throttle::replyBuffer[6] = functionMap & 0xFF; // function F13 to F20; F13 is bit0
if (DCC::getFn(locoAddress, 15)) bitSet(Z21Throttle::replyBuffer[6], 2); functionMap >>=8; // shift out 8 more
if (DCC::getFn(locoAddress, 16)) bitSet(Z21Throttle::replyBuffer[6], 3); Z21Throttle::replyBuffer[7] = functionMap & 0xFF; // function F21 to F28; F21 is bit0
if (DCC::getFn(locoAddress, 17)) bitSet(Z21Throttle::replyBuffer[6], 4); functionMap >>=8; // shift out 8 more
if (DCC::getFn(locoAddress, 18)) bitSet(Z21Throttle::replyBuffer[6], 5); Z21Throttle::replyBuffer[8] = functionMap & 0xFF; // function F29 to F31; F28 is bit0
if (DCC::getFn(locoAddress, 19)) bitSet(Z21Throttle::replyBuffer[6], 6);
if (DCC::getFn(locoAddress, 20)) bitSet(Z21Throttle::replyBuffer[6], 7);
Z21Throttle::replyBuffer[7] = B00000000; // function F21 to F28 F21 is bit0 notify(HEADER_LAN_XPRESS_NET, LAN_X_HEADER_LOCO_INFO, Z21Throttle::replyBuffer, 9, false);
if (DCC::getFn(locoAddress, 21)) bitSet(Z21Throttle::replyBuffer[7], 0);
if (DCC::getFn(locoAddress, 22)) bitSet(Z21Throttle::replyBuffer[7], 1);
if (DCC::getFn(locoAddress, 23)) bitSet(Z21Throttle::replyBuffer[7], 2);
if (DCC::getFn(locoAddress, 24)) bitSet(Z21Throttle::replyBuffer[7], 3);
if (DCC::getFn(locoAddress, 25)) bitSet(Z21Throttle::replyBuffer[7], 4);
if (DCC::getFn(locoAddress, 26)) bitSet(Z21Throttle::replyBuffer[7], 5);
if (DCC::getFn(locoAddress, 27)) bitSet(Z21Throttle::replyBuffer[7], 6);
if (DCC::getFn(locoAddress, 28)) bitSet(Z21Throttle::replyBuffer[7], 7);
notify(HEADER_LAN_XPRESS_NET, LAN_X_HEADER_LOCO_INFO, Z21Throttle::replyBuffer, 8, false);
} }
void Z21Throttle::notifyTurnoutInfo(byte inMSB, byte inLSB) { void Z21Throttle::notifyTurnoutInfo(byte inMSB, byte inLSB) {
Z21Throttle::replyBuffer[0] = inMSB; // turnout address msb Z21Throttle::replyBuffer[0] = inMSB; // turnout address msb
Z21Throttle::replyBuffer[1] = inLSB; // turnout address lsb Z21Throttle::replyBuffer[1] = inLSB; // turnout address lsb
Z21Throttle::replyBuffer[2] = B00000000; // 000000ZZ ZZ : 00 not switched 01 pos1 10 pos2 11 invalid Z21Throttle::replyBuffer[2] = B00000011; // 000000ZZ ZZ : 00 not switched 01 pos1 10 pos2 11 invalid
char c = '?';
uint16_t addr = (inMSB << 8) + inLSB + 1;
Turnout *tt = Turnout::get(addr);
if (tt) { // if the tt does not exist we fall through with replyBuffer set to invalid
if (tt->isClosed()) {
Z21Throttle::replyBuffer[2] = B00000010;
c = 'c';
} else {
Z21Throttle::replyBuffer[2] = B00000001;
c = 't';
}
}
if (Diag::Z21THROTTLE)
DIAG(F("Z21 Throttle %d : Turnoutinfo %d %c"), clientid, addr, c);
notify(HEADER_LAN_XPRESS_NET, LAN_X_HEADER_TURNOUT_INFO, Z21Throttle::replyBuffer, 3, false); notify(HEADER_LAN_XPRESS_NET, LAN_X_HEADER_TURNOUT_INFO, Z21Throttle::replyBuffer, 3, false);
} }
@ -488,6 +482,30 @@ void Z21Throttle::setSpeed(byte inNbSteps, byte inDB1, byte inDB2, byte inDB3) {
notifyLocoInfo(inDB1, inDB2); notifyLocoInfo(inDB1, inDB2);
} }
void Z21Throttle::setTurnout(byte addrMSB, byte addrLSB, byte command) {
// 1000A00P
// A=0 ... Deactivate turnout output
// A=1 ... Activate turnout output
// P=0 ... Select output 1 of the turnout
// P=1 ... Select output 2 of the turnout
// Q=0 ... Execute command immediately
// means that the client (app/mouse) does send activate and deactive
// Q=1 ... From Z21 FW V1.24: Insert turnout command into the queue of Z21
bool queue = (command & B00100000) != 0;
bool activate = (command & B00001000) != 0;
byte output = command & B00000001;
uint16_t addr = (addrMSB << 8) + addrLSB + 1;
if (Diag::Z21THROTTLE) DIAG(F("Z21 Throttle %d : turnout %d cmd 0x%x"), clientid, addr, command);
(void)queue; // We probably do not need to care (as we are a CS that maps to internal
// turnouts and not to accessory commands) about the difference.
if (activate) {
Turnout::setClosed(addr, output == 1);
} // else ignore the deactivate message
notifyTurnoutInfo(addrMSB, addrLSB); // sent for both activate and deactivate
}
// //
// TODO Pass through a text message to avoid multi thread locks... // TODO Pass through a text message to avoid multi thread locks...
// //
@ -623,7 +641,13 @@ void diagPacket(byte *networkPacket, int len) {
bool Z21Throttle::parse(byte *networkPacket, int len) { bool Z21Throttle::parse(byte *networkPacket, int len) {
bool done = false; bool done = false;
// same names as in Z21 LAN Protocol Specification
byte *DB; byte *DB;
byte *Data;
byte Xheader;
int Header;
byte *p = networkPacket; byte *p = networkPacket;
int l = len; int l = len;
@ -636,12 +660,13 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
} }
if (l < 0) { if (l < 0) {
DIAG(F("ERROR: Xbus data exceeds UDP packet size: l < 0 pos=%d, l=%d"), p-networkPacket, l); DIAG(F("ERROR: Xbus data exceeds UDP packet size: l < 0 pos=%d, l=%d"), p-networkPacket, l);
diagPacket(networkPacket, len); diagPacket(p, len);
return false; return false;
} }
if (l > 0 && lengthData < 4) { if (l > 0 && lengthData < 4) {
DIAG(F("WARNING: Xbus data does not fill UDP packet size: l > 0 pos=%d, l=%d"), p-networkPacket, l); DIAG(F("WARNING: Xbus data does not fill UDP packet size: l > 0 pos=%d, l=%d, lengthData=%d"),
diagPacket(networkPacket, len); p-networkPacket, l, lengthData);
diagPacket(p, len);
return true; return true;
} }
// length of the data = total length - length of length (!) - length of header // length of the data = total length - length of length (!) - length of header
@ -652,36 +677,36 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
return false; return false;
} }
p += 2; p += 2;
int header = GETINT16(p); Header = GETINT16(p);
byte Xheader = 0;
p += 2; p += 2;
DB = p; // now p is at start of Data, networkPacket + 4
byte DB0 = 0; Data = p;
Xheader = Data[0];
DB = Data + 1;
int nbLocos = CountLocos(); int nbLocos = CountLocos();
// set p for next round // set p for next round
p += lengthData; p += lengthData;
if (Diag::Z21THROTTLEDATA && DB[1] != LAN_X_DB0_GET_STATUS) if (l > 0 && Diag::Z21THROTTLEDATA) DIAG(F("next packet follows at pos=%d"), p-networkPacket);
DIAG(F("%d <- lengthData:%d header:0x%02x : 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"), if (Diag::Z21THROTTLEDATA &&
this->clientid, lengthData, header, !((DB[0] == LAN_X_DB0_GET_STATUS) && (Xheader == LAN_X_HEADER_GENERAL)))
(lengthData > 0)?DB[0]:0, DIAG(F("%d <- lengthData:%d Header:0x%02x : 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x 0x%02x"),
(lengthData > 1)?DB[1]:0, this->clientid, lengthData, Header,
(lengthData > 2)?DB[2]:0, (lengthData > 0)?Data[0]:0,
(lengthData > 3)?DB[3]:0, (lengthData > 1)?Data[1]:0,
(lengthData > 4)?DB[4]:0, (lengthData > 2)?Data[2]:0,
(lengthData > 5)?DB[5]:0, (lengthData > 3)?Data[3]:0,
(lengthData > 6)?DB[6]:0, (lengthData > 4)?Data[4]:0,
(lengthData > 7)?DB[7]:0, (lengthData > 5)?Data[5]:0,
(lengthData > 8)?DB[8]:0, (lengthData > 6)?Data[6]:0,
(lengthData > 9)?DB[9]:0); (lengthData > 7)?Data[7]:0,
if (l > 0 && Diag::Z21THROTTLEDATA) DIAG(F("next packet follows")); (lengthData > 8)?Data[8]:0,
(lengthData > 9)?Data[9]:0);
switch (header) { switch (Header) {
case HEADER_LAN_XPRESS_NET: case HEADER_LAN_XPRESS_NET:
Xheader = DB[0];
switch (Xheader) { switch (Xheader) {
case LAN_X_HEADER_GENERAL: case LAN_X_HEADER_GENERAL:
DB0 = DB[1]; switch (DB[0]) {
switch (DB0) {
case LAN_X_DB0_GET_VERSION: case LAN_X_DB0_GET_VERSION:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d GET_VERSION"), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d GET_VERSION"), this->clientid);
break; break;
@ -719,29 +744,28 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
done = true; done = true;
break; break;
case LAN_X_HEADER_SET_LOCO: case LAN_X_HEADER_SET_LOCO:
DB0 = DB[1]; switch (DB[0]) {
switch (DB0) {
case LAN_X_DB0_LOCO_DCC14: case LAN_X_DB0_LOCO_DCC14:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO DCC 14 SPEED"), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO DCC 14 SPEED"), this->clientid);
setSpeed(14, DB[2], DB[3], DB[4]); setSpeed(14, DB[1], DB[2], DB[3]);
done = true; done = true;
break; break;
case LAN_X_DB0_LOCO_DCC28: case LAN_X_DB0_LOCO_DCC28:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO DCC 28 SPEED"), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO DCC 28 SPEED"), this->clientid);
setSpeed(28, DB[2], DB[3], DB[4]); setSpeed(28, DB[1], DB[2], DB[3]);
done = true; done = true;
break; break;
case LAN_X_DB0_LOCO_DCC128: case LAN_X_DB0_LOCO_DCC128:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO DCC 128 SPEED"), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO DCC 128 SPEED"), this->clientid);
setSpeed(128, DB[2], DB[3], DB[4]); setSpeed(128, DB[1], DB[2], DB[3]);
done = true; done = true;
break; break;
case LAN_X_DB0_SET_LOCO_FUNCTION: case LAN_X_DB0_SET_LOCO_FUNCTION:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO DCC FUNCTION"), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO DCC FUNCTION"), this->clientid);
setFunction(DB[2], DB[3], DB[4]); setFunction(DB[1], DB[2], DB[3]);
if (Diag::Z21THROTTLE) { if (Diag::Z21THROTTLE) {
// Debug capacity to print data... // Debug capacity to print data...
byte function = DB[4]; byte function = DB[3];
bitClear(function, 6); bitClear(function, 6);
bitClear(function, 7); bitClear(function, 7);
if (function == 12) { // why not ? if (function == 12) { // why not ?
@ -754,14 +778,15 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
} }
break; break;
case LAN_X_HEADER_GET_LOCO_INFO: case LAN_X_HEADER_GET_LOCO_INFO:
// XXX Should we switch(DB[0]) here?
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO INFO: "), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d LOCO INFO: "), this->clientid);
notifyLocoInfo(DB[2], DB[3]); notifyLocoInfo(DB[1], DB[2]);
done = true; done = true;
break; break;
case LAN_X_HEADER_GET_TURNOUT_INFO: case LAN_X_HEADER_GET_TURNOUT_INFO:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d TURNOUT INFO "), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d TURNOUT INFO "), this->clientid);
notifyTurnoutInfo(DB[1], DB[2]); notifyTurnoutInfo(DB[0], DB[1]);
done = true; done = true;
break; break;
@ -774,7 +799,7 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
if (TrackManager::getProgDriver() != NULL) { if (TrackManager::getProgDriver() != NULL) {
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d CV READ PROG "), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d CV READ PROG "), this->clientid);
// DB0 should be 0x11 // DB0 should be 0x11
cvReadProg(DB[2], DB[3]); cvReadProg(DB[1], DB[2]);
} }
else { else {
// //
@ -784,14 +809,14 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
// If no prog track, read on the main track ! // If no prog track, read on the main track !
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d CV READ MAIN "), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d CV READ MAIN "), this->clientid);
// DB0 should be 0x11 // DB0 should be 0x11
cvReadMain(DB[2], DB[3]); cvReadMain(DB[1], DB[2]);
} }
done = true; done = true;
break; break;
case LAN_X_HEADER_CV_POM: case LAN_X_HEADER_CV_POM:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d CV READ POM"), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d CV READ POM"), this->clientid);
// DB0 should be 0x11 // DB0 should be 0x11
cvReadPom(DB[2], DB[3], DB[4], DB[5]); cvReadPom(DB[1], DB[2], DB[3], DB[4]);
done = true; done = true;
break; break;
case LAN_X_HEADER_CV_WRITE: case LAN_X_HEADER_CV_WRITE:
@ -800,13 +825,18 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
done = true; done = true;
break; break;
case LAN_X_HEADER_SET_TURNOUT: case LAN_X_HEADER_SET_TURNOUT:
case 0x22: // XXX sent when operating a turnout
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d SET TURNOUT "), this->clientid);
setTurnout(DB[0], DB[1], DB[2]);
done = true;
break;
case LAN_X_HEADER_READ_REGISTER:
break; break;
} }
break; break;
case HEADER_LAN_SET_BROADCASTFLAGS: case HEADER_LAN_SET_BROADCASTFLAGS:
this->broadcastFlags = int32_t(DB[3] << 24 | DB[2] << 16 | DB[1] << 8 | DB[0]); this->broadcastFlags = int32_t(Data[3] << 24 | Data[2] << 16 | Data[1] << 8 | Data[0]);
if (Diag::Z21THROTTLEDATA) DIAG(F("BROADCAST FLAGS %d : %s %s %s %s %s %s %s %s %s %s %s"), this->clientid, if (Diag::Z21THROTTLEDATA) DIAG(F("BROADCAST FLAGS %d : %s %s %s %s %s %s %s %s %s %s %s"), this->clientid,
(this->broadcastFlags & BROADCAST_BASE) ? "BASE " : "" , (this->broadcastFlags & BROADCAST_BASE) ? "BASE " : "" ,
(this->broadcastFlags & BROADCAST_RBUS) ? "RBUS " : "" , (this->broadcastFlags & BROADCAST_RBUS) ? "RBUS " : "" ,
@ -822,14 +852,26 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
done = true; done = true;
break; break;
case HEADER_LAN_GET_LOCOMODE: case HEADER_LAN_GET_LOCOMODE:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d GET LOCOMODE"), this->clientid); {
notifyLocoMode(DB[0], DB[1]); // big endian here, but resend the same as received, so no problem. if (Diag::Z21THROTTLEVERBOSE) {
uint16_t addr = (Data[0] << 8) + Data[1];
DIAG(F("%d GET LOCOMODE %d"), this->clientid, addr);
}
notifyLocoMode(Data[0], Data[1]); // big endian here, but resend the same as received, so no problem.
done = true; done = true;
}
break; break;
case HEADER_LAN_SET_LOCOMODE: case HEADER_LAN_SET_LOCOMODE:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d SET LOCOMODE"), this->clientid); {
// as we currently can not change loco mode, nothing to do
if (Diag::Z21THROTTLEVERBOSE) {
uint16_t addr = (Data[0] << 8) + Data[1];
DIAG(F("%d SET LOCOMODE %d"), this->clientid, addr);
}
notifyLocoMode(Data[0], Data[1]); // big endian here, but resend the same as received, so no problem.
done = true; done = true;
}
break; break;
case HEADER_LAN_GET_HWINFO: case HEADER_LAN_GET_HWINFO:
if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d GET HWINFO"), this->clientid); if (Diag::Z21THROTTLEVERBOSE) DIAG(F("%d GET HWINFO"), this->clientid);
@ -847,6 +889,7 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
done = true; done = true;
break; break;
case HEADER_LAN_GET_SERIAL_NUMBER: case HEADER_LAN_GET_SERIAL_NUMBER:
// XXX this has been seen, return dummy number
case HEADER_LAN_GET_BROADCASTFLAGS: case HEADER_LAN_GET_BROADCASTFLAGS:
case HEADER_LAN_GET_TURNOUTMODE: case HEADER_LAN_GET_TURNOUTMODE:
case HEADER_LAN_SET_TURNOUTMODE: case HEADER_LAN_SET_TURNOUTMODE:
@ -861,7 +904,7 @@ bool Z21Throttle::parse(byte *networkPacket, int len) {
} }
if (!done) { if (!done) {
if (Diag::Z21THROTTLE) DIAG(F("Z21 Throttle %d : not treated : header:%x Xheader:%x DB0:%x"), this->clientid, header, Xheader, DB0); if (Diag::Z21THROTTLE) DIAG(F("Z21 Throttle %d : not treated : Header:%x Xheader:%x DB0:%x"), this->clientid, Header, Xheader, DB[0]);
} else { } else {
int newNbLocos = CountLocos(); int newNbLocos = CountLocos();
if (nbLocos != newNbLocos) if (nbLocos != newNbLocos)

View File

@ -145,6 +145,7 @@ class Z21Throttle {
void setSpeed(byte inNbSteps, byte inDB1, byte inDB2, byte inDB3); void setSpeed(byte inNbSteps, byte inDB1, byte inDB2, byte inDB3);
void setFunction(byte inDB1, byte inDB2, byte inDB3); void setFunction(byte inDB1, byte inDB2, byte inDB3);
void setTurnout(byte addrMSB, byte addrLSB, byte command);
void cvReadProg(byte inDB1, byte inDB2); void cvReadProg(byte inDB1, byte inDB2);
void cvWriteProg(byte inDB1, byte inDB2, byte inDB3); void cvWriteProg(byte inDB1, byte inDB2, byte inDB3);
void cvReadMain(byte inDB1, byte inDB2); void cvReadMain(byte inDB1, byte inDB2);
@ -182,6 +183,7 @@ class Z21Throttle {
#define LAN_GET_CONFIG 0x12 #define LAN_GET_CONFIG 0x12
#define LAN_X_HEADER_GENERAL 0x21 #define LAN_X_HEADER_GENERAL 0x21
#define LAN_X_HEADER_READ_REGISTER 0x22
#define LAN_X_HEADER_SET_STOP 0x80 #define LAN_X_HEADER_SET_STOP 0x80
#define LAN_X_HEADER_GET_FIRMWARE_VERSION 0xF1 //0x141 0x21 0x21 0x00 #define LAN_X_HEADER_GET_FIRMWARE_VERSION 0xF1 //0x141 0x21 0x21 0x00
#define LAN_X_HEADER_GET_LOCO_INFO 0xE3 #define LAN_X_HEADER_GET_LOCO_INFO 0xE3
@ -192,6 +194,7 @@ class Z21Throttle {
#define LAN_X_HEADER_CV_WRITE 0x24 #define LAN_X_HEADER_CV_WRITE 0x24
#define LAN_X_HEADER_CV_POM 0xE6 #define LAN_X_HEADER_CV_POM 0xE6
#define LAN_X_DB0_READ_REGISTER 0x11
#define LAN_X_DB0_GET_VERSION 0x21 #define LAN_X_DB0_GET_VERSION 0x21
#define LAN_X_DB0_GET_STATUS 0x24 #define LAN_X_DB0_GET_STATUS 0x24
#define LAN_X_DB0_SET_TRACK_POWER_OFF 0x80 #define LAN_X_DB0_SET_TRACK_POWER_OFF 0x80

View File

@ -3,7 +3,14 @@
#include "StringFormatter.h" #include "StringFormatter.h"
#define VERSION "5.2.38" #define VERSION "5.X.Y"
// Z21: report the (unchanged) loco mode back after every attempted change
// Add turnout functionality to Z21
// Fix index of DB[] to comply with Roco documentation
// add READ_REGISTER name to Z21 parser
//
// 5.2.40 - Allow no shield
// 5.2.39 - Functions for DC frequency: Use func up to F31
// 5.2.38 - Exrail MESSAGE("text") to send a user message to all // 5.2.38 - Exrail MESSAGE("text") to send a user message to all
// connected throttles (uses <m "text"> and withrottle Hmtext. // connected throttles (uses <m "text"> and withrottle Hmtext.
// 5.2.37 - Bugfix ESP32: Use BOOSTER_INPUT define // 5.2.37 - Bugfix ESP32: Use BOOSTER_INPUT define