Eine sehr kleine Platine, ich will Sie als Funktionsdekoder und Weichen, Signaldekoder in meiner Modellbahn verbauen,
In der Aruino Umgebung ist in den Einstellungen diese Zeile einzufügen.:
https://files.seeedstudio.com/arduino/package_seeeduino_boards_index.json
Als CPU ist die Seeed Xiao RP2040 auszuwählen
In der Programmierung ist LED_PIN 0 der graue P0, oder LED_PIN D10 dann ist es der grüne.
Das ist etwas zickick und erfordert Disziplin.
Ich hab das mit einer Blinkroutine sowie einer einfachen Interruptroutine getestet.
Hier ein Monitorprogramm zum anzeigen von DCC Commands auf digitalen Modelbahnen nach Nmra.
/*
DCC-Monitor zur Ausgabe der DCC-Befehle auf dem seriellen Monitor
=================================================================
Geändert für RP 2040 mit Arduino IDE
V 1.5, 20.12.2023 domapi
NEU 20.12.2023:
- Filtermöglichkeiten für Lokadressen und Zubehöradressen eingebaut. Damit wird die Auswertung des DCC-Datentraffic´s auf je eine bestimmte Adresse eingeschränkt.
- macht dann aber nur Sinn, wenn man die Adresse laufend anzeigen lässt und sich nicht auf Änderungen beschränlt (Menüpunkte 4 und 5 sollten EIN sein)
- domapi Loknamen aktualisiert
NEU 15.12.2020
- Anpassung der Pufferroutine bei Schaltartikel-Befehlen (Acc), da bei DCC++ teilweise kein "Signal aus"-Befehl gesendet wird. Die Folge war, dass Befehle verschluckt wurden.
- 3 x break eingebaut in Puffer-Löschroutine.
NEU 12.12.2020
- Der ESU-Lokprogrammer sendet offensichtlich zusätzliche DCC-Kommandos "Analogfunktionsgruppen"
- diese wurden eingebaut, incl. Zähler für Spezialbefehle für Loks
NEU 09.11.2020
- Bei den Zubehördekodern wurde die Schaltrichtung/Spule umgedreht und die Textausgabe passt jetzt z.B. zur Anzeige im ESU Lokprogrammer Führerstand Weichenstellpult
- rote Taste = A
- grüne Taste = B
- Bei Lokdekoder-Adressen von 112 - 127 wurden falsche DCC-Befehle ausgegeben, da diese vom Sketch fälschlicherweise als Programmierbefehle für das Programmiergleis interpretiert wurden.
- Nun wird eine zusätzliche Routine der NRMA-Library aufgerufen, die prüft, ob der DCC-Monitor vom Programmiergleis angesprochen wurde.
- In diesem Fall sendet die Zentrale diverse Befehlsketten, um den Dekoder bzw. Monitor in den Programmiermodus zu versetzen.
- Die Programmierbefehle fürs Programmiergleis und die Befehle für kurze Lokadressen ähneln sich dummerweise stark ...
Features:
---------
- Ausgabe der Lok-Befehle, incl. Filtermöglichlkeit auf eine bestimmte Adresse
- Ausgabe der Lok-CV-Kommandos
- Ausgabe der Accessory-Befehle, incl. Filteroption
- 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.h. 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 Loknamen in Klartext
- Ausgabe einer Statistik über die detektierten DCC-Befehle
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.
Seeed Studio:
// +-----+
// +------------| USB |------------+
// | +-----+ |
// | [ ]26 VCC[ ] |
// | [ ]27 GND[ ]~|
// | [ ]28 ___ 3,3V[ ]~|
// | [ ]29 / N \ 3[ ]~|INT Pin
// | [ ]6 /Xiao \ 4[ ] |
// | [ ]7 \RP2040 2[ ] |
// | [ ]0 \_0_/ 1[ ]~|
// | |
// +-------------------------------+
//-------------------------------------------------------------------------------------------------------
#include <NmraDcc.h>
#define DCC_PIN 3
NmraDcc Dcc;
//#define debug
//-------------------------------------------------------------------------------------------------------
// 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;
uint16_t Lok_Filter, Acc_Filter; // Werte für die Filterung des DCC-Datenstroms. Es werden dann nur Befehle für diese Adressen gezeigt
//-------------------------------------------------------------------------------------------------------
const byte DccAckPin = 5; // Arduino-Pin zur Erzeugung eines ACK-Signals
const char *comp_date = __DATE__ ", " __TIME__;
int blinker_aus = 10000; // toggelt die LED an Pin 13, wenn ein DCC-Paket gefunden wurde --> zeigt DCC-Signal an
int blinker_an = 2;
const byte bufferSizeAcc = 10; // 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 Befehle
typedef struct
{
int WADR;
byte DIR;
byte POW;
} 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 Direction;
byte Power;
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;
unsigned long z_spezial = 0;
bool Service_Mode;
//-------------------------------------------------------------------------------------------------------
void
setup ()
//-------------------------------------------------------------------------------------------------------
{
pinMode ( 6, OUTPUT ); // eingebaute LED
Serial.begin ( 115200 );
while ( !Serial )
{
// Warte, bis die serielle Schnittstelle verbunden ist.
}
pinMode ( DccAckPin, OUTPUT ); // Configure the DCC CV Programing ACK pin for an output
Serial.println ( F ( "NMRA DCC Monitor V 1.5" ) );
Serial.print ( F ( "Sketch-Upload am: " ) );
Serial.println ( comp_date );
Serial.println ( F ( "? = Zeige Tastaturbefehle für den seriellen Arduino-Monitor" ) );
//Dcc.pin (3);
Dcc.pin(digitalPinToInterrupt(DCC_PIN), DCC_PIN, false);// Setup which External Interrupt, the Pin it's associated with that we're using and enable the Pull-Up(0,2,1)
Dcc.init ( MAN_ID_DIY, 12, CV29_ACCESSORY_DECODER | CV29_OUTPUT_ADDRESS_MODE, 0 ); // Call the main DCC Init function to enable the DCC Receiver
start_time = millis ();
}
//-------------------------------------------------------------------------------------------------------
void
loop ()
//-------------------------------------------------------------------------------------------------------
{
Dcc.process (); // You MUST call the NmraDcc.process() method frequently from the Arduino loop() function for correct library operation
Receive_serial_command (); // Schauen, ob am seriellen Monitor etwas eingegeben wurde
// LED ausschalten
blinker_aus--;
if ( blinker_aus == 0 )
{
digitalWrite ( 1, 0 ); // wenn kein DCC-Signal mehr kommt, die LED ausschalten
blinker_aus = 10000;
}
}
//-------------------------------------------------------------------------------------------------------
// 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++;
}
//-------------------------------------------------------------------------------------------------------
// Wird aufgerufen, wenn ein Progammierbefehl von der Zentrale gesendet wurde
// Brauchen wir, um nicht mit den Lok-Adressen 112 - 127 zu kollidieren ;-)
void
notifyServiceMode ( bool Prg_gleis_Mode )
//-------------------------------------------------------------------------------------------------------
{
Serial.print ( "Service-Mode/Programmiergleismodus: " );
if ( Service_Mode == 0 ) { Serial.println ( "Ein" ); }
else { Serial.println ( "Aus" ); }
Service_Mode = Prg_gleis_Mode;
}
//-------------------------------------------------------------------------------------------------------
void
notifyDccMsg ( DCC_MSG *Msg )
//-------------------------------------------------------------------------------------------------------
{
// LED einschalten
blinker_an--;
if ( blinker_an == 0 )
{
digitalWrite ( 26, 1 );
blinker_an = 5;
}
//-------------------------------------------------------------------------------------------------------------------------------
// Alle gefundenen 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 !
//-------------------------------------------------------------------------------------------------------------------------------
// Achtung:
// Die Programmierbefehle ohne Adresse kollidieren mit den Lok-Adressen 112 - 127 !!!!!!!!!!!!!!!!!!
// Der Dekoder muss erst erkennen, dass er im Programmiermodus angesprochen wird.
// Hierfür werden verschiedene Sequenzen von Befehlen von der Zenrale gesendet.
// Ob man im Prg.modus auf dem Prg.gleis ist, kann mit der NRMA-Routine notifyServiceMode() geprüft werden.
if ( Service_Mode && ( Msg->Data [0] > 111 ) && ( Msg->Data [0] < 128 ) )
{
/* 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];
// Nur auswerten, wenn die Adresse dem Lok_Filter entspricht oder gar kein Filter gesetzt ist!
if ( Lok_Filter > 0 && decoderAddress != Lok_Filter ) return;
// 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 ) == 64 && pktByteCount == 3 )
{
Befehl = 0; // 01RSSSSS
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++;
}
// NEU 12.12.2020
else if ( Befehls_Byte == B00111101 ) // 0011-1101 scheint ein Spezialkommando des ESU Lokprogrammers zu sein. Ws. eine Kennung für den Dekoder, dass er von einem Lokprogrammer
// angesprochen wird
{
Befehl = Befehls_Byte;
Funktion = Msg->Data [2]; // Erweiterter Betriebsbefehl Analogfunktionsgruppe
z_spezial++;
}
// NEU 12.12.2020
}
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;
// Nur auswerten, wenn die Adresse dem Lok_Filter entspricht oder gar kein Filter gesetzt ist!
if ( Lok_Filter > 0 && decoderAddress != Lok_Filter ) return;
// 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 ) == 64 && pktByteCount == 4 )
{
Befehl = 0; // 01RSSSSS
z_lok_speed++;
Funktion = Befehls_Byte; // Speed 28 Stufen
}
else if ( Befehls_Byte == B00111111 && pktByteCount == 5 )
{
Befehl = Befehls_Byte; // 00111111
Funktion = Msg->Data [3]; // RSSSSSSS Speed 127 Stufen
z_lok_speed++;
}
// NEU 12.12.2020
else if ( Befehls_Byte == B00111101 )
{
Befehl = Befehls_Byte; //
Funktion = Msg->Data [3]; // Analogfunktionsgruppe
z_spezial++;
}
// NEU 12.12.2020
}
//-------------------------------------------------------------------------------------------------------------------------------
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 ) )
{
if ( ( Befehls_Byte & B10000000 ) && ( Msg->Size == 3 ) ) // Steuerbefehl für Zubehör Dekoder (Basic Accessory)
{
// Dekoder haben eine Dekoderadresse (die wird zum Programmieren der CVs verwendet) und umfassen i.d.R. 4 Ausgänge zum Ansteuern vn Schaltartikel mit wiederum jeweils 2
// Richtungen/Zuständen. Die Zentrale sendet für Magnetartikel (accessories) immer ein Paket bestehend aus Dekoder-Adresse (= Signal), Ausgang (0 - 3), Richtung (rot/grün
// bzw. A/B) und Power (ein/aus). 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") (entspricht quasi der Dekoderadresse und dem Ausgang)
- P = Power (0 = off, 1 = on) ("OutputPower") also bei Doppelspulenantrieben z.B. "ein" oder "aus"
- R = Richtung/Direction, z.B. Abzweig/Gerade oder rot/grün (in welche Richtung sich z.B. 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; // das ist die DCC-Adresse des Dekoders
Ausgang = ( Befehls_Byte & B00000110 ) >> 1; // entspricht 0 - 3 bzw. Ausgang 1 - 4 eines Dekoders
Weichenadresse = ( decoderAddress - 1 ) * 4 + Ausgang + 1; // das ist die DCC-Adresse des Schaltartikels
Direction = ( bitRead ( Befehls_Byte, 0 ) ); // Richtung A/B
Power = ( bitRead ( Befehls_Byte, 3 ) ); // Power on/off
// Nur auswerten, wenn die Weichen-Adresse dem Acc_Filter entspricht oder gar kein Filter gesetzt ist!
if ( Acc_Filter > 0 && Weichenadresse != Acc_Filter ) return;
z_acc++;
if ( puffern_Acc )
{
//-------------------------------------------------------------------------------------------------------------------------------
// alle Werte im Puffer ausgeben
#ifdef debug
Serial.println ( F ( "1. Befehl erkannt, Stand Puffer vor der Verarbeitung:" ) );
for ( byte j = 0; j < bufferSizeAcc; j++ )
{
Serial.print ( j );
Serial.print ( " = " );
Serial.print ( Acc_received [j].WADR, HEX );
Serial.print ( " | " );
Serial.print ( Acc_received [j].DIR, HEX );
Serial.print ( " | " );
Serial.println ( Acc_received [j].POW, HEX );
}
#endif
// 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;
#ifdef debug
Serial.println ( F ( "2. Prüfen, ob neu oder bekannt:" ) );
Serial.print ( j );
Serial.print ( " = " );
Serial.print ( Acc_received [j].WADR, HEX );
Serial.print ( " | " );
Serial.print ( Weichenadresse, HEX );
Serial.print ( " | " );
Serial.print ( Acc_received [j].DIR, HEX );
Serial.print ( " | " );
Serial.print ( Direction, HEX );
Serial.print ( " | " );
Serial.print ( Acc_received [j].POW, HEX );
Serial.print ( " | " );
Serial.print ( Power, HEX );
Serial.println ( " | " );
#endif
if ( Acc_received [j].WADR == Weichenadresse ) { Paket_bekannt_A++; }
if ( Acc_received [j].DIR == Direction ) { Paket_bekannt_A++; }
if ( Acc_received [j].POW == Power ) { Paket_bekannt_A++; }
if ( Paket_bekannt_A == 3 )
{
#ifdef debug
Serial.print ( F ( "--> Paket bereits bekannt" ) );
Serial.println ( " " );
#endif
return; // nix machen, keine Ausgabe im seriellen Monitor: war lediglich eine Wiederholung !
}
}
}
#ifdef debug
Serial.println ();
Serial.println ( F ( "Neuer Befehl erkannt !" ) );
#endif
if ( Acc_counter > 0 )
{
// Pufferzeilen mit der gleichen Adresse und Richtung rauslöschen
for ( byte j = 0; j < Acc_counter; j++ )
{
// if (Acc_received [j].WADR == Weichenadresse && Acc_received [j].DIR == Direction)
if ( Acc_received [j].WADR == Weichenadresse ) // bei DCC++ wird kein "off-Befehl" gesendet, hier alte Befehle mit gleicher Weichenadresse löschen
{
// 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].WADR = Acc_received [k + 1].WADR;
Acc_received [k].DIR = Acc_received [k + 1].DIR;
Acc_received [k].POW = Acc_received [k + 1].POW;
}
// }
Acc_counter--; // ein Eintrag wurde gelöscht
#ifdef debug
Serial.println ();
Serial.print ( F ( "3. 1 Pufferzeile gelöscht: " ) );
Serial.println ( Weichenadresse, HEX );
#endif
// es kann eigentlich nur eine Zeile im Puffer stehen mit gleicher Adresse und Richtung, wenn die gelöscht ist, kann man aufhören
break;
}
}
}
#ifdef debug
// alle Werte im Puffer ausgeben
Serial.println ();
Serial.println ( F ( "4. Stand Puffer nach Löschen:" ) );
for ( byte j = 0; j < bufferSizeAcc; j++ )
{
Serial.print ( j );
Serial.print ( " = " );
Serial.print ( Acc_received [j].WADR, HEX );
Serial.print ( " | " );
Serial.print ( Acc_received [j].DIR, HEX );
Serial.print ( " | " );
Serial.println ( Acc_received [j].POW, HEX );
}
#endif
// eine Zeile im Acc_received mit neuem Paket befüllen
if ( Acc_counter < bufferSizeAcc )
{
Acc_received [Acc_counter].WADR = Weichenadresse;
Acc_received [Acc_counter].DIR = Direction;
Acc_received [Acc_counter].POW = Power;
#ifdef debug
Serial.println ();
Serial.println ( F ( "5. Fülle Puffer:" ) );
Serial.print ( Acc_counter );
Serial.print ( " = " );
Serial.print ( Acc_received [Acc_counter].WADR, HEX );
Serial.print ( " | " );
Serial.print ( Acc_received [Acc_counter].DIR, HEX );
Serial.print ( " | " );
Serial.print ( Acc_received [Acc_counter].POW, HEX );
Serial.println ( F ( " --> Neuer Eintrag" ) );
#endif
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].WADR = Acc_received [j + 1].WADR;
Acc_received [j].DIR = Acc_received [j + 1].DIR;
Acc_received [j].POW = Acc_received [j + 1].POW;
}
// den letzten Eintrag nun mit dem neuen Paket befüllen
Acc_received [bufferSizeAcc - 1].WADR = Weichenadresse;
Acc_received [bufferSizeAcc - 1].DIR = Direction;
Acc_received [bufferSizeAcc - 1].POW = Power;
Acc_counter = bufferSizeAcc;
}
#ifdef debug
// alle Werte im Puffer ausgeben
Serial.println ();
Serial.println ( F ( "6. Stand Puffer nach der Verarbeitung:" ) );
for ( byte j = 0; j < bufferSizeAcc; j++ )
{
Serial.print ( j );
Serial.print ( " = " );
Serial.print ( Acc_received [j].WADR, HEX );
Serial.print ( " | " );
Serial.print ( Acc_received [j].DIR, HEX );
Serial.print ( " | " );
Serial.println ( Acc_received [j].POW, HEX );
}
#endif
}
//-------------------------------------------------------------------------------------------------------------------------------
// Zubehör-Daten ausgeben
Serial.print ( F ( "DCC-Adresse " ) );
if ( Weichenadresse < 1000 ) Serial.print ( " " );
if ( Weichenadresse < 100 ) Serial.print ( " " );
if ( Weichenadresse < 10 ) Serial.print ( " " );
Serial.print ( Weichenadresse );
Serial.print ( " (" );
if ( decoderAddress < 100 ) Serial.print ( " " );
if ( decoderAddress < 10 ) Serial.print ( " " );
Serial.print ( decoderAddress );
Serial.print ( " : " );
Serial.print ( Ausgang + 1 );
Serial.print ( ")" );
if ( !bitRead ( Befehls_Byte, 0 ) ) Serial.print ( " A" );
else Serial.print ( " B" );
if ( bitRead ( Befehls_Byte, 3 ) ) Serial.print ( " On " );
else Serial.print ( " Off" );
print_spaces ( 37 );
}
else
// Accessory Extended NMRA --> angepasste Version noch nicht getestet !!!
// extended Befehle bestehen aus 4 Bytes (incl. Checkbyte)
// Byte 1 und 2 enthalten die Adresse, Byte 3 den eigentlichen Befehl
// die Adresse berechnet sich wie beim Basis Accessory Befehl !
{
z_acc++;
Serial.print ( "Acc Ext " );
decoderAddress = ( ( ( ~Befehls_Byte ) & B01110000 ) << 2 ) + decoderAddress; // das ist die DCC-Adresse des Dekoders
Ausgang = ( Befehls_Byte & B00000110 ) >> 1; // entspricht 0 - 3 bzw. Ausgang 1 - 4 eines Dekoders
Weichenadresse = ( decoderAddress - 1 ) * 4 + Ausgang + 1; // das ist die DCC-Adresse des Schaltartikels
Direction = ( bitRead ( Befehls_Byte, 0 ) ); // Richtung A/B
Power = ( bitRead ( Befehls_Byte, 3 ) ); // Power on/off
Serial.print ( F ( "DCC-Adresse " ) );
if ( Weichenadresse < 1000 ) Serial.print ( " " );
if ( Weichenadresse < 100 ) Serial.print ( " " );
if ( Weichenadresse < 10 ) Serial.print ( " " );
Serial.print ( Weichenadresse );
Serial.print ( " (" );
if ( decoderAddress < 100 ) Serial.print ( " " );
if ( decoderAddress < 10 ) Serial.print ( " " );
Serial.print ( decoderAddress );
Serial.print ( " : " );
Serial.print ( Ausgang + 1 );
Serial.print ( ")" );
if ( !bitRead ( Befehls_Byte, 0 ) ) Serial.print ( " A" );
else Serial.print ( " B" );
if ( bitRead ( Befehls_Byte, 3 ) ) Serial.print ( " On " );
else Serial.print ( " Off" );
print_spaces ( 37 );
Serial.print ( " Asp / 3. Byte: " );
Byte_to_Bits ( Msg->Data [2] );
}
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 1 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 1 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 )
{
#ifdef debug
// alle Werte im Puffer ausgeben
Serial.print ( F ( "1. Befehl erkannt, Stand Puffer vor der Verarbeitung:" ) );
for ( byte j = 0; j < bufferSizeLok; j++ )
{
Serial.print ( j );
Serial.print ( " = " );
Serial.print ( Lok_received [j].ADR, HEX );
Serial.print ( " | " );
Serial.print ( Lok_received [j].ORDER, HEX );
Serial.print ( " | " );
Serial.println ( Lok_received [j].FUNC, HEX );
}
#endif
// 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;
#ifdef debug
Serial.println ( "2. Prüfen, ob neu oder bekannt:" );
Serial.print ( j );
Serial.print ( " = " );
Serial.print ( Lok_received [j].ADR, HEX );
Serial.print ( " | " );
Serial.print ( decoderAddress, HEX );
Serial.print ( " | " );
Serial.print ( Lok_received [j].ORDER, HEX );
Serial.print ( " | " );
Serial.print ( Befehl, HEX );
Serial.print ( " | " );
Serial.print ( Lok_received [j].FUNC, HEX );
Serial.print ( " | " );
Serial.print ( Funktion, HEX );
Serial.println ( " | " );
#endif
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 )
{
#ifdef debug
Serial.print ( F ( "--> Paket bereits bekannt" ) );
Serial.println ( " " );
#endif
return; // nix machen, keine Ausgabe im seriellen Monitor: war lediglich eine Wiederholung !
}
}
}
#ifdef debug
Serial.println ();
Serial.println ( F ( "Neuer Befehl erkannt !" ) );
#endif
if ( Lok_counter > 0 )
{
// Pufferzeilen mit der gleichen Adresse und dem gleichen Befehl rauslöschen
for ( byte j = 0; j < Lok_counter; j++ )
{
if ( Befehl != B00111101 ) // bei allen Fahrzeugbefehlen ohne Analoggruppe
{
if ( Lok_received [j].ADR == decoderAddress && Lok_received [j].ORDER == Befehl )
{
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
#ifdef debug
Serial.println ();
Serial.print ( F ( "3. 1 Pufferzeile gelöscht: " ) );
Serial.println ( decoderAddress, HEX );
#endif
break;
}
}
else // bei einem Analoggruppenbefehl nur dann alte Einträge löschen, wenn alle 3 Felder gleich sind
{
// alle 3 Werte prüfen wg. der Sonderlocke von ESU "Analoggruppe"
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
#ifdef debug
Serial.println ();
Serial.print ( F ( "3. 1 Pufferzeile gelöscht: " ) );
Serial.println ( decoderAddress, HEX );
#endif
break;
}
}
}
}
// alle Werte im Puffer ausgeben
#ifdef debug
Serial.println ();
Serial.println ( F ( "4. Stand Puffer nach Löschen:" ) );
for ( byte j = 0; j < bufferSizeLok; j++ )
{
Serial.print ( j );
Serial.print ( " = " );
Serial.print ( Lok_received [j].ADR, HEX );
Serial.print ( " | " );
Serial.print ( Lok_received [j].ORDER, HEX );
Serial.print ( " | " );
Serial.println ( Lok_received [j].FUNC, HEX );
}
#endif
// 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;
#ifdef debug
Serial.println ();
Serial.println ( F ( "5. Fülle Puffer:" ) );
Serial.print ( Lok_counter );
Serial.print ( " = " );
Serial.print ( Lok_received [Lok_counter].ADR, HEX );
Serial.print ( " | " );
Serial.print ( Lok_received [Lok_counter].ORDER, HEX );
Serial.print ( " | " );
Serial.print ( Lok_received [Lok_counter].FUNC, HEX );
Serial.println ( F ( " --> Neuer Eintrag" ) );
#endif
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;
}
// alle Werte im Puffer ausgeben
#ifdef debug
Serial.println ();
Serial.println ( F ( "6. Stand Puffer nach der Verarbeitung:" ) );
for ( byte j = 0; j < bufferSizeLok; j++ )
{
Serial.print ( j );
Serial.print ( " = " );
Serial.print ( Lok_received [j].ADR, HEX );
Serial.print ( " | " );
Serial.print ( Lok_received [j].ORDER, HEX );
Serial.print ( " | " );
Serial.println ( Lok_received [j].FUNC, HEX );
}
#endif
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
Serial.print ( "Lok " );
if ( decoderAddress < 10 ) Serial.print ( " " );
else if ( decoderAddress < 100 ) Serial.print ( " " );
else if ( decoderAddress < 1000 ) Serial.print ( " " );
else if ( decoderAddress < 10000 ) Serial.print ( " " );
Serial.print ( decoderAddress );
Serial.print ( " " );
Lokname ( decoderAddress );
Serial.print ( " " );
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
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 ( " -->> " );
else Serial.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
else if ( speed == 1 ) Serial.print ( " Nothalt" ); // wenn = 1, dann Notbremse
else
{
Serial.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 );
}
// NEU 12.12.2020
else if ( Befehls_Byte == B00111101 )
{
/*
Analogfunktionsgruppe
Dieser Befehl ist drei Byte lang und hat das Format: 0011-1101 SSSS-SSSS DDDD-DDDD
Er ist bestimmt, um bis zu 256 Analogkanäle zu steuern. Dabei legt SSSS-SSSS im zweiten Befehlsbyte den Analogfunktionsausgang (Steuerkanal) (0-255) und
das dritte Befehlsbyte DDDD-DDDD die Analogfunktionsdaten Daten (0-255) fest.
Verwendung der Analogausgänge:
SSSS-SSSS = 0000-0001 - Lautstärkesteuerung
SSSS-SSSS = 0001-0000 bis 0001-1111 - Positionssteuerung
Alle oben nicht definierten Werte von SSSS-SSSS zwischen 0000-0000 und 0111-1111 einschließlich sind reserviert.
Die Werte von 1000-0000 bis 1111-1111 können beliebig verwendet werden. Diese Funktion darf nicht zur Geschwindigkeitssteuerung eines Fahrzeugdecoders
verwendet werden.
*/
Serial.print ( " Analogfkt.gruppe Steuerkanal " );
if ( Msg->Data [2] < 100 ) Serial.print ( " " );
if ( Msg->Data [2] < 10 ) Serial.print ( " " );
Serial.print ( Msg->Data [2] );
Serial.print ( " = " );
if ( Msg->Data [3] < 100 ) Serial.print ( " " );
if ( Msg->Data [3] < 10 ) Serial.print ( " " );
Serial.print ( Msg->Data [3] );
}
// NEU 12.12.2020
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 " );
else if ( speed == 255 || speed == 0 ) Serial.print ( " Nothalt" );
else
{
Serial.print ( " <<-- " );
Serial.print ( speed );
if ( speed < 10 ) Serial.print ( " " );
}
print_spaces ( 31 );
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case 3: // Forward speed step 28 Stufen
// 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 " );
else if ( speed == 255 || speed == 0 ) Serial.print ( " Nothalt" );
else
{
Serial.print ( " -->> " );
Serial.print ( speed );
if ( speed < 10 ) Serial.print ( " " );
}
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 ) Serial.print ( " F0" );
else Serial.print ( " f0" );
for ( byte k = 0; k < 4; k++ )
{
if ( bitRead ( Funktion, k ) ) Serial.print ( " F" );
else Serial.print ( " f" );
Serial.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 ) )
{
for ( byte k = 0; k < 4; k++ )
{
if ( bitRead ( Funktion, k ) ) Serial.print ( " F" );
else Serial.print ( " f" );
Serial.print ( k + 5 );
}
print_spaces ( 20 );
}
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
else // Loc Function 12-11-10-9
{
// 1010-xxxx Funktionssteuerung F9-F12
for ( byte k = 0; k < 4; k++ )
{
if ( k == 0 ) Serial.print ( " " );
if ( bitRead ( Funktion, k ) ) Serial.print ( " F" );
else Serial.print ( " f" );
Serial.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
for ( byte k = 0; k < 8; k++ )
{
if ( bitRead ( Funktion, k ) ) Serial.print ( " F" );
else Serial.print ( " f" );
Serial.print ( k + 13 );
}
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00011111: // F21-F28 Function Control
// 1101-1111 Funktionssteuerung F21-F28
for ( byte k = 0; k < 8; k++ )
{
if ( bitRead ( Funktion, k ) ) Serial.print ( " F" );
else Serial.print ( " f" );
Serial.print ( k + 21 );
}
break;
//-----------------------------------------------------------------------------------------------------------------------------------------------------------------
case B00011000: // F29-F36 Function Control
// 1101-1000 Funktionssteuerung F29 - F36
for ( byte k = 0; k < 8; k++ )
{
if ( bitRead ( Funktion, k ) ) Serial.print ( " F" );
else Serial.print ( " f" );
Serial.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;
}
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 )
{
case 1: Serial.print ( F ( "BR 01 Brawa " ) ); break;
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 11: Serial.print ( F ( "DXI Trix " ) ); break;
case 12: Serial.print ( F ( "B XII Trix " ) ); break;
case 13: Serial.print ( F ( "BR 13 FLM " ) ); break;
case 16: Serial.print ( F ( "V 16 Trix " ) ); break;
case 18: Serial.print ( F ( "BR 18 FLM Zimo " ) ); break;
case 20: Serial.print ( F ( "V20 Lenz " ) ); 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, ohne Prüfbyte
{
Serial.print ( " " );
for ( byte n = 0; n < pktByteCount - 1; n++ ) // wenn man das "- 1" löscht, wird auch das Prüfbyte angezeigt!
{
Serial.print ( " " );
Byte_to_Bits ( Msg->Data [n] );
}
Serial.println ( " " );
}
//--------------------------
void
Receive_serial_command ()
//--------------------------
// Liest ein oder mehrere Zeichen aus der seriellen Schnittstelle und stellt die Infos in einem "Buffer" zur Verfügung
// Bei "Enter" (\n) wird die Routine zur Auswertung aufgerufen
{
static char Buffer [20] = "";
if ( Serial.available () > 0 )
{
char c = Serial.read ();
switch ( c )
{
case '\n': // Proc buffer (For tests with the serial console of Arduino use "Neue Zeile" and not "..(CR)" )
Serial.print ( F ( "\nCmd: " ) );
Serial.println ( Buffer );
Proc_Cmd ( Buffer );
Buffer [0] = '\0';
break;
default: // Add character to buffer
{
uint8_t len = strlen ( Buffer );
if ( len < sizeof ( Buffer ) - 1 )
{
Buffer [len++] = c;
Buffer [len] = '\0';
}
else
{
*Buffer = '\0';
// Serial.println(F("Buffer overflow"));
}
}
}
}
}
//----------------------------
void
Proc_Cmd ( const char *Cmd )
//----------------------------
// verarbeitet die Eingaben über die serielle Schnittstelle
{
// 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
// l = Filtern Lokadresse lnnnn --> zeigt nur Befehle für Adresse #nnnn an, l ohne weitere Angaben setzt den Filter zurück
// z = Filtern Zubehöradresse znnnn --> zeigt nur Befehle für Adresse #nnnn an, z ohne weitere Angaben setzt den Filter zurück
// 7 = Statistik
// ? = Befehle anzeigen
switch ( *Cmd )
{
case '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 '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 '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 '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 '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 '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 'l':
Lok_Filter = atoi ( Cmd + 1 ); // Filter auf eingegebene Lokadresse setzen
break;
case 'z':
Acc_Filter = atoi ( Cmd + 1 ); // Filter auf eingegebene Accessory-Dekoder-Adresse setzen
break;
case '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 ( "Spezialbefehle Lok :" ) );
print_Zahl_rechts_ln ( z_spezial );
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 '?':
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 ( "lnnnn = Lokadresse filtern, nnnn = Adresse" ) );
Serial.println ( F ( "znnnn = Zubehöradresse filtern, nnnn = Adresse" ) );
Serial.println ( F ( "7 = Statistik anzeigen" ) );
Serial.println ( F ( "? = Befehle anzeigen" ) );
break;
}
Serial.println ( );
}