Z21ServerEmulator/z21library/z21.cpp

1294 lines
44 KiB
C++
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*
*****************************************************************************
* z21.cpp - library for Roco Z21 LAN protocoll
* Copyright (c) 2013-2022 Philipp Gahtow All right reserved.
*
*
*****************************************************************************
* IMPORTANT:
*
* Please contact ROCO Inc. for more details.
*****************************************************************************
*/
// include this library's description file
#include <z21.h>
#include <z21header.h>
#if defined(__arm__)
#include <DueFlashStorage.h>
DueFlashStorage FlashStore;
#define FSTORAGE FlashStore
#define FSTORAGEMODE write
#elif defined(ESP32) //use NVS on ESP32!
#include "z21nvs.h"
z21nvsClass NVSZ21;
#define FSTORAGE NVSZ21
#define FSTORAGEMODE write
#else
// AVR based Boards follows
#include <EEPROM.h>
#define FSTORAGE EEPROM
#if defined(ESP8266) || defined(ESP32) //ESP8266 or ESP32
#define FSTORAGEMODE write
#else
#define FSTORAGEMODE update
#endif
#endif
// Constructor /////////////////////////////////////////////////////////////////
// Function that handles the creation and setup of instances
z21Class::z21Class()
{
// initialize this instance's variables
z21IPpreviousMillis = 0;
Railpower = csTrackVoltageOff;
clearIPSlots();
}
// Public Methods //////////////////////////////////////////////////////////////
// Functions available in Wiring sketches, this library, and other libraries
//*********************************************************************************************
//Daten ermitteln und Auswerten
void z21Class::receive(uint8_t client, uint8_t *packet)
{
addIPToSlot(client, 0);
// send a reply, to the IP address and port that sent us the packet we received
int header = (packet[3]<<8) + packet[2];
byte data[16]; //z21 send storage
#if defined(ESP32)
portMUX_TYPE myMutex = portMUX_INITIALIZER_UNLOCKED;
#endif
switch (header) {
case LAN_GET_SERIAL_NUMBER:
#if defined(SERIALDEBUG)
ZDebug.println("GET_SERIAL_NUMBER");
#endif
data[0] = FSTORAGE.read(CONFz21SnLSB);
data[1] = FSTORAGE.read(CONFz21SnMSB);
data[2] = 0x00;
data[3] = 0x00;
EthSend(client, 0x08, LAN_GET_SERIAL_NUMBER, data, false, Z21bcNone); //Seriennummer 32 Bit (little endian)
break;
case LAN_GET_HWINFO:
#if defined(SERIALDEBUG)
ZDebug.println("GET_HWINFO");
#endif
data[0] = z21HWTypeLSB; //HwType 32 Bit
data[1] = z21HWTypeMSB;
data[2] = 0x00;
data[3] = 0x00;
data[4] = z21FWVersionLSB; //FW Version 32 Bit
data[5] = z21FWVersionMSB;
data[6] = 0x00;
data[7] = 0x00;
EthSend (client, 0x0C, LAN_GET_HWINFO, data, false, Z21bcNone);
break;
case LAN_LOGOFF:
#if defined(SERIALDEBUG)
ZDebug.println("LOGOFF");
#endif
clearIPSlot(client);
//Antwort von Z21: keine
break;
case LAN_GET_CODE: //SW Feature-Umfang der Z21
/*#define Z21_NO_LOCK 0x00 // keine Features gesperrt
#define z21_START_LOCKED 0x01 // „z21 start”: Fahren und Schalten per LAN gesperrt
#define z21_START_UNLOCKED 0x02 // „z21 start”: alle Feature-Sperren aufgehoben */
data[0] = 0x00; //keine Features gesperrt
EthSend (client, 0x05, LAN_GET_CODE, data, false, Z21bcNone);
break;
case (LAN_X_Header):
//---------------------- LAN X-Header BEGIN ---------------------------
switch (packet[4]) { //X-Header
case LAN_X_GET_SETTING:
//---------------------- Switch BD0 BEGIN ---------------------------
switch (packet[5]) { //DB0
case 0x21:
#if defined(SERIALDEBUG)
ZDebug.println("X_GET_VERSION");
#endif
data[0] = LAN_X_GET_VERSION; //X-Header: 0x63
data[1] = 0x21; //DB0
data[2] = 0x30; //X-Bus Version
data[3] = 0x12; //ID der Zentrale
EthSend (client, 0x09, LAN_X_Header, data, true, Z21bcNone);
break;
case 0x24:
data[0] = LAN_X_STATUS_CHANGED; //X-Header: 0x62
data[1] = 0x22; //DB0
data[2] = Railpower; //DB1: Status
//ZDebug.print("X_GET_STATUS ");
//csEmergencyStop 0x01 // Der Nothalt ist eingeschaltet
//csTrackVoltageOff 0x02 // Die Gleisspannung ist abgeschaltet
//csShortCircuit 0x04 // Kurzschluss
//csProgrammingModeActive 0x20 // Der Programmiermodus ist aktiv
EthSend (client, 0x08, LAN_X_Header, data, true, Z21bcNone);
break;
case 0x80:
#if defined(SERIALDEBUG)
ZDebug.println("X_SET_TRACK_POWER_OFF");
data[0] = LAN_X_BC_TRACK_POWER;
data[1] = 0x00;
EthSend(client, 0x07, LAN_X_Header, data, true, Z21bcNone);
#endif
if (notifyz21RailPower)
notifyz21RailPower(csTrackVoltageOff);
break;
case 0x81:
#if defined(SERIALDEBUG)
ZDebug.println("X_SET_TRACK_POWER_ON");
#endif
data[0] = LAN_X_BC_TRACK_POWER;
data[1] = 0x01;
EthSend(client, 0x07, LAN_X_Header, data, true, Z21bcNone);
if (notifyz21RailPower)
notifyz21RailPower(csNormal);
break;
}
//---------------------- Switch DB0 ENDE ---------------------------
break; //ENDE DB0
case LAN_X_DCC_READ_REGISTER:
if (packet[5] == 0x15) { //DB0 - SPECIAL: WLANMaus CV Read!
if (notifyz21CVREAD)
notifyz21CVREAD(0, packet[6]-1); //CV_MSB, CV_LSB
}
break;
case LAN_X_CV_READ:
if (packet[5] == 0x11) { //DB0
#if defined(SERIALDEBUG)
ZDebug.println("X_CV_READ");
#endif
if (notifyz21CVREAD)
notifyz21CVREAD(packet[6], packet[7]); //CV_MSB, CV_LSB
}
if (packet[5] == 0x16) { //DB0 - SPECIAL: WLANMaus CV Write!
if (notifyz21CVWRITE)
notifyz21CVWRITE(0, packet[6]-1, packet[7]); //CV_MSB, CV_LSB, value
}
break;
case LAN_X_CV_WRITE:
if (packet[5] == 0x12) { //DB0
#if defined(SERIALDEBUG)
ZDebug.println("X_CV_WRITE");
#endif
if (notifyz21CVWRITE)
notifyz21CVWRITE(packet[6], packet[7], packet[8]); //CV_MSB, CV_LSB, value
}
break;
case LAN_X_CV_POM: { //X-Header = 0xE6
uint16_t CVAdr = ((packet[8] & 0b11) << 8) + packet[9];
byte value = packet[10];
if (packet[5] == 0x30) { //DB0 = LAN_X_CV_POM
uint16_t Adr = ((packet[6] & 0x3F) << 8) + packet[7];
if ((packet[8] & 0xFC) == LAN_X_CV_POM_WRITE_BYTE) { //DB3 Option 0xEC
#if defined(SERIALDEBUG)
ZDebug.println("LAN_X_CV_POM_WRITE_BYTE");
#endif
if (notifyz21CVPOMWRITEBYTE)
notifyz21CVPOMWRITEBYTE (Adr, CVAdr, value); //set Byte
}
else if ((packet[8] & 0xFC) == LAN_X_CV_POM_WRITE_BIT) { //DB3 Option 0xE8
#if defined(SERIALDEBUG)
ZDebug.println("LAN_X_CV_POM_WRITE_BIT");
#endif
if (notifyz21CVPOMWRITEBIT)
notifyz21CVPOMWRITEBIT (Adr, CVAdr, value); //set Bit
}
else if ((packet[8] & 0xFC) == LAN_X_CV_POM_READ_BYTE) { //DB3 Option 0xE4
#if defined(SERIALDEBUG)
ZDebug.println("LAN_X_CV_POM_READ_BYTE");
#endif
if (notifyz21CVPOMREADBYTE)
notifyz21CVPOMREADBYTE (Adr, CVAdr); //read byte
}
}
else if (packet[5] == 0x31) { //DB0 = LAN_X_CV_POM_ACCESSORY
uint16_t Adr = ((packet[6] & 0x1F) << 8) + packet[7];
if ((packet[8] & 0xFC) == LAN_X_CV_POM_ACCESSORY_WRITE_BYTE) { //DB3 Option 0xEC
#if defined(SERIALDEBUG)
ZDebug.println("LAN_X_CV_POM_ACCESSORY_WRITE_BYTE");
#endif
if (notifyz21CVPOMACCWRITEBYTE)
notifyz21CVPOMACCWRITEBYTE (Adr, CVAdr, value); //set Byte
}
else if ((packet[8] & 0xFC) == LAN_X_CV_POM_ACCESSORY_WRITE_BIT) { //DB3 Option 0xE8
#if defined(SERIALDEBUG)
ZDebug.println("LAN_X_CV_POM_ACCESSORY_WRITE_BIT");
#endif
if (notifyz21CVPOMACCWRITEBIT)
notifyz21CVPOMACCWRITEBIT (Adr, CVAdr, value); //set Bit
}
else if ((packet[8] & 0xFC) == LAN_X_CV_POM_ACCESSORY_READ_BYTE) { //DB3 Option 0xE4
#if defined(SERIALDEBUG)
ZDebug.println("LAN_X_CV_POM_ACCESSORY_READ_BYTE");
#endif
if (notifyz21CVPOMACCREADBYTE)
notifyz21CVPOMACCREADBYTE (Adr, CVAdr); //read byte
}
}
break;
}
case LAN_X_SET_TURNOUT: { //and notify other Clients with LAN_X_GET_TURNOUT_INFO!
#if defined(SERIALDEBUG)
ZDebug.print("X_SET_TURNOUT Adr.:");
ZDebug.print((packet[5] << 8) + packet[6]);
ZDebug.print(":");
ZDebug.print(bitRead(packet[7], 0));
ZDebug.print("-");
ZDebug.println(bitRead(packet[7], 3));
#endif
//bool TurnOnOff = bitRead(packet[7],3); //Spule EIN/AUS
if (notifyz21Accessory) {
notifyz21Accessory((packet[5] << 8) + packet[6], bitRead(packet[7], 0), bitRead(packet[7], 3));
} // Addresse Links/Rechts Spule EIN/AUS
//Check if Broadcast Flag is correct set up?
bool BCset = true;
for (byte i = 0; i < z21clientMAX; i++) {
if (ActIP[i].client == client && ActIP[i].BCFlag == 0) {
BCset = false;
break;
}
}
//Fall to next if no BCFlag is set!
if (BCset)
break;
}
case LAN_X_GET_TURNOUT_INFO: {
#if defined(SERIALDEBUG)
ZDebug.print("X_GET_TURNOUT_INFO ");
#endif
if (notifyz21AccessoryInfo) {
data[0] = 0x43; //X-HEADER
data[1] = packet[5]; //High
data[2] = packet[6]; //Low
if (notifyz21AccessoryInfo((packet[5] << 8) + packet[6]) == true)
data[3] = 0x02; //active
else data[3] = 0x01; //inactive
EthSend (client, 0x09, LAN_X_Header, data, true, Z21bcNone); //BC new 23.04. !!!(old = 0)
}
break;
}
case LAN_X_SET_EXT_ACCESSORY: {
//Schalten Erweiterten Zubehördecoder
#if defined(SERIALDEBUG)
ZDebug.print("X_SET_EXT_ACCESSORY RAdr.:");
ZDebug.print((packet[5] << 8) + packet[6]);
ZDebug.print(":0x");
ZDebug.println(packet[7], HEX);
#endif
if (notifyz21ExtAccessory)
notifyz21ExtAccessory((packet[5] << 8) + packet[6], packet[7]);
LAST_EXTACC_msg = packet[7]; //speichere letztes Kommando!
LAST_EXTACC_received = true; //wir haben eine EXTACC empfangen!
setExtACCInfo((packet[5] << 8) + packet[6], packet[7]);
break;
}
case LAN_X_GET_EXT_ACCESSORY_INFO: {
//kann mit folgendem Kommando der letzte an einen Erweiterten Zubehördecoder übertragene Befehl abgefragt werden.
#if defined(SERIALDEBUG)
ZDebug.print("X_EXT_ACCESSORY_INFO RAdr.:");
ZDebug.print((packet[5] << 8) + packet[6]);
ZDebug.print(":0x");
ZDebug.println(packet[7], HEX); //DB2 Reserviert für zukünftige Erweiterungen
#endif
if (LAST_EXTACC_received == true)
setExtACCInfo((packet[5] << 8) + packet[6], LAST_EXTACC_msg); //0x00 … Data Valid;
else setExtACCInfo((packet[5] << 8) + packet[6], LAST_EXTACC_msg, 0xFF); //0xFF … Data Unknown
break;
}
case LAN_X_SET_STOP:
#if defined(SERIALDEBUG)
ZDebug.println("X_SET_STOP");
#endif
if (notifyz21RailPower)
notifyz21RailPower(csEmergencyStop);
break;
case LAN_X_GET_LOCO_INFO:
if (packet[5] == 0xF0) { //DB0
//ZDebug.print("X_GET_LOCO_INFO: ");
//Antwort: LAN_X_LOCO_INFO Adr_MSB - Adr_LSB
returnLocoStateFull(client, word(packet[6] & 0x3F, packet[7]), false);
}
break;
case LAN_X_SET_LOCO:
//setLocoBusy:
addBusySlot(client,word(packet[6] & 0x3F, packet[7]));
if ((packet[5] & 0xF0) == 0x10) { //DB0 => 0x1x = LAN_X_SET_LOCO_DRIVE
//ZDebug.print("X_SET_LOCO_DRIVE ");
byte steps = 128; //default value S=3; DCC 128 Fahrstufen
if (packet[5] == 0x12) //S=2; DCC 28 Fahrstufen
steps = 28;
else if (packet[5] == 0x10) //S=0; DCC 14 Fahrstufen
steps = 14;
if (notifyz21LocoSpeed)
notifyz21LocoSpeed(word(packet[6] & 0x3F, packet[7]), packet[8],steps);
}
else if (packet[5] == LAN_X_SET_LOCO_FUNCTION) { //DB0 = 0xF8
//LAN_X_SET_LOCO_FUNCTION Adr_MSB Adr_LSB Type (00=AUS/01=EIN/10=UM) Funktion
if (notifyz21LocoFkt)
notifyz21LocoFkt(word(packet[6] & 0x3F, packet[7]), packet[8] >> 6, packet[8] & 0b00111111);
//uint16_t Adr, uint8_t type, uint8_t fkt
}
//LAN_X_SET_LOCO_FUNCTION_GROUP:
else if ((packet[5] == 0x20) && notifyz21LocoFkt0to4)
notifyz21LocoFkt0to4(word(packet[6] & 0x3F, packet[7]), packet[8] & 0x1F); //0 0 0 F0 F4 F3 F2 F1
else if ((packet[5] == 0x21) && notifyz21LocoFkt5to8)
notifyz21LocoFkt5to8(word(packet[6] & 0x3F, packet[7]), packet[8] & 0x0F); //0 0 0 0 F8 F7 F6 F5
else if ((packet[5] == 0x22) && notifyz21LocoFkt9to12)
notifyz21LocoFkt9to12(word(packet[6] & 0x3F, packet[7]), packet[8] & 0x1F); //0 0 0 0 F12 F11 F10 F9
else if ((packet[5] == 0x23) && notifyz21LocoFkt13to20)
notifyz21LocoFkt13to20(word(packet[6] & 0x3F, packet[7]), packet[8]); //F20 F19 F18 F17 F16 F15 F14 F13
else if ((packet[5] == 0x28) && notifyz21LocoFkt21to28)
notifyz21LocoFkt21to28(word(packet[6] & 0x3F, packet[7]), packet[8]); //F28 F27 F26 F25 F24 F23 F22 F21
else if ((packet[5] == 0x29) && notifyz21LocoFkt29to36)
notifyz21LocoFkt29to36(word(packet[6] & 0x3F, packet[7]), packet[8]); //F36 F35 F34 F33 F32 F31 F30 F29
else if (packet[5] == 0x2A) { // F44 - F37
if (notifyz21LocoFkt37to44)
notifyz21LocoFkt37to44(word(packet[6] & 0x3F, packet[7]), packet[8]); //F44 F43 F42 F41 F40 F39 F38 F37
return; //keine Rückmeldung an die LAN-Clients
}
else if (packet[5] == 0x2B) { // F52 - F45
if (notifyz21LocoFkt45to52)
notifyz21LocoFkt45to52(word(packet[6] & 0x3F, packet[7]), packet[8]); //F52 F51 F50 F49 F48 F47 F46 F45
return; //keine Rückmeldung an die LAN-Clients
}
else if (packet[5] == 0x50) { // F60 - F53
if (notifyz21LocoFkt53to60)
notifyz21LocoFkt53to60(word(packet[6] & 0x3F, packet[7]), packet[8]); //F60 F59 F58 F57 F56 F55 F54 F53
return; //keine Rückmeldung an die LAN-Clients
}
else if (packet[5] == 0x51) { // F68 - F61
if (notifyz21LocoFkt61to68)
notifyz21LocoFkt61to68(word(packet[6] & 0x3F, packet[7]), packet[8]); //F68 F67 F66 F65 F64 F63 F62 F61
return; //keine Rückmeldung an die LAN-Clients
}
returnLocoStateFull(client, word(packet[6] & 0x3F, packet[7]), true); //Rückmeldung an die LAN-Clients!
break;
case LAN_X_SET_LOCO_BINARY_STATE:
if (packet[5] == 0x5F) { //DB0 = Binary State
if (notifyz21LocoFktExt)
notifyz21LocoFktExt(word(packet[6] & 0x3F, packet[7]), packet[8], packet[9]);
}
break;
case LAN_X_GET_FIRMWARE_VERSION:
#if defined(SERIALDEBUG)
ZDebug.println("X_GET_FIRMWARE_VERSION");
#endif
data[0] = 0xF3; //identify Firmware (not change)
data[1] = 0x0A; //identify Firmware (not change)
data[2] = z21FWVersionMSB; //V_MSB
data[3] = z21FWVersionLSB; //V_LSB
EthSend (client, 0x09, LAN_X_Header, data, true, Z21bcNone);
break;
case 0x73:
//LAN_X_??? WLANmaus periodische Abfrage:
//0x09 0x00 0x40 0x00 0x73 0x00 0xFF 0xFF 0x00
//length X-Header XNet-Msg speed?
#if defined(SERIALDEBUG)
ZDebug.println("LAN-X_WLANmaus");
#endif
//set Broadcastflags for WLANmaus:
if (addIPToSlot(client, 0x00) == 0)
addIPToSlot(client, Z21bcAll);
break;
default:
#if defined(SERIALDEBUG)
ZDebug.print("UNKNOWN_LAN-X_COMMAND");
//for (byte i = 0; i < packet[0]; i++) {
// ZDebug.print(" 0x");
// ZDebug.print(packet[i], HEX);
//}
ZDebug.println();
#endif
data[0] = 0x61;
data[1] = 0x82;
EthSend (client, 0x07, LAN_X_Header, data, true, Z21bcNone);
}
//---------------------- LAN X-Header ENDE ---------------------------
break;
case (LAN_SET_BROADCASTFLAGS): {
unsigned long bcflag = packet[7];
bcflag = packet[6] | (bcflag << 8);
bcflag = packet[5] | (bcflag << 8);
bcflag = packet[4] | (bcflag << 8);
addIPToSlot(client, getLocalBcFlag(bcflag));
//no inside of the protokoll, but good to have:
if (notifyz21RailPower)
notifyz21RailPower(Railpower); //Zustand Gleisspannung Antworten
#if defined(SERIALDEBUG)
ZDebug.print(packet[7], BIN);
ZDebug.print("-");
ZDebug.print(packet[6], BIN);
ZDebug.print("-");
ZDebug.print(packet[5], BIN);
ZDebug.print("-");
ZDebug.print(packet[4], BIN);
ZDebug.print(" SET_BROADCASTFLAGS: ");
ZDebug.println(addIPToSlot(client, 0x00), BIN);
// 1=BC Power, Loco INFO, Trnt INFO; 2=BC Änderungen der Rückmelder am R-Bus
#endif
break;
}
case (LAN_GET_BROADCASTFLAGS): {
unsigned long flag = getz21BcFlag(addIPToSlot(client, 0x00));
data[0] = flag;
data[1] = flag >> 8;
data[2] = flag >> 16;
data[3] = flag >> 24;
EthSend (client, 0x08, LAN_GET_BROADCASTFLAGS, data, false, Z21bcNone);
#if defined(SERIALDEBUG)
ZDebug.print("GET_BROADCASTFLAGS: ");
ZDebug.println(flag, BIN);
#endif
break;
}
case (LAN_GET_LOCOMODE):
/*
In der Z21 kann das Ausgabeformat (DCC, MM) pro Lok-Adresse persistent gespeichert werden.
Es können maximal 256 verschiedene Lok-Adressen abgelegt werden. Jede Adresse >= 256 ist automatisch DCC.
*/
data[0] = packet[4];
data[1] = packet[5];
data[2] = 0; //0=DCC Format; 1=MM Format
EthSend (client, 0x07, LAN_GET_LOCOMODE, data, false, Z21bcNone);
break;
case (LAN_SET_LOCOMODE):
//nothing to replay all DCC Format
break;
case (LAN_GET_TURNOUTMODE):
/*
In der Z21 kann das Ausgabeformat (DCC, MM) pro Funktionsdecoder-Adresse persistent gespeichert werden.
Es können maximal 256 verschiedene Funktionsdecoder -Adressen gespeichert werden. Jede Adresse >= 256 ist automatisch DCC.
*/
data[0] = packet[4];
data[1] = packet[5];
data[2] = 0; //0=DCC Format; 1=MM Format
EthSend (client, 0x07, LAN_GET_LOCOMODE, data, false, Z21bcNone);
break;
case (LAN_SET_TURNOUTMODE):
//nothing to replay all DCC Format
break;
case (LAN_RMBUS_GETDATA):
if (notifyz21S88Data) {
#if defined(SERIALDEBUG)
ZDebug.println("RMBUS_GETDATA");
#endif
//ask for group state 'Gruppenindex'
notifyz21S88Data(packet[4]); //normal Antwort hier nur an den anfragenden Client! (Antwort geht hier an alle!)
}
break;
case (LAN_RMBUS_PROGRAMMODULE):
break;
case (LAN_SYSTEMSTATE_GETDATA): { //System state
#if defined(SERIALDEBUG)
ZDebug.println("LAN_SYS-State");
#endif
if (notifyz21getSystemInfo)
notifyz21getSystemInfo(client);
break;
}
case (LAN_RAILCOM_GETDATA): {
uint16_t Adr = 0;
if (packet[4] == 0x01) { //RailCom-Daten für die gegebene Lokadresse anfordern
Adr = word(packet[6],packet[5]);
}
if (notifyz21Railcom)
Adr = notifyz21Railcom(); //return global Railcom Adr
data[0] = Adr >> 8; //LocoAddress
data[1] = Adr & 0xFF; //LocoAddress
data[2] = 0x00; //UINT32 ReceiveCounter Empfangszähler in Z21
data[3] = 0x00;
data[4] = 0x00;
data[5] = 0x00;
data[6] = 0x00; //UINT32 ErrorCounter Empfangsfehlerzähler in Z21
data[7] = 0x00;
data[8] = 0x00;
data[9] = 0x00;
/*
data[10] = 0x00; //UINT8 Reserved1 experimentell, siehe Anmerkung
data[11] = 0x00; //UINT8 Reserved2 experimentell, siehe Anmerkung
data[12] = 0x00; //UINT8 Reserved3 experimentell, siehe Anmerkung
*/
EthSend (client, 0x0E, LAN_RAILCOM_DATACHANGED, data, false, Z21bcNone);
break;
}
case (LAN_LOCONET_FROM_LAN): {
#if defined(SERIALDEBUG)
ZDebug.println("LOCONET_FROM_LAN");
#endif
byte LNdata[packet[0] - 0x04]; //n Bytes
for (byte i = 0; i < (packet[0] - 0x04); i++)
LNdata[i] = packet[0x04+i];
if (notifyz21LNSendPacket)
notifyz21LNSendPacket(LNdata, packet[0] - 0x04);
//Melden an andere LAN-Client das Meldung auf LocoNet-Bus geschrieben wurde
EthSend(client, packet[0], LAN_LOCONET_FROM_LAN, LNdata, false, Z21bcLocoNet_s); //LAN_LOCONET_FROM_LAN not to the client!
break;
}
case (LAN_LOCONET_DISPATCH_ADDR): {
if (notifyz21LNdispatch) {
data[0] = packet[4];
data[1] = packet[5];
data[2] = notifyz21LNdispatch(word(packet[5], packet[4])); //dispatchSlot
#if defined(SERIALDEBUG)
ZDebug.print("LOCONET_DISPATCH_ADDR ");
ZDebug.print(word(packet[5], packet[4]));
ZDebug.print(",");
ZDebug.println(data[2]);
#endif
EthSend(client, 0x07, LAN_LOCONET_DISPATCH_ADDR, data, false, Z21bcNone);
}
break; }
case (LAN_LOCONET_DETECTOR):
if (notifyz21LNdetector) {
#if defined(SERIALDEBUG)
ZDebug.println("LOCONET_DETECTOR Abfrage");
#endif
notifyz21LNdetector(client, packet[4], word(packet[6], packet[5])); //Anforderung Typ & Reportadresse
}
break;
case (LAN_CAN_DETECTOR):
if (notifyz21CANdetector) {
#if defined(SERIALDEBUG)
ZDebug.println("CAN_DETECTOR Abfrage");
#endif
notifyz21CANdetector(client, packet[4], word(packet[6], packet[5])); //Anforderung Typ & CAN-ID
}
break;
case (0x12): //configuration read
// <-- 04 00 12 00
// 0e 00 12 00 01 00 01 03 01 00 03 00 00 00
for (byte i = 0; i < 10; i++) {
data[i] = FSTORAGE.read(CONF1STORE+i);
}
EthSend(client, 0x0e, 0x12, data, false, Z21bcNone);
#if defined(SERIALDEBUG)
ZDebug.print("Z21 Eins(read) ");
ZDebug.print("RailCom: ");
ZDebug.print(data[0], HEX);
ZDebug.print(", PWR-Button: ");
ZDebug.print(data[2], HEX);
ZDebug.print(", ProgRead: ");
switch (data[3]) {
case 0x00: ZDebug.print("nothing"); break;
case 0x01: ZDebug.print("Bit"); break;
case 0x02: ZDebug.print("Byte"); break;
case 0x03: ZDebug.print("both"); break;
}
ZDebug.println();
#endif
break;
case (0x13): { //configuration write
//<-- 0e 00 13 00 01 00 01 03 01 00 03 00 00 00
//0x0e = Length; 0x12 = Header
/* Daten:
(0x01) RailCom: 0=aus/off, 1=ein/on
(0x00)
(0x01) Power-Button: 0=Gleisspannung aus, 1=Nothalt
(0x03) Auslese-Modus: 0=Nichts, 1=Bit, 2=Byte, 3=Beides
*/
#if defined(SERIALDEBUG)
ZDebug.print("Z21 Eins(write) ");
ZDebug.print("RailCom: ");
ZDebug.print(packet[4], HEX);
ZDebug.print(", PWR-Button: ");
ZDebug.print(packet[6], HEX);
ZDebug.print(", ProgRead: ");
switch (packet[7]) {
case 0x00: ZDebug.print("nothing"); break;
case 0x01: ZDebug.print("Bit"); break;
case 0x02: ZDebug.print("Byte"); break;
case 0x03: ZDebug.print("both"); break;
}
ZDebug.println();
#endif
for (byte i = 0; i < 10; i++) {
FSTORAGE.FSTORAGEMODE(CONF1STORE+i,packet[4+i]);
}
#if defined(ESP32)
portENTER_CRITICAL(&myMutex);
#endif
/*
#if defined(ESP8266) || defined(ESP32)
FSTORAGE.commit();
#endif
*/
#if defined(ESP32)
portEXIT_CRITICAL(&myMutex);
#endif
//Request DCC to change
if (notifyz21UpdateConf)
notifyz21UpdateConf();
break;
}
case (0x16): //configuration read
//<-- 04 00 16 00
//14 00 16 00 19 06 07 01 05 14 88 13 10 27 32 00 50 46 20 4e
#if defined(ESP32)
portENTER_CRITICAL(&myMutex);
#endif
for (byte i = 0; i < 16; i++) {
data[i] = FSTORAGE.read(CONF2STORE+i);
}
#if defined(ESP32)
portEXIT_CRITICAL(&myMutex);
#endif
//check range of MainV:
if ((word(data[13],data[12]) > 0x59D8) || (word(data[13],data[12]) < 0x2A8F)) {
//set to 20V default:
data[13] = highByte(0x4e20);
data[12] = lowByte(0x4e20);
}
//check range of ProgV:
if ((word(data[15],data[14]) > 0x59D8) || (word(data[15],data[14]) < 0x2A8F)) {
//set to 20V default:
data[15] = highByte(0x4e20);
data[14] = lowByte(0x4e20);
}
EthSend(client, 0x14, 0x16, data, false, Z21bcNone);
#if defined(SERIALDEBUG)
ZDebug.print("Z21 Eins(read) ");
ZDebug.print("RstP(s): ");
ZDebug.print(data[0]); //EEPROM Adr 60
ZDebug.print(", RstP(f): ");
ZDebug.print(data[1]); //EEPROM Adr 61
ZDebug.print(", ProgP: ");
ZDebug.print(data[2]); //EEPROM Adr 62
ZDebug.print(", MainV: ");
ZDebug.print(word(data[13],data[12])); //Value only: 11000 - 23000
ZDebug.print(", ProgV: ");
ZDebug.print(word(data[15],data[14])); //Value only: 11000=0x2A8F - 23000=0x59D8
ZDebug.println();
#endif
break;
case (0x17): { //configuration write
//<-- 14 00 17 00 19 06 07 01 05 14 88 13 10 27 32 00 50 46 20 4e
//0x14 = Length; 0x16 = Header(read), 0x17 = Header(write)
/* Daten:
(0x19) Reset Packet (starten) (25-255)
(0x06) Reset Packet (fortsetzen) (6-64)
(0x07) Programmier-Packete (7-64)
(0x01) ?
(0x05) ?
(0x14) ?
(0x88) ?
(0x13) ?
(0x10) ?
(0x27) ?
(0x32) ?
(0x00) ?
(0x50) Hauptgleis (LSB) (11-23V)
(0x46) Hauptgleis (MSB)
(0x20) Programmiergleis (LSB) (11-23V): 20V=0x4e20, 21V=0x5208, 22V=0x55F0
(0x4e) Programmiergleis (MSB)
*/
#if defined(SERIALDEBUG)
ZDebug.print("Z21 Eins(write) ");
ZDebug.print("RstP(s): ");
ZDebug.print(packet[4]); //EEPROM Adr 60
ZDebug.print(", RstP(f): ");
ZDebug.print(packet[5]); //EEPROM Adr 61
ZDebug.print(", ProgP: ");
ZDebug.print(packet[6]); //EEPROM Adr 62
ZDebug.print(", MainV: ");
ZDebug.print(word(packet[17],packet[16]));
ZDebug.print(", ProgV: ");
ZDebug.print(word(packet[19],packet[18]));
ZDebug.println();
#endif
for (byte i = 0; i < 16; i++) {
FSTORAGE.FSTORAGEMODE(CONF2STORE+i,packet[4+i]);
}
/*
#if defined(ESP8266) || defined(ESP32)
FSTORAGE.commit();
#endif
*/
//Request DCC to change
if (notifyz21UpdateConf)
notifyz21UpdateConf();
break;
}
default:
#if defined(SERIALDEBUG)
ZDebug.print("UNKNOWN_COMMAND");
// for (byte i = 0; i < packet[0]; i++) {
// ZDebug.print(" 0x");
// ZDebug.print(packet[i], HEX);
// }
ZDebug.println();
#endif
data[0] = 0x61;
data[1] = 0x82;
EthSend (client, 0x07, LAN_X_Header, data, true, Z21bcNone);
}
//---------------------------------------------------------------------------------------
//check if IP is still used:
unsigned long currentMillis = millis();
if ((currentMillis - z21IPpreviousMillis) > z21IPinterval) {
z21IPpreviousMillis = currentMillis;
for (byte i = 0; i < z21clientMAX; i++) {
if (ActIP[i].time > 0) {
ActIP[i].time--; //Zeit herrunterrechnen
}
else {
clearIP(i); //clear IP DATA
//send MESSAGE clear Client
}
}
}
}
//--------------------------------------------------------------------------------------------
//Zustand der Gleisversorgung setzten
void z21Class::setPower(byte state)
{
byte data[] = { LAN_X_BC_TRACK_POWER, 0x00 };
Railpower = state;
switch (state) {
case csNormal:
data[1] = 0x01;
break;
case csTrackVoltageOff:
data[1] = 0x00;
break;
case csServiceMode:
data[1] = 0x02;
break;
case csShortCircuit:
data[1] = 0x08;
break;
case csEmergencyStop:
data[0] = 0x81;
data[1] = 0x00;
break;
}
EthSend(0, 0x07, LAN_X_Header, data, true, Z21bcAll_s);
#if defined(SERIALDEBUG)
ZDebug.print("set_X_BC_TRACK_POWER ");
ZDebug.println(state, HEX);
#endif
}
//--------------------------------------------------------------------------------------------
//Abfrage letzte Meldung über Gleispannungszustand
byte z21Class::getPower()
{
return Railpower;
}
//--------------------------------------------------------------------------------------------
//return request for POM read byte
void z21Class::setCVPOMBYTE (uint16_t CVAdr, uint8_t value) {
byte data[5];
data[0] = 0x64; //X-Header
data[1] = 0x14; //DB0
data[2] = (CVAdr >> 8) & 0x3F; //CV_MSB;
data[3] = CVAdr & 0xFF; //CV_LSB;
data[4] = value;
EthSend (0, 0x0A, LAN_X_Header, data, true, Z21bcAll_s);
}
//--------------------------------------------------------------------------------------------
//Zustand Rückmeldung non - Z21 device - Busy!
void z21Class::setLocoStateExt (int Adr)
{
/* uint8_t ldata[6];
if (notifyz21LocoState)
notifyz21LocoState(Adr, 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]
byte data[10];
data[0] = LAN_X_LOCO_INFO; //0xEF X-HEADER
data[1] = (Adr >> 8) & 0x3F;
data[2] = Adr & 0xFF;
// Fahrstufeninformation: 0=14, 2=28, 4=128
if ((ldata[0] & 0x03) == DCCSTEP14)
data[3] = 0; // 14 steps
if ((ldata[0] & 0x03) == DCCSTEP28)
data[3] = 2; // 28 steps
if ((ldata[0] & 0x03) == DCCSTEP128)
data[3] = 4; // 128 steps
data[3] = data[3] | 0x08; //BUSY!
data[4] = (char) ldata[1]; //DSSS SSSS
data[5] = (char) ldata[2] & 0x1F; //F0, F4, F3, F2, F1
data[6] = (char) ldata[3]; //F5 - F12; Funktion F5 ist bit0 (LSB)
data[7] = (char) ldata[4]; //F13-F20
data[8] = (char) ldata[5]; //F21-F28
data[9] = (char) ldata[8] >> 7; //F31-F29 only
*/
reqLocoBusy(Adr);
returnLocoStateFull(0, Adr, true);
//EthSend(0, 15, LAN_X_Header, data, true, Z21bcAll_s | Z21bcNetAll_s); //Send Loco Status und Funktions to all active Apps
}
//--------------------------------------------------------------------------------------------
//Gibt aktuellen Lokstatus an Anfragenden Zurück
void z21Class::returnLocoStateFull (byte client, uint16_t Adr, bool bc)
//bc = true => to inform also other client over the change.
//bc = false => just ask about the loco state
{
if (Adr == 0) {
//Not a valid loco adr!
return;
}
uint8_t ldata[6];
if (notifyz21LocoState)
notifyz21LocoState(Adr, 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]
byte data[10];
data[0] = LAN_X_LOCO_INFO; //0xEF X-HEADER
data[1] = (Adr >> 8) & 0x3F;
data[2] = Adr & 0xFF;
// Fahrstufeninformation: 0=14, 2=28, 4=128
if ((ldata[0] & 0x03) == DCCSTEP14)
data[3] = 0; // 14 steps
if ((ldata[0] & 0x03) == DCCSTEP28)
data[3] = 2; // 28 steps
if ((ldata[0] & 0x03) == DCCSTEP128)
data[3] = 4; // 128 steps
data[3] = data[3] | 0x08; //BUSY!
data[4] = (char) ldata[1]; //DSSS SSSS
data[5] = (char) ldata[2] & 0x1F; //F0, F4, F3, F2, F1
data[6] = (char) ldata[3]; //F5 - F12; Funktion F5 ist bit0 (LSB)
data[7] = (char) ldata[4]; //F13-F20
data[8] = (char) ldata[5]; //F21-F28
data[9] = (char) ldata[2] >> 7; //F31-F29
//Info to all:
for (byte i = 0; i < z21clientMAX; i++) {
if (ActIP[i].client != client) {
if ((ActIP[i].BCFlag & (Z21bcAll_s | Z21bcNetAll_s)) > 0) {
if (bc == true)
EthSend (ActIP[i].client, 15, LAN_X_Header, data, true, Z21bcNone); //Send Loco status und Funktions to BC Apps
}
}
else { //Info to client that ask:
if (ActIP[i].adr == Adr) {
data[3] = data[3] & 0b111; //clear busy flag!
}
EthSend (client, 15, LAN_X_Header, data, true, Z21bcNone); //Send Loco status und Funktions to request App
data[3] = data[3] | 0x08; //BUSY!
}
}
}
//--------------------------------------------------------------------------------------------
//return state of S88 sensors
void z21Class::setS88Data(byte *data) {
EthSend(0, 0x0F, LAN_RMBUS_DATACHANGED, data, false, Z21bcRBus_s); //RMBUS_DATACHANED
}
//--------------------------------------------------------------------------------------------
//return state from LN detector
void z21Class::setLNDetector(uint8_t client, byte *data, byte DataLen) {
if (client > 0)
EthSend(client, 0x04 + DataLen, LAN_LOCONET_DETECTOR, data, false, Z21bcNone); //LAN_LOCONET_DETECTOR
else EthSend(0, 0x04 + DataLen, LAN_LOCONET_DETECTOR, data, false, Z21bcLocoNet_s); //LAN_LOCONET_DETECTOR
}
//--------------------------------------------------------------------------------------------
//LN Meldungen weiterleiten
bool z21Class::setLNMessage(byte *data, byte DataLen, byte bcType, bool TX) {
if (DataLen > 20) //Z21 LocoNet tunnel DATA has max 20 Byte!
return false;
if (TX) //Send by Z21 or Receive a Packet?
EthSend(0, 0x04 + DataLen, LAN_LOCONET_Z21_TX, data, false, bcType); //LAN_LOCONET_Z21_TX
else EthSend(0, 0x04 + DataLen, LAN_LOCONET_Z21_RX, data, false, bcType); //LAN_LOCONET_Z21_RX
return true;
}
//--------------------------------------------------------------------------------------------
//return state from CAN detector
void z21Class::setCANDetector(uint16_t NID, uint16_t Adr, uint8_t port, uint8_t typ, uint16_t v1, uint16_t v2) {
byte data[10];
data[0] = NID & 0xFF;
data[1] = NID >> 8;
data[2] = Adr & 0xFF;
data[3] = Adr >> 8;
data[4] = port;
data[5] = typ;
data[6] = v1 & 0xFF;
data[7] = v1 >> 8;
data[8] = v2 & 0xFF;
data[9] = v2 >> 8;
EthSend(0, 0x0E, LAN_CAN_DETECTOR, data, false, Z21bcCANDetector_s); //CAN_DETECTOR
}
//--------------------------------------------------------------------------------------------
//Return the state of accessory
void z21Class::setTrntInfo(uint16_t Adr, bool State) {
byte data[4];
data[0] = LAN_X_TURNOUT_INFO; //0x43 X-HEADER
data[1] = Adr >> 8; //High
data[2] = Adr & 0xFF; //Low
data[3] = State + 1;
// if (State == true)
// data[3] = 2;
// else data[3] = 1;
EthSend(0, 0x09, LAN_X_Header, data, true, Z21bcAll_s);
}
//--------------------------------------------------------------------------------------------
//Return EXT accessory info
void z21Class::setExtACCInfo(uint16_t Adr, byte State, bool Status) {
byte data[5];
data[0] = LAN_X_GET_EXT_ACCESSORY_INFO; //0x44 X-HEADER
data[1] = Adr >> 8; //High
data[2] = Adr & 0xFF; //Low
data[3] = State;
data[4] = Status; //0x00 … Data Valid; 0xFF … Data Unknown
EthSend(0, 0x0A, LAN_X_Header, data, true, Z21bcAll_s);
}
//--------------------------------------------------------------------------------------------
//Return CV Value for Programming
void z21Class::setCVReturn (uint16_t CV, uint8_t value) {
byte data[5];
data[0] = LAN_X_CV_RESULT; //0x64 X-Header
data[1] = 0x14; //DB0
data[2] = CV >> 8; //CV_MSB;
data[3] = CV & 0xFF; //CV_LSB;
data[4] = value;
EthSend (0, 0x0A, LAN_X_Header, data, true, Z21bcAll_s);
}
//--------------------------------------------------------------------------------------------
//Return no ACK from Decoder
void z21Class::setCVNack() {
byte data[2];
data[0] = LAN_X_CV_NACK; //0x61 X-Header
data[1] = 0x13; //DB0
EthSend (0, 0x07, LAN_X_Header, data, true, Z21bcAll_s);
}
//--------------------------------------------------------------------------------------------
//Return Short while Programming
void z21Class::setCVNackSC() {
byte data[2];
data[0] = LAN_X_CV_NACK_SC; //0x61 X-Header
data[1] = 0x12; //DB0
EthSend (0, 0x07, LAN_X_Header, data, true, Z21bcAll_s);
}
//--------------------------------------------------------------------------------------------
//Send Changing of SystemInfo
void z21Class::sendSystemInfo(byte client, uint16_t maincurrent, uint16_t mainvoltage, uint16_t temp) {
byte data[16];
data[0] = maincurrent & 0xFF; //MainCurrent mA
data[1] = maincurrent >> 8; //MainCurrent mA
data[2] = data[0]; //ProgCurrent mA
data[3] = data[1]; //ProgCurrent mA
data[4] = data[0]; //FilteredMainCurrent
data[5] = data[1]; //FilteredMainCurrent
data[6] = temp & 0xFF; //Temperature
data[7] = temp >> 8; //Temperature
data[8] = mainvoltage & 0xFF; //SupplyVoltage
data[9] = mainvoltage >> 8; //SupplyVoltage
data[10] = data[8]; //VCCVoltage
data[11] = data[9]; //VCCVoltage
data[12] = Railpower; //CentralState
if (data[12] == csServiceMode)
data[12] = 0x20;
/*Bitmasken für CentralState:
#define csEmergencyStop 0x01 // Der Nothalt ist eingeschaltet
#define csTrackVoltageOff 0x02 // Die Gleisspannung ist abgeschaltet
#define csShortCircuit 0x04 // Kurzschluss
#define csProgrammingModeActive 0x20 // Der Programmiermodus ist aktiv
*/
data[13] = 0x00; //CentralStateEx
/* Bitmasken für CentralStateEx:
#define cseHighTemperature 0x01 // zu hohe Temperatur
#define csePowerLost 0x02 // zu geringe Eingangsspannung
#define cseShortCircuitExternal 0x04 // am externen Booster-Ausgang
#define cseShortCircuitInternal 0x08 // am Hauptgleis oder Programmiergleis
#define cseRCN213 0x20 // Weichenadressierung gem. RCN213
*/
data[14] = 0x00; //reserved
data[15] = 0x01; //Capabilitie DCC only
if (FSTORAGE.read(CONF1STORE) == 0x01) //RailCom
data[15] |= 0x08; //RailCom aktiv!
data[15] |= 0x10 | 0x20 | 0x40; //LAN-Befehle
/*
#define capDCC 0x01 // beherrscht DCC
#define capMM 0x02 // beherrscht MM
//#define capReserved 0x04 // reserviert für zukünftige Erweiterungen
#define capRailCom 0x08 // RailCom ist aktiviert
#define capLocoCmds 0x10 // akzeptiert LAN-Befehle für Lokdecoder
#define capAccessoryCmds 0x20 // akzeptiert LAN-Befehle für Zubehördecoder
#define capDetectorCmds 0x40 // akzeptiert LAN-Befehle für Belegtmelder
#define capNeedsUnlockCode 0x80 // benötigt Freischaltcode (z21start)
*/
//only to the request client if or if client = 0 to all that select this message (Abo)!
if (client > 0)
EthSend (client, 0x14, LAN_SYSTEMSTATE_DATACHANGED, data, false, Z21bcNone);
else EthSend (0, 0x14, LAN_SYSTEMSTATE_DATACHANGED, data, false, Z21bcSystemInfo_s);
}
// Private Methods ///////////////////////////////////////////////////////////////////////////////////////////////////
// Functions only available to other functions in this library *******************************************************
//--------------------------------------------------------------------------------------------
void z21Class::EthSend (byte client, unsigned int DataLen, unsigned int Header, byte *dataString, boolean withXOR, byte BC) {
byte data[DataLen]; //z21 send storage
//--------------------------------------------
//XOR bestimmen:
data[0] = DataLen & 0xFF;
data[1] = DataLen >> 8;
data[2] = Header & 0xFF;
data[3] = Header >> 8;
data[DataLen - 1] = 0; //XOR
for (byte i = 0; i < (DataLen-5+!withXOR); i++) { //Ohne Length und Header und XOR
if (withXOR)
data[DataLen-1] = data[DataLen-1] ^ *dataString;
data[i+4] = *dataString;
dataString++;
}
//--------------------------------------------
if (client > 0 && BC == Z21bcNone) {
if (notifyz21EthSend)
notifyz21EthSend(client, data);
#if defined (SERIALDEBUG)
ZDebug.print("CTX ");
ZDebug.print(client);
ZDebug.print(" : ");
for (byte x = 0; x < data[0]; x++) {
ZDebug.print(data[x], HEX);
ZDebug.print(" ");
}
ZDebug.println();
#endif
}
else {
byte clientOut = 0; //client;
for (byte i = 0; i < z21clientMAX; i++) {
if ( (ActIP[i].time > 0) && ( (BC & ActIP[i].BCFlag) > 0) ) { //Boradcast & Noch aktiv
if (BC != 0) {
if (BC == Z21bcAll_s)
clientOut = 0; //ALL
else clientOut = ActIP[i].client;
}
if ((clientOut != client) || (clientOut == 0)) { //wenn client > 0 und nicht Z21bcNone, sende an alle außer den client!
//--------------------------------------------
if (notifyz21EthSend)
notifyz21EthSend(clientOut, data);
#if defined (SERIALDEBUG)
ZDebug.print(i);
ZDebug.print("BTX ");
ZDebug.print(clientOut);
ZDebug.print(" BC:");
ZDebug.print(BC & ActIP[i].BCFlag, BIN);
ZDebug.print(" : ");
for (byte x = 0; x < data[0]; x++) {
ZDebug.print(data[x], HEX);
ZDebug.print(" ");
}
ZDebug.println();
#endif
if (clientOut == 0)
return;
}
}
}
}
}
//--------------------------------------------------------------------------------------------
//Convert local stored flag back into a Z21 Flag
unsigned long z21Class::getz21BcFlag (byte flag) {
unsigned long outFlag = 0;
if ((flag & Z21bcAll_s) != 0)
outFlag |= Z21bcAll;
if ((flag & Z21bcRBus_s) != 0)
outFlag |= Z21bcRBus;
if ((flag & Z21bcSystemInfo_s) != 0)
outFlag |= Z21bcSystemInfo;
if ((flag & Z21bcNetAll_s) != 0)
outFlag |= Z21bcNetAll;
if ((flag & Z21bcLocoNet_s) != 0)
outFlag |= Z21bcLocoNet;
if ((flag & Z21bcLocoNetLocos_s) != 0)
outFlag |= Z21bcLocoNetLocos;
if ((flag & Z21bcLocoNetSwitches_s) != 0)
outFlag |= Z21bcLocoNetSwitches;
if ((flag & Z21bcLocoNetGBM_s) != 0)
outFlag |= Z21bcLocoNetGBM;
return outFlag;
}
//--------------------------------------------------------------------------------------------
//Convert Z21 LAN BC flag to local stored flag
byte z21Class::getLocalBcFlag (unsigned long flag) {
byte outFlag = 0;
if ((flag & Z21bcAll) != 0)
outFlag |= Z21bcAll_s;
if ((flag & Z21bcRBus) != 0)
outFlag |= Z21bcRBus_s;
if ((flag & Z21bcSystemInfo) != 0)
outFlag |= Z21bcSystemInfo_s;
if ((flag & Z21bcNetAll) != 0)
outFlag |= Z21bcNetAll_s;
if ((flag & Z21bcLocoNet) != 0)
outFlag |= Z21bcLocoNet_s;
if ((flag & Z21bcLocoNetLocos) != 0)
outFlag |= Z21bcLocoNetLocos_s;
if ((flag & Z21bcLocoNetSwitches) != 0)
outFlag |= Z21bcLocoNetSwitches_s;
if ((flag & Z21bcLocoNetGBM) != 0)
outFlag |= Z21bcLocoNetGBM_s;
return outFlag;
}
//--------------------------------------------------------------------------------------------
// delete the stored IP-Address
void z21Class::clearIP (byte pos) {
ActIP[pos].client = 0;
ActIP[pos].BCFlag = 0;
ActIP[pos].time = 0;
ActIP[pos].adr = 0;
}
//--------------------------------------------------------------------------------------------
void z21Class::clearIPSlots() {
for (int i = 0; i < z21clientMAX; i++)
clearIP(i);
}
//--------------------------------------------------------------------------------------------
void z21Class::clearIPSlot(byte client) {
for (int i = 0; i < z21clientMAX; i++) {
if (ActIP[i].client == client) {
clearIP(i);
return;
}
}
}
//--------------------------------------------------------------------------------------------
//speichern des BCFlag im EEPROM
void z21Class::setEEPROMBCFlag(byte IPHash, byte BCFlag) {
FSTORAGE.FSTORAGEMODE(CLIENTHASHSTORE | IPHash, BCFlag);
#if defined(SERIALDEBUG)
ZDebug.print(CLIENTHASHSTORE | IPHash);
ZDebug.print(" write: ");
ZDebug.println(BCFlag, BIN);
#endif
}
//--------------------------------------------------------------------------------------------
//lesen des BCFlag im EEPROM
byte z21Class::findEEPROMBCFlag(byte IPHash) {
uint8_t flag = FSTORAGE.read(CLIENTHASHSTORE | IPHash);
#if defined(SERIALDEBUG)
ZDebug.print(CLIENTHASHSTORE | IPHash);
ZDebug.print("read: ");
ZDebug.println(flag, BIN);
#endif
//wurde BC im EEPROM bereits erfasst?
if (flag == 0xFF)
return 0x00; //not found!
return flag;
}
//--------------------------------------------------------------------------------------------
byte z21Class::addIPToSlot (byte client, byte BCFlag) {
byte Slot = z21clientMAX;
for (byte i = 0; i < z21clientMAX; i++) {
if (ActIP[i].client == client) {
ActIP[i].time = z21ActTimeIP;
if (BCFlag != 0) { //Falls BC Flag übertragen wurde diesen hinzufügen!
ActIP[i].BCFlag = BCFlag;
if (notifyz21ClientHash)
setEEPROMBCFlag(notifyz21ClientHash(client), BCFlag);
}
return ActIP[i].BCFlag; //BC Flag 4. Byte Rückmelden
}
else if (ActIP[i].time == 0 && Slot == z21clientMAX)
Slot = i;
}
ActIP[Slot].client = client;
ActIP[Slot].time = z21ActTimeIP;
setPower(Railpower); //inform the client with last power state
//read out last BCFlag from EEPROM:
if (notifyz21ClientHash)
ActIP[Slot].BCFlag = findEEPROMBCFlag(notifyz21ClientHash(client));
return ActIP[Slot].BCFlag; //BC Flag 4. Byte Rückmelden
}
//--------------------------------------------------------------------------------------------
//check if there are slots with the same loco, set them to busy
void z21Class::setOtherSlotBusy(byte slot) {
for (byte i = 0; i < z21clientMAX; i++) {
if ((i != slot) && (ActIP[slot].adr == ActIP[i].adr)) { //if in other Slot -> set busy
ActIP[i].adr = 0; //clean slot that informed as busy & let it activ
//Inform with busy message:
//not used!
}
}
}
//--------------------------------------------------------------------------------------------
//Add loco to slot.
void z21Class::addBusySlot (byte client, uint16_t adr) {
for (byte i = 0; i < z21clientMAX; i++) {
if (ActIP[i].client == client) {
if (ActIP[i].adr != adr) { //skip is already used by this client
ActIP[i].adr = adr; //store loco that is used
setOtherSlotBusy(i); //make other busy
}
break;
}
}
}
//--------------------------------------------------------------------------------------------
//used by non Z21 client
void z21Class::reqLocoBusy (uint16_t adr) {
for (byte i = 0; i < z21clientMAX; i++) {
if (adr == ActIP[i].adr) {
ActIP[i].adr = 0; //clear
}
}
}