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.