//-------------------------------------------------------------- /* 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 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[,<3F>,<0>,] 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 ,<3A>,<0>, 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