Índice general Foros Digital, Electricidad e Informática Emular la Z21 con Arduino

Emular la Z21 con Arduino

Moderador: 241-2001


Nota 19 Abr 2017 06:12

Desconectado
Mensajes: 262
Registrado: 28 Oct 2015 09:35
La fuente es del fabricante Recom r-785, en Digi-Key las he visto, pero quien nos
puede dar más información es Norber.
Seguiré haciendo pruebas con el Mega hasta que me llegue el UNO, ya te iré haciendo
algunas preguntillas.
Respecto al pulsado, de momento no es mi prioridad, pero no lo he entendido bien
“” El pulsador es para modificar los ajustes por defecto de la z21f ””.
Ya comentaré mis avances ( si lo consigo ).
Un saludos a todos.

Nota 19 Abr 2017 07:39

Desconectado
Mensajes: 751
Ubicación: Salamanca
Registrado: 12 Ene 2012 14:44
Pablob escribió:
Respecto al pulsado, de momento no es mi prioridad, pero no lo he entendido bien
“” El pulsador es para modificar los ajustes por defecto de la z21f ””.


La dirección IP autoasignada en caso de que el router no le dé una automáticamente mediante el DHCP. Yo le tengo configurada la IP 192.168.1.112 y le he dicho al router de casa que cuando esta interface pida conexión le asigne también esa misma IP. Así cuando enciendo la maqueta y abro la z21mobile.app en el móvil no tengo que reconfigurar la dirección de la z21f nunca, pues siempre se le asigna la que he citado y la conexión se produce en cosa de 20 s.
Saludos

[Multimaus + GenLi-S88 + +z21f. + RocRail (MacOsX)]
H0 Renfe, sin catenaria

Nota 20 Abr 2017 15:31

Desconectado
Mensajes: 998
Ubicación: Granada
Registrado: 30 Nov 2013 14:33
Muy interesante, ya he probado la central Dcc++, pero este sistema, tiene la ventaja de no necesitar el pc y de usar el multimaus.

Me lo apunto para investigar cuando tenga tiempo.

Saludos
TRATOS POSITIVOS: TODOS
TRATOS NEGATIVOS: NINGUNO

Nota 21 Abr 2017 06:53

Desconectado
Mensajes: 262
Registrado: 28 Oct 2015 09:35
Hola, dando vuelta al tema tengo una duda, el cable que se utiliza desde la centralita Roco salida (slave) al arduino es cruzado?.
Gracias.

Nota 21 Abr 2017 22:44

Desconectado
Mensajes: 262
Registrado: 28 Oct 2015 09:35
He estado haciendo pruebas, y he conseguido conectarme al arduino.
El problema que tengo es que, salta la señal de corto, como puedes ver en la imagen

Imagen

, pero realmente no lo hay, ya que con el multimaus muevo una locomotora.
He estado viendo que hay diferente placas Ethernet w5100 y la serigrafía son diferentes, os
Envío también la foto por si me podéis ayudar.
Imagen

La patilla 2-3 del MAX 485, en donde la conecta, viendo el esquema entiendo que va al pin 3 de la placa Ethernet, me lo puedes confirmar y el nivel lógico que tiene.
Muchas gracias de antemano.

Nota 21 Abr 2017 22:49

Desconectado
Mensajes: 751
Ubicación: Salamanca
Registrado: 12 Ene 2012 14:44
Si te has 'conectado al Arduino' como dices es que has conseguido que el móvil detecte la nueva z21f. Enhorabuena. La tarjeta Ethernet W5100 parece estar cumpliendo su función.

Tu problema está en otra parte.

Esta es mi placa con la fuente de alimentación y el MAX485.

NORBER_z21f_V1.1.png


Como puedes ver la señal del bus XpressNet, adaptada por el MAX485, se aplica al puerto serie del Arduino, pines 0 y 1. No es en el pin 3 como tú dices. Probablemente no esté llegando información del bus al micro. Ello explicaría el fallo de tensión que reporta la aplicación en el móvil. Usa los pines 0 y 1 como ves en mi placa.

Ánimo que estás muy, muy cerca de lograrlo.
Éxito seguro!! :D
Saludos

[Multimaus + GenLi-S88 + +z21f. + RocRail (MacOsX)]
H0 Renfe, sin catenaria

Nota 21 Abr 2017 22:58

Desconectado
Mensajes: 751
Ubicación: Salamanca
Registrado: 12 Ene 2012 14:44
Ahí va un .PDF con el fotolito de mi placa.
Espero que os resulte útil.

NORBER_z21f_V1.1.pdf
(273.64 KiB) 495 veces
Saludos

[Multimaus + GenLi-S88 + +z21f. + RocRail (MacOsX)]
H0 Renfe, sin catenaria

Nota 22 Abr 2017 17:34

Desconectado
Mensajes: 262
Registrado: 28 Oct 2015 09:35
Por fin ya me funciona.
Como puedo subir un video?, es corto pero muy contento :) :) .
En el caso mío he utilizado un Mega, y utilizando los pines RX1-TX1, lo demás como ha hecho Norber.

Un saludo, muchas gracias.

Nota 09 May 2017 20:57

Desconectado
Mensajes: 751
Ubicación: Salamanca
Registrado: 12 Ene 2012 14:44
Hola! Un apunte más de última hora:

He comprobado que la z21f de este hilo también es una interface estupenda entre Rocrail y Multimaus. Ni lo sospechaba. Funciona fenomenal. Simplemente defino una nueva interface de control en Rocrail server y hala, por arte de magia controlo la maqueta igual que con la GenLi-S88 de Paco Cañada. Pero esta nueva interface me da, además, la posibilidad de usar la z21mobile.app oficial de Roco.

Lo más gracioso de esto es que ni sospechaba que pudiera ser así. Me han 'insistido' en que lo verificara y, voilà, ha resultado que sí :lol: .
Saludos

[Multimaus + GenLi-S88 + +z21f. + RocRail (MacOsX)]
H0 Renfe, sin catenaria

Nota 18 May 2017 18:57

Desconectado
Mensajes: 262
Registrado: 28 Oct 2015 09:35
Hola, buenas noticias son esas del Rocrail.
Tengo un pequeño problema con el invento, haber si me podéis ayudar.
Tengo 8 servos conectados como el montaje que está en la página de Paco Cañada mediante dos pic, con el multimaus los mueves perfectamente los 8 sin problema, pero cuando los intento mover mediante la tablet (z21mobile.app oficial) solamente me funciona 4 (un solo pic).
Si la dirección va desde 1 al 4 los mueve perfectamente, pero cuando llamo a la dirección 5 a la 8, (el segundo pic) mueve el mismo servo, es decir, se mueve el mismo servo con la direcciónes 1, 5, 9,13, y así sucesivamente hasta que me canse de meter direcciones.
Os ha pasado este problema?, me podéis ayudar, muchas gracias.
Un saludo.

Nota 19 May 2017 12:10

Desconectado
Mensajes: 751
Ubicación: Salamanca
Registrado: 12 Ene 2012 14:44
Busca y rebusca, que el problema "lo tienes en casa": yo uso exactamente lo mismo que tú, lo mismo mismo (y en más cantidad) y me funciona perfectamente. Yo diría que no has configurado bien los desvíos en la z21mobile.app.
Saludos

[Multimaus + GenLi-S88 + +z21f. + RocRail (MacOsX)]
H0 Renfe, sin catenaria

Nota 19 May 2017 17:04

Desconectado
Mensajes: 262
Registrado: 28 Oct 2015 09:35
Qué alegría me das, pensaba que pudiera haber otro problema, aunque con el multimaus me funciona bien.

Y qué razón tienes, era una autentica tontería, pensaba que estaban correlativos y lo único que me faltaban poner un dos delante, no sé si es por ser el segundo dispositivo o lo grabe así, ya que en el multimaus es correlativos, es decir, con la app el primer pic mueve los servos 1, 2, 3,4 y el segundo pic el 25, 26, 27,28, por lo menos en mi caso.
Has podido probar el montaje de los semáforos con el arduino nano con la z21 mobile.app.
Saludos,……

Nota 20 May 2017 10:00

Desconectado
Mensajes: 751
Ubicación: Salamanca
Registrado: 12 Ene 2012 14:44
Pablob escribió:
Qué alegría me das, pensaba que pudiera haber otro problema...


Me has hecho reír. Estás permanentemente dudando de la z21f que te has construido, exactamente igual que yo los dos primeros meses. Ahora ya me he acostumbrado a que funcione :lol:

Pablob escribió:
Has podido probar el montaje de los semáforos con el arduino nano con la z21 mobile.app?


Ves? Lo que te decía: sigues dudando!! Pues claro, hombre, claro. Una vez que controlas desvíos, es decir, accesorios, pues controlas cualquier cosa que sea un accesorio. Sí funciona. Probado con descodificadores de accesorios comprados y, por supuesto, con los de made in Norber, hechos a medida según necesidad.

Lo que me recuerda que tengo por ahí funcionando algunos que sirven para 6 servos y 5 señales de 4 aspectos (a la vez, el mismo tamagochi) y que no lo he publicado. Hacer las instrucciones me lleva tanto como crear el aparato :roll:
Saludos

[Multimaus + GenLi-S88 + +z21f. + RocRail (MacOsX)]
H0 Renfe, sin catenaria

Nota 20 May 2017 10:59

Desconectado
Mensajes: 751
Ubicación: Salamanca
Registrado: 12 Ene 2012 14:44
Pablob escribió:
...me faltaban poner un dos delante ... es decir, con la app el primer pic mueve los servos 1, 2, 3,4 y el segundo pic el 25, 26, 27,28, por lo menos en mi caso.


Rarísimo eso. O no lo he entendido bien o sigues con el problema, pero ahora "disfrazado" de solución. A mi me coinciden exactamente los números asignados a los servos en el mando rojo del Multimaus con los números asignados a esos mismos servos en la aplicación del móvil z21mobile.app. Que es como debe ser. Uso la última versión de la .app para iPhone.
Saludos

[Multimaus + GenLi-S88 + +z21f. + RocRail (MacOsX)]
H0 Renfe, sin catenaria

Nota 20 May 2017 22:30

Desconectado
Mensajes: 262
Registrado: 28 Oct 2015 09:35
Hola Norber, si duda de que funcione no las tengo, lo único que mi habitación creo que hay fantasmas.

Efectivamente he conseguido hacerlo funcionar pero, en el multimaus son correlativos y en la tablet tengo que añadir el dos, es decir las direcciones con la app son 1,2,3,4,25,26,27,28.
Y lógicamente intento llamar al montaje que hiciste de los semáforos con el multimaus, funciona correctamente pero con la app no, averigua que dirección le está llegando.
La versión que tengo es 2.6.8, no he configurado nada, solamente la ip para mi router.
Los pasos que realizo son:
.-entro en el menú de configuración de control, pincho en el +, me genera un numero al desvió por ejemplo 4, entro ahora en el menú de configuración del desvió activo de las dos opciones que hay la DCC (se queda más clara) y descarto la de Motorola, salgo, y ya voy al play.
Lo único que me atrae la atención es que en la pantalla del control de los dispositivos en la parte superior derecha tengo siempre la dirección 1, no sé si esto es así, me imagino que sí.
Una posible diferencia es que estoy utilizando un Mega, pero con las locomotoras no tengo problemas.
Si se te ocurre algo por donde atacar, o me tocará llamar a un cura para que me saque el fantasma de la habitación.
Saludos a todos.

Nota 22 May 2017 09:47

Desconectado
Mensajes: 751
Ubicación: Salamanca
Registrado: 12 Ene 2012 14:44
Pablob escribió:
-entro en el menú de configuración de control, pincho en el +, me genera un numero al desvió por ejemplo 4, entro ahora en el menú de configuración del desvió activo de las dos opciones que hay la DCC (se queda más clara) y descarto la de Motorola, salgo, y ya voy al play.


Eso debería bastar para crear el accesorio "4". Además en "configuración" debes elegir la combinación "1" "0" correcta, y supongo que en tu versión de la .app también funcionará la prueba del desvío, que se realiza desde esa misma pantalla de "configuración" pulsando sobre los iconos situados bajo "Test:".

z21mobile.app_desvíos.png
z21mobile.app_desvíos.png (105.3 KiB) Visto 8725 veces



Pablob escribió:
Una posible diferencia es que estoy utilizando un Mega, pero con las locomotoras no tengo problemas.


Lo de Arduino Mega o Arduino Uno en principio es irrelevante. Si la z21f funciona para una cosa debería funcionar para todas y, como lo que tu planteas es una cosa tan rara, en el sentido de que funciona pero con el truco de las direcciones extrañas, pues me inclino a pensar que el problema esté en otro lado, en la .app quizá... O bien en el software que has usado para programar la z21f. Quizá Philip Gahtow haya introducido alguna modificación que no está en la versión que yo usé en su día. Sube aquí el código C++ que le has metido a tu Mega a ver si veo alguna cosa rara.
Saludos

[Multimaus + GenLi-S88 + +z21f. + RocRail (MacOsX)]
H0 Renfe, sin catenaria

Nota 22 May 2017 19:41

Desconectado
Mensajes: 262
Registrado: 28 Oct 2015 09:35
Hola Norber, gracias por la ayuda.

Imagen

Como ves la imagen es algo diferente a la tuya, yo utilizo android, hay un comentario en la App del IPhone, que mejora el tema de los accesorios.
La opción del test no la tengo.
Soy incapaz de subir los ficheros al foro me lo deniega, haber si lo averiguo y los cuelgo, o te envío un privado.
Saludos

Nota 22 May 2017 20:10

Desconectado
Mensajes: 751
Ubicación: Salamanca
Registrado: 12 Ene 2012 14:44
La última versión de Android creo que es de febrero de este 2017. La de iOS es de agosto 2016, muy anterior como ves...
Echo sinceramente de menos las prometidas actualizaciones de las aplicaciones para tablet/móvil. Miro cada semana a ver si han sacado ya algo, pues los pequeños fallos que tiene me molestan cada vez más...

Eso y que uno es inquieto por naturaleza: estoy pensando seriamente en retomar el tema del tablero de control físico, con sus pulsadores y lucecitas, conectado al bus XpressNet como un componente más... Realmente es para lo único para lo que uso la z21f en mi maqueta, para tener todos los paneles de control que no he construido, pero no es tan difícil hacerlos bien chulos con botones y luces de verdad. De hecho tenemos uno prácticamente terminado, aunque este solo controla los servos directamente, sin usar el Xbus, pero es una monada porque tiene lucecitas... En cuanto el dueño lo termine lo podremos enseñar :D .
Saludos

[Multimaus + GenLi-S88 + +z21f. + RocRail (MacOsX)]
H0 Renfe, sin catenaria

Nota 22 May 2017 20:41

Desconectado
Mensajes: 262
Registrado: 28 Oct 2015 09:35
Code para el arduino
"/*
    Z21 Ethernet Emulation für die App-Steuerung via Smartphone über XpressNet.
    by Philipp Gahtow (c) 2016
    Email: digitalmoba@arcor.de
   
    Version 2.1
   
    Änderungen:
    - Soft Serial Debug Funktion für Arduino MEGA
    - Enc28j60 kompartibel (keine sichere Kommunikation!!!)
    - S88 Rückmelde Bus
    - DHCP  (neu!)
 */
 
//----------------------------------------------------------------------------
#if defined(__AVR_ATmega1280__) || defined(__AVR_ATmega2560__)
#define DEBUG  //For Serial Port Debugging (Arduino Mega only) //  //////
#endif 

#define WEBCONIFIG //HTTP Port 80 Website zur Konfiguration
#define DHCP      //Activate to Receive a IP Adress from the DHCP Server, if no DHCP found fix IP Adress vom EEPROM will be load.
//----------------------------------------------------------------------------

#include <EEPROM.h>

#include <XpressNet.h>
XpressNetClass XpressNet;

//For use with Arduino_UIP and Enc28j60
//#define USEENC28
//#include <UIPEthernet.h>

//For use with Standard W5100 Library
#include <SPI.h>         // needed for Arduino versions later than 0018
#include <Ethernet.h>
#include <EthernetUdp.h>         // UDP library

#define EES88Moduls 38      //Adresse EEPROM Anzahl der Module für S88
#define EEip 40    //Startddress im EEPROM für die IP
#define EEXNet 45   //Adresse im XNet-Bus

// Enter a MAC address and IP address for your controller below.
// The IP address will be dependent on your local network:
byte mac[6] = {0xEC, 0xF4, 0xBB, 0x0C, 0x99, 0x5A }; //CASA
IPAddress ip(192, 168, 1, 20); // CASA

// An EthernetUDP instance to let us send and receive packets over UDP
EthernetUDP Udp;

#if defined(WEBCONFIG)
  // (port 80 is default for HTTP):
  EthernetServer server(80);
#endif

#define localPort 21105      // Z21 local port to listen on
#define XNetTxRxPin 9    //Send/Receive Pin MAX valor 9

#define Z21ResetPin A5  //Reset Pin bei Neustart betätigen um Standard IP zu setzten!

//--------------------------------------------------------------
//S88 Timer frequency is 250kHz for ( /64 prescale from 16MHz )
#define TIMER_Time 0x50 //je größer desto schneller die Abfrageintervalle
/*
  Der Timer erzeugt den notwendigen Takt für die S88 Schiebeabfragen.
 Je nach verwendten Modulen kann der Takt beliebigt in seiner Geschwindigkeit
 geändert werden, aber nicht jede Hardware unterstützt ein "fast" Auslesen!
 */
//Pinbelegungen am Dekoder:
//Eingänge:
#define S88DataPin A0      //S88 Data IN

//Ausgänge:
#define S88ClkPin A1    //S88 Clock
#define S88PSPin A2    //S88 PS/LOAD
#define S88ResetPin A3    //S88 Reset

uint8_t S88RCount = 0;    //Lesezähler 0-39 Zyklen
uint8_t S88RMCount = 0;   //Lesezähler Modul-Pin

/*
'0' = keine
 's' = Änderungen vorhanden, noch nicht fertig mit Auslesen
 'i' = Daten vollständig, senden an PC
 */
char S88sendon = '0';        //Bit Änderung

byte S88Module = 0;    //Anzahl der Module - maximal 62 Module à 16 Ports

byte data[62];     //Zustandsspeicher für 62x 8fach Modul

//--------------------------------------------------------------
// XpressNet address: must be in range of 1-31; must be unique. Note that some IDs
// are currently used by default, like 2 for a LH90 or LH100 out of the box, or 30
// for PC interface devices like the XnTCP.
byte XNetAddress = 30;    //Adresse im XpressNet
#define XBusVer 0x30      //Version XNet-Bus (default 3.0)

// buffers for receiving and sending data
#define UDP_TX_MAX_SIZE 10
unsigned char packetBuffer[UDP_TX_MAX_SIZE]; //buffer to hold incoming packet,
//--> UDP_TX_PACKET_MAX_SIZE

#define maxIP 10        //Speichergröße für IP-Adressen
#define ActTimeIP 20    //Aktivhaltung einer IP für (sec./2)
#define interval 2000   //interval at milliseconds

struct TypeActIP {
  byte ip0;    // Byte IP
  byte ip1;    // Byte IP
  byte ip2;    // Byte IP
  byte ip3;    // Byte IP
  byte BCFlag;  //BoadCastFlag 4. Byte Speichern
  byte time;  //Zeit
};
TypeActIP ActIP[maxIP];    //Speicherarray für IPs

long previousMillis = 0;        // will store last time of IP decount updated

#if defined(DEBUG)
  #include <SoftwareSerial.h>
  SoftwareSerial Debug(0, 1); // RX, TX
#endif

//--------------------------------------------------------------------------------------------
void setup() {
  #if defined(USEENC28)
    /* Disable SD card */
    pinMode(4, OUTPUT);
    digitalWrite(4, HIGH);
  #endif 
 
  #if defined(DEBUG)
    Debug.begin(115200);
    Debug.println("Z21");
  #endif
  pinMode(S88ResetPin, OUTPUT);    //Reset
  pinMode(S88PSPin, OUTPUT);      //PS/LOAD
  pinMode(S88ClkPin, OUTPUT);      //Clock
  digitalWrite(S88ResetPin, LOW);
  digitalWrite(S88PSPin, LOW);      //init
  digitalWrite(S88ClkPin, LOW);
  pinMode(S88DataPin, INPUT_PULLUP);    //Dateneingang

  pinMode(Z21ResetPin, INPUT_PULLUP); 
  delay(50);
  if (digitalRead(Z21ResetPin) == LOW || EEPROM.read(EEXNet) > 32) {
    #if defined(DEBUG)
      Debug.println("RESET IP");
    #endif 
    EEPROM.write(EEXNet, XNetAddress);
    EEPROM.write(EEip, ip[0]);
    EEPROM.write(EEip+1, ip[1]);
    EEPROM.write(EEip+2, ip[2]);
    EEPROM.write(EEip+3, ip[3]);
  }
  XNetAddress = EEPROM.read(EEXNet);
  ip[0] = EEPROM.read(EEip);
  ip[1] = EEPROM.read(EEip+1);
  ip[2] = EEPROM.read(EEip+2);
  ip[3] = EEPROM.read(EEip+3);
 
  #if defined(DHCP)
  if (Ethernet.begin(mac) == 0) {    //IP via DHCP
    #if defined(DEBUG)
       Debug.println(F("DHCP fail!"));
    #endif
    #undef DHCP
  }
  else {
    //Save IP that receive from DHCP
    ip = Ethernet.localIP();
  }
  #endif
  #if !defined(DHCP)
  // initialize the Ethernet device not using DHCP:
  Ethernet.begin(mac,ip);  //IP and MAC Festlegung
  #endif
 
  #if defined(DEBUG)
    Debug.println(ip);
    Debug.print("XAdr: ");
    Debug.println(XNetAddress);
    Debug.print("S88 Module: ");
    Debug.println(S88Module);
  #endif
  // start the Webserver:
  #if defined(WEBCONFIG)
    server.begin();    //HTTP Server
  #endif 
  // start the UDP Server
  Udp.begin(localPort);  //UDP Z21 Port

  XpressNet.start(XNetAddress, XNetTxRxPin);    //Initialisierung XNet und Send/Receive-PIN

  for (int i = 0; i < maxIP; i++)
    clearIPSlot(i);  //löschen gespeicherter aktiver IP's
 
  SetupS88();    //initialize Timer2 for S88
}

/*
//--------------------------------------------------------------------------------------------
void notifyXNetVer(uint8_t V, uint8_t ID ) {
}

//--------------------------------------------------------------------------------------------
 void notifyXNetStatus(uint8_t LedState ) {
 }
 */

//--------------------------------------------------------------------------------------------
void loop() {

  XpressNet.receive();  //Check for XpressNet

  Ethreceive();    //Read Data on UDP Port

  XpressNet.receive();  //Check for XpressNet

  #if defined(WEBCONFIG)
    Webconfig();    //Webserver for Configuration
  #endif 
 
  notifyS88Data();    //R-Bus geänderte Daten Melden

  //Nicht genutzte IP's aus Speicher löschen
  unsigned long currentMillis = millis();
  if(currentMillis - previousMillis > interval) {
    previousMillis = currentMillis;   
    for (int i = 0; i < maxIP; i++) {
      if (ActIP[i].ip3 != 0) {  //Slot nicht leer?
        if (ActIP[i].time > 0)
          ActIP[i].time--;    //Zeit herrunterrechnen
        else {
          #if defined(DEBUG)
            Debug.print("Clear IP ");
            Debug.println(ActIP[i].ip3);
          #endif 
          clearIPSlot(i);   //clear IP DATA
        }
      }
    }
   #if defined(DEBUG)
     Debug.print("RAM: ");
     Debug.println(freeRam()); 
   #endif
  }
}

//--------------------------------------------------------------------------------------------
void clearIPSlots() {
  for (int i = 0; i < maxIP; i++)
    clearIPSlot(i);
}

//--------------------------------------------------------------------------------------------
//Slot mit Nummer "i" löschen
void clearIPSlot(byte i) {
  ActIP[i].ip0 = 0;
  ActIP[i].ip1 = 0;
  ActIP[i].ip2 = 0;
  ActIP[i].ip3 = 0;
  ActIP[i].BCFlag = 0;
  ActIP[i].time = 0;
}

//--------------------------------------------------------------------------------------------
void clearIPSlot(byte ip0, byte ip1, byte ip2, byte ip3) {
  for (int i = 0; i < maxIP; i++) {
    if (ActIP[i].ip0 == ip0 && ActIP[i].ip1 == ip1 && ActIP[i].ip2 == ip2 && ActIP[i].ip3 == ip3)
      clearIPSlot(i);
  }
}

//--------------------------------------------------------------------------------------------
byte addIPToSlot (byte ip0, byte ip1, byte ip2, byte ip3, byte BCFlag) {
  byte Slot = maxIP;
  for (int i = 0; i < maxIP; i++) {
    if (ActIP[i].ip0 == ip0 && ActIP[i].ip1 == ip1 && ActIP[i].ip2 == ip2 && ActIP[i].ip3 == ip3) {
      ActIP[i].time = ActTimeIP;
      if (BCFlag != 0)    //Falls BC Flag übertragen wurde diesen hinzufügen!
        ActIP[i].BCFlag = BCFlag;
      return ActIP[i].BCFlag;    //BC Flag 4. Byte Rückmelden
    }
    else if (ActIP[i].time == 0 && Slot == maxIP)
      Slot = i;
  }
  ActIP[Slot].ip0 = ip0;
  ActIP[Slot].ip1 = ip1;
  ActIP[Slot].ip2 = ip2;
  ActIP[Slot].ip3 = ip3;
  ActIP[Slot].time = ActTimeIP;
  notifyXNetPower(XpressNet.getPower());
  return ActIP[Slot].BCFlag;   //BC Flag 4. Byte Rückmelden
}

//--------------------------------------------------------------------------------------------
#if defined(WEBCONFIG)
void Webconfig() {
  EthernetClient client = server.available();
  if (client) {
    String receivedText = String(50);
    // an http request ends with a blank line
    boolean currentLineIsBlank = true;
    while (client.connected()) {
      if (client.available()) {
        char c = client.read();
        if (receivedText.length() < 50) {
          receivedText += c;
        }
        // if you've gotten to the end of the line (received a newline
        // character) and the line is blank, the http request has ended,
        // so you can send a reply
        if (c == '\n' && currentLineIsBlank) {
          // send a standard http response header
          client.println("HTTP/1.1 200 OK");
          client.println("Content-Type: text/html");
          //client.println("Connection: close");  // the connection will be closed after completion of the response
          //client.println("Refresh: 5");  // refresh the page automatically every 5 sec
          client.println();
          //Website:
          client.println("<html><head><title>Z21</title></head><body>");
          client.println("<h1>Z21</h1><br />");
          //----------------------------------------------------------------------------------------------------         
          int firstPos = receivedText.indexOf("?");
          if (firstPos > -1) {
            client.println("-> accept change after RESET!");
            byte lastPos = receivedText.indexOf(" ", firstPos);
            String theText = receivedText.substring(firstPos+3, lastPos); // 10 is the length of "?A="
            byte S88Pos = theText.indexOf("&S88=");
            S88Module = theText.substring(S88Pos+5, theText.length()).toInt();
            byte XNetPos = theText.indexOf("&XNet=");
            XNetAddress = theText.substring(XNetPos+6, S88Pos).toInt();
            byte Aip = theText.indexOf("&B=");
            byte Bip = theText.indexOf("&C=", Aip);
            byte Cip = theText.indexOf("&D=", Bip);
            byte Dip = theText.substring(Cip+3, XNetPos).toInt();
            Cip = theText.substring(Bip+3, Cip).toInt();
            Bip = theText.substring(Aip+3, Bip).toInt();
            Aip = theText.substring(0, Aip).toInt();
            ip[0] = Aip;
            ip[1] = Bip;
            ip[2] = Cip;
            ip[3] = Dip;
            if (EEPROM.read(EES88Moduls) != S88Module) {
              EEPROM.write(EES88Moduls, S88Module);
              SetupS88();
            }
            if (EEPROM.read(EEXNet) != XNetAddress)
              EEPROM.write(EEXNet, XNetAddress);
            if (EEPROM.read(EEip) != Aip) 
              EEPROM.write(EEip, Aip);
            if (EEPROM.read(EEip+1) != Bip) 
              EEPROM.write(EEip+1, Bip);
            if (EEPROM.read(EEip+2) != Cip) 
              EEPROM.write(EEip+2, Cip);
            if (EEPROM.read(EEip+3) != Dip) 
              EEPROM.write(EEip+3, Dip);
          }
          //----------------------------------------------------------------------------------------------------         
          client.print("<form method=get>IP-Adr.: <input type=number min=10 max=254 name=A value=");
          client.println(ip[0]);
          client.print(">.<input type=number min=0 max=254 name=B value=");
          client.println(ip[1]);
          client.print(">.<input type=number min=0 max=254 name=C value=");
          client.println(ip[2]);
          client.print(">.<input type=number min=0 max=254 name=D value=");
          client.println(ip[3]);
          client.print("><br /> XBus Adr.: <input type=number min=1 max=31 name=XNet value=");
          client.print(XNetAddress);
          client.print("><br /> S88 8x Module: <input type=number min=0 max=62 name=S88 value=");
          client.print(S88Module);
          client.println("><br /><br />");
          client.println("<input type=submit></form>");
          client.println("</body></html>");
          break;
        }
        if (c == '\n')
          currentLineIsBlank = true; // you're starting a new line
        else if (c != '\r')
          currentLineIsBlank = false; // you've gotten a character on the current line
      }
    }
    client.stop();  // close the connection:
  }
}
#endif

//--------------------------------------------------------------------------------------------
void Ethreceive() {
  int packetSize = Udp.parsePacket();
  if(packetSize > 0) {
    addIPToSlot(Udp.remoteIP()[0], Udp.remoteIP()[1], Udp.remoteIP()[2], Udp.remoteIP()[3], 0);
    Udp.read(packetBuffer,UDP_TX_MAX_SIZE);  // read the packet into packetBufffer
    // send a reply, to the IP address and port that sent us the packet we received
    int header = (packetBuffer[3]<<8) + packetBuffer[2];
    //    int datalen = (packetBuffer[1]<<8) + packetBuffer[0];
    byte data[16];
    //boolean ok = false;
    switch (header) {
    case 0x10:
      #if defined(DEBUG)
        Debug.println("LAN_GET_SERIAL_NUMBER"); 
      #endif
      data[0] = 0xF5;  //Seriennummer 32 Bit (little endian)
      data[1] = 0x0A;
      data[2] = 0x00;
      data[3] = 0x00;
      EthSend (0x08, 0x10, data, false, 0x00);
      break;
    case 0x1A:
      #if defined(DEBUG)
        Debug.println("LAN_GET_HWINFO");
      #endif
      data[0] = 0x01;  //HwType 32 Bit
      data[1] = 0x02;
      data[2] = 0x02;
      data[3] = 0x00;
      data[4] = 0x20;  //FW Version 32 Bit
      data[5] = 0x01;
      data[6] = 0x00;
      data[7] = 0x00;
      EthSend (0x0C, 0x1A, data, false, 0x00);
      break; 
    case 0x30:
      #if defined(DEBUG)
        Debug.println("LAN_LOGOFF");
      #endif
      clearIPSlot(Udp.remoteIP()[0], Udp.remoteIP()[1], Udp.remoteIP()[2], Udp.remoteIP()[3]);
      //Antwort von Z21: keine
      break;
      case (0x40):
      switch (packetBuffer[4]) { //X-Header
      case 0x21:
        switch (packetBuffer[5]) {  //DB0
        case 0x21:
          #if defined(DEBUG)
            Debug.println("LAN_X_GET_VERSION");
          #endif
          data[0] = 0x63;
          data[1] = 0x21;
          data[2] = XBusVer;   //X-Bus Version
          data[3] = 0x12;  //ID der Zentrale
          EthSend (0x09, 0x40, data, true, 0x00);
          break;
        case 0x24:
          data[0] = 0x62;
          data[1] = 0x22;
          data[2] = XpressNet.getPower();
          //Debug.print("LAN_X_GET_STATUS ");
          //Debug.println(data[2], HEX);
          EthSend (0x08, 0x40, data, true, 0x00);
          break;
        case 0x80:
          #if defined(DEBUG)
            Debug.println("LAN_X_SET_TRACK_POWER_OFF");
          #endif
          XpressNet.setPower(csTrackVoltageOff);
          break;
        case 0x81:
          #if defined(DEBUG)
            Debug.println("LAN_X_SET_TRACK_POWER_ON");
          #endif
          XpressNet.setPower(csNormal);
          break; 
        }
        break;
      case 0x23:
        if (packetBuffer[5] == 0x11) {  //DB0
          #if defined(DEBUG)
            Debug.println("LAN_X_CV_READ");
          #endif
          byte CV_MSB = packetBuffer[6];
          byte CV_LSB = packetBuffer[7];
          XpressNet.readCVMode(CV_LSB+1);
        }
        break;             
      case 0x24:
        if (packetBuffer[5] == 0x12) {  //DB0
          #if defined(DEBUG)
            Debug.println("LAN_X_CV_WRITE");
          #endif
          byte CV_MSB = packetBuffer[6];
          byte CV_LSB = packetBuffer[7];
          byte value = packetBuffer[8];
          XpressNet.writeCVMode(CV_LSB+1, value);
        }
        break;             
      case 0x43:
        #if defined(DEBUG)
          Debug.println("LAN_X_GET_TURNOUT_INFO");
        #endif
        XpressNet.getTrntInfo(packetBuffer[5], packetBuffer[6]);
        break;             
      case 0x53:
        #if defined(DEBUG)
          Debug.println("LAN_X_SET_TURNOUT");
        #endif
        XpressNet.setTrntPos(packetBuffer[5], packetBuffer[6], packetBuffer[7] & 0x0F);
        break; 
      case 0x80:
        #if defined(DEBUG)
          Debug.println("LAN_X_SET_STOP");
        #endif
        XpressNet.setPower(csEmergencyStop);
        break; 
      case 0xE3:
        if (packetBuffer[5] == 0xF0) {  //DB0
/*          #if defined(DEBUG)
            Debug.print("LAN_X_GET_LOCO_INFO: ");
            Debug.println(word(packetBuffer[6] & 0x3F, packetBuffer[7]));  //mit F1-F12
          #endif  */
          //Antwort: LAN_X_LOCO_INFO  Adr_MSB - Adr_LSB
          XpressNet.getLocoInfo(packetBuffer[6] & 0x3F, packetBuffer[7]);
          XpressNet.getLocoFunc(packetBuffer[6] & 0x3F, packetBuffer[7]);  //F13 bis F28
        }
        break; 
      case 0xE4:
        if (packetBuffer[5] == 0xF8) {  //DB0
          //LAN_X_SET_LOCO_FUNCTION  Adr_MSB        Adr_LSB            Type (EIN/AUS/UM)      Funktion
          XpressNet.setLocoFunc(packetBuffer[6] & 0x3F, packetBuffer[7], packetBuffer[8] >> 5, packetBuffer[8] & B00011111);
        }
        else {
          //LAN_X_SET_LOCO_DRIVE            Adr_MSB          Adr_LSB      DB0          Dir+Speed
          XpressNet.setLocoDrive(packetBuffer[6] & 0x3F, packetBuffer[7], packetBuffer[5] & B11, packetBuffer[8]);       
        }
        break; 
      case 0xE6:
        if (packetBuffer[5] == 0x30) {  //DB0
          byte Option = packetBuffer[8] & B11111100;  //Option DB3
          byte Adr_MSB = packetBuffer[6] & 0x3F;  //DB1
          byte Adr_LSB = packetBuffer[7];    //DB2
          int CVAdr = packetBuffer[9] | ((packetBuffer[8] & B11) << 7);
          if (Option == 0xEC) {
            #if defined(DEBUG)
              Debug.println("LAN_X_CV_POM_WRITE_BYTE");
            #endif
            byte value = packetBuffer[10];  //DB5
          }
          if (Option == 0xE8) {
            #if defined(DEBUG)
              Debug.println("LAN_X_CV_POM_WRITE_BIT");
            #endif
            //Nicht von der APP Unterstützt
          }
        }
        break; 
      case 0xF1:
        #if defined(DEBUG)
          Debug.println("LAN_X_GET_FIRMWARE_VERSION");
        #endif
        data[0] = 0xf3;
        data[1] = 0x0a;
        data[2] = 0x01;   //V_MSB
        data[3] = 0x23;  //V_LSB
        EthSend (0x09, 0x40, data, true, 0x00);
        break;     
      }
      break;
      case (0x50):
        #if defined(DEBUG)
          Debug.print("LAN_SET_BROADCASTFLAGS: ");
          Debug.println(packetBuffer[4], BIN); // 1=BC Power, Loco INFO, Trnt INFO; 2=BC Änderungen der Rückmelder am R-Bus
        #endif   
        addIPToSlot(Udp.remoteIP()[0], Udp.remoteIP()[1], Udp.remoteIP()[2], Udp.remoteIP()[3], packetBuffer[4]);
        notifyXNetPower (XpressNet.getPower());  //Zustand Gleisspannung Antworten
      break;
      case (0x51):
        #if defined(DEBUG)
          Debug.println("LAN_GET_BROADCASTFLAGS");
        #endif
        data[0] = 0x00;
        data[1] = 0x00;
        data[2] = 0x00;   
        data[3] = addIPToSlot(Udp.remoteIP()[0], Udp.remoteIP()[1], Udp.remoteIP()[2], Udp.remoteIP()[3], 0); 
        EthSend (0x08, 0x51, data, false, 0x00);
      break;
      case (0x60):
        #if defined(DEBUG)
          Debug.println("LAN_GET_LOCOMODE");
        #endif
      break;
      case (0x61):
        #if defined(DEBUG)
          Debug.println("LAN_SET_LOCOMODE");
        #endif
      break;
      case (0x70):
        #if defined(DEBUG)
          Debug.println("LAN_GET_TURNOUTMODE");
        #endif
      break;
      case (0x71):
        #if defined(DEBUG)
          Debug.println("LAN_SET_TURNOUTMODE");
        #endif
      break;
      case (0x81):
        #if defined(DEBUG)
          Debug.println("LAN_RMBUS_GETDATA");
        #endif
        S88sendon = 'm';    //Daten werden gemeldet!
        notifyS88Data();
      break;
      case (0x82):
        #if defined(DEBUG)
          Debug.println("LAN_RMBUS_PROGRAMMODULE");
        #endif 
      break;
      case (0x85):
        #if defined(DEBUG)
          Debug.println("LAN_SYSTEMSTATE_GETDATA");  //LAN_SYSTEMSTATE_DATACHANGED
        #endif 
        data[0] = 0x00;  //MainCurrent mA
        data[1] = 0x00;  //MainCurrent mA
        data[2] = 0x00;  //ProgCurrent mA
        data[3] = 0x00;  //ProgCurrent mA       
        data[4] = 0x00;  //FilteredMainCurrent
        data[5] = 0x00;  //FilteredMainCurrent
        data[6] = 0x00;  //Temperature
        data[7] = 0x20;  //Temperature
        data[8] = 0x0F;  //SupplyVoltage
        data[9] = 0x00;  //SupplyVoltage
        data[10] = 0x00;  //VCCVoltage
        data[11] = 0x03;  //VCCVoltage
        data[12] = XpressNet.getPower();  //CentralState
        data[13] = 0x00;  //CentralStateEx
        data[14] = 0x00;  //reserved
        data[15] = 0x00;  //reserved
        EthSend (0x14, 0x84, data, false, 0x00);
      break;
      case (0x89):
        #if defined(DEBUG)
          Debug.println("LAN_RAILCOM_GETDATA");
        #endif 
      break;
      case (0xA0):
        #if defined(DEBUG)
          Debug.println("LAN_LOCONET_RX");
        #endif
      break;
      case (0xA1):
        #if defined(DEBUG)
          Debug.println("LAN_LOCONET_TX");
        #endif
      break;
      case (0xA2):
        #if defined(DEBUG)
          Debug.println("LAN_LOCONET_FROM_LAN");
        #endif
      break;
      case (0xA3):
        #if defined(DEBUG)
          Debug.println("LAN_LOCONET_DISPATCH_ADDR");
        #endif 
      break;
      case (0xA4):
        #if defined(DEBUG)
          Debug.println("LAN_LOCONET_DETECTOR");   
        #endif 
      break;
    default:
      #if defined(DEBUG)
        Debug.print("LAN_UNKNOWN_COMMAND 0x");
        Debug.println(header, HEX);
      #endif
      data[0] = 0x61;
      data[1] = 0x82;
      EthSend (0x07, 0x40, data, true, 0x00);
    }
  }
}

//--------------------------------------------------------------------------------------------
void EthSend (unsigned int DataLen, unsigned int Header, byte *dataString, boolean withXOR, byte BC) {
  if (BC != 0x00) {
    IPAddress IPout = Udp.remoteIP();
    for (int i = 0; i < maxIP; i++) {
      if (ActIP[i].time > 0 && ActIP[i].BCFlag >= BC) {    //Noch aktiv?
        IPout[0] = ActIP[i].ip0;
        IPout[1] = ActIP[i].ip1;
        IPout[2] = ActIP[i].ip2;
        IPout[3] = ActIP[i].ip3;
        Udp.beginPacket(IPout, Udp.remotePort());    //Broadcast
        Ethwrite (DataLen, Header, dataString, withXOR);
        Udp.endPacket();
      }
    }
  }
  else {
    Udp.beginPacket(Udp.remoteIP(), Udp.remotePort());    //Broadcast
    Ethwrite (DataLen, Header, dataString, withXOR);
    Udp.endPacket();
  }
}

//--------------------------------------------------------------------------------------------
//Senden von Lokdaten via Ethernet
void Ethwrite (unsigned int DataLen, unsigned int Header, byte *dataString, boolean withXOR) {
  Udp.write(DataLen & 0xFF);
  Udp.write(DataLen >> 8);
  Udp.write(Header & 0xFF);
  Udp.write(Header >> 8);

  unsigned char XOR = 0;
  byte ldata = DataLen-5;  //Ohne Length und Header und XOR
  if (!withXOR)    //XOR vorhanden?
    ldata++;
  for (int i = 0; i < (ldata); i++) {
    XOR = XOR ^ *dataString;
    Udp.write(*dataString);
    dataString++;
  }
  if (withXOR)
    Udp.write(XOR);
}

//--------------------------------------------------------------------------------------------
void notifyXNetPower (uint8_t State)
{
  byte data[] = { 0x61, 0x00  };
  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;
  default: return; 
  }
  EthSend(0x07, 0x40, data, true, 0x01);
}

//--------------------------------------------------------------------------------------------
void notifyLokFunc(uint8_t Adr_High, uint8_t Adr_Low, uint8_t F2, uint8_t F3 ) {
  #if defined(DEBUG)
  // Debug.print("Loco Fkt: ");
  // Debug.print(Adr_Low);
  // Debug.print(", Fkt2: ");
  // Debug.print(F2, BIN);
  // Debug.print("; ");
  // Debug.println(F3, BIN);
  #endif
}

//--------------------------------------------------------------------------------------------
void notifyLokAll(uint8_t Adr_High, uint8_t Adr_Low, boolean Busy, uint8_t Steps, uint8_t Speed, uint8_t Direction, uint8_t F0, uint8_t F1, uint8_t F2, uint8_t F3, boolean Req ) {
  byte DB2 = Steps;
  if (DB2 == 3)  //nicht vorhanden!
    DB2 = 4;
  if (Busy)
    bitWrite(DB2, 3, 1);
  byte DB3 = Speed;
  if (Direction == 1) 
    bitWrite(DB3, 7, 1);
  byte data[9];
  data[0] = 0xEF;  //X-HEADER
  data[1] = Adr_High & 0x3F;
  data[2] = Adr_Low;
  data[3] = DB2;
  data[4] = DB3;
  data[5] = F0;    //F0, F4, F3, F2, F1
  data[6] = F1;    //F5 - F12; Funktion F5 ist bit0 (LSB)
  data[7] = F2;  //F13-F20
  data[8] = F3;  //F21-F28
  if (Req == false)  //kein BC
    EthSend (14, 0x40, data, true, 0x00);  //Send Power und Funktions ask App
  else EthSend (14, 0x40, data, true, 0x01);  //Send Power und Funktions to all active Apps
}

//--------------------------------------------------------------------------------------------
void notifyTrnt(uint8_t Adr_High, uint8_t Adr_Low, uint8_t Pos) {
  #if defined(DEBUG)
  // Debug.print("Weiche: ");
  // Debug.print(word(Adr_High, Adr_Low));
  // Debug.print(", Position: ");
  // Debug.println(Pos, BIN);
  //LAN_X_TURNOUT_INFO
  #endif
  byte data[4];
  data[0] = 0x43;  //HEADER
  data[1] = Adr_High;
  data[2] = Adr_Low;
  data[3] = Pos;
  EthSend (0x09, 0x40, data, true, 0x01); 
}

//--------------------------------------------------------------------------------------------
void notifyCVInfo(uint8_t State ) {
  #if defined(DEBUG)
  // Debug.print("CV Prog STATE: ");
  // Debug.println(State);
  #endif
  if (State == 0x01 || State == 0x02) {  //Busy or No Data
    //LAN_X_CV_NACK
    byte data[2];
    data[0] = 0x61;  //HEADER
    data[1] = 0x13; //DB0
    EthSend (0x07, 0x40, data, true, 0x00); 
  }
}

//--------------------------------------------------------------------------------------------
void notifyCVResult(uint8_t cvAdr, uint8_t cvData ) {
  #if defined(DEBUG)
  // Debug.print("CV Prog Read: ");
  // Debug.print(cvAdr);
  // Debug.print(", ");
  // Debug.println(cvData);
  #endif
  //LAN_X_CV_RESULT
  byte data[5];
  data[0] = 0x64; //HEADER
  data[1] = 0x14;  //DB0
  data[2] = 0x00;  //CVAdr_MSB
  data[3] = cvAdr;  //CVAdr_LSB
  data[4] = cvData;  //Value
  EthSend (0x0A, 0x40, data, true, 0x00);
}

//--------------------------------------------------------------
void SetupS88() {
  S88Module = EEPROM.read(EES88Moduls);
  if (S88Module > 62 || S88Module == 0) { //S88 off!
    S88Module = 0;
    TCCR2B = 0<<CS22 | 0<<CS21 | 0<<CS20;  //Timer 2 off
    return;
  }
  //S88 Aktivieren!

  //Setup Timer2.
  //Configures the 8-Bit Timer2 to generate an interrupt at the specified frequency.
  //Returns the time load value which must be loaded into TCNT2 inside your ISR routine.
  /*
   16Mhz / 1 prescaler = 16Mhz = CS 001
   16Mhz / 8 prescaler = 2MHz oder 0,5usec = CS 010
   16Mhz / 64 prescaler = 250kHz = CS 011
   16Mhz / 256 prescaler = CS 100
   16Mhz / 1024 prescaler = CS 101
   */
  //Timer2 Settings: Timer Prescaler /256
  //Timmer clock = 16MHz/256
  TCCR2A = 0;
  TCCR2B = 1<<CS22 | 0<<CS21 | 0<<CS20;
  TIMSK2 = 1<<TOIE2; //Timer2 Overflow Interrupt Enable
  TCNT2=TIMER_Time; //load the timer for its first cycle
}

//--------------------------------------------------------------
//Timer ISR Routine
//Timer2 overflow Interrupt vector handler
ISR(TIMER2_OVF_vect) {
  if (S88RCount == 3)    //Load/PS Leitung auf 1, darauf folgt ein Schiebetakt nach 10 ticks!
    digitalWrite(S88PSPin, HIGH);
  if (S88RCount == 4)   //Schiebetakt nach 5 ticks und S88Module > 0
    digitalWrite(S88ClkPin, HIGH);       //1. Impuls
  if (S88RCount == 5)   //Read Data IN 1. Bit und S88Module > 0
    S88readData();    //LOW-Flanke während Load/PS Schiebetakt, dann liegen die Daten an
  if (S88RCount == 9)    //Reset-Plus, löscht die den Paralleleingängen vorgeschaltetetn Latches
    digitalWrite(S88ResetPin, HIGH);
  if (S88RCount == 10)    //Ende Resetimpuls
    digitalWrite(S88ResetPin, LOW);
  if (S88RCount == 11)    //Ende PS Phase
    digitalWrite(S88PSPin, LOW);
  if (S88RCount >= 12 && S88RCount < 10 + (S88Module * 8) * 2) {    //Auslesen mit weiteren Schiebetakt der Latches links
    if (S88RCount % 2 == 0)      //wechselnder Taktimpuls/Schiebetakt
      digitalWrite(S88ClkPin, HIGH); 
    else S88readData();    //Read Data IN 2. bis (Module*8) Bit
  }
  S88RCount++;      //Zähler für Durchläufe/Takt
  if (S88RCount >= 10 + (S88Module * 8) * 2) {  //Alle Module ausgelesen?
    S88RCount = 0;                    //setzte Zähler zurück
    S88RMCount = 0;                  //beginne beim ersten Modul von neuem
    //init der Grundpegel
    digitalWrite(S88PSPin, LOW);   
    digitalWrite(S88ClkPin, LOW);
    digitalWrite(S88ResetPin, LOW);
    if (S88sendon == 's')  //Änderung erkannt
      S88sendon = 'i';      //senden
  }
  //Capture the current timer value. This is how much error we have due to interrupt latency and the work in this function
  TCNT2 = TCNT2 + TIMER_Time;    //Reload the timer and correct for latency.
}

//--------------------------------------------------------------
//Einlesen des Daten-Bit und Vergleich mit vorherigem Durchlauf
void S88readData() {
  digitalWrite(S88ClkPin, LOW);  //LOW-Flanke, dann liegen die Daten an
  byte Modul = S88RMCount / 8;
  byte Port = S88RMCount % 8;
  byte getData = digitalRead(S88DataPin);  //Bit einlesen
  if (bitRead(data[Modul],Port) != getData) {     //Zustandsänderung Prüfen?
    bitWrite(data[Modul],Port,getData);          //Bitzustand Speichern
    S88sendon = 's';  //Änderung vorgenommen. (SET)
  }
  S88RMCount++;
}

//--------------------------------------------------------------------------------------------
void notifyS88Data() {
  if (S88sendon == 'i' || S88sendon == 'm') {
    byte MAdr = 1;  //Rückmeldemodul
    byte datasend[11];  //Array Gruppenindex (1 Byte) & Rückmelder-Status (10 Byte)
    datasend[0] = 0; //Gruppenindex für Adressen 1 bis 10
    for(byte m = 0; m < S88Module; m++) {  //Durchlaufe alle aktiven Module im Speicher
      datasend[MAdr] = data[m];
      MAdr++;  //Nächste Modul in der Gruppe
      if (MAdr >= 11) {  //10 Module à 8 Ports eingelesen
        MAdr = 1;  //beginne von vorn
        EthSend (0x0F, 0x80, datasend, false, 0x02); //RMBUS_DATACHANED
        datasend[0]++; //Gruppenindex erhöhen
      }
    }
    if (MAdr < 11) {  //noch unbenutzte Module in der Gruppe vorhanden? Diese 0x00 setzten und dann Melden!
      while (MAdr < 11) {
        datasend[MAdr] = 0x00;  //letzten leeren Befüllen
        MAdr++;   //Nächste Modul in der Gruppe   
      }
      EthSend (0x0F, 0x80, datasend, false, 0x02); //RMBUS_DATACHANED
    }
    S88sendon = '0';        //Speicher Rücksetzten
  }
}

//--------------------------------------------------------------------------------------------
#if defined(DEBUG)
int freeRam () {
  extern int __heap_start, *__brkval;
  int v;
  return (int) &v - (__brkval == 0 ? (int) &__heap_start : (int) __brkval);
}
#endif

   "


XpressNet.cpp
 /*
*****************************************************************************
  *      XpressNet.h - library for a client XpressNet protocoll
  *      Copyright (c) 2015 Philipp Gahtow  All right reserved.
  *
  **    Free for Private usage only!   
*****************************************************************************
  * IMPORTANT:
  *
  *    Please contact Lenz Inc. for details.
*****************************************************************************
 *  see for changes in XpressNet.h!
*/

// include this library's description file
#include "XpressNet.h"
#include <avr/interrupt.h>

#define interval 10500      //interval for Status LED (milliseconds)

XpressNetClass *XpressNetClass::active_object = 0;   //Static

// Constructor /////////////////////////////////////////////////////////////////
// Function that handles the creation and setup of instances

XpressNetClass::XpressNetClass()
{
   // initialize this instance's variables
   Railpower = 0xFF;      //Ausgangs undef.
   XNetclearSendBuf();
   XNetRun = false;   //XNet ist inactive;
   xLokStsclear();      //löschen aktiver Loks in Slotserver
   ReqLocoAdr = 0;
   ReqLocoAgain = 0;
   ReqFktAdr = 0;
   SlotLast = 0;         
   ReadData = false;      //keine Serial Daten Speichern
}

//******************************************Serial*******************************************
void XpressNetClass::start(byte XAdr, int XControl)  //Initialisierung Serial
{
   ledState = LOW;       // Status LED, used to set the LED
   previousMillis = 0;      //Reset Time Count
   SlotTime = millis();   // will store last time LED was updated
   if (notifyXNetStatus)
      notifyXNetStatus (ledState);

   MY_ADDRESS = XAdr;
   MAX485_CONTROL = XControl;
   // LISTEN_MODE
   pinMode(MAX485_CONTROL, OUTPUT);
   digitalWrite (MAX485_CONTROL, LOW);

   myRequestAck = callByteParity (MY_ADDRESS | 0x00) | 0x100;
   myCallByteInquiry = callByteParity (MY_ADDRESS | 0x40) | 0x100;
   myDirectedOps = callByteParity (MY_ADDRESS | 0x60) | 0x100;

   //Set up on 62500 Baud
   cli();  //disable interrupts while initializing the USART
   #ifdef __AVR_ATmega8__
    UBRRH = 0;
    UBRRL = 0x0F;
    UCSRA = 0;
    UCSRB = (1<<RXEN) | (1<<TXEN) | (1<<RXCIE) | (1<<UCSZ2);
    UCSRC = (1<<UCSZ1) | (1<<UCSZ0);
   #else
   #ifdef SERIAL_PORT_0
    UBRR0H = 0;
    UBRR0L = 0x0F;
    UCSR0A = 0;
    UCSR0B = (1<<RXEN0) | (1<<TXEN0) | (1<<RXCIE0) | (1<<UCSZ02);
    UCSR0C = (1<<UCSZ01) | (1<<UCSZ00);
    #else
    UBRR1H = 0;
    UBRR1L = 0x0F;
    UCSR1A = 0;
    UCSR1B = (1<<RXEN1) | (1<<TXEN1) | (1<<RXCIE1) | (1<<UCSZ12);
    UCSR1C = (1<<UCSZ11) | (1<<UCSZ10);
    #endif
   #endif
   sei(); // Enable the Global Interrupt Enable flag so that interrupts can be processed
    /*
    *  Enable reception (RXEN = 1).
    *  Enable transmission (TXEN0 = 1).
    *   Enable Receive Interrupt (RXCIE = 1).
    *  Set 8-bit character mode (UCSZ00, UCSZ01, and UCSZ02 together control this,
    *  But UCSZ00, UCSZ01 are in Register UCSR0C).
    */
   
   active_object = this;      //hold Object to call it back in ISR
}

// Public Methods //////////////////////////////////////////////////////////////
// Functions available in Wiring sketches, this library, and other libraries

//*******************************************************************************************
//Daten ermitteln und Auswerten
void XpressNetClass::receive(void)
{
   /*
   XNetMsg[XNetlength] = 0x00;
   XNetMsg[XNetmsg] = 0x00;
   XNetMsg[XNetcommand] = 0x00;   savedData[1]
   XNetMsg[XNetdata1] = 0x00;   savedData[2]
   */
  unsigned long currentMillis = millis();   //aktuelle Zeit setzten

  if (XNetMsg[XNetmsg] != 0x00 && ReadData == false) {    //Serial Daten dekodieren
//     previousMillis = millis();   // will store last time LED was updated
     //Daten, setzte LED = ON!
     if (ledState == LOW) {   //LED -> aktivieren!
      ledState = HIGH;
      if (notifyXNetStatus)
         notifyXNetStatus (ledState);
     }
     if (XNetMsg[XNetmsg] == GENERAL_BROADCAST) {
        if (XNetMsg[XNetlength] == 4 && XNetMsg[XNetcom] == 0x61) {
           if ((XNetMsg[XNetdata1] == 0x01) && (XNetMsg[XNetdata2] == 0x60)) {
            // Normal Operation Resumed
            Railpower = csNormal;
            if (notifyXNetPower)
               notifyXNetPower(Railpower);
           }
           else if ((XNetMsg[XNetdata1] == 0x00) && (XNetMsg[XNetdata2] == 0x61)) {
            // Track power off
              Railpower = csTrackVoltageOff;
            if (notifyXNetPower)
               notifyXNetPower(Railpower);
           }
           else if ((XNetMsg[XNetdata1] == 0x08)) {
              // Track Short
              Railpower = csShortCircuit;
              if (notifyXNetPower) {
                 notifyXNetPower(csTrackVoltageOff);
                 notifyXNetPower(Railpower);
              }
           }
           else if ((XNetMsg[XNetdata1] == 0x02) && (XNetMsg[XNetdata2] == 0x63)) {
            // Service Mode Entry
             Railpower = csServiceMode;
            if (notifyXNetPower)
               notifyXNetPower(Railpower);
            }
        }
        else if (XNetMsg[XNetcom] == 0x81) {
         if ((XNetMsg[XNetdata1] == 0x00) && (XNetMsg[XNetdata2] == 0x81)) {
            //Emergency Stop
            Railpower = csEmergencyStop;
            if (notifyXNetPower)
               notifyXNetPower(Railpower);
           }
        }
        else if (XNetMsg[XNetlength] == 8 && XNetMsg[XNetcom] == 0x05 && XNetMsg[XNetdata1] == 0xF1) {
           //DCC FAST CLOCK set request
           /* 0x05 0xF1 TCODE1 TCODE2 TCODE3 TCODE4 [XOR]
            00mmmmmm   TCODE1, mmmmmm = denotes minutes, range 0...59.
            100HHHHH   TCODE2, HHHHH = denotes hours, range 0...23.
            01000www   TCODE3, www = denotes day of week, 0=monday, 1=tuesday, a.s.o.
            110fffff   TCODE4, fffff = denotes speed ratio, range 0..31. (0=stopped)
            */
        }
   }
   else if (XNetMsg[XNetmsg] == myDirectedOps && XNetMsg[XNetlength] >= 3) {
/*      Serial.print("RX: ");
      Serial.print(XNetMsg[XNetcom], HEX);
      Serial.print("-");
      Serial.print(XNetMsg[XNetdata1], HEX);
      Serial.print(" ");
      Serial.print(XNetMsg[XNetdata2], HEX);
      Serial.print(" ");
      Serial.print(XNetMsg[XNetdata3], HEX);
      Serial.print(" ");
      Serial.print(XNetMsg[XNetdata4], HEX);
      Serial.print("..");
*/
      switch (XNetMsg[XNetcom]) {
      //add by Norberto Redondo Melchor:   
      case 0x52:   // Some other device asked for an accessory change
          if (XNetMsg[XNetlength] >= 3) {
            // Pos = 0000A00P A = turnout output (active 0/inactive 1); P = Turn v1 or --0
            byte A_bit = (XNetMsg[XNetdata2] >> 3) & B0001;
            if (!A_bit) { // Accessory activation request
              unsigned int Adr = (XNetMsg[XNetdata1] << 2) | ((XNetMsg[XNetdata2] & B0110) >> 1);  // Dir afectada
              byte Pos = (XNetMsg[XNetdata2] & B0001) + 1;
              notifyTrnt(highByte(Adr), lowByte(Adr), Pos);
              //digitalWrite(6, HIGH); // Nice to see some blink
            }
            else { // Accessory deactivation request
              //digitalWrite(6, LOW); // Just a blink
            }
          }
      break;
      case 0x62:
         if (XNetMsg[XNetdata1] == 0x21 && XNetMsg[XNetlength] >= 4) {    //Sw Version 2.3
            // old version - version 1 and version 2 response.
           }
         else if (XNetMsg[XNetdata1] == 0x22 && XNetMsg[XNetlength] >= 5) {
            if (XNetRun == false) {   //Softwareversion anfragen
                  unsigned char commandVersionSequence[] = {0x21, 0x21, 0x00};
                  XNetSendadd (commandVersionSequence, 3);
                  XNetRun = true;
            }
            Railpower = csNormal;
            if (XNetMsg[XNetdata2] != 0) {
               // is track power turned off?
               if ((XNetMsg[XNetdata2] & 0x01) == 0x01) { Railpower = csEmergencyStop; } //Bit 0: wenn 1, Anlage in Nothalt
                  // is it in emergency stop?
               if ((XNetMsg[XNetdata2] & 0x02) == 0x02) { Railpower = csTrackVoltageOff; }  //Bit 1: wenn 1, Anlage in Notaus
               // in service mode?
               if ((XNetMsg[XNetdata2] & 0x08) == 0x08) {Railpower = csServiceMode;}   //Bit 3: wenn 1, dann Programmiermode aktiv         
               // in powerup mode - wait until complete
               if ((XNetMsg[XNetdata2] & 0x40) == 0x40) {
                  // put us in a state where we do the status request again...
                  XNetRun = false;
               }
            }
            if (notifyXNetPower)
               notifyXNetPower(Railpower);
         }
      break;
      case 0x61:
        if (XNetMsg[XNetlength] >= 4) {
         if (XNetMsg[XNetdata1] == 0x13) {
            //Programmierinfo „Daten nicht gefunden“
            if (notifyCVInfo)
               notifyCVInfo(0x02);
         }
         if (XNetMsg[XNetdata1] == 0x1F) {
            //Programmierinfo „Zentrale Busy“
            if (notifyCVInfo)
               notifyCVInfo(0x01);
         }
         if (XNetMsg[XNetdata1] == 0x11) {
            //Programmierinfo „Zentrale Bereit“
            if (notifyCVInfo)
               notifyCVInfo(0x00);
         }
         if (XNetMsg[XNetdata1] == 0x12) {
            //Programmierinfo „short-circuit“
            if (notifyCVInfo)
               notifyCVInfo(0x03);
         }
         if (XNetMsg[XNetdata1] == 0x80) {
            //Transfer Error
            if (notifyCVInfo)
               notifyCVInfo(0xE1);
         }
         if (XNetMsg[XNetdata1] == 0x82) {
            //Befehl nicht vorhanden Rückmeldung
         }
        }
      break;   
      case 0x63:
         //Softwareversion Zentrale
         if ((XNetMsg[XNetdata1] == 0x21) && (XNetMsg[XNetlength] >= 5)) {
            if (notifyXNetVer)
               notifyXNetVer(XNetMsg[XNetdata2], XNetMsg[XNetdata3]);
         }
         //Programmierinfo „Daten 3-Byte-Format“ & „Daten 4-Byte-Format“
         if ((XNetMsg[XNetdata1] == 0x10 || XNetMsg[XNetdata1] == 0x14) && XNetMsg[XNetlength] >= 5) {
            byte cvAdr = XNetMsg[XNetdata2];
            byte cvData = XNetMsg[XNetdata3];
            if (notifyCVResult)
               notifyCVResult(cvAdr, cvData);
         }
      break;
      case 0xE4:   //Antwort der abgefragen Lok
         if (XNetMsg[XNetlength] >= 7 && ReqLocoAdr != 0) {
            byte Adr_MSB = highByte(ReqLocoAdr);
            byte Adr_LSB = lowByte(ReqLocoAdr);
            ReqLocoAdr = 0;
            uint8_t Steps = XNetMsg[XNetdata1];      //0000 BFFF - B=Busy; F=Fahrstufen   
            bitWrite(Steps, 3, 0);   //Busy bit löschen
            if (Steps == B100)
               Steps = B11;
            boolean Busy = false;
            if (bitRead(XNetMsg[XNetdata1], 3) == 1)
               Busy = true;
            uint8_t Speed = XNetMsg[XNetdata2];      //RVVV VVVV - R=Richtung; V=Geschwindigkeit
            bitWrite(Speed, 7, 0);   //Richtungs bit löschen
            uint8_t Direction = false;
            if (bitRead(XNetMsg[XNetdata2], 7) == 1)
               Direction = true;
            uint8_t F0 = XNetMsg[XNetdata3];   //0 0 0 F0 F4 F3 F2 F1
            uint8_t F1 = XNetMsg[XNetdata4];   //F12 F11 F10 F9 F8 F7 F6 F5
            
            byte BSteps = Steps;
            if (Busy)
               bitWrite(BSteps, 3, 1);
            byte funcsts = F0;   //FktSts = Chg-F, X, Dir, F0, F4, F3, F2, F1
            bitWrite(funcsts, 5, Direction);   //Direction hinzufügen
            
            bool chg = xLokStsadd (Adr_MSB, Adr_LSB, BSteps, Speed, funcsts);   //Eintrag in SlotServer
            chg = chg | xLokStsFunc1 (Adr_MSB, Adr_LSB, F1);
            if (chg == true)          //Änderungen am Zustand?
               getLocoStateFull(Adr_MSB, Adr_LSB, true);
            
            if (Speed == 0) { //Lok auf Besetzt schalten
               setLocoHalt (Adr_MSB, Adr_LSB);//Sende Lok HALT um Busy zu erzeugen!
            }
         }
      break;
      case 0xE3:   //Antwort abgefrage Funktionen F13-F28
         if (XNetMsg[XNetdata1] == 0x52 && XNetMsg[XNetlength] >= 6 && ReqFktAdr != 0) {   //Funktionszustadn F13 bis F28
            byte Adr_MSB = highByte(ReqFktAdr);
            byte Adr_LSB = lowByte(ReqFktAdr);
            ReqFktAdr = 0;
            byte F2 = XNetMsg[XNetdata2];   //F2 = F20 F19 F18 F17 F16 F15 F14 F13
            byte F3 = XNetMsg[XNetdata3];   //F3 = F28 F27 F26 F25 F24 F23 F22 F21
            if (xLokStsFunc23 (Adr_MSB, Adr_LSB, F2, F3) == true) {   //Änderungen am Zustand?
               if (notifyLokFunc)
                  notifyLokFunc(Adr_MSB, Adr_LSB, F2, F3 );
               getLocoStateFull(Adr_MSB, Adr_LSB, true);
            }
         }
         if (XNetMsg[XNetdata1] == 0x40 && XNetMsg[XNetlength] >= 6) {    // Locomotive is being operated by another device
            XLokStsSetBusy (XNetMsg[XNetdata2], XNetMsg[XNetdata3]);
         }
      break;
      case 0xE1:
         if (XNetMsg[XNetlength] >= 3) {
            //Fehlermeldung Lok control
            if (notifyCVInfo)
               notifyCVInfo(0xE1);
         }
      break;
      case 0x42:   //Antwort Schaltinformation
         if (XNetMsg[XNetlength] >= 4) {
            int Adr = XNetMsg[XNetdata1] * 4;
            byte nibble = bitRead(XNetMsg[XNetdata2], 4);
            byte Pos1 = XNetMsg[XNetdata2] & B11;
            byte Pos2 = (XNetMsg[XNetdata2] >> 2) & B11;
            if (nibble == 1)
               Adr = Adr + 2;
            if (notifyTrnt)
               notifyTrnt(highByte(Adr), lowByte(Adr), Pos1);
            if (notifyTrnt)
               notifyTrnt(highByte(Adr+1), lowByte(Adr+1), Pos2);
         }
      break;
      case 0xA3:   // Locomotive is being operated by another device
         if (XNetMsg[XNetlength] >= 4) {
            if (notifyXNetPower)
               notifyXNetPower(XNetMsg[XNetdata1]);
         }
      break;
      }   //switch myDirectedOps ENDE
   }
//   if (ReadData == false)   //Nachricht komplett empfangen, dann hier löschen!
      XNetclear();   //alte verarbeitete Nachricht löschen
  }      //Daten vorhanden ENDE
  else {   //keine Daten empfangen, setzte LED = Blink
     previousMillis++;
     if (previousMillis > interval) {         //WARTEN
        XNetRun = false;   //Keine Zentrale vorhanden
      // save the last time you blinked the LED
      previousMillis = 0;   
      // if the LED is off turn it on and off (Blink):
      ledState = !ledState;
      if (notifyXNetStatus)
         notifyXNetStatus (ledState);
     }
  }
  //Slot Server aktualisieren
  if (currentMillis - SlotTime > SlotInterval) {
     SlotTime = currentMillis;
     UpdateBusySlot();      //Server Update - Anfrage nach Statusänderungen
  }
}

//--------------------------------------------------------------------------------------------
//Zustand der Gleisversorgung setzten
bool XpressNetClass::setPower(byte Power)
{
   switch (Power) {   
   case csNormal: {
         unsigned char PowerAn[] = { 0x21, 0x81, 0xA0 };
         return XNetSendadd(PowerAn, 3);
      }
      case csEmergencyStop: {
         unsigned char EmStop[] = { 0x80, 0x80 };
         return XNetSendadd(EmStop, 2);
      }
      case csTrackVoltageOff: {
         unsigned char PowerAus[] = { 0x21, 0x80, 0xA1 };
         return XNetSendadd(PowerAus, 3);
      }
/*      case csShortCircuit:
         return false;
      case csServiceMode:
         return false;   */
   }
   return false;
}

//--------------------------------------------------------------------------------------------
//Abfrage letzte Meldung über Gleispannungszustand
byte XpressNetClass::getPower()
{
   return Railpower;
}

//--------------------------------------------------------------------------------------------
//Halt Befehl weiterleiten
void XpressNetClass::setHalt()
{
   setPower(csEmergencyStop);
}

//--------------------------------------------------------------------------------------------
//Abfragen der Lokdaten (mit F0 bis F12)
bool XpressNetClass::getLocoInfo (byte Adr_High, byte Adr_Low)   
{
   bool ok = false;
   
   getLocoStateFull(Adr_High, Adr_Low, false);
   
   byte Slot = xLokStsgetSlot(Adr_High, Adr_Low);
   if (xLokSts[Slot].state < 0xFF)
      xLokSts[Slot].state++;      //aktivität
      
   if (xLokStsBusy(Slot) == true && ReqLocoAdr == 0)   {      //Besetzt durch anderen XPressNet Handregler
      ReqLocoAdr = word(Adr_High, Adr_Low); //Speichern der gefragen Lok Adresse
      unsigned char getLoco[] = {0xE3, 0x00, Adr_High, Adr_Low, 0x00};
      getXOR(getLoco, 5);
      ok = XNetSendadd (getLoco, 5);
   }
   
   return ok;
}

//--------------------------------------------------------------------------------------------
//Abfragen der Lok Funktionszustände F13 bis F28
bool XpressNetClass::getLocoFunc (byte Adr_High, byte Adr_Low)   
{
   if (ReqFktAdr == 0) {
      ReqFktAdr = word(Adr_High, Adr_Low); //Speichern der gefragen Lok Adresse
      unsigned char getLoco[] = {0xE3, 0x09, Adr_High, Adr_Low, 0x00};
      getXOR(getLoco, 5);
      return XNetSendadd (getLoco, 5);
   }
   unsigned char getLoco[] = {0xE3, 0x09, highByte(ReqFktAdr), lowByte(ReqFktAdr), 0x00};
   getXOR(getLoco, 5);
   return XNetSendadd (getLoco, 5);
}

//--------------------------------------------------------------------------------------------
//Lok Stoppen
bool XpressNetClass::setLocoHalt (byte Adr_High, byte Adr_Low)
{
   bool ok = false;
   unsigned char setLocoStop[] = {0x92, Adr_High, Adr_Low, 0x00};
   getXOR(setLocoStop, 4);
   ok = XNetSendadd (setLocoStop, 4);

   byte Slot = xLokStsgetSlot(Adr_High, Adr_Low);
   xLokSts[Slot].speed = 0;   //STOP

   getLocoStateFull(Adr_High, Adr_Low, true);
   return ok;
}

//--------------------------------------------------------------------------------------------
//Lokdaten setzten
bool XpressNetClass::setLocoDrive (byte Adr_High, byte Adr_Low, uint8_t Steps, uint8_t Speed)
{
   bool ok = false;
   unsigned char setLoco[] = {0xE4, 0x10, Adr_High, Adr_Low, Speed, 0x00};
   setLoco[1] |= Steps;
   
   getXOR(setLoco, 6);
   ok = XNetSendadd (setLoco, 6);

   byte Slot = xLokStsgetSlot(Adr_High, Adr_Low);
   xLokSts[Slot].mode = (xLokSts[Slot].mode & B11111100) | Steps;   //Fahrstufen
   xLokSts[Slot].speed = Speed & B01111111;
   bitWrite(xLokSts[Slot].f0, 5, bitRead(Speed, 7));   //Dir

//   getLocoStateFull(Adr_High, Adr_Low, true);   

   //Nutzung protokollieren:
   if (xLokSts[Slot].state < 0xFF)
      xLokSts[Slot].state++;      //aktivität
   return ok;
}

//--------------------------------------------------------------------------------------------
//Lokfunktion setzten
bool XpressNetClass::setLocoFunc (byte Adr_High, byte Adr_Low, uint8_t type, uint8_t fkt)
{
   bool ok = false;   //Funktion wurde nicht gesetzt!
   bool fktbit = 0;   //neue zu ändernde fkt bit
   if (type == 1)   //ein
      fktbit = 1;
   byte Slot = xLokStsgetSlot(Adr_High, Adr_Low);
   //zu änderndes bit bestimmen und neu setzten:
   if (fkt <= 4) {
      byte func = xLokSts[Slot].f0 & B00011111;   //letztes Zustand der Funktionen 000 F0 F4..F1
      if (type == 2) { //um
         if (fkt == 0)
            fktbit = !(bitRead(func, 4));
         else fktbit = !(bitRead(func, fkt-1));
      }
      if (fkt == 0)
         bitWrite(func, 4, fktbit);
      else bitWrite(func, fkt-1, fktbit);
      //Daten über XNet senden:
      unsigned char setLocoFunc[] = {0xE4, 0x20, Adr_High, Adr_Low, func, 0x00};   //Gruppe1 = 0 0 0 F0 F4 F3 F2 F1
      getXOR(setLocoFunc, 6);
      ok = XNetSendadd (setLocoFunc, 6);
      //Slot anpassen:
      if (fkt == 0)
         bitWrite(xLokSts[Slot].f0, 4, fktbit);
      else bitWrite(xLokSts[Slot].f0, fkt-1, fktbit);
   }
   else if ((fkt >= 5) && (fkt <= 8)) {
      byte funcG2 = xLokSts[Slot].f1 & 0x0F;   //letztes Zustand der Funktionen 0000 F8..F5
      if (type == 2) //um
         fktbit = !(bitRead(funcG2, fkt-5));
      bitWrite(funcG2, fkt-5, fktbit);
      //Daten über XNet senden:
      unsigned char setLocoFunc[] = {0xE4, 0x21, Adr_High, Adr_Low, funcG2, 0x00};   //Gruppe2 = 0 0 0 0 F8 F7 F6 F5
      getXOR(setLocoFunc, 6);
      ok = XNetSendadd (setLocoFunc, 6);
      //Slot anpassen:
      bitWrite(xLokSts[Slot].f1, fkt-5, fktbit);
   }
   else if ((fkt >= 9) && (fkt <= 12)) {
      byte funcG3 = xLokSts[Slot].f1 >> 4;   //letztes Zustand der Funktionen 0000 F12..F9
      if (type == 2) //um
         fktbit = !(bitRead(funcG3, fkt-9));
      bitWrite(funcG3, fkt-9, fktbit);
      //Daten über XNet senden:
      unsigned char setLocoFunc[] = {0xE4, 0x22, Adr_High, Adr_Low, funcG3, 0x00};   //Gruppe3 = 0 0 0 0 F12 F11 F10 F9
      getXOR(setLocoFunc, 6);
      ok = XNetSendadd (setLocoFunc, 6);
      //Slot anpassen:
      bitWrite(xLokSts[Slot].f1, fkt-9+4, fktbit);
   }
   else if ((fkt >= 13) && (fkt <= 20)) {
      byte funcG4 = xLokSts[Slot].f2;
      if (type == 2) //um
         fktbit = !(bitRead(funcG4, fkt-13));
      bitWrite(funcG4, fkt-13, fktbit);
      //Daten über XNet senden:
      //unsigned char setLocoFunc[] = {0xE4, 0x23, Adr_High, Adr_Low, funcG4, 0x00};   //Gruppe4 = F20 F19 F18 F17 F16 F15 F14 F13
      unsigned char setLocoFunc[] = {0xE4, 0xF3, Adr_High, Adr_Low, funcG4, 0x00};   //Gruppe4 = F20 F19 F18 F17 F16 F15 F14 F13
      //0xF3 = undocumented command is used when a mulitMAUS is controlling functions f20..f13.
      getXOR(setLocoFunc, 6);
      ok = XNetSendadd (setLocoFunc, 6);
      //Slot anpassen:
      bitWrite(xLokSts[Slot].f2, (fkt-13), fktbit);
   }
   else if ((fkt >= 21) && (fkt <= 28)) {
      byte funcG5 = xLokSts[Slot].f3;
      if (type == 2) //um
         fktbit = !(bitRead(funcG5, fkt-21));
      bitWrite(funcG5, fkt-21, fktbit);
      //Daten über XNet senden:
      unsigned char setLocoFunc[] = {0xE4, 0x28, Adr_High, Adr_Low, funcG5, 0x00};   //Gruppe5 = F28 F27 F26 F25 F24 F23 F22 F21
      getXOR(setLocoFunc, 6);
      ok = XNetSendadd (setLocoFunc, 6);
      //Slot anpassen:
      bitWrite(xLokSts[Slot].f3, (fkt-21), fktbit);
   }
   getLocoStateFull(Adr_High, Adr_Low, true);   //Alle aktiven Geräte Senden!
   return ok;
}

//--------------------------------------------------------------------------------------------
//Gibt aktuellen Lokstatus an Anfragenden Zurück
void XpressNetClass::getLocoStateFull (byte Adr_High, byte Adr_Low, bool bc)
{
   byte Slot = xLokStsgetSlot(Adr_High, Adr_Low);
   byte Busy = bitRead(xLokSts[Slot].mode, 3);
   byte Dir = bitRead(xLokSts[Slot].f0, 5);
   byte F0 = xLokSts[Slot].f0 & B00011111;
   byte F1 = xLokSts[Slot].f1;
   byte F2 = xLokSts[Slot].f2;
   byte F3 = xLokSts[Slot].f3;
      if (notifyLokAll)
         notifyLokAll(Adr_High, Adr_Low, Busy, xLokSts[Slot].mode & B11, xLokSts[Slot].speed, Dir, F0, F1, F2, F3, bc);
   //Nutzung protokollieren:
   if (xLokSts[Slot].state < 0xFF)
      xLokSts[Slot].state++;      //aktivität
}

//--------------------------------------------------------------------------------------------
//Ermitteln der Schaltstellung einer Weiche
bool XpressNetClass::getTrntInfo (byte FAdr_High, byte FAdr_Low)
{
   int Adr = word(FAdr_High, FAdr_Low);
   byte nibble = 0;   // 0 = Weiche 0 und 1; 1 = Weiche 2 und 3
   if ((Adr & 0x03) >= 2)
      nibble = 1;
   unsigned char getTrntPos[] = {0x42, 0x00, 0x80, 0x00};
   getTrntPos[1] = Adr >> 2;
   getTrntPos[2] += nibble;
   getXOR(getTrntPos, 4);
   return XNetSendadd (getTrntPos, 4);
}

//--------------------------------------------------------------------------------------------
//Schalten einer Weiche
bool XpressNetClass::setTrntPos (byte FAdr_High, byte FAdr_Low, byte Pos)
//Pos = 0000A00P   A=Weichenausgang (Aktive/Inaktive); P=Weiche nach links oder nach rechts
{
   int Adr = word(FAdr_High, FAdr_Low);
   byte AdrL = ((Pos & 0x0F) | B110) & (((Adr & 0x03) << 1) | B1001); //1000ABBP -> A00P = Pos | BB = Adr & 0x03 (LSB Weichenadr.)

   Adr = Adr >> 2;
   bitWrite(AdrL, 7, 1);
   unsigned char setTrnt[] = {0x52, 0x00, AdrL, 0x00};   //old: 0x52, Adr, AdrL, 0x00
   setTrnt[1] =  (Adr >> 2) & 0xFF;
   getXOR(setTrnt, 4);

   //getTrntInfo(FAdr_High, FAdr_Low);  //Schaltstellung abfragen
   if (notifyTrnt)
      notifyTrnt(FAdr_High, FAdr_Low, (Pos & B1) + 1);

   return XNetSendadd (setTrnt, 4);
}

//--------------------------------------------------------------------------------------------
//CV-Mode CV Lesen
void XpressNetClass::readCVMode (byte CV)      
{
   unsigned char cvRead[] = {0x22, 0x15, CV, 0x00};
   getXOR(cvRead, 4);
   XNetSendadd (cvRead, 4);
   getresultCV(); //Programmierergebnis anfordern
}

//--------------------------------------------------------------------------------------------
//Schreiben einer CV im CV-Mode
void XpressNetClass::writeCVMode (byte CV, byte Data)
{
   unsigned char cvWrite[] = {0x23, 0x16, CV, Data, 0x00};
   getXOR(cvWrite, 5);
   XNetSendadd (cvWrite, 5);
   //getresultCV(); //Programmierergebnis anfordern

   if (notifyCVResult)
      notifyCVResult(CV, Data);
}

//--------------------------------------------------------------------------------------------
//Programmierergebnis anfordern
void XpressNetClass::getresultCV ()
{
   unsigned char getresult[] = {0x21, 0x10, 0x31};
   XNetSendadd (getresult, 3);
}

// Private Methods ///////////////////////////////////////////////////////////////////////////////////////////////////
// Functions only available to other functions in this library *******************************************************

//--------------------------------------------------------------------------------------------
// calculate the XOR
void XpressNetClass::getXOR (unsigned char *data, byte length) {
   byte XOR = 0x00;
   for (int i = 0; i < (length-1); i++) {
        XOR = XOR ^ *data;
      data++;
    }
   *data = XOR;
}

//--------------------------------------------------------------------------------------------
// calculate the parity bit in the call byte for this guy
unsigned int XpressNetClass::callByteParity (unsigned int me) {
 int parity = (1==0);
 unsigned int vv;
 me = me & 0x7f;
 vv = me;

 while (vv) {
       parity = !parity;
       vv = vv & (vv-1);
 }
 if (parity) me = me | 0x80;
 return me;
}

//--------------------------------------------------------------------------------------------
int XpressNetClass::USART_Receive(void)
{
   unsigned char status, resh, resl;
   // Wait for data to be received
#ifdef __AVR_ATmega8__
   status = UCSRA;
   while (!(status & (1 << RXC))) { return -1; }//status = UCSRA;}

   // Get status and 9th bit, then data
   resh = UCSRB;
   resl = UDR;

   // If error, return -1
   if (status & ((1 << FE) | (1 << DOR) | (1 << PE))) { return -1; }

#else
#ifdef SERIAL_PORT_0
   status = UCSR0A;
   while (!(status & (1 << RXC0))) { return -1; }//status = UCSR0A;}

   // Get status and 9th bit, then data
   resh = UCSR0B;
   resl = UDR0;

   //If error, return -1
   if (status & ((1 << FE0) | (1 << DOR0) | (1 << UPE0))) { return -1; }

#else
   status = UCSR1A;
   while (!(status & (1 << RXC1))) { return -1; }//status = UCSR1A;}

   // Get status and 9th bit, then data
   resh = UCSR1B;
   resl = UDR1;

   // If error, return -1
   if (status & ((1 << FE1) | (1 << DOR1) | (1 << UPE1))) { return -1; }
#endif

#endif

   // Filter the 9th bit, then return
   resh = (resh >> 1) & 0x01;
   return ((resh << 8) | resl);
}

//--------------------------------------------------------------------------------------------
void XpressNetClass::USART_Transmit(unsigned char data8) {
 // wait for empty transmit buffer
 #ifdef __AVR_ATmega8__
  while (!(UCSRA & (1<<UDRE))) {}
 // put the data into buffer, and send
 UDR = data8;
 #else
 #ifdef SERIAL_PORT_0
  while (!(UCSR0A & (1<<UDRE0))) {}
 // put the data into buffer, and send
 UDR0 = data8;
 #else
 while (!(UCSR1A & (1<<UDRE1))) {}
 // put the data into buffer, and send
 UDR1 = data8;
 #endif
 #endif
}

//--------------------------------------------------------------------------------------------
//Löschen des letzten gesendeten Befehls
void XpressNetClass::XNetclear()
{
   XNetMsg[XNetlength] = 0x00;
   XNetMsg[XNetmsg] = 0x00;
   XNetMsg[XNetcom] = 0x00;
   XNetMsg[XNetdata1] = 0x00;
   XNetMsg[XNetdata2] = 0x00;
   XNetMsg[XNetdata3] = 0x00;
   XNetMsg[XNetdata4] = 0x00;
   XNetMsg[XNetdata5] = 0x00;
}

//--------------------------------------------------------------------------------------------
//Interrupt routine for reading via Serial
#ifdef __AVR_ATmega328P__
ISR(USART_RX_vect)  {
   XpressNetClass::handle_interrupt();    //weiterreichen an die Funktion
}

#else
#ifdef SERIAL_PORT_0
ISR(USART0_RX_vect) {
   XpressNetClass::handle_interrupt();    //weiterreichen an die Funktion
}

#else
ISR(USART1_RX_vect) {
   XpressNetClass::handle_interrupt();    //weiterreichen an die Funktion
}
#endif

#endif

// Interrupt handling
/* static */
inline void XpressNetClass::handle_interrupt()
{
  if (active_object)
  {
    active_object->XNetget();   //Daten Einlesen und Speichern
  }
}

//--------------------------------------------------------------------------------------------
//Serial einlesen:
void XpressNetClass::XNetget()
{
   unsigned int rxdata = USART_Receive();
   
   if ((int)rxdata != -1) {      //Daten wurden korrekt empfangen?
      previousMillis = 0;      //Reset Time Count
      // This IS a Call Byte
      if (rxdata >= 0x100) {      //Neue Nachricht beginnen
         ReadData = false;      //keine Speichern der Serial Daten
         if (rxdata == myRequestAck) {
            unsigned char requestAckAck[] = {0x20, 0x20};
            XNetsend(requestAckAck, 2);
            //Transfer Error
            if (notifyCVInfo)
               notifyCVInfo(0xE1);
            return;      //Daten wurden verarbeitet
         }
         else if (rxdata == myCallByteInquiry) {
            unsigned char commandStatusSequence[] = {0x21, 0x24, 0x05};
            if (XNetRun == false || Railpower == 0xFF) {
               XNetsend(commandStatusSequence, 3);
            }
            else XNetsend();
            return;      //Daten wurden verarbeitet
         }
         else if (rxdata == GENERAL_BROADCAST || rxdata == myDirectedOps) {   //Datenempfang aktivieren
            XNetclear();   //alte Nachricht löschen
            ReadData = true;
         }
      }
      //add by Norberto Redondo Melchor:
      else if (rxdata == 0x52) {  // Let's spy on the bus: someone has requested an accessory change
         XNetMsg[XNetmsg] = 0x01;  // Any non-zero value would do
         XNetMsg[XNetlength] = 1;
         ReadData = true;          // Let's record this someone's request
      }
      if (ReadData == true) {   //Data is for our own address
         XNetMsg[XNetlength]++;      //Let's make room for it...
         XNetMsg[XNetMsg[XNetlength]] = rxdata;  //...and store it
      }
   }
}

//--------------------------------------------------------------------------------------------
void XpressNetClass::XNetclearSendBuf()      //Buffer leeren
{
   for (int i = 0; i < XSendMax; i++) {
      XNetSend[i].length = 0x00;         //Länge zurücksetzten
      for (int j = 0; j < XSendMaxData; j++) {
         XNetSend[i].data[j] = 0x00;      //Daten löschen
      }
   }
}

//--------------------------------------------------------------------------------------------
boolean XpressNetClass::XNetSendadd(unsigned char *dataString, byte byteCount)
{
   for (int i = 0; i < XSendMax; i++) {
      if (XNetSend[i].length == 0) {   //Daten hier Eintragen:
         XNetSend[i].length = byteCount;    //Datenlaenge
         for (int b = 0; b < byteCount; b++) {
            XNetSend[i].data[b] = *dataString;
            dataString++;
         }
         return true;   //leeren Platz gefunden -> ENDE
      }
   }
   return false;   //Kein Platz im Sendbuffer frei!
}

//--------------------------------------------------------------------------------------------
//Byte via Serial senden
void XpressNetClass::XNetsend(void)
{   
   if (XNetSend[0].length != 0) { // && XNetSend[0].length < XSendMaxData) {
      if (XNetSend[0].data[0] != 0)
         XNetsend(XNetSend[0].data,XNetSend[0].length);
      for (int i = 0; i < (XSendMax-1); i++) {
         XNetSend[i].length = XNetSend[i+1].length;
         for (int j = 0; j < XSendMaxData; j++) {
            XNetSend[i].data[j] = XNetSend[i+1].data[j];      //Daten kopieren
         }
      }
      //letzten Leeren
      XNetSend[XSendMax-1].length = 0x00;
      for (int j = 0; j < XSendMaxData; j++) {
         XNetSend[XSendMax-1].data[j] = 0x00;      //Daten löschen
      }
   }
   else XNetSend[0].length = 0;
}

//--------------------------------------------------------------------------------------------
// send along a bunch of bytes to the Command Station
void XpressNetClass::XNetsend(unsigned char *dataString, byte byteCount) {
   unsigned int i;
   digitalWrite (MAX485_CONTROL, HIGH);
//   delayMicroseconds(3);
   for (i=0; i< byteCount; i++) {
     USART_Transmit (*dataString);
     dataString ++;
   }
   WAIT_FOR_XMIT_COMPLETE;
   digitalWrite (MAX485_CONTROL, LOW);
}


/*
***************************************** SLOTSERVER ****************************************
   uint8_t low;      // A7, A6, A5, A4, A3, A2, A1, A0
   uint8_t high;      //X, X, A13, A12, A11, A10, A9, A8
   uint8_t speed;      //Speed 0..127 (0x00 - 0x7F)
   uint8_t f0;      //0, 0, Dir, F0, F4, F3, F2, F1
   uint8_t f1;
   uint8_t func3;
   uint8_t func4;
   uint8_t state;   //Zahl der Zugriffe
*/

//--------------------------------------------------------------------------------------------
void XpressNetClass::UpdateBusySlot(void)   //Fragt Zentrale nach aktuellen Zuständen
{
/*
   if (ReqLocoAdr == 0) {
      if (xLokStsIsEmpty(SlotLast) == false && xLokSts[SlotLast].state > 0 && xLokStsBusy(SlotLast) == true) {
         byte Adr_High = xLokSts[SlotLast].high & 0x3F;
         byte Adr_Low = xLokSts[SlotLast].low;
         ReqLocoAdr = word(Adr_High, Adr_Low); //Speichern der gefragen Lok Adresse
         unsigned char getLoco[] = {0xE3, 0x00, Adr_High, Adr_Low, 0x00};
         getXOR(getLoco, 5);
         XNetSendadd (getLoco, 5);
//         if (bitRead(xLokSts[SlotLast].mode, 3) == 1)   //Slot BUSY?
            getLocoFunc (Adr_High, Adr_Low);   //F13 bis F28 abfragen
      }
      int Slot = SlotLast;
      SlotLast = getNextSlot(SlotLast);   //nächste Lok holen
      while (SlotLast != Slot) {
         if (xLokStsBusy(SlotLast) == true) {
            Slot = SlotLast;
            break;
         }
         SlotLast = getNextSlot(SlotLast);   //nächste Lok holen
      }
      
   }
   else
*/   
   if (ReqLocoAdr != 0) {
      ReqLocoAgain++;
      if (ReqLocoAgain > 9) {
         unsigned char getLoco[] = {0xE3, 0x00, highByte(ReqLocoAdr), lowByte(ReqLocoAdr), 0x00};
         getXOR(getLoco, 5);
         XNetSendadd (getLoco, 5);
         ReqLocoAgain = 0;
      }
   }
/*
   //Nichtnutzung von Slots erfassen:
   for (int i = 0; i < SlotMax; i++) {
      if (xLokSts[i].state > 0)
         xLokSts[i].state--;
      if (xLokSts[i].state > 0)
         xLokSts[i].state--;
   }
*/
}

//--------------------------------------------------------------------------------------------
void XpressNetClass::xLokStsclear (void)   //löscht alle Slots
{
   for (int i = 0; i < SlotMax; i++) {
      xLokSts[i].low = 0xFF;
      xLokSts[i].high = 0xFF;
      xLokSts[i].mode = 0xFF;
      xLokSts[i].speed = 0xFF;
      xLokSts[i].f0 = 0xFF;
      xLokSts[i].f1 = 0xFF;
      xLokSts[i].f2 = 0xFF;
      xLokSts[i].f3 = 0xFF;
      xLokSts[i].state = 0x00;
   }
}

//--------------------------------------------------------------------------------------------
bool XpressNetClass::xLokStsadd (byte MSB, byte LSB, byte Mode, byte Speed, byte FktSts)   //Eintragen Änderung / neuer Slot XLok
{
   bool change = false;
   byte Slot = xLokStsgetSlot(MSB, LSB);
   if (xLokSts[Slot].mode != Mode) {   //Busy & Fahrstufe (keine 14 Fahrstufen!)
      xLokSts[Slot].mode = Mode;
      change = true;
   }
   if (xLokSts[Slot].speed != Speed) {
      xLokSts[Slot].speed = Speed;
      change = true;
   }
   //FktSts = X, X, Dir, F0, F4, F3, F2, F1
   if (xLokSts[Slot].f0 != FktSts) {
      xLokSts[Slot].f0 = FktSts;
      change = true;
   }
   if (change == true && xLokSts[Slot].state < 0xFF)
      xLokSts[Slot].state++;
   return change;
}

//--------------------------------------------------------------------------------------------
bool XpressNetClass::xLokStsFunc0 (byte MSB, byte LSB, byte Func)   //Eintragen Änderung / neuer Slot XFunc
{
   bool change = false;
   byte Slot = xLokStsgetSlot(MSB, LSB);
   if ((xLokSts[Slot].f0 & B00011111) != Func) {
      xLokSts[Slot].f0 = Func | (xLokSts[Slot].f0 & B00100000);   //Dir anhängen!
      change = true;
   }
   if (change == true && xLokSts[Slot].state < 0xFF)
      xLokSts[Slot].state++;
   return change;
}

//--------------------------------------------------------------------------------------------
bool XpressNetClass::xLokStsFunc1 (byte MSB, byte LSB, byte Func1)   //Eintragen Änderung / neuer Slot XFunc1
{
   bool change = false;
   byte Slot = xLokStsgetSlot(MSB, LSB);
   if (xLokSts[Slot].f1 != Func1) {
      xLokSts[Slot].f1 = Func1;
      change = true;
   }
   if (change == true && xLokSts[Slot].state < 0xFF)
      xLokSts[Slot].state++;
   return change;
}

//--------------------------------------------------------------------------------------------
bool XpressNetClass::xLokStsFunc23 (byte MSB, byte LSB, byte Func2, byte Func3)   //Eintragen Änderung / neuer Slot
{
   bool change = false;
   byte Slot = xLokStsgetSlot(MSB, LSB);
   if (xLokSts[Slot].f2 != Func2) {
      xLokSts[Slot].f2 = Func2;
      change = true;
   }
   if (xLokSts[Slot].f3 != Func3) {
      xLokSts[Slot].f3 = Func3;
      change = true;
   }
   if (change == true && xLokSts[Slot].state < 0xFF)
      xLokSts[Slot].state++;
   return change;
}

bool XpressNetClass::xLokStsBusy (byte Slot) {
   bool Busy = false;
   if (bitRead(xLokSts[Slot].mode, 3) == 1)
      Busy = true;
   return Busy;
}

void XpressNetClass::XLokStsSetBusy (byte MSB, byte LSB) {
   byte Slot = xLokStsgetSlot(MSB, LSB);
   bitWrite(xLokSts[Slot].mode, 3, 1);
   if (xLokSts[Slot].state < 0xFF)
      xLokSts[Slot].state++;
}

//--------------------------------------------------------------------------------------------
byte XpressNetClass::xLokStsgetSlot (byte MSB, byte LSB)      //gibt Slot für Adresse zurück / erzeugt neuen Slot (0..126)
{
   byte Slot = 0x00;   //kein Slot gefunden!
   for (int i = 0; i < SlotMax; i++) {
      if ((xLokSts[i].low == LSB && xLokSts[i].high == MSB) || xLokStsIsEmpty(i)) {
         Slot = i;         //Slot merken
         if (xLokStsIsEmpty(Slot))    //neuer freier Slot - Lok eintragen
            xLokStsSetNew(Slot, MSB, LSB);   //Eintragen
         return Slot;
      }
   }
   //kein Slot mehr vorhanden!
   byte zugriff = 0xFF;
   for (int i = 0; i < SlotMax; i++) {
      if (xLokSts[i].state < zugriff) {
         Slot = i;
         zugriff = xLokSts[i].state;
      }
   }
   xLokStsSetNew(Slot, MSB, LSB);   //Eintragen
   return Slot;
}

//--------------------------------------------------------------------------------------------
int XpressNetClass::xLokStsgetAdr (byte Slot)         //gibt Lokadresse des Slot zurück, wenn 0x0000 dann keine Lok vorhanden
{
   if (!xLokStsIsEmpty(Slot))
      return word(xLokSts[Slot].high, xLokSts[Slot].low);   //Addresse zurückgeben
   return 0x0000;
}

//--------------------------------------------------------------------------------------------
bool XpressNetClass::xLokStsIsEmpty (byte Slot)   //prüft ob Datenpacket/Slot leer ist?
{
   if (xLokSts[Slot].low == 0xFF && xLokSts[Slot].high == 0xFF && xLokSts[Slot].speed == 0xFF && xLokSts[Slot].f0 == 0xFF &&
      xLokSts[Slot].f1 == 0xFF && xLokSts[Slot].f2 == 0xFF && xLokSts[Slot].f3 == 0xFF && xLokSts[Slot].state == 0x00)
      return true;
   return false;
}

//--------------------------------------------------------------------------------------------
void XpressNetClass::xLokStsSetNew (byte Slot, byte MSB, byte LSB)   //Neue Lok eintragen mit Adresse
{
   xLokSts[Slot].low = LSB;
   xLokSts[Slot].high = MSB;
   xLokSts[Slot].mode = B1011;   //Busy und 128 Fahrstufen
   xLokSts[Slot].speed = 0x00;
   xLokSts[Slot].f0 = 0x00;
   xLokSts[Slot].f1 = 0x00;
   xLokSts[Slot].f2 = 0x00;
   xLokSts[Slot].f3 = 0x00;
   xLokSts[Slot].state = 0x00;
}

//--------------------------------------------------------------------------------------------
byte XpressNetClass::getNextSlot (byte Slot)   //gibt nächsten genutzten Slot
{
   byte nextS = Slot;
   for (int i = 0; i < SlotMax; i++) {
      nextS++;   //nächste Lok
      if (nextS >= SlotMax)
         nextS = 0;   //Beginne von vorne
      if (xLokStsIsEmpty(nextS) == false)
         return nextS;
   }
   return nextS;
}

//--------------------------------------------------------------------------------------------
void XpressNetClass::setFree(byte MSB, byte LSB)      //Lok aus Slot nehmen
{
   byte Slot = xLokStsgetSlot(MSB, LSB);
   xLokSts[Slot].low = 0xFF;
   xLokSts[Slot].high = 0xFF;
   xLokSts[Slot].mode = 0xFF;
   xLokSts[Slot].speed = 0xFF;
   xLokSts[Slot].f0 = 0xFF;
   xLokSts[Slot].f1 = 0xFF;
   xLokSts[Slot].f2 = 0xFF;
   xLokSts[Slot].f3 = 0xFF;
   xLokSts[Slot].state = 0x00;
}   


fichero XpressNet.h
 /*
  XpressNet.h - library for XpressNet protocoll
  Copyright (c) 2013-2017 Philipp Gahtow  All right reserved.
  for Private use only!

  Version 1.9 (02.02.2017)

  Notice:
  Works until now, only with XPressNet Version 3.0 or higher!
  *********************************************************************
  21.07.2015 Philipp Gahtow - change adressing of switch commands
                     - optimize memory use of setPower Function
  29.09.2015 Philipp Gahtow - fix F13 to F20 command for Multimaus
  17.11.2015 Philipp Gahtow - fix in setTrntPos for AdrL
  02.02.2017 Philipp Gahtow - add accessory change 0x52 (by Norberto Redondo Melchor)
                     - fix in setTrntPos Adr convert
                     - fix narrow conversations in arrays
*/

// ensure this library description is only included once
#ifndef XpressNet_h
#define XpressNet_h

// include types & constants of Wiring core API
#if defined(WIRING)
 #include <Wiring.h>
#elif ARDUINO >= 100
 #include <Arduino.h>
#else
 #include <WProgram.h>
#endif

/* From the ATMega datasheet: */
//--------------------------------------------------------------------------------------------
// Which serial port is used, if we have more than one on the chip?
// note that the 328s (the currently produced "smaller" chips) only
// have one serial port, so we force this.
#ifdef __AVR_ATmega328P__
#define SERIAL_PORT_0
#undef SERIAL_PORT_1
#else
//Maybe we are running on a MEGA chip with more than 1 port? If so, you
//can put the serial port to port 1, and use the port 0 for status messages
//to your PC.
#define SERIAL_PORT_1
#undef SERIAL_PORT_0
#endif


// when sending data, do NOT continue until the hardware has sent the data out

#ifdef __AVR_ATmega8__
#define WAIT_FOR_XMIT_COMPLETE {while (!(UCSRA & (1<<TXC))); UCSRA = (1<<TXC); UCSRA = 0;}
#else
#ifdef SERIAL_PORT_0
#define WAIT_FOR_XMIT_COMPLETE {while (!(UCSR0A & (1<<TXC0))); UCSR0A = (1<<TXC0); UCSR0A = 0;}
#else
#define WAIT_FOR_XMIT_COMPLETE {while (!(UCSR1A & (1<<TXC1))); UCSR1A = (1<<TXC1); UCSR1A = 0;}
#endif

#endif

//--------------------------------------------------------------------------------------------

// XPressnet Call Bytes.
// broadcast to everyone, we save the incoming data and process it later.
#define GENERAL_BROADCAST 0x160

// certain global XPressnet status indicators
#define csNormal 0x00 // Normal Operation Resumed ist eingeschaltet
#define csEmergencyStop 0x01 // Der Nothalt ist eingeschaltet
#define csTrackVoltageOff 0x02 // Die Gleisspannung ist abgeschaltet
#define csShortCircuit 0x04 // Kurzschluss
#define csServiceMode 0x20 // Der Programmiermodus ist aktiv - Service Mode

//XpressNet Befehl, jedes gesendete Byte
#define XNetlength   0      //Länge
#define XNetmsg      1      //Message
#define XNetcom      2      //Kennung/Befehl
#define XNetdata1   3      //Databyte1
#define XNetdata2   4      //Databyte2
#define XNetdata3   5      //Databyte3
#define XNetdata4   6      //Databyte4
#define XNetdata5   7      //Databyte5

typedef struct   //Lokdaten   (Lok Events)
{
   uint8_t low;      // A7, A6, A5, A4, A3, A2, A1, A0
   uint8_t high;      // 0, 0, A13, A12, A11, A10, A9, A8 -> DFAA AAAA
   uint8_t mode;      //Kennung 0000 B0FF -> B=Busy(1), F=Fahrstufen (0=14, 1=27, 2=28, 3=128)
   uint8_t speed;      //0, Speed 0..127 (0x00 - 0x7F) -> 0SSS SSSS
   uint8_t f0;      //X X Dir F0 F4 F3 F2 F1         
   uint8_t f1;      //F12 F11 F10 F9 F8 F7 F6 F5   
   uint8_t f2;      //F20 F19 F18 F17 F16 F15 F14 F13
   uint8_t f3;      //F28 F27 F26 F25 F24 F23 F22 F21
   uint8_t state;   //Zahl der Zugriffe
} XNetLok;


/* Slotliste Loks */
#define XSendMax 16         //Maximalanzahl Daten im Sendepuffer
#define SlotMax 15         //Slots für Lokdaten
#define SlotInterval 200   //Zeitintervall zur Aktualisierung der Slots (ms)
#define XSendMaxData 8      //Anzahl Elm im Data Array XSend

typedef struct   //Antwort/Abfragespeicher
{
   uint8_t length;         //Speicher für Datenlänge
   byte data[XSendMaxData];   //zu sendende Daten
} XSend;

// library interface description
class XpressNetClass
{
  // user-accessible "public" interface
  public:
    XpressNetClass(void);   //Constuctor
   void start(byte XAdr, int XControl);  //Initialisierung Serial
   void receive(void);            //Prüfe ob XNet Packet vorhanden und werte es aus.

   bool setPower(byte Power);      //Zustand Gleisspannung Melden
   byte getPower();      //Zusand Gleisspannung geben
   void setHalt();         //Zustand Halt Melden
   bool getLocoInfo (byte Adr_High, byte Adr_Low);   //Abfragen der Lokdaten (mit F0 bis F12)
   bool getLocoFunc (byte Adr_High, byte Adr_Low);   //Abfragen der Lok Funktionszustände F13 bis F28
   bool setLocoHalt (byte Adr_High, byte Adr_Low);   //Lok anhalten
   bool setLocoDrive (byte Adr_High, byte Adr_Low, uint8_t Steps, uint8_t Speed); //Lokdaten setzten
   bool setLocoFunc (byte Adr_High, byte Adr_Low, uint8_t type, uint8_t fkt);   //Lokfunktion setzten
   void getLocoStateFull (byte Adr_High, byte Adr_Low, bool Anfrage);  //Gibt Zustand der Lok zurück.
   bool getTrntInfo (byte FAdr_High, byte FAdr_Low);      //Ermitteln der Schaltstellung einer Weiche
   bool setTrntPos (byte FAdr_High, byte FAdr_Low, byte Pos);      //Schalten einer Weiche
   //Programming:
   void readCVMode (byte CV);   //Lesen der CV im CV-Mode
   void writeCVMode (byte CV, byte Data);      //Schreiben einer CV im CV-Mode
   void getresultCV();      //Programmierergebnis anfordern
   //Slot:
   void setFree(byte Adr_High, byte Adr_Low);      //Lok aus Slot nehmen

   // public only for easy access by interrupt handlers
   static inline void handle_interrupt();      //Serial Interrupt bearbeiten

  // library-accessible "private" interface
  private:
     //Variables:
   boolean XNetRun;   //XpressNet ist aktiv
   byte MY_ADDRESS;   //XpressNet address: must be in range of 1-31; must be unique.
   byte MAX485_CONTROL; //Port for send or receive control
   unsigned int myDirectedOps;      // the address we look for when we are listening for ops
   unsigned int myCallByteInquiry;   // the address we look for for our Call Byte Window
   unsigned int myRequestAck;      // the address for a request acknowlegement sent
   unsigned int XNetMsg[8];      //Serial receive (Length, Message, Command, Data1 to Data5)
   boolean ReadData;            //Empfangene Serial Daten: (Speichern = true/Komplett = false)
   static XpressNetClass *active_object;   //aktuelle aktive Object
   void XNetget(void);         //Empfangene Daten eintragen
   XSend XNetSend[XSendMax];      //Sendbuffer
   XNetLok xLokSts[SlotMax];      //Speicher für aktive Lokzustände

      //Functions:
   void getXOR (unsigned char *data, byte length); // calculate the XOR
   unsigned int callByteParity (unsigned int me);   // calculate the parity bit
   int USART_Receive( void );   //Serial Empfangen
   void USART_Transmit (unsigned char data8); //Serial Senden
   void XNetclear(void);      //Serial Nachricht zurücksetzten

   void XNetclearSendBuf();   //Sendbuffer leeren
   boolean XNetSendadd(unsigned char *dataString, byte byteCount);   //Zum Sendebuffer Hinzufügen
   void XNetsend(void); //Send Saved Data aus Sendebuffer
   void XNetsend(unsigned char *dataString, byte byteCount);   //Sende Daten aus Array

      //Adressrequest:
   int ReqLocoAdr;      //Adresse für die Lok Daten angefragt wurden
   int ReqLocoAgain;
   int ReqFktAdr;      //Adresse für die F2 und F3 angefragt wurde

      //SlotServer:
   long SlotTime;      //store last time the Slot ask
   int SlotLast;      //letzter bearbeiteter Slot
   void UpdateBusySlot(void);   //Fragt Zentrale nach aktuellen Zuständen
   void xLokStsclear (void); //löscht alle Slots
   bool xLokStsadd (byte MSB, byte LSB, byte Mode, byte Speed, byte FktSts);   //Eintragen Änderung / neuer Slot XLok
   bool xLokStsFunc0 (byte MSB, byte LSB, byte Func);   //Eintragen Änderung / neuer Slot XFunc0
   bool xLokStsFunc1 (byte MSB, byte LSB, byte Func1);   //Eintragen Änderung / neuer Slot XFunc1
   bool xLokStsFunc23 (byte MSB, byte LSB, byte Func2, byte Func3);   //Eintragen Änderung / neuer Slot XFunc23
   bool xLokStsBusy (byte Slot); //Busy Bit Abfragen
   void XLokStsSetBusy (byte MSB, byte LSB);      //Lok Busy setzten
   byte xLokStsgetSlot (byte MSB, byte LSB);      //gibt Slot für Adresse zurück / erzeugt neuen Slot (0..126)
   int xLokStsgetAdr (byte Slot);         //gibt Lokadresse des Slot zurück, wenn 0x0000 dann keine Lok vorhanden
   bool xLokStsIsEmpty (byte Slot);   //prüft ob Datenpacket/Slot leer ist?
   void xLokStsSetNew (byte Slot, byte MSB, byte LSB);   //Neue Lok eintragen mit Adresse
   byte getNextSlot (byte Slot);   //gibt nächsten genutzten Slot

   //Spannung und GO/STOP Events:
   byte Railpower;     //Gleisspannung

   //Programming:

   //Lok Status:
   

   //Funktionen

   
   //Status LED:
   int ledState;             // ledState used to set the LED
   long previousMillis;        // will store last time LED was updated
};

#if defined (__cplusplus)
   extern "C" {
#endif

      //extern void notifyXNetDebug(String s) __attribute__((weak));
   extern void notifyXNetStatus(uint8_t LedState ) __attribute__ ((weak));
   extern void notifyXNetVer(uint8_t V, uint8_t ID ) __attribute__ ((weak));
   extern void notifyXNetPower(uint8_t State ) __attribute__ ((weak));
   extern void notifyLokFunc(uint8_t Adr_High, uint8_t Adr_Low,  uint8_t F2, uint8_t F3 ) __attribute__ ((weak));
   extern void notifyLokAll(uint8_t Adr_High, uint8_t Adr_Low, boolean Busy, uint8_t Steps, uint8_t Speed, uint8_t Direction, uint8_t F0, uint8_t F1, uint8_t F2, uint8_t F3, boolean Req ) __attribute__ ((weak));
   extern void notifyCVInfo(uint8_t State ) __attribute__ ((weak));
   extern void notifyCVResult(uint8_t cvAdr, uint8_t cvData ) __attribute__ ((weak));
   extern void notifyTrnt(uint8_t Adr_High, uint8_t Adr_Low, uint8_t Pos) __attribute__ ((weak));

//   extern void notifyXNetData(unsigned int data, bool line) __attribute__((weak));

#if defined (__cplusplus)
}
#endif


#endif

 


El tema de los paneles esta muy bien, pero viendo esto y si funciona nos ahorra mucho tiempo.
Ya me cuentas si ves algo extraño.

Nota 01 Dic 2017 00:50

Desconectado
Mensajes: 434
Ubicación: Madrid
Registrado: 05 Ene 2017 11:09
Norber escribió:
En principio el planteamiento está bien: la interface para crear una z21f a partir de un Multimaus solo requiere un Arduino (yo uso el Uno), un módulo Ethernet W5100 bien hecho, y un circuito acoplador para el bus XpressNet.



Hola Norber, perdona que te pregunte algo que quizás sea una perogrullada, pero no llego a verlo ...

Estoy tratando de montar este conjunto para poder conectar un mando Multimaus a la central que ya he montado y que funciona con un arduino mega y un motor shield y lo que no veo claro es como conjuntar estas dos partes o es que con tú z21f ya es suficiente y de ahí se saca la señal a las vías?

AnteriorSiguiente

Volver a Digital, Electricidad e Informática

Síguenos en Facebook Síguenos en Youtube Síguenos en Instagram Feed - Nuevos Temas
©2017   -   Información Legal