Deiner email hat mir erreicht, und ich hab ihn antwortet, aber der Antwort könnte nicht liefert werden....
Es gibt Problemen mit 28 Fahrstufen, aber weil ich nicht nutze 28 Fahrstufen, ist es egal für mich.
Ich hab auf Version 1.8 geendet.
Code: Alles auswählen
//-------------------------------------------------------------------------------------------------------
/*
DCC-Monitor zur Ausgabe der DCC-Befehle auf dem seriellen Monitor und 4x20 LCD display
======================================================================================
V 1.0, 18.01.2020 domapi
erweitert bis V 1.7 mit LCD, bei Moppe 19.04.2020
Features:
---------
- Ausgabe der Lok-Befehle
- Ausgabe der Lok-CV-Kommandos
- Ausgabe der Accessory-Befehle
- Ausgabe der Accessory-CV-Kommandos
- Ausgabe der CV-Befehle auf dem Programmiergleis
- Eingaben über seriellen Monitor zur Steuerung der Anzeige
- DCC-Paket-Speicher zum Verhindern der mehrfachen Ausgabe von Befehlen
- einstellbar getrennt nach Loks, Accessories und CVs
- Wurde ein gültiges Paket gefunden und im seriellen Monitor ausgegeben, wird es in einer Tabelle gespeichert
- Vor jeder Paketausgabe wird geprüft, ob es in der Tabelle bereits enthalten ist, d.1h. schon ausgegeben wurde
- Die Tabelle umfasst für Loks 240 x 4 Bytes, ohne Präambel und ohne Check-Byte; für Zubehörbefehle nur 5 x 4 Bytes
(Lok-Befehle werden sehr häufig und vor allem periodisch wiederholt, Zubehör mehrfach, aber nur bei Änderungen )
- Die Tabelle wird sukzessive aufgefüllt, wenn sie voll ist, wird der erste Eintrag gelöscht und alle anderen Einträge nach vorne geschoben
Das neue Paket wandert dann an die letzte Stelle
- Die Tabellengröße sollte für ca. 70 Loks reichen
- Über das Menü kann die Speicherung ein-/aus-geschaltet werden (separat für Acc, CVs, Loks)
- Ausgabe der Lokname in Klartext
- Ausgabe einer Statistik über die detektierten DCC-Befehle
- LCD kann letzter Lok oder Weichen befehl zeigen
Hardware:
---------
- Schaltplan, siehe: https://www.stummiforum.de/viewtopic.php?t=165060&start=225#p1957001
- DCC-Signal-Auswertung über 6N137 Optokoppler, Schutzdiode (1N4148) und Vorwiderstand (1k). Oder über Brückengleichrichter (siehe Link).
Pin 6 Optokoppler mit 10 kOhm an 5V reicht aus. Pin 7 muss nicht an 5V angeschlossen sein, er kann unbelegt bleiben.
- ACK-Signal über Optokoppler CNY17 und Transistor BC557 anschließen, erzeugt Stromimpuls an +/- des Brückengleichrichters.
Arduino Nano:
// +-----+
// +------------| USB |------------+
// | +-----+ |
// | [ ]D13/SCK MISO/D12[ ] |
// | [ ]3.3V MOSI/D11[ ]~|
// | [ ]V.ref ___ SS/D10[ ]~|
// | [ ]A0 / N \ D9[ ]~|
// | [ ]A1 / A \ D8[ ] |
// | [ ]A2 \ N / D7[ ] |
// | [ ]A3 \_0_/ D6[ ]~|
// | [ ]A4/SDA D5[ ]~|
// ACK-Pin | [ ]A5/SCL D4[ ] |
// | [ ]A6 INT1/D3[ ]~|
// | [ ]A7 INT0/D2[ ] | DCC-Eingang über Optokoppler Pin 6
// | [ ]5V GND[ ] |
// | [ ]RST RST[ ] |
// | [ ]GND 5V MOSI GND TX1[ ] |
// | [ ]Vin [ ] [ ] [ ] RX1[ ] |
// | [ ] [ ] [ ] |
// | MISO SCK RST |
// | NANO-V3 |
// +-------------------------------+
LCD:
LCD RS pin to digital pin 12
LCD Enable pin to digital pin 11
LCD D4 pin to digital pin 6
LCD D5 pin to digital pin 5
LCD D6 pin to digital pin 4
LCD D7 pin to digital pin 3
LCD R/W pin to ground
LCD VSS pin to ground
LCD VCC pin to 5V
10K Pot:
ends to +5V and ground
wiper to LCD VO pin (pin 3)
Todos:
- Komplettanzeige für eine Lok auf Basis Puffer
- Anzeige auf bestimmte Adressen beschränken
*/
//-------------------------------------------------------------------------------------------------------
#include <NmraDcc.h>
#include <LiquidCrystal.h>
NmraDcc Dcc ;
//-------------------------------------------------------------------------------------------------------
// Mit diesen Variablen kann man einstellen, was auf dem seriellen Monitor ausgegeben wird
// Die Einstellungen können während des laufenden Betriebs des DCC-Monitors später im seriellen Monitor geändert werden
//-------------------------------------------------------------------------------------------------------
byte Anzeige_Loks = 1; // Lok-Befehle für Lok-Dekoder und funktionsdekoder
byte Anzeige_Acc = 1; // Zubehör-Befehle für Weichendekoder
byte Anzeige_CV = 1; // Ausgabe von CV-Aktionen (Lesen und Schreiben etc.)
byte puffern_Lok = 1; // Schaltet die Speicherung und Prüfung bereits empfangener DCC-Pakete ein/aus
byte puffern_Acc = 1;
byte puffern_CV = 1;
//-------------------------------------------------------------------------------------------------------
const byte DccAckPin = A5; // Arduino-Pin zur Erzeugung eines ACK-Signals
byte blinker = 5; // toggelt die LED an Pin 13 jedes 5. Mal, wenn ein DCC-Paket gefunden wurde --> zeigt DCC-Signal an
const byte bufferSizeAcc = 5; // Schaltartikelbefehle werden nicht andauernd wiederholt; hier reichen ein paar Pufferplätze aus
const byte bufferSizeLok = 240;
byte Acc_counter = 0; // läuft von 1 - bufferSizeAcc
byte Paket_bekannt_A = 0;
byte Lok_counter = 0; // läuft von 1 - bufferSizeLok
byte Paket_bekannt_L = 0;
// Strukturen zur Speicherung der bereits empfangenen Werte
typedef struct
{
int ADR;
byte DIR;
byte COIL;
} ACC_Befehl;
typedef struct
{
int ADR;
byte ORDER;
byte FUNC;
} Lok_Befehl;
ACC_Befehl Acc_received [bufferSizeAcc];
Lok_Befehl Lok_received [bufferSizeLok];
byte pktByteCount = 0;
unsigned int decoderAddress;
unsigned int decoderAddress_alt = 0;
unsigned int weichenadresse;
byte Ausgang;
byte Spule;
byte Befehl;
byte Funktion;
byte Befehls_Byte;
byte decoderType; //0=Lok, 1=Zubehör/Accessory
byte command;
int CV_address;
byte CV_value;
byte command_alt = 0;
int CV_address_alt = 0;
byte CV_value_alt = 0;
byte speed;
byte checksum = 0;
// Zähler für die Statistik
unsigned long start_time = 0;
unsigned long z_bytes = 0;
unsigned long z_invalid = 0;
unsigned long z_idle = 0;
unsigned long z_lok_speed = 0;
unsigned long z_lok_F0 = 0;
unsigned long z_lok_F5 = 0;
unsigned long z_lok_F9 = 0;
unsigned long z_lok_F13 = 0;
unsigned long z_lok_F21 = 0;
unsigned long z_lok_F29 = 0;
unsigned long z_acc = 0;
unsigned long z_dec_reset = 0;
unsigned long z_acc_cv = 0;
unsigned long z_lok_cv = 0;
unsigned long z_prg_CV = 0;
unsigned long z_ack = 0;
// initialize the library by associating any needed LCD interface pin
// with the arduino pin number it is connected to
LiquidCrystal lcd(12, 11, 6, 5, 4, 3);
// these constants won't change. But you can change the size of
// your LCD using them:
const int numRows = 4;
const int numCols = 20;
const int splashdelay = 5000;
//-------------------------------------------------------------------------------------------------------
void setup()
//-------------------------------------------------------------------------------------------------------
{
pinMode(13, OUTPUT); // eingebaute LED
Serial.begin(115200);
pinMode(DccAckPin, OUTPUT); // Configure the DCC CV Programing ACK pin for an output
Serial.println(F("Domapi´s DCC Monitor V 1.0"));
Serial.println(F("und Moppes DCC display"));
// set up the LCD's number of columns and rows:
lcd.begin(numCols, numRows);
lcd.setCursor(0, 0);
lcd.write("Domapi's DCC-Monitor ");
lcd.setCursor(0, 1);
lcd.write(" erweitert bis:");
lcd.setCursor(0, 2);
lcd.write(" Moppes DCC display");
lcd.setCursor(0, 3);
lcd.write(" von stummiforum.de");
delay(splashdelay);
lcd.clear();
lcd.write("Moppes DCC display ");
Dcc.pin(0, 2, 1); // Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up
Dcc.init( MAN_ID_DIY, 10, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 ); // Call the main DCC Init function to enable the DCC Receiver
start_time = millis();
}
//-------------------------------------------------------------------------------------------------------
void loop()
//-------------------------------------------------------------------------------------------------------
{
// You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
Dcc.process();
if (Serial.available())
{
// Tastatur-Befehle via seriellen Monitor des Arduinos:
// 1 = Anzeige Loks ein/aus
// 2 = Anzeige Zubehör ein/aus
// 3 = Anzeige CV-Befehle ein/aus
// 4 = Nur neue Lok-Pakete ein/aus
// 5 = Nur neue Zubehör-Pakete ein/aus
// 6 = Nur neue CV-Befehle ein/aus
// 7 = Statistik
// ? = Befehle anzeigen
switch (Serial.read())
{
case 49: // 1
Serial.print(F("1 Anzeige Loks ein/aus = "));
Anzeige_Loks = !Anzeige_Loks;
if (Anzeige_Loks) Serial.println("ein");
else Serial.println("aus");
break;
case 50: //2
Serial.print(F("2 Anzeige Zubehör ein/aus = "));
Anzeige_Acc = !Anzeige_Acc;
if (Anzeige_Acc) Serial.println("ein");
else Serial.println("aus");
break;
case 51: // 3
Serial.print(F("3 Anzeige CV-Befehle ein/aus = "));
Anzeige_CV = !Anzeige_CV;
if (Anzeige_CV) Serial.println("ein");
else Serial.println("aus");
break;
case 52: // 4
Serial.print(F("4 Nur neue Lok-Pakete anzeigen ein/aus = "));
puffern_Lok = !puffern_Lok;
if (puffern_Lok) Serial.println("ein");
else Serial.println("aus");
break;
case 53: // 5
Serial.print(F("5 Nur neue Zubehör-Pakete anzeigen ein/aus = "));
puffern_Acc = !puffern_Acc;
if (puffern_Acc) Serial.println("ein");
else Serial.println("aus");
break;
case 54: // 6
Serial.print(F("6 Nur neue CV-Befehle anzeigen ein/aus = "));
puffern_CV = !puffern_CV;
if (puffern_CV) Serial.println("ein");
else Serial.println("aus");
break;
case 55: // 7
Serial.println();
Serial.println(F("S t a t i s t i k"));
Serial.println(F("-----------------"));
Serial.print(F("Zeitraum [sec] :")); print_Zahl_rechts_ln((millis() - start_time) / 1000);
Serial.print(F("Anzahl empfangene Bytes:")); print_Zahl_rechts_ln(z_bytes);
Serial.print(F("Gültige Kommandos :"));
print_Zahl_rechts_ln(z_invalid + z_idle + z_lok_speed + z_lok_F0 + z_lok_F5 + z_lok_F9 + z_lok_F13 + z_lok_F21 + z_lok_F29 + z_acc + z_dec_reset + z_acc_cv + z_lok_cv + z_prg_CV);
Serial.print(F("Ungültige Kommandos :")); print_Zahl_rechts_ln(z_invalid);
Serial.print(F("Idle-Pakete :")); print_Zahl_rechts_ln(z_idle);
Serial.print(F("Geschwindigkeitsbefehle:")); print_Zahl_rechts_ln(z_lok_speed);
Serial.print(F("F0 - F4 Funktionen :")); print_Zahl_rechts_ln(z_lok_F0);
Serial.print(F("F5 - F8 Funktionen :")); print_Zahl_rechts_ln(z_lok_F5);
Serial.print(F("F9 - F12 Funktionen :")); print_Zahl_rechts_ln(z_lok_F9);
Serial.print(F("F13 - F20 Funktionen :")); print_Zahl_rechts_ln(z_lok_F13);
Serial.print(F("F21 - F28 Funktionen :")); print_Zahl_rechts_ln(z_lok_F21);
Serial.print(F("F29 - F36 Funktionen :")); print_Zahl_rechts_ln(z_lok_F29);
Serial.print(F("Zubehör-Befehle :")); print_Zahl_rechts_ln(z_acc);
Serial.print(F("Dekoder-Reset-Befehle :")); print_Zahl_rechts_ln(z_dec_reset);
Serial.print(F("Zubehör-CV-Befehle :")); print_Zahl_rechts_ln(z_acc_cv);
Serial.print(F("Lok-CV-Befehle :")); print_Zahl_rechts_ln(z_lok_cv);
Serial.print(F("Programmiergleisbefehle:")); print_Zahl_rechts_ln(z_prg_CV);
Serial.print(F("Acknowledgments :")); print_Zahl_rechts_ln(z_ack);
Serial.print(F("Counter Lok :")); print_Zahl_rechts_ln(Lok_counter);
Serial.print(F("Counter Acc :")); print_Zahl_rechts_ln(Acc_counter);
break;
case 63: // ?
Serial.println(); Serial.println(F("Tastaturbefehle für den seriellen Monitor:")); Serial.println();
Serial.print (F("1 = Anzeige Loks ein/aus "));
if (Anzeige_Loks) Serial.println("ein");
else Serial.println("aus");
Serial.print (F("2 = Anzeige Zubehör ein/aus "));
if (Anzeige_Acc) Serial.println("ein");
else Serial.println("aus");
Serial.print (F("3 = Anzeige CV-Befehle ein/aus "));
if (Anzeige_CV) Serial.println("ein");
else Serial.println("aus");
Serial.print (F("4 = Nur neue Lok-Pakete anzeigen ein/aus "));
if (puffern_Lok) Serial.println("ein");
else Serial.println("aus");
Serial.print (F("5 = Nur neue Zubehör-Pakete anzeigen ein/aus "));
if (puffern_Acc) Serial.println("ein");
else Serial.println("aus");
Serial.print (F("6 = Nur neue CV-Befehle ein/aus "));
if (puffern_CV) Serial.println("ein");
else Serial.println("aus");
Serial.println(F("7 = Statistik anzeigen"));
Serial.println(F("? = Befehle anzeigen"));
break;
}
Serial.println(" ");
}
}
//-------------------------------------------------------------------------------------------------------
// This function is called by the NmraDcc library when a DCC ACK needs to be sent
// Calling this function should cause an increased 60ma current drain on the power supply for 6ms to ACK a CV Read
void notifyCVAck()
//-------------------------------------------------------------------------------------------------------
{
digitalWrite( DccAckPin, HIGH );
delay( 6 );
digitalWrite( DccAckPin, LOW );
z_ack++;
}
//-------------------------------------------------------------------------------------------------------
void notifyDccMsg(DCC_MSG * Msg)
//-------------------------------------------------------------------------------------------------------
{
// LED toggeln
blinker--;
if (blinker == 0)
{
digitalWrite(13, !digitalRead(13));
blinker = 5;
}
//-------------------------------------------------------------------------------------------------------------------------------
// Alle gefunden Bytes XOR-verknüpfen; muss 0 ergeben, dann wurde ein gültiger Befehl gefunden!
//-------------------------------------------------------------------------------------------------------------------------------
pktByteCount = Msg->Size; // Anzahl gefundene Bytes ohne Präambel aber incl. Prüfbyte !!!
checksum = 0; // Wir starten mit 0
z_bytes = z_bytes + pktByteCount;
for (byte n = 0; n < pktByteCount; n++)
{
checksum ^= Msg->Data[n];
}
if (checksum)
{
z_invalid++;
return; // Ungültige Checksumme --> nix tun !
}
//-------------------------------------------------------------------------------------------------------------------------------
// Start Dekodierung
//-------------------------------------------------------------------------------------------------------------------------------
//-------------------------------------------------------------------------------------------------------------------------------
// Idle Kommando
//-------------------------------------------------------------------------------------------------------------------------------
if (Msg->Data[0] == B11111111)
{
z_idle++;
return; // Idle packet
}
//-------------------------------------------------------------------------------------------------------------------------------
// Reset Befehl zur Einleitung der CV-Programmierung
//-------------------------------------------------------------------------------------------------------------------------------
if (Msg->Data[0] == 0)
{
z_dec_reset++;
command = Msg->Data[0];
CV_address = Msg->Data[1];
CV_value = Msg->Data[2];
// Nur verarbeiten, wenn neuer Befehl!
if (!(((CV_value == CV_value_alt) && (CV_address == CV_address_alt) && (command == command_alt)) && puffern_CV))
{
Serial.print(F("Prg Dekoder-Reset-Befehl"));
print_spaces(43);
printPacket(Msg);
command_alt = command;
CV_address_alt = CV_address;
CV_value_alt = CV_value;
}
return;
}
//-------------------------------------------------------------------------------------------------------------------------------
// Programmiermodus auf dem Programmiergleis ohne Adresse !
//-------------------------------------------------------------------------------------------------------------------------------
if ((Msg->Data[0] & B11110000) == B01110000)
{
/* Service Mode.Prog: [preamble] 0 [0111CCVV] 0 [VVVVVVVV] 0 [DDDDDDDD] 0 [EEEEEEEE] 1
CC = Command
VV VVVVVVVV = 10 bit CV Number
DDDDDDDD = New Value (8 bit)
EEEEEEEE = Checksum
Lesen/Schreiben auf dem Prg.gleis geht ohne Dekoderadresse !
Schreiben: z.B. CV 6, Wert 20 (--> 4 Bytes ohne Präambel und Trennbits!)
Byte 0 Byte 1 Byte 2 Byte 3
01111100 -----101 ---10100 10000001
0111CCVV VVVVVVVV DDDDDDDD EEEEEEEE
11 = Schreiben
01 = Lesen/Überprüfen
10 = 10 Bit Manipulation
V = CV - 1 --> 5 = CV 5 + 1 = CV6 !
D = 20 */
z_prg_CV++;
// Nur verarbeiten, wenn neuer Befehl!
command = Msg->Data[0] & B00001100; // 2 Bits enthalten den Schreib-/ Verify-Befehl etc.
CV_address = ((Msg->Data[0] & B00000011) * 256) + Msg->Data[1] + 1; // Nummer der CV
CV_value = Msg->Data[2]; // CV-Wert für das Schreiben
if (!(((CV_value == CV_value_alt) && (CV_address == CV_address_alt) && (command == command_alt)) && puffern_CV))
{
decoderType = 255; // vorsichtshalber mal auf einen dämlichen Wert setzen, damit weiter unten nix passiert
Serial.print("Prg CV");
CV_address = ((Msg->Data[0] & B00000011) * 256) + Msg->Data[1] + 1;
Serial.print(CV_address);
if (CV_address < 1000) Serial.print(" ");
if (CV_address < 100) Serial.print(" ");
if (CV_address < 10) Serial.print(" ");
Serial.print(" ");
switch (Msg->Data[0] & B00001100)
{
/* Die für den Befehlstyp (xxxx-KKxx) im ersten Befehlsbyte festgelegten Werte sind:
KK = 00 – reserviert
KK = 01 – Byte Überprüfen
KK = 11 – Byte Schreiben
KK = 10 – Bit Manipulation*/
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00000100: // Verify Byte
Serial.print(F("Lese CV"));
print_spaces(49);
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00001100: // Write Byte
Serial.print(F("Schreibe CV ="));
if (Msg->Data[2] < 100) Serial.print(" ");
if (Msg->Data[2] < 10) Serial.print(" ");
Serial.print(" ");
Serial.print(Msg->Data[2]);
print_spaces(39);
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00001000: // Bit Write
// 0111-10VV VVVV-VVVV 111K-DBBB EEEE-EEEE --> im Programmiermodus für Zugriffe auf einzelne Bits
// K = 1 – Bit Schreiben
// K = 0 – Bit Überprüfen
if (Msg->Data[2] & B00010000)
{
Serial.print("Schreibe Bit #");
Serial.print(Msg->Data[2] & B00000111);
Serial.print(" = ");
Serial.print((Msg->Data[2] & B00001000) >> 3);
print_spaces(36);
}
else
{
Serial.print("Lese Bit #");
Serial.print(Msg->Data[2] & B00000111);
print_spaces(42);
}
break;
}
printPacket(Msg);
command_alt = command;
CV_address_alt = CV_address;
CV_value_alt = CV_value;
return;
}
}
else
{
//-------------------------------------------------------------------------------------------------------------------------------
// 0xxx-xxxx --> bit7=0 -> Lok Dekoder kurze Adresse
//-------------------------------------------------------------------------------------------------------------------------------
if (!bitRead(Msg->Data[0], 7))
{
decoderType = 0; // Lok
decoderAddress = Msg->Data[0]; // kurze Adresse
Befehls_Byte = Msg->Data[1];
// Aufteilung der gefundenen Bytes auf Befehle (Was soll der Dekoder tun?) und Funktionen (Wie soll er es tun?)
if ((Befehls_Byte & B11100000) == B10000000)
{
Befehl = B00000100; // 100 = F0 - F4
Funktion = Befehls_Byte & B00011111; // x-xxxx = F0 F4 - F1
z_lok_F0++;
}
else if ((Befehls_Byte & B11110000) == B10110000)
{
Befehl = B00001011; // 1011 = F5 - F8
Funktion = Befehls_Byte & B00001111; // xxxx = F8 - F5
z_lok_F5++;
}
else if ((Befehls_Byte & B11110000) == B10100000)
{
Befehl = Befehls_Byte >> 4; // 1010 = F9 - F12
Funktion = Befehls_Byte & B00001111; // xxxx = F12 - F9
z_lok_F9++;
}
else if (Befehls_Byte == B11011110)
{
Befehl = Befehls_Byte; // 1101-1110 = F13 - F20
Funktion = Msg->Data[2]; // xxxx-xxxx = F20 - F13
z_lok_F13++;
}
else if (Befehls_Byte == B11011111)
{
Befehl = Befehls_Byte; // 1101-1111 = F21 - F28
Funktion = Msg->Data[2]; // xxxx-xxxx = F28 - F21
z_lok_F21++;
}
else if (Befehls_Byte == B11011000)
{
Befehl = Befehls_Byte; // 1101-1000 = F29 - F36
Funktion = Msg->Data[2]; // xxxx-xxxx = F36 - F29
z_lok_F29++;
}
else if (((Befehls_Byte & B11000000) == 0) && (pktByteCount == 3))
{
Befehl = 0; // 00RSSSSS
Funktion = Befehls_Byte; // Speed 28 Stufen
z_lok_speed++;
}
else if (((Befehls_Byte & B11000000) == 64) && (pktByteCount == 4)) // Adr 10000 ???
{
Befehl = 0; // 00RSSSSS
Funktion = Befehls_Byte; // Speed 28 Stufen
z_lok_speed++;
}
else if ((Befehls_Byte == B00111111) && (pktByteCount == 4))
{
Befehl = Befehls_Byte; // 00111111
Funktion = Msg->Data[2]; // RSSSSSSS Speed 127 Stufen
z_lok_speed++;
}
}
else
{
//-------------------------------------------------------------------------------------------------------------------------------
// 11xx-xxxx --> bit7 = 1 AND bit6 = 1 -> Lok Dekoder lange Adresse
//-------------------------------------------------------------------------------------------------------------------------------
if (bitRead(Msg->Data[0], 6))
{
// 11xx-xxxx
decoderAddress = 256 * (Msg->Data[0] & B00111111) + Msg->Data[1];
Befehls_Byte = Msg->Data[2];
decoderType = 0;
// Aufteilung der gefundenen Bytes auf Befehle und Funktionen
if ((Befehls_Byte & B11100000) == B10000000)
{
Befehl = B00000100; // 100 = F0 - F4
Funktion = Befehls_Byte & B00011111; // x-xxxx = F0 F4 - F1
z_lok_F0++;
}
else if ((Befehls_Byte & B11110000) == B10110000)
{
Befehl = B00001011; // 1011 = F5 - F8
Funktion = Befehls_Byte & B00001111; // xxxx = F8 - F5
z_lok_F5++;
}
else if ((Befehls_Byte & B11110000) == B10100000)
{
Befehl = B00001010; // 1010 = F9 - F12
Funktion = Befehls_Byte & B00001111; // xxxx = F12 - F9
z_lok_F9++;
}
else if (Befehls_Byte == B11011110)
{
Befehl = Befehls_Byte; // 1101-1110 = F13 - F20
Funktion = Msg->Data[3]; // xxxx-xxxx = F20 - F13
z_lok_F13++;
}
else if (Befehls_Byte == B11011111)
{
Befehl = Befehls_Byte; // 1101-1111 = F21 - F28
Funktion = Msg->Data[3]; // xxxx-xxxx = F28 - F21
z_lok_F21++;
}
else if (Befehls_Byte == B11011000)
{
Befehl = Befehls_Byte; // 1101-1000 = F29 - F36
Funktion = Msg->Data[2]; // xxxx-xxxx = F36 - F29
z_lok_F29++;
}
else if (((Befehls_Byte & B11000000) == 0) && (pktByteCount == 4))
{
Befehl = 0; // 00RSSSSS
z_lok_speed++;
Funktion = Befehls_Byte; // Speed 28 Stufen
}
else if (((Befehls_Byte & B11000000) == 64) && (pktByteCount == 4)) // Adr 10000 ???
{
Befehl = 0; // 00RSSSSS
Funktion = Befehls_Byte; // Speed 28 Stufen
z_lok_speed++;
}
else if ((Befehls_Byte == B00111111) && (pktByteCount == 5))
{
Befehl = Befehls_Byte; // 00111111
Funktion = Msg->Data[3]; // RSSSSSSS Speed 127 Stufen
z_lok_speed++;
}
}
//-------------------------------------------------------------------------------------------------------------------------------
else //bit7=1 AND bit6=0 -> Accessory Decoder
{
// 10xx-xxxx
//-------------------------------------------------------------------------------------------------------------------------------
decoderAddress = Msg->Data[0] & B00111111;
Befehls_Byte = Msg->Data[1];
decoderType = 1;
}
}
}
//-------------------------------------------------------------------------------------------------------------------------------
if (decoderType == 1) // Accessory Basic
{
if (Anzeige_Acc && (Msg->Size != 6) )
{
z_acc++;
if ((Befehls_Byte & B10000000) && (Msg->Size == 3)) // Steuerbefehl für Zubehör Dekoder (Basic Accessory)
{
// die Zentrale sendet für Magnetartikel (accessories) immer ein Paket bestehend aus Adresse (= Signal), Richtung (rot/grün) und ein/aus (Spule).
// Zunächst wird eine Richtung eingeschaltet, dazu wird das DCC-Telegramm ggf. mehrfach wiederholt
// Bei der ECOS wird nach einer einstellbaren Zeit pro "Magnetartikel" ein Ausschaltbefehl (auch mehrfach) hinterhergeschickt
/* Für Zubehör-/Weichendekoder sendet die DCC-Zentrale 3 Bytes:
Byte 1 Byte 2 Byte 3
__ __ __
1 0 A7 A6 A5 A4 A3 A2 : 1 AA A9 A8 P A1 A0 R : C7 C6 C5 C4 C3 C2 C1 C0
- AA..A0 sind die 11 Bit Adresse eines Zubehördekoders ("Addr")
- P = Power (0 = off, 1 = on) ("OutputPower"), bei Spulenantrieben von Weichen sind das Spule 1 und Spule 2
- R = Schaltrichtung (in welche Richtung "Direction" sich der Servo bewegt, 0 = rot, 1 = grün)
- C7..C0 = Checkbyte (Byte_3 = Byte_1 XOR Byte_2)
- Das 1. Byte beginnt immer mit "10" --> 10XX XXXX (Bit#7 = 1, Bit#6 = 0)
- Das 2. Byte beginnt immer mit "1" --> 1XXX XXXX (Bit#7 = 1)
- Das 3. Byte dient der überprüfung der gesendeten Informationen: Byte_3 = Byte_1 XOR Byte_2
- Die 11 Bit Adresse des zu steuernden Spulenpaares entsteht aus den 11 Adress-Bits (AA ... A0). Dabei ist zu
beachten, dass die Bits AA, A9, A8 invertiert im ursprünglichen DCC-Paket abgebildet sind.
*/
decoderAddress = (((~Befehls_Byte)&B01110000) << 2) + decoderAddress;
Ausgang = (Befehls_Byte & B00000110) >> 1;
Spule = (bitRead(Befehls_Byte, 3));
weichenadresse = (decoderAddress - 1) * 4 + Ausgang + 1;
if (puffern_Acc)
{
//-------------------------------------------------------------------------------------------------------------------------------
// alle Werte im Puffer ausgeben
// Schauen im Array, ob die gefundenen Bytes schon einmal ausgegeben wurden
if (Acc_counter > 0)
{
for (byte j = 0; j < Acc_counter; j++)
{
Paket_bekannt_A = 0;
if (Acc_received [j].ADR == weichenadresse)
{
Paket_bekannt_A++;
}
if (Acc_received [j].DIR == Ausgang)
{
Paket_bekannt_A++;
}
if (Acc_received [j].COIL == Spule)
{
Paket_bekannt_A++;
}
if (Paket_bekannt_A == 3)
{
return; // nix machen, keine Ausgabe im seriellen Monitor: war lediglich eine Wiederholung !
}
}
}
if (Acc_counter > 0)
{
// Pufferzeilen mit der gleichen Adresse rauslöschen
for (byte j = 0; j < Acc_counter; j++)
{
if (Acc_received [j].ADR == weichenadresse)
{
// Befehl mit der gleichen Adresse aus der Puffertabelle löschen!
// dazu beginnend mit der Zeile j alle weiter hinten vorziehen, bis Pufferende oder mindestens bis Acc_counter
for (byte k = j; k < min (Acc_counter, bufferSizeAcc - 1); k++)
{
Acc_received [k].ADR = Acc_received [k + 1].ADR;
Acc_received [k].DIR = Acc_received [k + 1].DIR;
Acc_received [k].COIL = Acc_received [k + 1].COIL;
}
// }
Acc_counter--; // ein Eintrag wurde gelöscht
}
}
}
// eine Zeile im Acc_received mit neuem Paket befüllen
if (Acc_counter < bufferSizeAcc)
{
Acc_received [Acc_counter].ADR = weichenadresse;
Acc_received [Acc_counter].DIR = Ausgang;
Acc_received [Acc_counter].COIL = Spule;
Acc_counter++;
}
else
{
// ersten ältesten Wert löschen und alle anderen Wertepaare nach links rücken
for (byte j = 0; j < bufferSizeAcc - 1; j++)
{
Acc_received [j].ADR = Acc_received [j + 1].ADR;
Acc_received [j].DIR = Acc_received [j + 1].DIR;
Acc_received [j].COIL = Acc_received [j + 1].COIL;
}
// den letzten Eintrag nun dem neuen Paket befüllen
Acc_received [bufferSizeAcc - 1].ADR = weichenadresse;
Acc_received [bufferSizeAcc - 1].DIR = Ausgang;
Acc_received [bufferSizeAcc - 1].COIL = Spule;
Acc_counter = bufferSizeAcc;
}
}
//-------------------------------------------------------------------------------------------------------------------------------
// Zubehör-Daten ausgeben
Serial.print(F("Weichen-Adresse "));
lcd.setCursor(0, 3);
lcd.write(" ");
lcd.setCursor(0, 3);
lcd.write("W ");
if ((decoderAddress - 1) * 4 + Ausgang + 1 < 10) Serial.print(" ");
Serial.print((decoderAddress - 1) * 4 + Ausgang + 1);
lcd.print((decoderAddress - 1) * 4 + Ausgang + 1, DEC);
Serial.print(" (");
lcd.write(" (");
if (decoderAddress < 10) Serial.print(" ");
Serial.print(decoderAddress);
lcd.print(decoderAddress, DEC);
Serial.print(" : ");
lcd.write(":");
Serial.print(Ausgang + 1);
lcd.print(Ausgang + 1, DEC);
Serial.print(")");
lcd.write(")");
if (bitRead(Befehls_Byte, 0)) {
Serial.print(" A");
lcd.write(" A");
}
else {
Serial.print(" B");
lcd.write(" B");
}
if (bitRead(Befehls_Byte, 3)) {
Serial.print(" On ");
lcd.write(" On ");
}
else {
Serial.print(" Off");
lcd.write(" Off");
}
print_spaces(36);
}
else // Accessory Extended NMRA --> noch nicht getestet !!!
{
Serial.print("Acc Ext ");
decoderAddress = (decoderAddress << 5) + ((Befehls_Byte & B01110000) >> 2) + ((Befehls_Byte & B00000110) >> 1);
Serial.print(decoderAddress);
Serial.print(" Asp ");
Serial.print(Msg->Data[2], BIN);
}
printPacket(Msg);
}
//----------------------------------------------------------------------------------------------------------------------------------------
// CV-Befehle für Schaltdekoder POM
if (Anzeige_CV && (Msg->Size == 6)) // 6 Bytes --> d.h. CV-Befehle für Schaltdekoder !
{
z_acc_cv++;
/* ECOS: POM-Schaltartikel POM-Adresse 12, CV 6, Wert 20 Schreiben: (6 Bytes ohne Präambel und Trennbits!)
Byte 0 Byte 1 Byte 2 Byte 3 Byte 4 Byte 5
10001100 11110000 11101100 -----101 ---10100 10000001
___ 3 MSB der Adresse sind invertiert !
10AAAAAA 1AAACDDD 1110CCVV VVVVVVVV DDDDDDDD EEEEEEEE
11 = Adresse 12
11 = Schreiben
10 = Lesen
V = CV + 1 --> 5 + 1 = 6
20
Lesen:
10001100 11110000 11100100 -----101 -------0 10011101
Bas.Op.Mode.Prog [preamble]0[10AAAAAA]0[1AAACDDD]0[CVACCESS]0[EEEEEEEE]1
AAAAAA AAA1DDD = Output Address
AAAAAA AAA0000 = Decoder Address
CVACCESS = DCC Programming CMD
EEEEEEEE = Checksum
CVACCESS [1110CCVV]0[VVVVVVVV]0[DDDDDDDD]
CC = Command
CC = 01 Verify Byte
CC = 11 Write Byte
CC = 10 Bit Manipulation
VV VVVVVVVV = CV Number
DDDDDDDD = New Value
EEEEEEEE = Checksum
*/
decoderAddress = (((~Msg->Data[1]) & B01110000) << 2) + (Msg->Data[0] & B00111111); // Adresse ist in 2 Bytes kodiert, MSB invertiert in Byte1 (Bit4-6) und der Rest in Byte0 (Bit 0-5)
command = Msg->Data[2] & B00001100; // 2 Bits enthalten den Schreib-/ Verify-Befehl etc.
CV_address = ((Msg->Data[2] & B00000011) * 256) + Msg->Data[3] + 1; // Nummer der CV
CV_value = Msg->Data[4]; // CV-Wert für das Schreiben
if (!(((decoderAddress == decoderAddress_alt) && (CV_address == CV_address_alt) && (command == command_alt)) && puffern_CV)) // immer ausgeben
{
switch (command)
{
case 12: //B1100 --> 12 ==> Schreiben
Serial.print(F("Acc " ));
Serial.print(decoderAddress);
if (decoderAddress < 100) Serial.print(" ");
if (decoderAddress < 10) Serial.print(" ");
Serial.print(" CV ");
Serial.print(CV_address); Serial.print(" ");
if (CV_address < 100) Serial.print(" ");
if (CV_address < 10) Serial.print(" ");
Serial.print(F("Schreibe CV ="));
if (CV_value < 100) Serial.print(" ");
if (CV_value < 10) Serial.print(" ");
Serial.print(" ");
Serial.print(CV_value);
print_spaces(33);
break;
case 4: //B0100 --> 4 ==> Lesen
Serial.print(F("Acc " ));
Serial.print(decoderAddress);
if (decoderAddress < 100) Serial.print(" ");
if (decoderAddress < 10) Serial.print(" ");
Serial.print(" CV ");
Serial.print(CV_address); Serial.print(" ");
if (CV_address < 100) Serial.print(" ");
if (CV_address < 10) Serial.print(" ");
Serial.print(F("Lese CV "));
print_spaces(42);
break;
case 8: //B1000 ==> Bit-Gefummel !
// ... 111K-DBBB EEEE-EEEE --> Zugriffe auf einzelne Bits
// K = 1 – Bit Schreiben
// K = 0 – Bit Überprüfen
if (Msg->Data[pktByteCount - 2] & B00010000)
{
Serial.print("Schreibe Bit #");
Serial.print(Msg->Data[pktByteCount - 2] & B00000111);
Serial.print(" = ");
Serial.print((Msg->Data[pktByteCount - 2] & B00001000) >> 3);
print_spaces(36);
}
else
{
Serial.print("Lese Bit #");
Serial.print(Msg->Data[2] & B00000111);
print_spaces(41);
}
break;
}
printPacket(Msg);
decoderAddress_alt = decoderAddress;
CV_address_alt = CV_address;
command_alt = command;
}
return;
}
}
//-------------------------------------------------------------------------------------------------------------------------------
else if (decoderType == 0) // --> Lok / Funktionsdekoder
{
/* Aufbau der Fahrbefehle:
Fahrbefehl, kurze Adressen
Byte 7 6 5 4 3 2 1 0
---------------------------------------------------------------------------
0 0 A A A A A A A Adresse 7 Bit
1 0 0 R S S S S S R = Fahrtrichtung, Sn = Geschwindigkeit 28 Stufen
2 XOR Prüfbyte
0 0 A A A A A A A Adresse 7 Bit
1 0 0 1 1 1 1 1 1 Befehlsbyte 0x3F
2 R S S S S S S S R = Fahrtrichtung, Sn = Geschwindigkeit 127 Stufen
3 XOR Prüfbyte
- Fahrbefehl 28 Stufen: unsigned char speed = ((Byte[1] & 0x0F) << 1) + ((Byte[1] & 0x10) >> 4);
- Fahrbefehl 127 Stufen: unsigned char speed = Byte[2] & 0x7F;
Fahrbefehl, lange Adressen
Byte 7 6 5 4 3 2 1 0
---------------------------------------------------------------------------
0 1 1 A A A A A A Adresse 6 Bit
1 A A A A A A A A Adresse 8 Bit
2 0 0 R S S S S S R = Fahrtrichtung, Sn = Geschwindigkeit 28 Stufen
3 XOR Prüfbyte
Byte 7 6 5 4 3 2 1 0
---------------------------------------------------------------------------
0 1 1 A A A A A A Adresse 6 Bit
1 A A A A A A A A Adresse 8 Bit
2 0 0 1 1 1 1 1 1 Befehlsbyte 0x3F
3 R S S S S S S S R = Fahrtrichtung, Sn = Geschwindigkeit 127 Stufen
4 XOR Prüfbyte
- Fahrbefehl 28 Stufen: unsigned char speed = ((Byte[2] & 0x0F) << 1) + ((Byte[2] & 0x10) >> 4);
- Fahrbefehl 127 Stufen: unsigned char speed = Byte[3] & 0x7F;
*/
if (Anzeige_Loks )
// zu Testzwecken filtern auf bestimmte Lok-Adresse
// if (Anzeige_Loks && (decoderAddress == 42 || decoderAddress == 888))
// if (Anzeige_Loks && (decoderAddress < 25))
{
byte instructionType = Befehls_Byte >> 5;
/* Bits 7-6-5 sind relevant, ergibt 0 - 7 = 8 unterschiedliche Befehlstypen
0 = Control ??
1 = 0011-1111 128 Geschwindigkeitsstufen-Befehl, 0011-1110 Sonderbetriebsarten-Befehl
2 = 01xx-xxxx Basis Geschwindigkeits- und Richtungsbefehl rückwärts 28 Stufen
3 = 01xx-xxxx Basis Geschwindigkeits- und Richtungsbefehl vorwärts 28 Stufen
4 = Lok Funktionen F0, F1 - F4
5 = Lok-Funktionen F5 - F12
6 = Lok-Funktionen F13 - F36
7 = CVs */
if (puffern_Lok)
{
// Schauen im Array, ob die gefundenen Bytes schon einmal ausgegeben wurden
if (Lok_counter > 0)
{
for (byte j = 0; j < Lok_counter; j++)
{
Paket_bekannt_L = 0;
if (Lok_received [j].ADR == decoderAddress)
{
Paket_bekannt_L++;
}
if (Lok_received [j].ORDER == Befehl)
{
Paket_bekannt_L++;
}
if (Lok_received [j].FUNC == Funktion)
{
Paket_bekannt_L++;
}
if (Paket_bekannt_L == 3)
{
return; // nix machen, keine Ausgabe im seriellen Monitor: war lediglich eine Wiederholung !
}
}
}
if (Lok_counter > 0)
{
// Pufferzeilen mit der gleichen Adresse und dem gleichen Befehl rauslöschen
for (byte j = 0; j < Lok_counter; j++)
{
if (Lok_received [j].ADR == decoderAddress && Lok_received [j].ORDER == Befehl) // && Lok_received [j].FUNC == Funktion )
{
for (byte k = j; k < min (Lok_counter, bufferSizeLok - 1); k++)
{
Lok_received [k].ADR = Lok_received [k + 1].ADR;
Lok_received [k].ORDER = Lok_received [k + 1].ORDER;
Lok_received [k].FUNC = Lok_received [k + 1].FUNC;
}
Lok_counter--; // ein Eintrag wurde gelöscht
}
}
}
// eine Zeile im Acc_received mit neuem Paket befüllen
if (Lok_counter < bufferSizeLok)
{
Lok_received [Lok_counter].ADR = decoderAddress;
Lok_received [Lok_counter].ORDER = Befehl;
Lok_received [Lok_counter].FUNC = Funktion;
Lok_counter++;
}
else
{
// ersten ältesten Wert löschen und alle anderen Wertepaare nach links rücken
for (byte j = 0; j < bufferSizeLok - 1; j++)
{
Lok_received [j].ADR = Lok_received [j + 1].ADR;
Lok_received [j].ORDER = Lok_received [j + 1].ORDER;
Lok_received [j].FUNC = Lok_received [j + 1].FUNC;
}
// den letzten Eintrag nun mit dem neuen Paket befüllen
Lok_received [bufferSizeLok - 1].ADR = decoderAddress;
Lok_received [bufferSizeLok - 1].ORDER = Befehl;
Lok_received [bufferSizeLok - 1].FUNC = Funktion;
Lok_counter = bufferSizeLok;
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Serial.print("Lok ");
lcd.setCursor(0, 1);
lcd.write(" ");
lcd.setCursor(0, 2);
lcd.write(" ");
lcd.setCursor(0, 1);
lcd.write("L");
if (decoderAddress < 10) {
Serial.print(" ");
lcd.print(" ");
}
else if (decoderAddress < 100) {
Serial.print(" ");
lcd.print(" ");
}
else if (decoderAddress < 1000) {
Serial.print(" ");
lcd.print(" ");
}
else if (decoderAddress < 10000) {
Serial.print(" ");
lcd.print("");
}
Serial.print(decoderAddress); Serial.print(" "); Lokname(decoderAddress); Serial.print(" ");
lcd.print(decoderAddress, DEC);
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
switch (instructionType)
{
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 0:
// 000x-xxxx
Serial.print(" Control ");
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 1:
// Advanced Operations
// 001x-xxxx
if (Befehls_Byte == B00111111) //128 speed steps
{
//0011-1111 128 Geschwindigkeitsstufen-Befehl
// Richtung auswerten Bit 7 "R" im vorletzten Byte
if (bitRead(Msg->Data[pktByteCount - 2], 7))
{
Serial.print(" -->> ");
lcd.print(" -->> ");
}
else
{
Serial.print(" <<-- ");
lcd.print(" <<-- ");
}
// Geschwindigkeit Bit 6 - 0 vom vorletzten Byte
byte speed = Msg->Data[pktByteCount - 2] & B01111111;
if (speed == 0)
{
Serial.print(" Stopp "); // wenn = 0, dann Stoppbefehl
lcd.print(" S"); // wenn = 0, dann Stoppbefehl
}
else if (speed == 1)
{
Serial.print(" Nothalt "); // wenn = 1, dann Notbremse
lcd.print(" NH"); // wenn = 1, dann Notbremse
}
else
{
Serial.print(speed - 1); // 1 abziehen; Stufen gehen von 2 - 127 = 126 Stufen
lcd.print(speed - 1); // 1 abziehen; Stufen gehen von 2 - 127 = 126 Stufen
if (speed - 1 < 10) Serial.print(" ");
else if (speed - 1 < 100) Serial.print(" ");
else Serial.print(" ");
}
print_spaces(25);
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
else if (Befehls_Byte == B00111110) //Speed Restriction
{
//0011-1110 Sonderbetriebsarten-Befehl
if (bitRead(Msg->Data[pktByteCount - 2], 7)) Serial.print(" On ");
else Serial.print(" Off ");
Serial.print(Msg->Data[pktByteCount - 1])&B01111111;
print_spaces(22);
}
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 2:
// Reverse speed step 28 Stufen
// 01xx-xxxx Basis Geschwindigkeits- und Richtungsbefehl
// speed = ((Befehls_Byte & B00001111) << 1) - 3 + bitRead(Befehls_Byte, 4);
speed = ((Befehls_Byte & B00001111) << 1) + ((Befehls_Byte & 0x10) >> 4) - 3;
if (speed == 253 || speed == 254)
{
Serial.print(" Stopp "); // wenn = 0, dann Stoppbefehl
lcd.print(" S"); // wenn = 0, dann Stoppbefehl
}
else if (speed == 255 || speed == 0)
{
Serial.print(" Nothalt"); // wenn = 1, dann Notbremse
lcd.print(" NH"); // wenn = 1, dann Notbremse
}
else
{
Serial.print(" <<-- ");
lcd.print(" <<-- ");
Serial.print(speed);
lcd.print(speed);
}
print_spaces(31);
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 3: // Forward speed step 28 Stufen
speed = ((Befehls_Byte & B00001111) << 1) + ((Befehls_Byte & 0x10) >> 4 - 3);
if (speed == 253 || speed == 254)
{
Serial.print(" Stopp "); // wenn = 0, dann Stoppbefehl
lcd.print(" S"); // wenn = 0, dann Stoppbefehl
}
else if (speed == 255 || speed == 0)
{
Serial.print(" Nothalt"); // wenn = 1, dann Notbremse
lcd.print(" NH"); // wenn = 1, dann Notbremse
}
else
{
Serial.print(" -->> ");
lcd.print(" -->> ");
Serial.print(speed);
lcd.print(speed);
}
print_spaces(31);
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 4: // Loc Function L-4-3-2-1
// 100x-xxxx Funktionssteuerung F0-F4, die F-Nummern sind allerdings seltsam verteilt ;-)
if (Funktion & 16) {
lcd.setCursor(5, 1);
Serial.print(" F0");
lcd.print(" F0");
}
else {
lcd.setCursor(5, 1);
Serial.print(" f0");
lcd.print(" 0");
}
for (byte k = 0; k < 4; k++)
{
if (bitRead (Funktion, k)) {
Serial.print(" F");
lcd.print(" F");
}
else {
Serial.print(" f");
lcd.print(" ");
}
Serial.print(k + 1);
lcd.print(k + 1);
}
print_spaces(15);
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 5: // Loc Function 8-7-6-5
// 1011-xxxx Funktionssteuerung F5-F8
if (bitRead(Befehls_Byte, 4))
{
lcd.setCursor(5, 1);
for (byte k = 0; k < 4; k++)
{
if (bitRead (Funktion, k)) {
Serial.print(" F");
lcd.print(" F");
}
else {
Serial.print(" f");
lcd.print(" ");
}
Serial.print(k + 5);
lcd.print(k + 5);
}
print_spaces(20);
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
else // Loc Function 12-11-10-9
{
//1010-xxxx Funktionssteuerung F9-F12
lcd.setCursor(5, 1);
for (byte k = 0; k < 4; k++)
{
if (k == 0) Serial.print(" ");
if (bitRead (Funktion, k)) {
Serial.print(" F");
lcd.print(" F");
}
else {
Serial.print(" f");
lcd.print(" ");
}
Serial.print(k + 9);
lcd.print(k + 9);
}
print_spaces(20);
}
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 6: // 110x-xxxx Eigenschaften-Erweiterungs-Befehle
switch (Befehls_Byte & B00011111)
{
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 0: // Binary State Control Instruction long form
// 1100-0000 Binärzustandssteuerungsbefehl lange Form
Serial.print(" BinStateLong ");
Serial.print(256 * Msg->Data[pktByteCount - 1] + (Msg->Data[pktByteCount - 2] & B01111111));
if bitRead(Msg->Data[pktByteCount - 2], 7) Serial.print(" On ");
else Serial.print(" Off ");
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00011101: // Binary State Control
// 1101-1101 Binärzustandssteuerungsbefehl kurze Form
Serial.print(" BinStateShort ");
Serial.print(Msg->Data[pktByteCount - 1] & B01111111);
if bitRead(Msg->Data[pktByteCount - 1], 7) Serial.print(" On ");
else Serial.print(" Off ");
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00011110: // F13-F20 Function Control
// 1101-1110 Funktionssteuerung F13-F20
lcd.setCursor(8, 1);
for (byte k = 0; k < 8; k++)
{
if (k == 3) lcd.setCursor(0, 2);
if (bitRead (Funktion, k)) {
Serial.print(" F");
lcd.print(" F");
}
else {
Serial.print(" f");
lcd.print(" ");
}
Serial.print(k + 13);
lcd.print(k + 13);
}
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00011111: // F21-F28 Function Control
//1101-1111 Funktionssteuerung F21-F28
lcd.setCursor(8, 1);
for (byte k = 0; k < 8; k++)
{
if (k == 3) lcd.setCursor(0, 2);
if (bitRead (Funktion, k)) {
Serial.print(" F");
lcd.print(" F");
}
else {
Serial.print(" f");
lcd.print(" ");
}
Serial.print(k + 21);
lcd.print(k + 21);
}
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00011000: // F29-F36 Function Control
//1101-1000 Funktionssteuerung F29 - F36
lcd.setCursor(8, 1);
for (byte k = 0; k < 8; k++)
{
if (k == 3) lcd.setCursor(0, 2);
if (bitRead (Funktion, k)) {
Serial.print(" F");
lcd.print(" F");
}
else {
Serial.print(" f");
lcd.print(" ");
}
Serial.print(k + 29);
lcd.print(k + 29);
}
break;
}
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 7:
Serial.print(" CV ");
if (Befehls_Byte & B00010000) // CV Short Form
{
byte cvType = Befehls_Byte & B00001111;
switch (cvType)
{
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00000010:
// KKKK = 0010 – Beschleunigungswert in einer Mehrfachtraktion (CV #23)
Serial.print("23 ");
Serial.print(Msg->Data[pktByteCount - 1]);
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00000011:
// KKKK = 0011 – Verzögerungswert in einer Mehrfachtraktion (CV #24)
Serial.print("24 ");
Serial.print(Msg->Data[pktByteCount - 1]);
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00001001:
/*
D.2 "Service Mode Decoder Lock Instruction"
Dieser Befehl wird in der Technical Note TN-1-05 der NMRA näher beschrieben.
Die "Service Mode Decoder Lock Instruction" (SMDLI) verhindert die Programmierung einiger Decoder, während andere auf dem
gleichen Gleis programmiert werden können. Es werden dazu Befehle für den Programmiermodus auf dem normalen Fahrgleis verwendet,
womit man nicht den Vorteil der strombegrenzten Testumgebung hat und Decoder, die diesen Befehl nicht unterstützen, ungewollt umprogrammiert werden können.
Das Befehlsformat lautet: {20 Synchronbits} 0 0000-0000 0 1111-1001 0 0AAA-AAAA 0 PPPP-PPPP 1
Dabei steht das AAA-AAAA für die kurze Adresse des Decoders, der weiterhin Befehle des Programmiermodus ausführen wird.
Ein Decoder, der diesen Befehl unterstützt, vergleicht seine Adresse in CV #1 gegen die Adresse in dem Befehl.
Stimmen die Adressen nicht überein, geht er in einen gesperrten Zustand über, in dem er auch nach Aus- und wieder Einschalten der
Spannung verbleibt. In diesem Zustand ignoriert er alle Befehle des Programmiermodus.
*/
Serial.print(F("Decoder Lock "));
Serial.print(Msg->Data[pktByteCount - 1]);
break;
}
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
else // CV Long Form für Betriebsmodus POM !
{
CV_address = ((Msg->Data[pktByteCount - 4] & B00000011) * 256) + Msg->Data[pktByteCount - 3] + 1;
Serial.print(CV_address);
if (CV_address < 1000) Serial.print(" ");
if (CV_address < 100) Serial.print(" ");
if (CV_address < 10) Serial.print(" ");
switch (Befehls_Byte & B00001100)
{
/* Die für den Befehlstyp (xxxx-KKxx) im ersten Befehlsbyte festgelegten Werte sind:
KK = 00 – reserviert
KK = 01 – Byte Überprüfen
KK = 11 – Byte Schreiben
KK = 10 – Bit Manipulation */
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00000100: // Lesen Byte
Serial.print(F("Lese CV")); print_spaces(24);
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00001100: // Schreiben Byte
Serial.print(F("Schreibe CV ="));
if (Msg->Data[pktByteCount - 2] < 100) Serial.print(" ");
if (Msg->Data[pktByteCount - 2] < 10) Serial.print(" ");
Serial.print(" ");
Serial.print(Msg->Data[pktByteCount - 2]);
print_spaces(14);
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00001000: //B1000 ==> Bit-Gefummel !
// ... 111K-DBBB EEEE-EEEE --> Zugriffe auf einzelne Bits
// K = 1 – Bit Schreiben
// K = 0 – Bit Überprüfen
if (Msg->Data[pktByteCount - 2] & B00010000)
{
Serial.print("Schreibe Bit #");
Serial.print(Msg->Data[pktByteCount - 2] & B00000111);
Serial.print(" = ");
Serial.print((Msg->Data[pktByteCount - 2] & B00001000) >> 3);
print_spaces(20);
}
else
{
Serial.print("Lese Bit #");
Serial.print(Msg->Data[2] & B00000111);
print_spaces(25);
}
break;
}
}
break;
}
Serial.print(" ");
printPacket(Msg);
}
}
}
//-------------------------------------------------------------------------------------------------------
void notifyCVChange( unsigned int CvAddr, byte Value )
//-------------------------------------------------------------------------------------------------------
// Wird aufgerufen, nachdem ein CV-Wert verändert wurde
{
Serial.print("notifyCVChange: ");
Serial.print("CV"); Serial.print(CvAddr); Serial.print(" = "); Serial.println(Value);
}
//-------------------------------------------------------------------------------------------------------
void Byte_to_Bits (byte b)
//-------------------------------------------------------------------------------------------------------
// Gibt einen Byte-Wert binär mit führenden Nullen und "-" nach der 4. Stelle aus
// z.B. 12 --> 0001 1000
{
for (int i = 7; i >= 0; i--)
{
Serial.print(bitRead(b, i));
if (i == 4) Serial.print("-");
}
}
//-------------------------------------------------------------------------------------------------------
void print_spaces (byte b)
//-------------------------------------------------------------------------------------------------------
// Gibt b mal ein Leerzeichen aus zur Formatierung
{
for (byte i = 0; i < b; i++)
{
Serial.print(" ");
}
}
//-------------------------------------------------------------------------------------------------------
void Lokname (unsigned int DCC_Adresse)
//-------------------------------------------------------------------------------------------------------
// Ausgabe der Loknamen zu den gefundenen Adressen
{
switch (DCC_Adresse)
{
// Adresse + Text manuell eintragen
case 4: Serial.print(F("P4 KPEV FLM ")); break;
case 5: Serial.print(F("EP5 bayr. FLM ")); break;
case 6: Serial.print(F("P6 KPEV FLM ")); break;
case 9: Serial.print(F("S9 KPEV Brawa ")); break;
case 10: Serial.print(F("P10 Trix ")); break;
// ...
case 8507: Serial.print(F("BR 85 007 Roco ")); break;
case 10000: Serial.print(F("ECoS Dummybefehl")); break;
default: Serial.print(F("??? ")); // 16 Leerzeichen, falls Lokname nicht gefunden !
}
}
//-------------------------------------------------------------------------------------------------------
void print_Zahl_rechts_ln (unsigned long zahl)
//-------------------------------------------------------------------------------------------------------
// Gibt eine ganze Zahl rechtsbündig aus
{
Serial.print(" ");
if (zahl < 10000000) Serial.print(" ");
if (zahl < 1000000) Serial.print(" ");
if (zahl < 100000) Serial.print(" ");
if (zahl < 10000) Serial.print(" ");
if (zahl < 1000) Serial.print(" ");
if (zahl < 100) Serial.print(" ");
if (zahl < 10) Serial.print(" ");
Serial.println(zahl);
}
//-------------------------------------------------------------------------------------------------------
void printPacket(DCC_MSG *Msg)
//-------------------------------------------------------------------------------------------------------
// Gibt alle gefundenen Bytes eines DCC-Befehls aus, ohne Präambel, aber incl. Prüfbyte
{
Serial.print(" ");
for (byte n = 0; n < pktByteCount; n++)
{
Serial.print(" ");
Byte_to_Bits(Msg->Data[n]);
}
Serial.println(" ");
}