LGB möchte in der Digital Technik eigene Wege gehen ...
Eine Dokumentation zum Interface gibt es nicht, dafür ein Stück Software.
Nun ist ja so, dass das Programm mit dem Interface
kommunizieren muss, irgendwie müssen ja die Dekoder und Weichenantriebe
Informationen bekommen. Dies ist der Ansatz um das Protokoll zu dekodieren.
Mit geeigneten Tools kann der Verkehr zwischen der
Zentrale und dem PC abgehorcht werden.
typedef enum
{
OPC_INFO_MZS = 0x00
, OPC_LOK = 0x01
, OPC_LOK_Fx = 0x02
, OPC_SWITCH_REQ_MZS = 0x03
, OPC_RESET_LOK = 0x06
, OPC_RESET_STATION = 0x07
, OPC_0x10 = 0x10
, OPC_0x11 = 0x11
, OPC_0x12 = 0x12
, OPC_0x13 = 0x13
, OPC_0x15 = 0x15
, OPC_EMERGENCY_STOP_MZS = 0x16
, OPC_POWER_MZS = 0x14
} LGB_MzsOpcode_t;
Oben sind die hexedezimalen Werte mit denen eine Nachricht
an das Interface beginnt.
Ein- und Ausschalten der Zentrale:
if ( m_interfaceRunning )
{
m_interfaceRunning = FALSE;
m_command[0] = OPC_EMERGENCY_STOP_MZS; // Nothalt
aller Loks
m_command[1] = 0; // ??
m_command[2] = 0; // ??
m_command[3] = OPC_EMERGENCY_STOP_MZS ^ 0 ^ 0 ; // xor check byte
int err = SendCommandToInterface( 4 );
DecodeCommandBuffer( _T("SHUTDOWN:"), 4 );
Sleep( 100 );
m_command[0] = OPC_RESET_STATION; // stop
m_command[1] = 0; // ??
m_command[2] = 0x80; // ??
m_command[3] = OPC_RESET_STATION ^ 0 ^ 0x80 ; // xor check byte
err = SendCommandToInterface( 4 );
DecodeCommandBuffer( _T("SHUTDOWN:"), 4 );
}
else
{
m_interfaceRunning = TRUE;
// power up the central unit
unsigned char xored = OPC_RESET_STATION ^ 0 ^ 0x81; // xor check byte
m_command[0] = OPC_RESET_STATION; // reset interface
m_command[1] = 0; // still the same
m_command[2] = 0x81; // xor check byte
m_command[3] = xored; // xor check byte
int err = SendCommandToInterface( 4 );
// power up the central unit
xored = OPC_POWER_MZS ^ 0 ^ 1; // xor check byte
m_command[0] = OPC_POWER_MZS; // start interface
m_command[1] = 0; // still the same
m_command[2] = 1; // xor check byte
m_command[3] = xored; // xor check byte
err = SendCommandToInterface( 4 );
}
1. Lokbefehl, d.h. Initialisierung der Lokomotive:
m_command[0] = OPC_RESET_LOK;
// Begin des Paketes
m_command[1] = addr;
// ??
m_command[2] = 1;
// ??
m_command[3] = OPC_RESET_LOK ^ addr ^ 1 ; // xor check byte
err = SendCommandToInterface( 4 );
2. Fahrbefehl:
m_command[0] = OPC_LOK;
// beginn der Nachricht
/*
* Second byte is the address
*/
m_command[1] = addr;
if ( direction_forward == curDirection )
data = 0x20; // forward direction
else
data = 0; // thats 'backwards'
speed = (unsigned char ) i_pLokControl->m_speed;
if ( i_pLokControl->m_emergencyStop )
// Nothalt definiert
{
i_pLokControl->m_emergencyStop = FALSE;
speed = 0;
offset = 1;
data = 0; // forward direction
}
// normaler Fahrbefehl
else if ( speed )
{
offset = 1;
}
data += offset; // add possible offset for the emergency stop
data += speed; // add speed settings
m_command[2] = data;
xored = OPC_LOK ^ addr ^ data;
// Ende der Nachricht
m_command[3] = xored;
err = SendCommandToInterface( 4 );
Licht Ein- und Ausschalten:
BOOL keyF0on = optionSettings & keyF0;
BOOL prevKeyF0on = prevOptionSettings & keyF0;
if ( prevKeyF0on != keyF0on )
// einfacher Drucktaster
{
m_command[0] = OPC_LOK_Fx;
// Lok Befehl
m_command[1] = 0x80 + addr;
// Adresse der Lok
m_command[2] = 0x80; // ??
m_command[3] = OPC_LOK_Fx ^ (0x80+addr) ^ 0x80 ; // xor check byte
err = SendCommandToInterface( 4 );
DecodeCommandBuffer( _T("toggle lights on/off:"), 4 );
if ( 0 != err )
return err;
}
// Funktionstaste F1 schalten
BOOL keyF1on = optionSettings & keyF1;
BOOL prevKeyF1on = prevOptionSettings & keyF1;
if ( prevKeyF1on != keyF1on )
{
err = SendFKey( addr, 1, _T("toggle F1") );
}
return err;
// Allgemeine Funktion um ein
Funktionstaste zu schalten
int CLGB_Mzs::SendFKey(
const unsigned char i_addr, // Adresse der Lok
const unsigned char i_fx, // Funktionstaste
const char *i_key
// Text zum Dekodieren
)
{
m_command[0] = OPC_LOK_Fx; // opcode for the extra function codes
m_command[1] = 0x80 +i_addr; // ??
m_command[2] = i_fx; // ??
m_command[3] = OPC_LOK_Fx ^ (0x80+i_addr) ^ i_fx ; // xor check byte
int err = SendCommandToInterface( 4 );
DecodeCommandBuffer( i_key, 4 );
return err;
}
Weichenbefehl:
if ( Straight == i_switchSettingRequest )
// wenn gerade aus
secondByte= 1;
else
secondByte= 0;
xored = OPC_SWITCH_REQ_MZS ^ addr ^ secondByte;
m_command[0] = OPC_SWITCH_REQ_MZS;
m_command[1] = addr;
// des Weichendekoders
m_command[2] = secondByte;
m_command[3] = xored;
bytesSend = 4;
err = SendCommandToInterface( bytesSend );
Das letzte Byte in der Nachricht ist das sogenannte
Checkbyte, es wird ein einfaches XOR über die vorangehenden Bytes berechnet.
Die Rückmeldungen sind nicht eindeutig zu dekodieren.
Hier fällt es mir schwer dem LGB Hochglanzprospekt glauben zu schenken.
Der Aufbau ist an und für sich klar und übersichtlich,
aber bei der Rückmeldung hat man die Antworten des 55070 Rückmeldemoduls so
definiert, dass diese nicht eindeutig sind.
In der jetzigen Form ist das Rückmeldemodul nicht für
den echten Mehrzugbetrieb geeignet.