1303 Zeilen
57 KiB
C
1303 Zeilen
57 KiB
C
//--------------------------------------------------------------
|
|
/*
|
|
|
|
LocoNet Master Interface with optional Slot Server
|
|
|
|
Functions:
|
|
- drive with and without slot Server
|
|
- control turnout switch
|
|
- Dispatch (put via Z21)
|
|
- fix direction problem in client mode
|
|
- add decoding data from Z21 Protokoll and insert into LocoNet
|
|
- add programming packages for JMRI Decoder Pro communikation
|
|
- add ignore data when slot is empty - no loco set (missing Slot request OPC_LOCO_ADR)
|
|
|
|
Copyright (c) by Philipp Gahtow, year 2022
|
|
|
|
*/
|
|
#if defined(LOCONET)
|
|
|
|
//**************************************************************
|
|
/* default: als SLOT Server Lok-Ereignisse ins LocoNet senden (Master-Mode only!) */
|
|
#define TXAllLokInfoOnLN true //sende alle Lok-Ereignisse ins LocoNet (MASTER-MODE only) - default: true*
|
|
#define LnInvDir //vertauscht die Fahrtrichtung bei Verwendung der Intellibox I oder II als LocoNet Master oder für DAISY II Handregler
|
|
//**************************************************************
|
|
|
|
//STAT1 => D2 = SL_SPDEX, D1 = SL_SPD14, D0 = SL_SPD28
|
|
#define LNLOCO14 0x02 //14 Speed Steps
|
|
#define LNLOCO28 0x01 //28 Speed Steps
|
|
#define LNLOCO128 0x07 //128 Speed Steps
|
|
|
|
//Setup up PIN-Configuration for different MCU
|
|
#include "MCU_config.h"
|
|
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
#include "z21header.h"
|
|
#endif
|
|
|
|
static void LNupdate();
|
|
|
|
//config:
|
|
#if defined(UNO_MCU) || defined(__AVR_ATmega644P__)
|
|
#if !defined(WIFI) && !defined(LAN)
|
|
#define MaxSlot 50
|
|
#else
|
|
#define MaxSlot 10
|
|
#endif
|
|
#else
|
|
#define MaxSlot 120 //max. 120 Slots
|
|
#endif
|
|
|
|
typedef struct //SLOT
|
|
{
|
|
uint16_t LAdr;
|
|
uint8_t Status;
|
|
/* Slot Status 1
|
|
D7-SL_SPURGE
|
|
1=SLOT purge en,ALSO adrSEL (INTERNAL use only, not seen on NET!)
|
|
CONDN/CONUP: bit encoding-Control double linked Consist List
|
|
|
|
2 BITS for Consist
|
|
D6-SL_CONUP
|
|
D3-SL_CONDN
|
|
11=LOGICAL MID CONSIST , Linked up AND down
|
|
10=LOGICAL CONSIST TOP, Only linked downwards
|
|
01=LOGICAL CONSIST SUB-MEMBER, Only linked upwards
|
|
00=FREE locomotive, no CONSIST indirection/linking
|
|
|
|
2 BITS for BUSY/ACTIVE
|
|
D5-SL_BUSY
|
|
D4-SL_ACTIVE
|
|
11=IN_USE loco adr in SLOT -REFRESHED
|
|
10=IDLE loco adr in SLOT, not refreshed
|
|
01=COMMON loco adr IN SLOT, refreshed
|
|
00=FREE SLOT, no valid DATA, not refreshed
|
|
|
|
3 BITS for Decoder TYPE encoding for this SLOT
|
|
D2-SL_SPDEX
|
|
D1-SL_SPD14
|
|
D0-SL_SPD28
|
|
010=14 step MODE
|
|
001=28 step. Generate Trinary packets for this Mobile ADR
|
|
000=28 step/ 3 BYTE PKT regular mode
|
|
011=128 speed mode packets
|
|
111=128 Step decoder, Allow Advanced DCC consisting
|
|
100=28 Step decoder ,Allow Advanced DCC consisting
|
|
*/
|
|
} TypeSlot;
|
|
|
|
TypeSlot slot[MaxSlot];
|
|
|
|
//define a
|
|
#if defined(ESP32_MCU)
|
|
lnMsg LnPacket;
|
|
#define LnPacketData LnPacket.data
|
|
uint8_t LNDataLength = 0;
|
|
#elif defined(ESP8266_MCU)
|
|
uint8_t LnPacket[20]; //empty Packet
|
|
#define LnPacketData LnPacket
|
|
uint8_t LNDataLength = 0;
|
|
#else //other MCUs
|
|
lnMsg *LnPacket;
|
|
#define LnPacketData LnPacket->data
|
|
#endif
|
|
|
|
#if defined(LnBufferUSB)
|
|
static LnBuf LnTxBuffer;
|
|
#define RX_BUF_LOW 32
|
|
#define RX_BUF_HIGH 96
|
|
#endif
|
|
|
|
byte dispatchSlot = 0; //To put and store a SLOT for DISPATCHING
|
|
boolean dpgetSLot = false;
|
|
boolean LNgetNext = false; //Z21 hat eine Meldung auf den LocoNet-Bus geschrieben?
|
|
boolean LNNextTX = false;
|
|
//--------------------------------------------------------------------------------------------
|
|
//Define new OPC:
|
|
#ifndef OPC_UHLI_FUN
|
|
#define OPC_UHLI_FUN 0xD4 //Function 9-28 by Uhlenbrock
|
|
#endif
|
|
#ifndef OPC_MULTI_SENSE
|
|
#define OPC_MULTI_SENSE 0xD0 //power management and transponding
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
#if defined(LnDEB)
|
|
//Print LN Message
|
|
void LNDebugPrint () {
|
|
#if defined(ESP32_MCU) || defined(ESP8266_MCU)
|
|
uint8_t msgLen = LNDataLength;
|
|
#else
|
|
uint8_t msgLen = getLnMsgSize(LnPacket); //get Length of Data
|
|
#endif
|
|
// First print out the packet in HEX
|
|
for (uint8_t x = 0; x < msgLen; x++) {
|
|
// Print a leading 0 if less than 16 to make 2 HEX digits
|
|
if(LnPacketData[x] < 16)
|
|
Debug.print('0');
|
|
Debug.print(LnPacketData[x], HEX);
|
|
Debug.print(' ');
|
|
}
|
|
Debug.println();
|
|
}
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
#if defined(LnBufferUSB)
|
|
void ReportLnBuffer() {
|
|
// Get the length of the received packet
|
|
uint8_t Length = getLnMsgSize( LnPacket ) ;
|
|
// Send the received packet out byte by byte to the PC
|
|
for( uint8_t Index = 0; Index < Length; Index++ )
|
|
#if defined(ARDUINO) && ARDUINO >= 100
|
|
Serial.write(LnPacket->data[ Index ]);
|
|
#else
|
|
Serial.print(LnPacket->data[ Index ], BYTE);
|
|
#endif
|
|
}
|
|
#endif
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//send bytes into LocoNet
|
|
bool LNSendPacket (byte *data, byte len, byte Z21bcType, bool Z21TX = true)
|
|
{
|
|
lnMsg SendPacket;
|
|
#if (defined(ESP8266_MCU) || defined(ESP32_MCU))
|
|
for (byte i = 0; i < (len-1); i++) { //copy everything without XOR
|
|
SendPacket.data[i] = *data; //prepare lnMsg to send
|
|
data++;
|
|
}
|
|
LocoNet_sendData(SendPacket.data);
|
|
|
|
#else
|
|
for (byte i = 0; i < (len-1); i++) { //copy everything without XOR
|
|
SendPacket.data[i] = *data;
|
|
data++;
|
|
}
|
|
#if defined(LnSLOTSRV)
|
|
LN_STATUS verify = LocoNet.send( &SendPacket, 20); //20 = min carrier detect backoff and we are the Master!
|
|
#else
|
|
LN_STATUS verify = LocoNet.send( &SendPacket); //normal
|
|
#endif
|
|
#if defined(LnDEB) || defined(DEBUG)
|
|
if (verify != LN_DONE) {
|
|
Debug.print(verify);
|
|
Debug.println(F(" LnSend ERROR!"));
|
|
}
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI) //Exception 9: LoadStoreAlignmentCause: Load or store to an unaligned address
|
|
if (Z21TX == true)
|
|
z21.setLNMessage(SendPacket.data, len, Z21bcType, Z21TX);
|
|
#endif
|
|
|
|
#if (defined(ESP8266_MCU) || defined(ESP32_MCU))
|
|
LocoNetRXTXupdate();
|
|
|
|
if (LocoNet_available()) {
|
|
LNDataLength = LocoNet_readLength();
|
|
LocoNet_readData(LnPacketData);
|
|
|
|
#else
|
|
LnPacket = LocoNet.receive();
|
|
|
|
if( LnPacket ) {
|
|
#if defined(LnBufferUSB)
|
|
ReportLnBuffer();
|
|
#endif
|
|
|
|
#endif
|
|
|
|
if (SendPacket.data[len-1] == LnPacketData[len-1] && SendPacket.data[0] == LnPacketData[0] && SendPacket.data[1] == LnPacketData[1]) {
|
|
//LNNextTX = true;
|
|
#if defined(LnDEB)
|
|
Debug.print("LnTX: ");
|
|
LNDebugPrint();
|
|
#endif
|
|
}
|
|
else {
|
|
#if defined(LnDEB)
|
|
Debug.print("retry: ");
|
|
#endif
|
|
|
|
LNgetNext = true;
|
|
LNupdate();
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//Status req
|
|
void LNGetLocoStatus(byte Slot) {
|
|
//default Answer:
|
|
byte SLOT_DATA_READ[] = {OPC_SL_RD_DATA, 0x0E, Slot, 0, 0, 0, 0, 0x07, 0, 0, 0, 0, 0, 0}; //length = 14 = 0x0E
|
|
if ((Slot != 0) && (Slot < MaxSlot)) { //wenn Lok im SLOT Server vorhanden ist --> update
|
|
uint8_t ldata[6];
|
|
AllLocoData(slot[Slot].LAdr, ldata); //uint8_t Steps[0], uint8_t Speed[1], uint8_t F0[2], uint8_t F1[3], uint8_t F2[4], uint8_t F3[5]
|
|
|
|
#if defined(LnInvDir)
|
|
byte DIRF = (((~ldata[1]) >> 2) & 0x20) | (ldata[2] & 0x1F);
|
|
#else
|
|
byte DIRF = ((ldata[1] >> 2) & 0x20) | (ldata[2] & 0x1F);
|
|
#endif
|
|
/*
|
|
#if defined(LnDEB)
|
|
Debug.print(Slot);
|
|
Debug.print(F("OPC_SL_RD_DATA "));
|
|
#endif
|
|
*/
|
|
SLOT_DATA_READ[3] = slot[Slot].Status; //D0-D2 = speed steps; D4,D5 = Busy/Activ; D3,D6 = CONSIST; D7 = 0 (SPURGE)
|
|
SLOT_DATA_READ[4] = lowByte(slot[Slot].LAdr & 0x7F);
|
|
SLOT_DATA_READ[5] = lowByte(ldata[1] & 0x7F);
|
|
SLOT_DATA_READ[6] = DIRF; //DIRF = 0,0,DIR,F0,F4,F3,F2,F1
|
|
SLOT_DATA_READ[9] = lowByte((slot[Slot].LAdr >> 7) & 0x7F); //ADR2
|
|
SLOT_DATA_READ[10] = lowByte(ldata[3] & 0x0F); //SND = 0,0,0,0,F8,F7,F6,F5
|
|
}
|
|
//only report Lok Slots, no system slots!
|
|
if (Slot < 120)
|
|
LNSendPacket (SLOT_DATA_READ, 0x0E, Z21bcLocoNet_s, true);
|
|
}
|
|
|
|
#if defined(LnSLOTSRV)
|
|
//--------------------------------------------------------------------------------------------
|
|
byte LNGetSetLocoSlot(unsigned int Adr, bool add) { //add command for MASTER-Mode only!
|
|
if (Adr == 0)
|
|
return 0;
|
|
byte getSlot = 0;
|
|
for (byte i = 1; i < MaxSlot; i++) {
|
|
if (slot[i].LAdr == Adr)
|
|
return i; //already inside a SLOT
|
|
if ((getSlot == 0) && (((slot[i].Status & 0x30) >> 4) == 0))
|
|
getSlot = i; //find a empty SLOT
|
|
}
|
|
if (getSlot != 0 && add == true) { //add Adr to SLOT Server!
|
|
slot[getSlot].Status = 0x10; //ACTIVE - COMMON loco adr IN SLOT => D5,D4 = 10 = IDLE loco adr in SLOT
|
|
#if defined(FS14)
|
|
slot[getSlot].Status |= LNLOCO14;
|
|
#elif defined(FS28)
|
|
slot[getSlot].Status |= LNLOCO28;
|
|
#else
|
|
slot[getSlot].Status |= LNLOCO128;
|
|
#endif
|
|
slot[getSlot].LAdr = Adr;
|
|
|
|
#if defined(LnDEB)
|
|
Debug.print(F("A:"));
|
|
Debug.print(slot[getSlot].LAdr);
|
|
Debug.print(F(" in Slot: "));
|
|
Debug.println(getSlot);
|
|
#endif
|
|
|
|
//Report Slot DATA:
|
|
LNGetLocoStatus(getSlot);
|
|
|
|
return getSlot;
|
|
}
|
|
return 0;
|
|
}
|
|
#else //LN Slave-Mode
|
|
//--------------------------------------------------------------------------------------------
|
|
//Find Slot via Address from the LocoNet Slot Master
|
|
byte LNGetSetLocoSlot (unsigned int Adr, bool add) { //add only for MASTER-Mode!
|
|
if (Adr == 0)
|
|
return 0;
|
|
for (byte i = 1; i < MaxSlot; i++) {
|
|
if (slot[i].LAdr == Adr) { // && (getLNSlotState(Slot) != 0)) {
|
|
return i; //Vorhanden!
|
|
}
|
|
}
|
|
//If not: Master puts Address into a SLOT
|
|
byte getSlot[] = {OPC_LOCO_ADR, lowByte(Adr >> 7), lowByte(Adr & 0x7F), 0x00}; //Request loco address
|
|
LNSendPacket(getSlot, 4, Z21bcLocoNetLocos_s, true);
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//Get Slot Information
|
|
void LNGetSlotInfo (byte Slot) {
|
|
byte getSlotInfo[] = {OPC_RQ_SL_DATA, Slot, 0, 0x00}; //Request Slot data/status
|
|
LNSendPacket(getSlotInfo, 4, Z21bcLocoNetLocos_s, true);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
#endif //LN Slave Mode
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//Set slot direction, function 0-4 state
|
|
// DIRF = 0,0,DIR,F0,F4,F3,F2,F1
|
|
void sendLNDIRF (unsigned int Adr, byte DIRF) {
|
|
byte Slot = LNGetSetLocoSlot(Adr, TXAllLokInfoOnLN);
|
|
if (Slot > 0) {
|
|
#if defined(LnInvDir)
|
|
byte setDIRF[] = {OPC_LOCO_DIRF, Slot, lowByte( ((~DIRF) & 0x20) | (DIRF & 0x1F) ), 0x00};
|
|
#else
|
|
byte setDIRF[] = {OPC_LOCO_DIRF, Slot, DIRF, 0x00};
|
|
#endif
|
|
LNSendPacket(setDIRF, 4, Z21bcLocoNetLocos_s, true);
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------
|
|
//Set slot speed and update dir
|
|
void sendLNSPD (unsigned int Adr, byte SPD) {
|
|
byte Slot = LNGetSetLocoSlot(Adr, TXAllLokInfoOnLN);
|
|
if (Slot == 0)
|
|
return;
|
|
//Check if direction will change also?
|
|
if (dcc.getLocoDir(Adr) != (SPD >> 7)) {
|
|
//calculate new direction:
|
|
byte DIRF = dcc.getFunktion0to4(Adr) | ((SPD >> 2) & 0x20);
|
|
sendLNDIRF(Adr, DIRF);
|
|
}
|
|
byte setSPD[] = {OPC_LOCO_SPD, Slot, 0x7F, 0x00};
|
|
setSPD[2] &= SPD;
|
|
LNSendPacket(setSPD, 4, Z21bcLocoNetLocos_s, true);
|
|
}
|
|
//--------------------------------------------------------------------------------------------
|
|
//Set slot second function
|
|
// SND = 0,0,0,0,F8,F7,F6,F5
|
|
void sendLNSND (unsigned int Adr, byte SND) {
|
|
byte Slot = LNGetSetLocoSlot(Adr, TXAllLokInfoOnLN);
|
|
if (Slot > 0) {
|
|
byte setSND[] = {OPC_LOCO_SND, Slot, SND, 0x00};
|
|
LNSendPacket(setSND, 4, Z21bcLocoNetLocos_s, true);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// F3 = 0,0,0,0,F12,F11,F10,F9
|
|
void sendLNF3 (unsigned int Adr, byte F3) {
|
|
byte Slot = LNGetSetLocoSlot(Adr, TXAllLokInfoOnLN);
|
|
if (Slot > 0) {
|
|
byte setF3[] = {0xA3, Slot, F3, 0x00};
|
|
LNSendPacket(setF3, 4, Z21bcLocoNetLocos_s, true);
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
// F4 = F20,F19,F18,F17,F16,F15,F14,F13
|
|
void sendLNF4 (unsigned int Adr, byte F4) {
|
|
byte Slot = LNGetSetLocoSlot(Adr, TXAllLokInfoOnLN);
|
|
if (Slot > 0) {
|
|
byte setF4[] = {OPC_UHLI_FUN, 0x20, Slot, 0x08, 0x7F, 0x00}; // - F19-F13
|
|
setF4[4] &= F4;
|
|
LNSendPacket(setF4, 6, Z21bcLocoNetLocos_s, true);
|
|
byte F2028 = ((dcc.getFunktion21to28(Adr) & 0x80) >> 1) | ((F4 & 0x80) >> 2); // - f28, f20, ---
|
|
byte setF2028[] = {OPC_UHLI_FUN, 0x20, Slot, 0x05, F2028, 0x00}; // - F28,F20 ----
|
|
LNSendPacket(setF2028, 6, Z21bcLocoNetLocos_s, true);
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------
|
|
// F5 = F28,F27,F26,F25,F24,F23,F22,F21
|
|
void sendLNF5 (unsigned int Adr, byte F5) {
|
|
byte Slot = LNGetSetLocoSlot(Adr, TXAllLokInfoOnLN);
|
|
if (Slot > 0) {
|
|
byte setF5[] = {OPC_UHLI_FUN, 0x20, Slot, 0x09, 0x7F, 0x00}; // - F27-F21
|
|
setF5[4] &= F5;
|
|
LNSendPacket(setF5, 6, Z21bcLocoNetLocos_s, true);
|
|
byte F2028 = ((F5 & 0x80) >> 1) | ((dcc.getFunktion13to20(Adr) & 0x80) >> 2); // - f28, f20, ---
|
|
byte setF2028[] = {OPC_UHLI_FUN, 0x20, Slot, 0x05, F2028, 0x00}; // - F28,F20 ----
|
|
LNSendPacket(setF2028, 6, Z21bcLocoNetLocos_s, true);
|
|
}
|
|
}
|
|
//--------------------------------------------------------------------------------------------
|
|
//Uhlenbrock F9 to F12 Message: A3 Slot F9-12 HEX (A3 09 01 54; A3 09 00 55)
|
|
//Uhlenbroch F13 to F20 and F21 to F28: D4 20 Slot F13-20 F20-21 HEX (D4 20 09 08 01 0B; D4 20 09 08 02 08)
|
|
/*
|
|
* 18:39:03.972: [Rx - A3 03 01 5E] Set (Intellibox-II format) loco in slot 3
|
|
F9=On F10=Off F11=Off F12=Off.
|
|
18:39:06.109: [Rx - A3 03 00 5F] Set (Intellibox-II format) loco in slot 3
|
|
F9=Off F10=Off F11=Off F12=Off.
|
|
18:39:07.061: [Rx - A3 03 02 5D] Set (Intellibox-II format) loco in slot 3
|
|
F9=Off F10=On F11=Off F12=Off.
|
|
18:39:07.934: [Rx - A3 03 00 5F] Set (Intellibox-II format) loco in slot 3
|
|
F9=Off F10=Off F11=Off F12=Off.
|
|
18:39:10.290: [Rx - A3 03 04 5B] Set (Intellibox-II format) loco in slot 3
|
|
F9=Off F10=Off F11=On F12=Off.
|
|
18:39:11.023: [Rx - A3 03 00 5F] Set (Intellibox-II format) loco in slot 3
|
|
F9=Off F10=Off F11=Off F12=Off.
|
|
18:39:13.519: [Rx - A3 03 08 57] Set (Intellibox-II format) loco in slot 3
|
|
F9=Off F10=Off F11=Off F12=On.
|
|
18:39:14.205: [Rx - A3 03 00 5F] Set (Intellibox-II format) loco in slot 3
|
|
F9=Off F10=Off F11=Off F12=Off.
|
|
|
|
18:39:15.188: [Rx - D4 20 03 08 01 01] Set (Intellibox-II format) loco in
|
|
slot 3 F13=On F14=Off F15=Off F16=Off F17=Off F18=Off F19=Off
|
|
18:39:16.483: [Rx - D4 20 03 08 00 00] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=Off F18=Off F19=Off
|
|
18:39:23.441: [Rx - D4 20 03 08 02 02] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=On F15=Off F16=Off F17=Off F18=Off F19=Off
|
|
18:39:26.576: [Rx - D4 20 03 08 00 00] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=Off F18=Off F19=Off
|
|
18:39:28.823: [Rx - D4 20 03 08 04 04] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=On F16=Off F17=Off F18=Off F19=Off
|
|
18:39:30.227: [Rx - D4 20 03 08 00 00] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=Off F18=Off F19=Off
|
|
18:39:42.176: [Rx - D4 20 03 08 08 08] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=On F17=Off F18=Off F19=Off
|
|
18:39:45.187: [Rx - D4 20 03 08 00 00] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=Off F18=Off F19=Off
|
|
18:39:47.121: [Rx - D4 20 03 08 10 10] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=On F18=Off F19=Off
|
|
18:39:48.291: [Rx - D4 20 03 08 00 00] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=Off F18=Off F19=Off
|
|
18:40:02.300: [Rx - D4 20 03 08 20 20] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=Off F18=On F19=Off
|
|
18:40:03.486: [Rx - D4 20 03 08 00 00] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=Off F18=Off F19=Off
|
|
18:40:04.516: [Rx - D4 20 03 08 40 40] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=Off F18=Off F19=On
|
|
18:40:05.296: [Rx - D4 20 03 08 00 00] Set (Intellibox-II format) loco in
|
|
slot 3 F13=Off F14=Off F15=Off F16=Off F17=Off F18=Off F19=Off
|
|
|
|
18:40:09.710: [Rx - D4 20 03 05 20 2D] Set (Intellibox-II format) loco in
|
|
slot 3 F20=On F28=Off
|
|
18:40:16.091: [Rx - D4 20 03 05 00 0D] Set (Intellibox-II format) loco in
|
|
slot 3 F20=Off F28=Off
|
|
|
|
18:40:16.964: [Rx - D4 20 03 09 01 00] Set (Intellibox-II format) loco in
|
|
slot 3 F21=On F22=Off F23=Off F24=Off F25=Off F26=Off F27=Off
|
|
18:40:26.449: [Rx - D4 20 03 09 00 01] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=Off F26=Off F27=Off
|
|
18:40:29.554: [Rx - D4 20 03 09 02 03] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=On F23=Off F24=Off F25=Off F26=Off F27=Off
|
|
18:40:31.020: [Rx - D4 20 03 09 00 01] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=Off F26=Off F27=Off
|
|
18:40:32.252: [Rx - D4 20 03 09 04 05] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=On F24=Off F25=Off F26=Off F27=Off
|
|
18:40:33.329: [Rx - D4 20 03 09 00 01] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=Off F26=Off F27=Off
|
|
18:40:46.885: [Rx - D4 20 03 09 08 09] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=On F25=Off F26=Off F27=Off
|
|
18:40:47.790: [Rx - D4 20 03 09 00 01] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=Off F26=Off F27=Off
|
|
18:40:50.473: [Rx - D4 20 03 09 10 11] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=On F26=Off F27=Off
|
|
18:40:51.128: [Rx - D4 20 03 09 00 01] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=Off F26=Off F27=Off
|
|
18:40:56.183: [Rx - D4 20 03 09 20 21] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=Off F26=On F27=Off
|
|
18:41:00.083: [Rx - D4 20 03 09 00 01] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=Off F26=Off F27=Off
|
|
18:41:02.142: [Rx - D4 20 03 09 40 41] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=Off F26=Off F27=On
|
|
18:41:03.484: [Rx - D4 20 03 09 00 01] Set (Intellibox-II format) loco in
|
|
slot 3 F21=Off F22=Off F23=Off F24=Off F25=Off F26=Off F27=Off
|
|
|
|
18:41:06.292: [Rx - D4 20 03 05 40 4D] Set (Intellibox-II format) loco in
|
|
slot 3 F20=Off F28=On
|
|
18:41:07.274: [Rx - D4 20 03 05 00 0D] Set (Intellibox-II format) loco in
|
|
slot 3 F20=Off F28=Off
|
|
*/
|
|
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//Check if Slot can be dispatched
|
|
byte LNdispatch (uint16_t Adr) {
|
|
dispatchSlot = LNGetSetLocoSlot(Adr, true); //add to SLOT
|
|
#if defined(LnDEB)
|
|
Debug.print(Adr);
|
|
Debug.print(F(" Loco in Dispatch Slot: "));
|
|
Debug.println(dispatchSlot);
|
|
#endif
|
|
#if !defined(LnSLOTSRV) //At Slave Mode ask Master to dispatch
|
|
if (dispatchSlot > 0) {
|
|
byte SetStat1[] = {OPC_SLOT_STAT1, dispatchSlot, 0x20, 0x00};
|
|
LNSendPacket(SetStat1, 4, Z21bcLocoNetLocos_s, true);
|
|
byte NullMove[] = {OPC_MOVE_SLOTS, dispatchSlot, 0x00, 0x00};
|
|
LNSendPacket(NullMove, 4, Z21bcLocoNetLocos_s, true);
|
|
}
|
|
else { //no Slot for loco that should be dispatched - get a Slot!
|
|
dpgetSLot = true; //get ready for Slot
|
|
return 0;
|
|
}
|
|
#endif
|
|
if (((slot[dispatchSlot].Status & 0x30) >> 4) != B11) { //not: D5,D4 = 11=IN_USE loco adr in SLOT
|
|
return dispatchSlot;
|
|
}
|
|
dispatchSlot = 0; //clear
|
|
return 0;
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//LocoNet Interface init
|
|
void LNsetup() {
|
|
#if (defined(ESP8266_MCU) || defined(ESP32_MCU))
|
|
return;
|
|
#else
|
|
|
|
// First initialize the LocoNet interface
|
|
LocoNet.init(LNTxPin);
|
|
|
|
#if defined(LnBufferUSB)
|
|
// Configure the serial port for 57600 baud
|
|
Serial.begin(57600);
|
|
// Initialize a LocoNet packet buffer to buffer bytes from the PC
|
|
initLnBuf(&LnTxBuffer) ;
|
|
|
|
#if defined(LnBufferUSB_CTS)
|
|
// Configure the CTS pin for hardware flow control to control
|
|
// the serial data flow from the PC to the LocoNet
|
|
pinMode(Ln_RX_CTS_PIN,OUTPUT);
|
|
digitalWrite(Ln_RX_CTS_PIN,LOW);
|
|
#endif
|
|
|
|
#endif
|
|
|
|
#endif
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//Send railpower state to LocoNet devices
|
|
void LNsetpower() {
|
|
byte code[] = { OPC_GPOFF, 0x00};
|
|
if (Railpower == csNormal)
|
|
code[0] = OPC_GPON;
|
|
else if (Railpower == csEmergencyStop)
|
|
code[0] = OPC_IDLE; //B'cast emerg. STOP
|
|
LNSendPacket(code,2, Z21bcLocoNet_s, true);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//Trnt Daten senden
|
|
void LNsetTrnt(uint16_t Adr, boolean state, boolean active) {
|
|
//OPC_SW_REQ
|
|
//dcc.setBasicAccessoryPos(LnPacketData[1] | ((LnPacketData[2] & 0x0F) << 7),(LnPacketData[2] >> 5) & 0x01, (LnPacketData[2] >> 4) & 0x01); //Adr, State, on/off
|
|
byte lAdr = Adr & 0x7F;
|
|
byte hAdr = (Adr >> 7) & 0x0F;
|
|
byte Trnt[] = {OPC_SW_REQ, lAdr, hAdr, 0x00};
|
|
bitWrite(Trnt[2], 5, state);
|
|
bitWrite(Trnt[2], 4, active);
|
|
LNSendPacket (Trnt, 4, Z21bcLocoNet_s, true);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//OPC SL Data Auswerten
|
|
void LN_OPC_SL_DATA() {
|
|
if (LnPacketData[2] < MaxSlot) {
|
|
slot[LnPacketData[2]].LAdr = (LnPacketData[9] << 7) | (LnPacketData[4] & 0x7F); //ADR2 | ADR
|
|
slot[LnPacketData[2]].Status = LnPacketData[3]; //Save new Status
|
|
|
|
#if defined(LnInvDir)
|
|
uint8_t speed = (LnPacketData[5] & 0x7F) | (((~LnPacketData[6]) << 2) & 0x80); //DIR, S, S, S, S, S, S, S
|
|
#else
|
|
uint8_t speed = (LnPacketData[5] & 0x7F) | ((LnPacketData[6] << 2) & 0x80); //DIR, S, S, S, S, S, S, S
|
|
#endif
|
|
|
|
#if defined(LnDEB)
|
|
Debug.print(slot[LnPacketData[2]].LAdr);
|
|
Debug.print(", ");
|
|
Debug.println(speed, BIN);
|
|
#endif
|
|
|
|
if ((slot[LnPacketData[2]].Status & B111) == LNLOCO14) {
|
|
#if defined(DCC)
|
|
dcc.setSpeed14(slot[LnPacketData[2]].LAdr, speed); //DIRF & SPD
|
|
#endif
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setSpeed(slot[LnPacketData[2]].LAdr, 14, speed);
|
|
#endif
|
|
}
|
|
else {
|
|
if ((slot[LnPacketData[2]].Status & B111) == LNLOCO28) {
|
|
#if defined(DCC)
|
|
dcc.setSpeed28(slot[LnPacketData[2]].LAdr, speed); //DIRF & SPD
|
|
#endif
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setSpeed(slot[LnPacketData[2]].LAdr, 28, speed);
|
|
#endif
|
|
}
|
|
else {
|
|
#if defined(DCC)
|
|
dcc.setSpeed128(slot[LnPacketData[2]].LAdr, speed); //DIRF & SPD
|
|
#endif
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setSpeed(slot[LnPacketData[2]].LAdr, 128, speed);
|
|
#endif
|
|
}
|
|
}
|
|
#if defined(DCC)
|
|
dcc.setFunctions0to4(slot[LnPacketData[2]].LAdr, LnPacketData[6] & 0x1F); //DIRF = - F0 F4 F3 F2 F1
|
|
dcc.setFunctions5to8(slot[LnPacketData[2]].LAdr, LnPacketData[10] & 0x0F); //SND = - F8 F7 F6 F5
|
|
#endif
|
|
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
z21.setLocoStateExt (slot[LnPacketData[2]].LAdr);
|
|
#endif
|
|
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setFunc0to4(slot[LnPacketData[2]].LAdr, LnPacketData[6] & 0x1F); //DIRF = - F0 F4 F3 F2 F1
|
|
XpressNet.setFunc5to8(slot[LnPacketData[2]].LAdr, LnPacketData[10] & 0x0F); //SND = - F8 F7 F6 F5
|
|
XpressNet.ReqLocoBusy(slot[LnPacketData[2]].LAdr); //Lok wird nicht von LokMaus gesteuert!
|
|
#endif
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//CV Rückmeldung
|
|
void LNsetCVReturn(uint16_t CV, uint8_t value, uint8_t fail) {
|
|
//This <E7> response is issued whenever a Programming task is completed.
|
|
/* [EF 0E 7C 2B 00 00 00 00 00 1C 00 7F 7F 55] Byte Read in Direct Mode on Service Track: CV29.
|
|
* [81 7E] Master is busy.
|
|
* [B4 6F 01 25] LONG_ACK: The Slot Write command was accepted.
|
|
* [E7 0E 7C 2B 01 00 20 07 00 1C 00 7F 7F 7B] Programming Response:
|
|
* Read Byte in Direct Mode on Service Track Failed,
|
|
* Service Mode programming track empty: CV29 value 0 (0x00, 00000000b).
|
|
*
|
|
* [EF 0E 7C 2B 00 00 00 00 00 1C 00 7F 7F 55] Byte Read in Direct Mode on Service Track: CV29.
|
|
* [B4 6F 01 25] LONG_ACK: The Slot Write command was accepted.
|
|
* [E7 0E 7C 2B 00 00 20 07 00 1C 0E 7F 7F 74] Programming Response:
|
|
* Read Byte in Direct Mode on Service Track Was Successful: CV29 value 14 (0x0E, 00001110b).
|
|
* [EF 0E 7C 2B 00 00 00 00 00 00 00 7F 7F 49] Byte Read in Direct Mode on Service Track: CV1.
|
|
* [B4 6F 01 25] LONG_ACK: The Slot Write command was accepted.
|
|
* [E7 0E 7C 2B 00 00 20 07 00 00 03 7F 7F 65] Programming Response:
|
|
* Read Byte in Direct Mode on Service Track Was Successful: CV1 value 3 (0x03, 00000011b).
|
|
* [EF 0E 7C 2B 00 00 00 00 00 06 00 7F 7F 4F] Byte Read in Direct Mode on Service Track: CV7.
|
|
* [B4 6F 01 25] LONG_ACK: The Slot Write command was accepted.
|
|
* [E7 0E 7C 2B 00 00 20 07 00 06 23 7F 7F 43] Programming Response:
|
|
* Read Byte in Direct Mode on Service Track Was Successful: CV7 value 35 (0x23, 00100011b).
|
|
* [EF 0E 7C 2B 00 00 00 00 00 07 00 7F 7F 4E] Byte Read in Direct Mode on Service Track: CV8.
|
|
* [B4 6F 01 25] LONG_ACK: The Slot Write command was accepted.
|
|
* [E7 0E 7C 2B 00 00 20 07 02 07 11 7F 7F 72] Programming Response:
|
|
* Read Byte in Direct Mode on Service Track Was Successful: CV8 value 145 (0x91, 10010001b).
|
|
*/
|
|
|
|
|
|
#if defined(LnDEB)
|
|
Debug.print("Ln CV: ");
|
|
#endif
|
|
uint8_t CVH = (CV >> 7) | ((value >> 6) & 0x02) | ((CV >> 4) & 0x30);
|
|
byte SLOT_DATA[] = {OPC_SL_RD_DATA, 0x0E, 0x7C, 0x2B, fail & 0x01, 0, 0x20, 0x07, CVH, CV & 0x7F, value & 0x7F, 0x7F, 0x7F};
|
|
LNSendPacket (SLOT_DATA, 0x0E, Z21bcLocoNet_s, true);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//LocoNet Daten dekodieren
|
|
void LNdecode(bool Z21Report) {
|
|
//Z21 Broadcast-Flag:
|
|
byte LnZ21bcType = Z21bcLocoNet_s; //Z21bcLocoNet or Z21bcLocoNetLocos or Z21bcLocoNetSwitches
|
|
|
|
switch (LnPacketData[0]) { //OPC = Operation-Code
|
|
case OPC_SL_RD_DATA: { //for LN Slave-Mode ONLY
|
|
#if !defined(LnSLOTSRV) //At Slave Mode ask Master to dispatch
|
|
|
|
#if defined(LnDEB)
|
|
Debug.print(F("OPC_SL_RD_DATA Slot: "));
|
|
Debug.print(LnPacketData[2]);
|
|
Debug.print(" ");
|
|
#endif
|
|
|
|
if (LnPacketData[2] < MaxSlot) {
|
|
/* Slot: 0 dispatch
|
|
* 1 - 119 active locos */
|
|
|
|
LN_OPC_SL_DATA();
|
|
|
|
if (dpgetSLot == true) { //dispatch this Slot!
|
|
#if defined(LnDEB)
|
|
Debug.println("Dispatch");
|
|
#endif
|
|
if (((slot[LnPacketData[2]].Status & 0x30) >> 4) != 0x01) { //not BUSY!
|
|
byte SetStat1[] = {OPC_SLOT_STAT1, LnPacketData[2], lowByte((slot[LnPacketData[2]].Status & 0xC0) | (slot[LnPacketData[2]].Status & 0x0F) | 0x20), 0x00}; //set BUSY
|
|
LNSendPacket(SetStat1, 4, Z21bcLocoNet_s, true);
|
|
}
|
|
byte NullMove[] = {OPC_MOVE_SLOTS, LnPacketData[2], 0x00, 0x00};
|
|
LNSendPacket(NullMove, 4, Z21bcLocoNet_s, true);
|
|
dpgetSLot = false; //reset
|
|
}
|
|
}
|
|
#endif
|
|
break; }
|
|
case OPC_WR_SL_DATA: { //Write slot data
|
|
uint8_t SLOT_RX = LnPacketData[2];
|
|
if (SLOT_RX < MaxSlot) {
|
|
/* Slot: 0 dispatch
|
|
* 1 - 119 active locos */
|
|
#if defined(LnDEB)
|
|
Debug.print(F("OPC_WR_SL_DATA: "));
|
|
#endif
|
|
|
|
LN_OPC_SL_DATA(); //send DCC message
|
|
}
|
|
else { //OPC_WR_PT_DATA
|
|
/*Slot: 120 - 127 reserved for System and Master control
|
|
* 123 Fast Clock
|
|
* 124 Programming Track
|
|
* 127 Command Station Options */
|
|
if (SLOT_RX == 0x7C) { //Write PT slot data.
|
|
uint16_t cvAdr = LnPacketData[9] | ( (LnPacketData[8] & 0x01) << 7) | ( (LnPacketData[8] & 0x30) << 4);
|
|
uint8_t value = ((LnPacketData[8] << 5) & 0x80) | (LnPacketData[10] & 0x7F);
|
|
|
|
#if defined(LnDEB)
|
|
Debug.print(F("CV#: "));
|
|
Debug.print(cvAdr+1);
|
|
Debug.print(F(" Value: "));
|
|
Debug.print(value);
|
|
#endif
|
|
|
|
if (LnPacketData[3] == 0x2B) { //Read in Direct Mode on Service Track
|
|
#if defined(LnDEB)
|
|
Debug.print(F(" Direct Mode Read "));
|
|
#endif
|
|
#if defined(DCC)
|
|
dcc.opsReadDirectCV(cvAdr); //read cv
|
|
#endif
|
|
}
|
|
else if (LnPacketData[3] == 23) { //Read in Paged Mode on Service Track
|
|
#if defined(LnDEB)
|
|
Debug.print(F(" Paged Mode Read "));
|
|
#endif
|
|
}
|
|
else if (LnPacketData[3] == 24) { //Read in Paged Mode on Service Track
|
|
#if defined(LnDEB)
|
|
Debug.print(F(" Paged Mode Read "));
|
|
#endif
|
|
#if defined(DCC)
|
|
dcc.opsProgDirectCV(cvAdr,value); //return value from DCC via 'notifyCVVerify'
|
|
#endif
|
|
}
|
|
}
|
|
else if (SLOT_RX == 0x7B){ //Write Fast Clock slot data.
|
|
#if defined(LnDEB)
|
|
Debug.print(F("OPC_WR_CL_DATA: "));
|
|
#endif
|
|
}
|
|
}
|
|
//Response:
|
|
//0=busy/aborted, 1=accepted(OPC_SL_RD_DATA), 0×40=accepted blind(OPC_SL_RD_DATA), 0x7F=not implemented
|
|
byte ACK[] = {OPC_LONG_ACK, OPC_WR_SL_DATA & B01111111, 1, 0x00};
|
|
LNSendPacket (ACK, 4, LnZ21bcType, true); //Send ACK
|
|
LNGetLocoStatus(SLOT_RX); //Send OPC_SL_RD_DATA
|
|
break; }
|
|
case OPC_RQ_SL_DATA: //Request slot data/status block
|
|
#if defined(LnSLOTSRV)
|
|
LNGetLocoStatus(LnPacketData[1]);
|
|
#endif
|
|
break;
|
|
case OPC_LOCO_ADR: { //0xBF = Request loco address
|
|
#if defined(LnSLOTSRV)
|
|
//add to a SLOT:
|
|
byte newSlot = LNGetSetLocoSlot((LnPacketData[1] << 7) | (LnPacketData[2] & 0x7F), true); //ADR2:7 ms-bits = 0 bei kurzer Adr; ADR:7 ls-bit
|
|
if (dispatchSlot != 0 && LnPacketData[1] == 0 && LnPacketData[2] == 0)
|
|
newSlot = dispatchSlot;
|
|
#if defined(LnDEB)
|
|
Debug.print("get Slot: ");
|
|
Debug.println(newSlot);
|
|
#endif
|
|
if (newSlot == 0) {
|
|
//0xB4 = OPC_LONG_ACK No free slot available
|
|
byte Fail[] = {OPC_LONG_ACK, 0x3F, 0x00, 0x00}; //LACK,0 is returned[<B4>,<3F>,<0>,<CHK>]
|
|
LNSendPacket (Fail, 4, Z21bcLocoNet_s, true);
|
|
}
|
|
else {
|
|
//Report Slot DATA:
|
|
LNGetLocoStatus(newSlot);
|
|
}
|
|
#endif
|
|
break;
|
|
}
|
|
case OPC_MOVE_SLOTS: { //0xBA = Move slot SRC to DST
|
|
#if defined(LnSLOTSRV)
|
|
if (LnPacketData[1] == 0) { //SRC = 0
|
|
//SLOT READ DATA of DISPATCH Slot
|
|
if (dispatchSlot != 0) {
|
|
slot[dispatchSlot].Status = slot[dispatchSlot].Status | 0x30; //IN_USE
|
|
LNGetLocoStatus(dispatchSlot); //Give slot that was DISPATCHED
|
|
dispatchSlot = 0; //reset the Dispatch SLOT
|
|
break;
|
|
}
|
|
}
|
|
else if (LnPacketData[1] == LnPacketData[2]) { //NULL move
|
|
//SRC=DEST is set to IN_USE , if legal move -> NULL move
|
|
slot[LnPacketData[1]].Status = slot[LnPacketData[1]].Status | 0x30; //B00011111; //IN_USE
|
|
LNGetLocoStatus(LnPacketData[1]);
|
|
break;
|
|
}
|
|
else if (LnPacketData[2] == 0) { //DST = 0
|
|
//DISPATCH Put, mark SLOT as DISPATCH;
|
|
dispatchSlot = LnPacketData[1];
|
|
//RETURN slot status <0xE7> of DESTINATION slot DEST if move legal
|
|
LNGetLocoStatus(dispatchSlot);
|
|
break;
|
|
}
|
|
//RETURN Fail LACK code if illegal move <B4>,<3A>,<0>,<chk>
|
|
byte Fail[] = {OPC_LONG_ACK, OPC_MOVE_SLOTS & 0x7F, 0x00, 0x00};
|
|
LNSendPacket (Fail, 4, LnZ21bcType, true);
|
|
|
|
#endif
|
|
break;
|
|
}
|
|
case OPC_LINK_SLOTS:
|
|
break; //Link slot ARG1 to slot ARG2
|
|
case OPC_UNLINK_SLOTS:
|
|
break; //Unlink slot ARG1 from slot ARG2
|
|
case OPC_SLOT_STAT1:
|
|
slot[LnPacketData[1]].Status = LnPacketData[2];
|
|
break;
|
|
case 0xBE: { //OPC_EXP_REQ_SLOT (0xBE)
|
|
/* Example:
|
|
* Ln: ED 0F 01 49 42 0D 00 15 00 00 00 00 00 00 0F
|
|
* Ln: E5 0F 00 49 4B 0B 00 15 00 52 11 15 00 00 5F
|
|
* Ln: BE 00 15 54
|
|
*/
|
|
#if defined(LnDEB)
|
|
Debug.println("OPC_EXP_REQ_SLOT");
|
|
#endif
|
|
break; }
|
|
case OPC_LOCO_SPD: { //0SSSSSS
|
|
if (LnPacketData[1] < MaxSlot) {
|
|
if (slot[LnPacketData[1]].LAdr == 0) {//unbekannt!
|
|
#if not defined(LnSLOTSRV)
|
|
LNGetSlotInfo(LnPacketData[1]); //request data
|
|
#endif
|
|
break;
|
|
}
|
|
#if defined(LnDEB)
|
|
Debug.print(slot[LnPacketData[1]].LAdr);
|
|
Debug.print(" OPC_LOCO_SPD: ");
|
|
Debug.println((dcc.getLocoDir(slot[LnPacketData[1]].LAdr) << 7) | LnPacketData[2], BIN);
|
|
#endif
|
|
if ((slot[LnPacketData[1]].Status & B111) == LNLOCO14)
|
|
dcc.setSpeed14(slot[LnPacketData[1]].LAdr, (dcc.getLocoDir(slot[LnPacketData[1]].LAdr) << 7) | map(LnPacketData[2],0,128,0,14));
|
|
else {
|
|
if ((slot[LnPacketData[1]].Status & B111) == LNLOCO28)
|
|
dcc.setSpeed28(slot[LnPacketData[1]].LAdr, (dcc.getLocoDir(slot[LnPacketData[1]].LAdr) << 7) | map(LnPacketData[2],0,128,0,28));
|
|
else dcc.setSpeed128(slot[LnPacketData[1]].LAdr, (dcc.getLocoDir(slot[LnPacketData[1]].LAdr) << 7) | LnPacketData[2]);
|
|
}
|
|
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
z21.setLocoStateExt (slot[LnPacketData[1]].LAdr);
|
|
LnZ21bcType = Z21bcLocoNetLocos_s; //Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads
|
|
#endif
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setSpeed(slot[LnPacketData[1]].LAdr, 128, (dcc.getLocoDir(slot[LnPacketData[1]].LAdr) << 7) | LnPacketData[2]);
|
|
XpressNet.ReqLocoBusy(slot[LnPacketData[1]].LAdr); //Lok wird nicht von LokMaus gesteuert!
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case OPC_LOCO_DIRF: { //0,0,DIR,F0,F4,F3,F2,F1
|
|
if (LnPacketData[1] < MaxSlot) {
|
|
if (slot[LnPacketData[1]].LAdr == 0) {//unbekannt!
|
|
#if not defined(LnSLOTSRV)
|
|
LNGetSlotInfo(LnPacketData[1]); //request data
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
#if defined(LnInvDir)
|
|
LnPacketData[2] = ((~LnPacketData[2]) & 0x20) | (LnPacketData[2] & 0x1F);
|
|
#endif
|
|
|
|
#if defined(LnDEB)
|
|
Debug.print(slot[LnPacketData[1]].LAdr);
|
|
Debug.print(" OPC_LOCO_DIRF: ");
|
|
Debug.println(LnPacketData[2], BIN);
|
|
#endif
|
|
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setFunc0to4(slot[LnPacketData[1]].LAdr, LnPacketData[2] & 0x1F);
|
|
XpressNet.ReqLocoBusy(slot[LnPacketData[1]].LAdr); //Lok wird nicht von LokMaus gesteuert!
|
|
#endif
|
|
|
|
byte lokspeed = dcc.getLocoSpeed(slot[LnPacketData[1]].LAdr); //lese aktuelle Geschwindigkeit
|
|
bitWrite(lokspeed, 7, ((LnPacketData[2] >> 5) & 0x01)); //Fahrrichtung
|
|
|
|
if ((slot[LnPacketData[1]].Status & B111) == LNLOCO14) {
|
|
#if defined(DCC)
|
|
dcc.setSpeed14(slot[LnPacketData[1]].LAdr, lokspeed ); //update DIRF in DCC library
|
|
#endif
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setSpeed(slot[LnPacketData[1]].LAdr, 14, lokspeed);
|
|
#endif
|
|
}
|
|
else {
|
|
if ((slot[LnPacketData[1]].Status & B111) == LNLOCO28) {
|
|
#if defined(DCC)
|
|
dcc.setSpeed28(slot[LnPacketData[1]].LAdr, lokspeed ); //update DIRF in DCC library
|
|
#endif
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setSpeed(slot[LnPacketData[1]].LAdr, 28, lokspeed);
|
|
#endif
|
|
}
|
|
else {
|
|
#if defined(DCC)
|
|
dcc.setSpeed128(slot[LnPacketData[1]].LAdr, lokspeed ); //update DIRF in DCC library
|
|
#endif
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setSpeed(slot[LnPacketData[1]].LAdr, 128, lokspeed);
|
|
#endif
|
|
}
|
|
}
|
|
dcc.setFunctions0to4(slot[LnPacketData[1]].LAdr, LnPacketData[2] & 0x1F); //- F0 F4 F3 F2 F1
|
|
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
z21.setLocoStateExt (slot[LnPacketData[1]].LAdr);
|
|
LnZ21bcType = Z21bcLocoNetLocos_s; //Exception 28: LoadProhibited: A load referenced a page mapped with an attribute that does not permit loads
|
|
#endif
|
|
|
|
|
|
}
|
|
break;
|
|
}
|
|
case OPC_LOCO_SND: { //0,0,0,0,F8,F7,F6,F5
|
|
if (LnPacketData[1] < MaxSlot) {
|
|
if (slot[LnPacketData[1]].LAdr == 0) {//unbekannt!
|
|
#if not defined(LnSLOTSRV)
|
|
LNGetSlotInfo(LnPacketData[1]); //request data
|
|
#endif
|
|
break;
|
|
}
|
|
|
|
#if defined(LnDEB)
|
|
Debug.print(slot[LnPacketData[1]].LAdr);
|
|
Debug.print(" OPC_LOCO_SND: ");
|
|
Debug.println(LnPacketData[2], BIN);
|
|
#endif
|
|
|
|
dcc.setFunctions5to8(slot[LnPacketData[1]].LAdr, LnPacketData[2]); //- F8 F7 F6 F5
|
|
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
z21.setLocoStateExt (slot[LnPacketData[1]].LAdr);
|
|
LnZ21bcType = Z21bcLocoNetLocos_s;
|
|
#endif
|
|
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setFunc5to8(slot[LnPacketData[1]].LAdr, LnPacketData[2]);
|
|
XpressNet.ReqLocoBusy(slot[LnPacketData[1]].LAdr); //Lok wird nicht von LokMaus gesteuert!
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case OPC_IMM_PACKET: { //Digitrax OPC_LOCO_F912 = Functions 9-12
|
|
/*
|
|
* 9.3.1 DCC Binary State Control Instruction
|
|
Ab FW Version V1.25 können mittels LAN_LOCONET_FROM_LAN und dem LocoNet Befehl
|
|
OPC_IMM_PACKET beliebige DCC Pakete am Gleisausgang generiert werden, darunter auch die Binary
|
|
State Control Instruction (auch „F29…F32767“ genannt).
|
|
|
|
Zum Aufbau des OPC_IMM_PACKET siehe LocoNet Spec (auch in personal edition zu Lernzwecken).
|
|
Zum Aufbau der Binary State Control Instruction siehe NMRA S-9.2.1 Abschnitt Feature Expansion
|
|
Instruction.
|
|
*/
|
|
LnZ21bcType = Z21bcLocoNetLocos_s;
|
|
#if defined(LnSLOTSRV)
|
|
//unkommend to make Daisy2 work!
|
|
//byte ACK[] = {OPC_LONG_ACK, OPC_IMM_PACKET & 0x7F, 0x00, 0x00}; //busy
|
|
//LNSendPacket (ACK, 4, LnZ21bcType, true); //Send ACK
|
|
#endif
|
|
break;
|
|
}
|
|
case 0xA3: { //0,0,0,0,F12,F11,F10,F9 by Uhlenbrock
|
|
if (LnPacketData[1] < MaxSlot) {
|
|
if (slot[LnPacketData[1]].LAdr == 0) {//unbekannt!
|
|
#if not defined(LnSLOTSRV)
|
|
LNGetSlotInfo(LnPacketData[1]); //request data
|
|
#endif
|
|
break;
|
|
}
|
|
dcc.setFunctions9to12(slot[LnPacketData[1]].LAdr, LnPacketData[2]); //- F12 F11 F10 F9
|
|
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
z21.setLocoStateExt (slot[LnPacketData[1]].LAdr);
|
|
LnZ21bcType = Z21bcLocoNetLocos_s;
|
|
#endif
|
|
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setFunc9to12(slot[LnPacketData[1]].LAdr, LnPacketData[2]);
|
|
XpressNet.ReqLocoBusy(slot[LnPacketData[1]].LAdr); //Lok wird nicht von LokMaus gesteuert!
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case OPC_UHLI_FUN: { //Function 9-28 by Uhlenbrock (0xD4)
|
|
if (LnPacketData[2] >= MaxSlot)
|
|
break;
|
|
if (LnPacketData[1] == 0x20) {
|
|
if (slot[LnPacketData[2]].LAdr == 0) { //unbekannt Slot in 3. Byte!
|
|
#if not defined(LnSLOTSRV)
|
|
LNGetSlotInfo(LnPacketData[2]); //request data
|
|
#endif
|
|
break;
|
|
}
|
|
#if defined(LnDEB)
|
|
Debug.print("OPC_UHLI_FUN: ");
|
|
#endif
|
|
byte Func = 0x00;
|
|
if (LnPacketData[3] == 0x07) { //Arg3
|
|
Func = ((LnPacketData[4] >> 4) & 0x07) | (dcc.getFunktion9to12(slot[LnPacketData[2]].LAdr) & 0x08);
|
|
#if defined(LnDEB)
|
|
Debug.print("9-12:");
|
|
Debug.println(Func, BIN);
|
|
#endif
|
|
dcc.setFunctions9to12(slot[LnPacketData[2]].LAdr, Func); //- F12 F11 F10 F9
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setFunc9to12(slot[LnPacketData[2]].LAdr, Func);
|
|
#endif
|
|
}
|
|
if (LnPacketData[3] == 0x08) { //for F13 to F19
|
|
Func = (LnPacketData[4] & 0x7f) | (dcc.getFunktion13to20(slot[LnPacketData[2]].LAdr) & 0x80);
|
|
#if defined(LnDEB)
|
|
Debug.print("13-20:");
|
|
Debug.println(Func, BIN);
|
|
#endif
|
|
dcc.setFunctions13to20(slot[LnPacketData[2]].LAdr, Func); //F20 to F13
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setFunc13to20(slot[LnPacketData[2]].LAdr, Func);
|
|
#endif
|
|
}
|
|
if (LnPacketData[3] == 0x09) { //for F21 to F27
|
|
Func = (LnPacketData[4] & 0x7f) | (dcc.getFunktion21to28(slot[LnPacketData[2]].LAdr) & 0x80);
|
|
#if defined(LnDEB)
|
|
Debug.print("21-27:");
|
|
Debug.println(Func, BIN);
|
|
#endif
|
|
dcc.setFunctions21to28(slot[LnPacketData[2]].LAdr, Func); //F28 to F21
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setFunc21to28(slot[LnPacketData[2]].LAdr, Func);
|
|
#endif
|
|
}
|
|
if (LnPacketData[3] == 0x05) { //for F12, F20 and F28
|
|
Func = ((LnPacketData[4] >> 1) & 0x08) | (dcc.getFunktion9to12(slot[LnPacketData[2]].LAdr) & 0x07); //add F12
|
|
#if defined(LnDEB)
|
|
Debug.print("9-12:");
|
|
Debug.print(Func, BIN);
|
|
#endif
|
|
dcc.setFunctions9to12(slot[LnPacketData[2]].LAdr, Func); //F9 to F12
|
|
Func = ((LnPacketData[4] << 2) & 0x80) | (dcc.getFunktion13to20(slot[LnPacketData[2]].LAdr) & 0x7f); //add F20
|
|
#if defined(LnDEB)
|
|
Debug.print("; 13-20:");
|
|
Debug.print(Func, BIN);
|
|
#endif
|
|
dcc.setFunctions13to20(slot[LnPacketData[2]].LAdr, Func); //F20 to F13
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setFunc13to20(slot[LnPacketData[2]].LAdr, Func);
|
|
#endif
|
|
Func = ((LnPacketData[4] << 1) & 0x80) | (dcc.getFunktion21to28(slot[LnPacketData[2]].LAdr) & 0x7f); //add F28
|
|
#if defined(LnDEB)
|
|
Debug.print("; 21-28:");
|
|
Debug.println(Func, BIN);
|
|
#endif
|
|
dcc.setFunctions21to28(slot[LnPacketData[2]].LAdr, Func); //F28 to F21
|
|
#if defined(XPRESSNET)
|
|
XpressNet.setFunc21to28(slot[LnPacketData[2]].LAdr, Func);
|
|
#endif
|
|
}
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
z21.setLocoStateExt (slot[LnPacketData[2]].LAdr);
|
|
LnZ21bcType = Z21bcLocoNetLocos_s;
|
|
#endif
|
|
|
|
#if defined(XPRESSNET)
|
|
XpressNet.ReqLocoBusy(slot[LnPacketData[2]].LAdr); //Lok wird nicht von LokMaus gesteuert!
|
|
#endif
|
|
}
|
|
break;
|
|
}
|
|
case OPC_SW_STATE: { //Request state of switch.
|
|
//dcc.getBasicAccessoryInfo(Address+inc)
|
|
#if defined(LnSLOTSRV)
|
|
//byte LOPC = LnPacketData[0] & 0x7F; //Kopie der Kommando-Codes. Das 7. Bit wird 0 gesetzt
|
|
byte ACK[] = {OPC_LONG_ACK, 0x00, 0x00, 0x00}; //Fail!!
|
|
ACK[1] = LnPacketData[0] & B01111111;
|
|
LNSendPacket (ACK, 4, Z21bcLocoNetSwitches_s, true); //Send ACK
|
|
#endif
|
|
break;
|
|
}
|
|
case OPC_SW_ACK: { //Request switch with acknoledge function.
|
|
dcc.setBasicAccessoryPos(LnPacketData[1] | ((LnPacketData[2] & 0x0F) << 7),(LnPacketData[2] >> 5) & 0x01, (LnPacketData[2] >> 4) & 0x01); //Adr, State, on/off
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
LnZ21bcType = Z21bcLocoNetSwitches_s;
|
|
#endif
|
|
#if defined(LnSLOTSRV)
|
|
//byte LOPC = LnPacketData[0] & B01111111; //Kopie der Kommando-Codes. Das 7. Bit wird 0 gesetzt
|
|
byte ACK[] = {OPC_LONG_ACK, 0x00, 0x7F, 0x00}; //Succsess
|
|
ACK[1] = LnPacketData[0] & 0x7F;
|
|
LNSendPacket (ACK, 4, LnZ21bcType, true); //Send ACK
|
|
#endif
|
|
break;
|
|
}
|
|
case OPC_SW_REQ: { //Request switch function
|
|
uint16_t Adr = ((LnPacketData[1] & 0x7F) | ((LnPacketData[2] & 0x0F) << 7));
|
|
dcc.setBasicAccessoryPos(Adr,(LnPacketData[2] >> 5) & 0x01, (LnPacketData[2] >> 4) & 0x01); //Adr, State, on/off
|
|
LnZ21bcType = Z21bcLocoNetSwitches_s;
|
|
#if defined(LnDEB)
|
|
Debug.print(F("OPC_SW_REQ:"));
|
|
Debug.print(Adr);
|
|
Debug.print(((LnPacketData[2] >> 5) & 0x01) ? "=left" : "=right");
|
|
Debug.println(((LnPacketData[2] >> 4) & 0x01) ? "=on" : "=off");
|
|
#endif
|
|
break;
|
|
}
|
|
case OPC_SW_REP: { //Turnout sensor state report
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
//LnPacketData[1] = 0,A6,A5,A4,A3,A2,A1,A0
|
|
//LnPacketData[2] = 0,X,I,L,A10,A9,A8,A7
|
|
byte Rdata[4];
|
|
uint16_t Adr = (LnPacketData[1] | ((LnPacketData[2] & 0x0F) << 7)) + 1;
|
|
Rdata[0] = 0x01; //Typ
|
|
Rdata[1] = lowByte(Adr); //A7-A0
|
|
Rdata[2] = highByte(Adr); //A10-A8
|
|
Rdata[3] = (LnPacketData[2] >> 5) & 0x01; //L, Rückmelde-Zustand
|
|
z21.setLNDetector(0, Rdata, 4);
|
|
|
|
#if defined(LnDEB)
|
|
Debug.print(F("OPC_SW_REP:"));
|
|
Debug.print(Adr);
|
|
Debug.println(((LnPacketData[2] >> 5) & 0x01) ? "=on" : "=off");
|
|
#endif
|
|
|
|
#endif
|
|
break;
|
|
}
|
|
case OPC_INPUT_REP: { //0xB2 = Besetztmelder - LAN_LOCONET_DETECTOR
|
|
//LnPacketData[1] = 0,A6,A5,A4,A3,A2,A1,A0
|
|
//LnPacketData[2] = 0,X,I,L,A10,A9,A8,A7
|
|
//X, Steuerbit 0=für zukünftige Verwendung reserviert
|
|
//I, Eingangsquelle 0=DS54, 1=Schalter
|
|
//"I"=0 for DS54 "aux" inputs and 1 for "switch" inputs mapped to 4K SENSOR space.
|
|
//(This is effectively a least significant adr bit when using DS54 input configuration)
|
|
//L, Rückmelde-Zustand 0=deaktiviert, 1=aktiv
|
|
byte Rdata[4];
|
|
uint16_t Adr = ( (LnPacketData[1] & 0x7F) | ((LnPacketData[2] & 0x0F) << 7));
|
|
Adr <<= 1;
|
|
Adr += (LnPacketData[2] & OPC_INPUT_REP_SW) ? 2 : 1;
|
|
Rdata[0] = 0x01; //Typ
|
|
Rdata[1] = lowByte(Adr); //A7-A0
|
|
Rdata[2] = highByte(Adr); //A10-A8
|
|
Rdata[3] = (LnPacketData[2] >> 4) & 0x01; //L, Rückmelde-Zustand
|
|
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
z21.setLNDetector(0, Rdata, 4);
|
|
#endif
|
|
|
|
#if defined(REPORT)
|
|
Debug.print(F("LN Sensor:"));
|
|
Debug.print(Adr);
|
|
Debug.println(((LnPacketData[2] >> 4) & 0x01) ? "=on" : "=off");
|
|
#endif
|
|
|
|
break; }
|
|
case OPC_MULTI_SENSE: {
|
|
byte Rdata[4];
|
|
Rdata[0] = LnPacketData[1]; //Type
|
|
Rdata[1] = LnPacketData[4]; //Adr
|
|
Rdata[2] = LnPacketData[3]; //Adr
|
|
Rdata[3] = LnPacketData[2]; //zone and section
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
z21.setLNDetector(0, Rdata, 4);
|
|
#endif
|
|
break; }
|
|
case OPC_PEER_XFER: { //0xE5
|
|
#if defined(LnDEB)
|
|
Debug.println(F("OPC_PEER_XFER"));
|
|
#endif
|
|
break; }
|
|
case 0xE6: { //OPC_ALM_READ // Undocumented name
|
|
#if defined(LnDEB)
|
|
Debug.println(F("OPC_ALM_READ"));
|
|
#endif
|
|
break;}
|
|
case 0xEE: { //OPC_ALM_WRITE // Undocumented name
|
|
#if defined(LnDEB)
|
|
Debug.println(F("OPC_ALM_WRITE"));
|
|
#endif
|
|
break;}
|
|
//Zustand Gleisspannung
|
|
case OPC_GPOFF: {
|
|
if (Railpower != csTrackVoltageOff)
|
|
globalPower(csTrackVoltageOff);
|
|
break; }
|
|
case OPC_GPON: {
|
|
if (Railpower != csNormal)
|
|
globalPower(csNormal);
|
|
break; }
|
|
case OPC_IDLE: {
|
|
if (Railpower != csEmergencyStop)
|
|
globalPower(csEmergencyStop);
|
|
break; }
|
|
}
|
|
|
|
#if defined(LAN) || defined(WIFI) || defined(ESP_WIFI)
|
|
if (Z21Report) {
|
|
//Report the RX-LocoNet Frame to z21:
|
|
#if defined(ESP32_MCU) || defined(ESP8266_MCU)
|
|
z21.setLNMessage(LnPacketData, LNDataLength, LnZ21bcType, false); //RX Packet
|
|
#else
|
|
z21.setLNMessage(LnPacketData, getLnMsgSize(LnPacket), LnZ21bcType, false); //RX Packet
|
|
#endif
|
|
}
|
|
#endif
|
|
}
|
|
|
|
//--------------------------------------------------------------------------------------------
|
|
//LocoNet update via each loop
|
|
void LNupdate() {
|
|
// Check for any received LocoNet packets
|
|
if (LNgetNext == false) {
|
|
#if defined(ESP32_MCU) || defined(ESP8266_MCU)
|
|
//Receive a Packet via LocoNet2 call back function!!!!
|
|
//for (byte i = 0; i < LNDataLength; i++) {
|
|
// LnPacketData = LNData;
|
|
//}
|
|
#else
|
|
LnPacket = LocoNet.receive();
|
|
#endif
|
|
}
|
|
#if (defined(ESP8266_MCU) || defined(ESP32_MCU))
|
|
LocoNetRXTXupdate(); //check if we have data RX/TX to store in Buffer
|
|
//neue Daten empfangen?
|
|
if (LocoNet_available()) {
|
|
LNDataLength = LocoNet_readLength();
|
|
LocoNet_readData(LnPacketData);
|
|
#else
|
|
if( LnPacket ) {
|
|
#endif
|
|
|
|
LNgetNext = false;
|
|
#if defined(LnDEB)
|
|
Debug.print("LnRX: ");
|
|
LNDebugPrint();
|
|
#endif
|
|
|
|
LNdecode(true); //verarbeite empfangene Daten!
|
|
|
|
#if defined(LnBufferUSB)
|
|
ReportLnBuffer();
|
|
#endif
|
|
|
|
} //end if (LnPacket)
|
|
|
|
#if defined(LnBufferUSB)
|
|
// Check to see if there are any bytes from the PC
|
|
if(int charWaiting = Serial.available()) {
|
|
#if defined(LNBufferUSB_CTS)
|
|
// If the number of bytes waiting is less than RX_BUF_LOW enable CTS
|
|
if( charWaiting < RX_BUF_LOW )
|
|
digitalWrite(Ln_RX_CTS_PIN,LOW);
|
|
// If the number of bytes waiting is more than RX_BUF_HIGH disable CTS
|
|
else if( charWaiting > RX_BUF_HIGH )
|
|
digitalWrite(Ln_RX_CTS_PIN,HIGH);
|
|
#endif
|
|
|
|
// Read the byte
|
|
uint8_t inByte = Serial.read() & 0xFF;
|
|
|
|
// Add it to the buffer
|
|
addByteLnBuf( &LnTxBuffer, inByte ) ;
|
|
|
|
// Check to see if we have received a complete packet yet
|
|
LnPacket = recvLnMsg( &LnTxBuffer ) ;
|
|
if(LnPacket ) { // Send the received packet from the PC to the LocoNet
|
|
LNSendPacket (LnPacket->data, getLnMsgSize( LnPacket ), Z21bcLocoNet_s, false); //Report nicht an Z21 Library!
|
|
LNdecode(false); //verarbeite empfangene Daten, aber nicht als Z21 RX!
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
//--------------------
|
|
#endif
|