Skip to content

OBD32 Protocol Documentation

Detailed protocol analysis from OBD32.dll decompilation.

Supported Protocols

ProtocolStandardBaud RateInitialization
ISO 9141-2OBD-II104005-baud slow init
ISO 14230 (KWP2000)OBD-II10400Fast init or 5-baud
BMW DS2Proprietary9600Direct
BMW KBUSProprietary9600Direct

Konzept IDs (Protocol Types)

From decompilation of the protocol selection switch:

KonzeptHexProtocolBaud RateDescription
50x05DS29600Converted to 6 internally
60x06BMW DS29600Standard DS2 protocol
110x0BSpecialvariesExtended protocol
120x0CKWP2000 + Wakeup10400With 5-baud init
130x0DISO 9141-210400Basic K-Line
150x0FKWP200010400/115200Fast init
160x10Extended KWPvariesExtended protocol

Protocol Selection Logic

c
switch ((char)*konzept) {
    case 0x05:
        *konzept = 6;      // Convert to DS2
        // fall through
        
    case 0x06:             // BMW DS2
        setup_ds2_params(params, state);
        if (kbus_mode) {
            // Use KBUS variant
            return ds2_kbus_send_recv(telegram, state);
        }
        return ds2_send_recv(telegram, state);
        
    case 0x0B:             // Extended
        return extended_protocol(params, state);
        
    case 0x0C:             // KWP2000 with wakeup
        if (!session_active) {
            do_5baud_wakeup(response, state);
            session_active = true;
        }
        return kwp_send_recv(telegram, state);
        
    case 0x0D:             // ISO 9141-2
        return iso9141_send_recv(telegram, state);
        
    case 0x0F:             // KWP2000
        if (subtype == 0) {
            setup_kwp_8bit(params, state);
        } else {
            setup_kwp_32bit(params, state);
        }
        return kwp_send_recv(telegram, state);
        
    case 0x10:             // Extended KWP
        return extended_kwp(params, state);
        
    default:
        *state = 0;
        return ERROR_INVALID_KONZEPT;
}

---

## 1. ISO 9141-2 (K-Line)

### Overview

ISO 9141-2 is a serial protocol using a single bidirectional K-Line at 10.4 kbaud with 5-baud initialization.

### Physical Layer

┌─────────────────────────────────────────────────────────┐ │ K-Line (half-duplex) │ │ │ │ Tester ◄──────────────────────────────────────► ECU │ │ │ │ Idle: HIGH (~12V) │ │ Active: LOW (0V) / HIGH (~12V) │ └─────────────────────────────────────────────────────────┘


### Serial Configuration

```c
// Default configuration
DCB config = {
    .BaudRate = 10400,      // 10.4 kbaud
    .ByteSize = 8,          // 8 data bits
    .Parity = NOPARITY,     // No parity (0)
    .StopBits = ONESTOPBIT  // 1 stop bit
};

5-Baud Initialization Sequence

The 5-baud init sends the ECU address (typically 0x33) at 5 baud using break signals:

Time (ms)
    0   ─┬─────────────────────────────────────────────────
        │                    IDLE (HIGH)
   200  ─┼─────────────────────────────────────────────────
        │ ┌─────┐     ┌─────┐     ┌─────┐     ┌─────┐
        │ │ ST  │     │ b0  │     │ b1  │     │ ... │
        │─┘     └─────┘     └─────┘     └─────┘     └──────
        │  LOW    HIGH  LOW   HIGH  ...

        │  Start  Bit0  Bit1  ...  Bit7  Stop
        │  200ms  200ms 200ms      200ms 200ms

Implementation:

c
int Wakeup_timer(uint timer_id, uint msg, int state, ...) {
    switch (state) {
        case 0:  // Start - drive K-Line LOW
            SetCommBreak(handle);
            timer_id = timeSetEvent(wakeup_low_ms, 1, Wakeup_timer, 1, 0);
            break;
            
        case 1:  // After LOW period - drive K-Line HIGH
            ClearCommBreak(handle);
            timer_id = timeSetEvent(wakeup_high_ms, 1, Wakeup_timer, 2, 0);
            break;
            
        case 2:  // Complete
            wakeup_complete = true;
            break;
    }
    return state;
}

Configuration (EDIABAS.ini):

ini
[OBD]
WAKEUP_LOW=25     ; LOW period in ms (default 25)
WAKEUP_HIGH=25    ; HIGH period in ms (default 25)

KeyBytes Response

After 5-baud init, ECU responds with sync byte and key bytes:

┌────────────────────────────────────────────────────────┐
│  Byte  │  Value  │  Description                        │
├────────┼─────────┼─────────────────────────────────────┤
│    1   │  0x55   │  Sync pattern (01010101)            │
│    2   │  0x08   │  Key Byte 1 (protocol info)         │
│    3   │  0x08   │  Key Byte 2 (protocol info)         │
│    4   │  ~KB2   │  Inverted Key Byte 2 (ACK)          │
└────────────────────────────────────────────────────────┘

Message Format

┌─────────┬─────────┬─────────┬─────────────┬──────────┐
│ Format  │ Target  │ Source  │   Data      │ Checksum │
│ (1 byte)│ (1 byte)│ (1 byte)│ (n bytes)   │ (1 byte) │
└─────────┴─────────┴─────────┴─────────────┴──────────┘

Format byte:
  Bit 7:   0 = No additional length byte
           1 = Additional length byte follows
  Bit 6:   0 = Physical addressing
           1 = Functional addressing
  Bits 5-0: Data length (if bit 7 = 0)

2. ISO 14230 (KWP2000)

Overview

KWP2000 extends ISO 9141 with standardized diagnostic services.

Serial Configuration

c
// KWP2000 configuration
DCB config = {
    .BaudRate = 10400,       // Can also be 9600 or higher
    .ByteSize = 8,
    .Parity = NOPARITY,
    .StopBits = ONESTOPBIT
};

Fast Initialization

KWP2000 supports fast init (no 5-baud sequence):

Time (ms)
    0   ─┬─ IDLE (HIGH)

   25   ─┼─ TiniL ──────┐
        │              │ LOW (break)
   50   ─┼─────────────┘
        │   TiniH ──────┐
   75   ─┼─             │ HIGH
        │──────────────┘

        │   Start Pattern Request (0xC1 0x33 0xF1)

Message Format

Header formats:

Format 1 - No address info:
┌─────────┬─────────────┬──────────┐
│  Fmt    │    Data     │ Checksum │
│ (1 byte)│  (n bytes)  │ (1 byte) │
└─────────┴─────────────┴──────────┘

Format 2 - With address:
┌─────────┬─────────┬─────────┬─────────────┬──────────┐
│  Fmt    │ Target  │ Source  │    Data     │ Checksum │
│ (1 byte)│ (1 byte)│ (1 byte)│  (n bytes)  │ (1 byte) │
└─────────┴─────────┴─────────┴─────────────┴──────────┘

Format 3 - With length byte:
┌─────────┬─────────┬─────────┬─────────┬─────────────┬──────────┐
│  Fmt    │ Target  │ Source  │  Length │    Data     │ Checksum │
│ (1 byte)│ (1 byte)│ (1 byte)│ (1 byte)│  (n bytes)  │ (1 byte) │
└─────────┴─────────┴─────────┴─────────┴─────────────┴──────────┘

Format Byte

Bit 7-6: Address mode
  00 = No address information
  01 = Exception (CARB mode)
  10 = Physical addressing
  11 = Functional addressing

Bit 5-0: Length (0-63 bytes)
  If 0, additional length byte follows

Common Addresses

AddressDescription
0x33ECU physical address (OBD)
0xF1Tester address
0x7FFunctional broadcast

TesterPresent Service (Keep-Alive)

KWP2000 sessions timeout after inactivity. TesterPresent (0x3E) keeps session alive:

c
void TesterPresentService(uint timer_id, uint msg, DWORD_PTR params) {
    // Send TesterPresent request: [Fmt][Tgt][Src][0x3E][CS]
    byte telegram[] = {0x81, 0x33, 0xF1, 0x3E};  // + checksum
    
    result = send_telegram(telegram);
    
    if (result == SUCCESS) {
        // Reschedule timer (default ~2 seconds)
        timer_id = timeSetEvent(
            tester_present_interval,  // params + 0x44
            10,
            TesterPresentService,
            params,
            TIME_ONESHOT
        );
    }
}

Common KWP2000 Services

SIDNameDescription
0x10StartDiagnosticSessionEnter diagnostic mode
0x20StopDiagnosticSessionExit diagnostic mode
0x21ReadDataByLocalIdRead data by local ID
0x22ReadDataByCommonIdRead data by common ID
0x27SecurityAccessUnlock ECU
0x2EWriteDataByLocalIdWrite data
0x30InputOutputControlByLocalIdActuator test
0x31StartRoutineByLocalIdRun routine
0x34RequestDownloadStart download
0x36TransferDataTransfer data block
0x3ETesterPresentKeep-alive

3. BMW DS2 (Diagnostic System 2)

Overview

BMW proprietary protocol used in E36, E38, E39, E46, E53 vehicles.

Serial Configuration

c
// DS2 configuration
DCB config = {
    .BaudRate = 9600,        // Standard DS2 baud
    .ByteSize = 8,
    .Parity = EVENPARITY,    // Even parity (2)
    .StopBits = ONESTOPBIT
};

Message Format

┌─────────┬─────────┬─────────────┬──────────┐
│ Address │  Length │    Data     │ Checksum │
│ (1 byte)│ (1 byte)│  (n bytes)  │ (1 byte) │
└─────────┴─────────┴─────────────┴──────────┘

Address: ECU address (0x00-0xFF)
Length:  Total message length including header and checksum
Data:    Command + parameters
Checksum: XOR of all preceding bytes

DS2 Frame Structure (Detailed)

Byte:     0        1        2        3...n-1    n
       ┌────────┬────────┬────────┬──────────┬────────┐
       │  ADDR  │  LEN   │  CMD   │  DATA    │   CS   │
       └────────┴────────┴────────┴──────────┴────────┘
       
ADDR:  Target ECU address
LEN:   Total frame length (including ADDR, LEN, and CS)
CMD:   Command/Service ID
DATA:  Command parameters (variable length)
CS:    XOR checksum of bytes 0 to n-1

DS2 Protocol Parameters

c
// DS2 setup (Konzept 6)
struct DS2_Params {
    uint16_t konzept;       // [0] = 6
    uint16_t baudrate;      // [1] = 0x2580 (9600)
    uint16_t reserved[4];   // [2-5] = 0
    uint16_t timeout;       // [6] = 100 ms
    uint16_t retry_delay;   // [7] = 10 ms
    uint16_t inter_byte;    // [8] = 10 ms
};

// Internal state structure
param_3[0]  = konzept;       // Protocol type (6)
param_3[1]  = baudrate;      // 9600
param_3[10] = 8;             // Data bits
param_3[11] = 2;             // Parity (EVEN)
param_3[14] = 0;             // Flags

DS2 Auto-Detection Sequence

OBD32 can auto-detect protocol by sending probe telegrams:

c
// Step 1: Try DS2 @ 9600 baud
setup_params = {
    .konzept = 6,
    .baudrate = 0x2580,  // 9600
    .timeout = 100,
    .retry = 10
};
configure_port(setup_params);

// Send probe: [00][55][FF]
probe_telegram = {0x00, 0x55, 0xFF};
if (send_receive(probe_telegram) == SUCCESS) {
    return PROTOCOL_DS2;
}

// Step 2: Try KWP2000 @ 115200 baud
setup_params = {
    .konzept = 0x0F,
    .baudrate = 0x1C200,  // 115200
    .timeout = 100
};
configure_port(setup_params);

// Send same probe
if (send_receive(probe_telegram) == SUCCESS) {
    return PROTOCOL_KWP;
}

return PROTOCOL_UNKNOWN;

Common DS2 Commands

CommandNameDescription
0x00IDENTRead ECU identification
0x04STATUS_LESENRead status
0x05DIAGNOSERead diagnostic data
0x06ENDEEnd diagnostic session
0x07STEUERNActuator control
0x08LESENRead memory
0x10SCHREIBENWrite memory
0x12JOB_STATUSRead job status

Common DS2 ECU Addresses

AddressHexModuleDescription
00x00DME/DDEEngine control
80x08EGSTransmission
160x10ASCStability control
240x18ABSAnti-lock brakes
320x20EWSImmobilizer
400x28AirbagSRS
480x30SteeringActive steering
680x44SZMCentral body
960x60IKEInstrument cluster
1040x68RadioAudio/Navigation
1280x80GMGeneral Module
1600xA0LCMLight check
1760xB0IHKAClimate control
2080xD0PDCParking distance

4. BMW KBUS (Body Bus)

Overview

BMW body CAN-like serial bus for comfort electronics. KBUS is essentially DS2 with source/destination addressing.

Serial Configuration

c
// KBUS configuration (same as DS2)
DCB config = {
    .BaudRate = 9600,        // KBUS speed
    .ByteSize = 8,
    .Parity = EVENPARITY,    // Even parity
    .StopBits = ONESTOPBIT
};

Message Format

┌─────────┬─────────┬─────────┬─────────────┬──────────┐
│ Source  │  Length │  Dest   │    Data     │ Checksum │
│ (1 byte)│ (1 byte)│ (1 byte)│  (n bytes)  │ (1 byte) │
└─────────┴─────────┴─────────┴─────────────┴──────────┘

Source:   Sender module address
Length:   Bytes following (Dest + Data + Checksum)
Dest:     Destination address (0x3F = broadcast)
Data:     Message content
Checksum: XOR of all preceding bytes

KBUS Frame Structure (Detailed)

Byte:     0        1        2        3...n-1    n
       ┌────────┬────────┬────────┬──────────┬────────┐
       │  SRC   │  LEN   │  DST   │  DATA    │   CS   │
       └────────┴────────┴────────┴──────────┴────────┘
       
SRC:   Source module address
LEN:   Number of bytes following (DST + DATA + CS)
DST:   Destination address (0x3F = broadcast to all)
DATA:  Message payload
CS:    XOR checksum of bytes 0 to n-1

DS2 vs KBUS Format Comparison

DS2 Format:
┌────────┬────────┬────────────────────────┬────────┐
│  ADDR  │  LEN   │         DATA           │   CS   │
└────────┴────────┴────────────────────────┴────────┘
   ECU     Total              Payload           XOR

KBUS Format:
┌────────┬────────┬────────┬────────────────┬────────┐
│  SRC   │  LEN   │  DST   │     DATA       │   CS   │
└────────┴────────┴────────┴────────────────┴────────┘
  From     Rest     To          Payload          XOR

DS2 ↔ KBUS Format Conversion

OBD32.dll automatically converts between formats based on frame analysis:

c
// Detection: Compare total length with LEN field
// DS2: total_len == telegram[1]
// KBUS: total_len == telegram[1] + 2 (SRC + LEN bytes not counted)

bool is_kbus_format(ushort* telegram) {
    byte len_field = ((byte*)telegram)[3];  // LEN byte
    ushort total_len = *telegram;
    
    // KBUS: length field counts from DST onwards
    // DS2: length field is total frame length
    return (total_len == len_field - 1);
}

// DS2 to KBUS conversion
void DS2_to_KBUS(ushort* telegram) {
    ushort len = *telegram;
    
    // Check if conversion needed (len > 3 and matches DS2 format)
    byte ds2_len = ((byte*)telegram)[3];
    if (ds2_len <= 3) return;
    if (len != ds2_len && len != ds2_len - 1) return;
    
    // Shift data right to make room for DST byte
    memmove(&telegram[5], &telegram[4], len - 2);
    
    // Build KBUS header
    ((byte*)telegram)[4] = ((byte*)telegram)[2];  // Copy original addr to DST
    *telegram += 1;                                // Increase total length
    ((byte*)telegram)[2] = 0x3F;                   // SRC = broadcast (tester)
    ((byte*)telegram)[3]--;                        // Adjust LEN field
    
    // Recalculate checksum if needed
    if (!use_raw_checksum) {
        *telegram -= 1;
        calculate_xor_checksum(telegram);
    }
    
    log("DS2 Format in KBUS Format", *telegram, &telegram[1]);
}

// KBUS to DS2 conversion
void KBUS_to_DS2(ushort* telegram) {
    // Remove SRC and DST, keep only ADDR+LEN+DATA+CS
    memmove(&telegram[4], &telegram[5], *telegram);
    *telegram -= 2;                    // Decrease total length
    ((byte*)telegram)[3]++;            // Adjust LEN field
    
    calculate_xor_checksum(telegram);
    
    log("KBUS Format in DS2 Format", *telegram, &telegram[1]);
}

KBUS Detection in Response

The driver detects KBUS responses by checking header patterns:

c
// 0xB8 (signed: -0x48) indicates KBUS-style extended header
if ((char)telegram[1] == 0xB8) {
    // Extended KBUS format - different parsing
    parse_extended_kbus(telegram);
} else {
    // Standard DS2/KBUS format
    parse_standard(telegram);
}

// Also check format byte upper bits for KWP addressing mode
byte format = telegram[1];
if ((format & 0xC0) == 0xC0) {
    // Functional addressing (broadcast)
} else if ((format & 0xC0) == 0x80) {
    // Physical addressing
}

KBUS Mode Configuration

KBUS mode is controlled via EDIABAS.INI configuration file:

ini
[OBD]
; FORMAT determines DS2 communication mode
; NORMAL = standard DS2 without conversion (default)
; KBUS   = DS2 with automatic KBUS frame conversion
FORMAT=KBUS

Initialization in OBD32.dll:

c
// Global flag for KBUS mode
int kbus_mode = 0;  // DAT_10011878

// Read from EDIABAS.INI during initialization
void init_kbus_mode(void) {
    char buffer[20];
    
    kbus_mode = 0;  // Default: disabled
    
    GetPrivateProfileStringA(
        "OBD",              // Section
        "FORMAT",           // Key (DAT_1000db04)
        "NORMAL",           // Default value
        buffer,
        20,
        ediabas_ini_path
    );
    
    // Convert to uppercase and compare
    strupr(buffer);
    
    if (strcmp(buffer, "KBUS") == 0) {
        kbus_mode = 1;      // Enable KBUS conversion
    }
    if (strcmp(buffer, "NORMAL") == 0) {
        kbus_mode = 0;      // Explicit disable
    }
}

Effect on Protocol Handling:

c
// In send/receive function (case 6 = DS2):
case 6:  // Konzept DS2
    if (kbus_mode == 1) {
        // Use KBUS-aware send/receive with format conversion
        result = ds2_kbus_send_recv(telegram, state);  // FUN_100034f0
    } else {
        // Standard DS2 without conversion
        result = ds2_send_recv(telegram, state);       // FUN_100022d0
    }
    break;

// In parameter setup:
if (kbus_mode == 1) {
    params[6] = 3;  // Modify retry count for KBUS timing
}

Mode Comparison:

Settingkbus_modeTX BehaviorRX Behavior
FORMAT=NORMAL0Send DS2 as-isReceive DS2 as-is
FORMAT=KBUS1Convert DS2→KBUS (add 0x3F src)Convert KBUS→DS2

When to Use KBUS Mode:

  • Communicating with body electronics (GM, LCM, IKE, etc.)
  • When ECU expects KBUS frame format with source addressing
  • Interfacing with I-BUS/K-BUS gateway modules

Common KBUS Addresses

AddressHexModuleFull Name
00x00GMGeneral Module (Body)
80x08SHDSunroof Module
240x18CDCCD Changer
400x28RDCTire Pressure Monitor
480x30CCMCheck Control Module
630x3FBROADCASTAll modules
680x44EWSImmobilizer
800x50MFLSteering Wheel Buttons
960x60PDCPark Distance Control
1040x68RADRadio
1060x6ADSPAudio Amplifier
1280x80IKEInstrument Cluster
1560x9CSESSeat Memory
1600xA0AHLHeadlight Aim
1760xB0IHKAClimate Control
1870xBBNAVNavigation Computer
1910xBFLCMLight Control Module
1920xC0MIDMulti-Info Display
2000xC8TELTelephone
2100xD2SMSeat Module
2240xE0IRISIntegrated Radio Info System
2370xEDTVTelevision Module
2400xF0BMOn-board Monitor
2550xFFLOCLocal (internal)

KBUS Message Examples

Lights On (from LCM to all):

SRC=0xBF  LEN=0x05  DST=0x3F  DATA=0x4B,0x00  CS=XOR
[BF] [05] [3F] [4B] [00] [CS]

Key Status (from GM to IKE):

SRC=0x00  LEN=0x05  DST=0x80  DATA=0x11,0x01  CS=XOR
[00] [05] [80] [11] [01] [CS]

CD Track Info (from RAD to MID):

SRC=0x68  LEN=0x07  DST=0xC0  DATA=0x39,0x02,0x01,0x00  CS=XOR
[68] [07] [C0] [39] [02] [01] [00] [CS]

5. Checksum Algorithms

XOR Checksum (DS2/KBUS)

c
// Calculate XOR checksum and append to telegram
void calculate_xor_checksum(ushort* telegram) {
    byte checksum = 0;
    ushort length = *telegram;
    byte* data = (byte*)(telegram + 1);
    
    // XOR all bytes
    for (int i = 0; i < length; i++) {
        checksum ^= data[i];
    }
    
    // Append checksum byte
    data[length] = checksum;
    *telegram = length + 1;
}

// Verify XOR checksum (result should be 0)
bool verify_xor_checksum(ushort* telegram) {
    byte checksum = 0;
    ushort length = *telegram;
    byte* data = (byte*)(telegram + 1);
    
    // XOR all bytes including checksum
    for (int i = 0; i < length; i++) {
        checksum ^= data[i];
    }
    
    return checksum == 0;  // Valid if all XOR to zero
}

ADD Checksum (Alternative)

Some protocols use simple addition:

c
// Calculate ADD checksum
void calculate_add_checksum(ushort* telegram) {
    char checksum = 0;
    ushort length = *telegram;
    byte* data = (byte*)(telegram + 1);
    
    // Sum all bytes (with overflow)
    for (int i = 0; i < length; i++) {
        checksum += data[i];
    }
    
    // Append checksum
    data[length] = checksum;
    *telegram = length + 1;
}

6. Protocol Parameters Structure

Based on decompilation, the protocol parameters structure:

c
struct ProtocolParams {
    /* 0x00 */ uint32_t konzept;         // Protocol type
    /* 0x04 */ uint32_t baudrate;        // Baud rate
    /* 0x08 */ uint32_t reserved1[8];
    /* 0x28 */ uint8_t  data_bits;       // Data bits (8)
    /* 0x2C */ uint8_t  parity;          // 0=None, 1=Odd, 2=Even
    /* 0x30 */ uint32_t verify_checksum; // 1 = verify checksum
    /* 0x34 */ uint32_t add_checksum;    // 1 = add checksum to TX
    /* 0x38 */ uint32_t pullup;          // DTR/RTS control
    /* 0x3C */ uint32_t max_retries;     // Max retry count
    /* 0x40 */ uint32_t retry_delay;     // Retry delay (ms)
    /* 0x44 */ uint32_t tp_interval;     // TesterPresent interval (ms)
    /* 0x48 */ uint32_t tp_enabled;      // TesterPresent enabled
    /* 0x4C */ uint8_t  tp_telegram[11]; // TesterPresent data
    /* 0x57 */ uint16_t wakeup_len;      // Wakeup telegram length
    /* 0x5B */ uint8_t  wakeup_data[32]; // Wakeup telegram data
    /* ... */
};

// Protocol types (Konzept)
enum ProtocolType {
    KONZEPT_DS2    = 0x06,   // BMW DS2
    KONZEPT_KWP    = 0x0F,   // KWP2000
    KONZEPT_KBUS   = 0x??    // KBUS (DS2 variant)
};

6. Timing Parameters

Inter-byte Timing (P1)

Time between bytes within a message:

c
// Byte delay transmission
if (params->byte_delay > 0) {
    for (int i = 0; i < length; i++) {
        if (i > 0) Sleep(params->byte_delay);
        WriteFile(handle, &data[i], 1, &written, NULL);
    }
}

Inter-message Timing (P2/P3)

c
// Wait between messages
if (params->inter_msg_delay > 0) {
    while (timeGetTime() < last_send_time + 1) {
        Sleep(1);
    }
}

Response Timeout

c
// Timeout calculation
DWORD timeout = timeGetTime() + 20 +                    // Base
                (length * 11000 / baudrate) +           // TX time
                (length * byte_delay);                  // Delay time

do {
    Sleep(1);
    ClearCommError(handle, &errors, &comstat);
} while (comstat.cbInQue < length && timeGetTime() < timeout);

7. Error Handling

Error Codes

CodeDescription
0x01Success
0x02Timeout on first byte
0x03Timeout on subsequent bytes
0x05Unknown command
0x06Init failed
0x08Not supported
0x0BTesterPresent failed
0x71 ('q')Send error

Retry Logic

c
// Retry mechanism for send/receive
int result = send_receive(telegram, params);

if (result == 2) {  // Timeout
    // Retry once
    result = send_receive(telegram, params);
}

Protocol Selection Flow

┌─────────────────────────────────────────────────────────────┐
│                    OBD32.dll Protocol Flow                   │
├─────────────────────────────────────────────────────────────┤
│                                                              │
│  1. SetupConnection(konzept, baudrate, parity, ...)         │
│                        │                                     │
│                        ▼                                     │
│  ┌─────────────────────────────────────────────────────┐    │
│  │  Configure COM port (SetCommState)                   │    │
│  └─────────────────────────────────────────────────────┘    │
│                        │                                     │
│                        ▼                                     │
│  2. Check Konzept (protocol type)                           │
│                        │                                     │
│         ┌──────────────┼──────────────┐                     │
│         ▼              ▼              ▼                      │
│     DS2/KBUS        KWP2000        Other                    │
│         │              │              │                      │
│         │         ┌────┴────┐        │                      │
│         │         ▼         ▼        │                      │
│         │     5-baud     Fast        │                      │
│         │      init      init        │                      │
│         │         │         │        │                      │
│         └────────┬┴─────────┴────────┘                      │
│                  ▼                                           │
│  3. Send/Receive telegrams                                  │
│     - Add checksum if configured                            │
│     - Convert DS2↔KBUS if needed                            │
│     - Handle echo (half-duplex)                             │
│     - Verify response checksum                              │
│                        │                                     │
│                        ▼                                     │
│  4. TesterPresent (if KWP2000 and enabled)                  │
│     - Periodic 0x3E service                                 │
│     - Keeps diagnostic session alive                        │
│                                                              │
└─────────────────────────────────────────────────────────────┘