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.