Files
obd_emulator/ECU_EMULATOR_SPEC.md
Alexander Poletaev 2ec05d2e9d Add UDS protocol support (ISO 14229) for ECU emulator
Implement UDS Service 0x22 (Read Data By Identifier) with support for:
- Engine ECU (0x7E0): boost pressure, fuel rail pressure, lambda, torque,
  wastegate position, ignition timing, knock correction per cylinder
- DSG Transmission (0x7E1): current gear, selector position, oil temp
- Instrument Cluster (0x714): RPM, ambient light sensor

Also adds ECU_EMULATOR_SPEC.md with full protocol documentation.

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-01-30 16:28:55 +03:00

599 lines
16 KiB
Markdown

# ECU Emulator Specification
Документация для создания эмулятора ECU для тестирования Carpibord.
## Общие сведения
- **CAN Bus Speed:** 500 kbps
- **CAN ID Format:** 11-bit (standard)
- **Протоколы:** OBD2 (ISO 15765-4) + UDS (ISO 14229)
---
## OBD2 Protocol (Mode 0x01)
### Формат запроса
```
CAN ID: 0x7DF (broadcast) или 0x7E0 (ECU direct)
Data: [Length, Mode, PID, 0x00, 0x00, 0x00, 0x00, 0x00]
Пример запроса RPM:
7DF [02 01 0C 00 00 00 00 00]
│ │ │
│ │ └── PID 0x0C (RPM)
│ └───── Mode 0x01 (Current Data)
└──────── Length: 2 bytes
```
### Формат ответа
```
CAN ID: 0x7E8 (ECU response)
Data: [Length, Mode+0x40, PID, DataA, DataB, ...]
Пример ответа RPM = 2000:
7E8 [04 41 0C 1F 40 00 00 00]
│ │ │ │ │
│ │ │ └───┴── Data: (0x1F * 256 + 0x40) / 4 = 2000 RPM
│ │ └──────── PID 0x0C
│ └─────────── Mode 0x41 (response to 0x01)
└────────────── Length: 4 bytes
```
---
## OBD2 PIDs — Полная спецификация
### PID 0x00 — Supported PIDs [01-20]
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 00 00 00 00 00 00]` |
| Response | `7E8 [06 41 00 XX XX XX XX 00]` |
| Formula | Bitmask of supported PIDs |
**Ответ для нашего набора PIDs:**
```
Поддерживаем: 0x04, 0x05, 0x0C, 0x0D, 0x0F, 0x10, 0x11
Bitmask: 0x18 3F 80 00
7E8 [06 41 00 18 3F 80 00 00]
```
### PID 0x04 — Engine Load
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 04 00 00 00 00 00]` |
| Response | `7E8 [03 41 04 XX 00 00 00 00]` |
| Formula | `A * 100 / 255` (%) |
| Min | 0% |
| Max | 100% |
| Bytes | 1 |
**Примеры:**
```
Load = 0%: 7E8 [03 41 04 00 00 00 00 00]
Load = 50%: 7E8 [03 41 04 80 00 00 00 00] (0x80 = 128 → 50.2%)
Load = 100%: 7E8 [03 41 04 FF 00 00 00 00]
```
### PID 0x05 — Coolant Temperature
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 05 00 00 00 00 00]` |
| Response | `7E8 [03 41 05 XX 00 00 00 00]` |
| Formula | `A - 40` (°C) |
| Min | -40°C |
| Max | 215°C |
| Bytes | 1 |
**Примеры:**
```
Temp = -40°C: 7E8 [03 41 05 00 00 00 00 00]
Temp = 0°C: 7E8 [03 41 05 28 00 00 00 00] (0x28 = 40)
Temp = 90°C: 7E8 [03 41 05 82 00 00 00 00] (0x82 = 130)
Temp = 105°C: 7E8 [03 41 05 91 00 00 00 00] (0x91 = 145)
```
### PID 0x0C — Engine RPM
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 0C 00 00 00 00 00]` |
| Response | `7E8 [04 41 0C XX YY 00 00 00]` |
| Formula | `(A * 256 + B) / 4` (RPM) |
| Min | 0 RPM |
| Max | 16383.75 RPM |
| Bytes | 2 |
**Примеры:**
```
RPM = 0: 7E8 [04 41 0C 00 00 00 00 00]
RPM = 800: 7E8 [04 41 0C 0C 80 00 00 00] (0x0C80 = 3200, /4 = 800)
RPM = 2000: 7E8 [04 41 0C 1F 40 00 00 00] (0x1F40 = 8000, /4 = 2000)
RPM = 3500: 7E8 [04 41 0C 36 B0 00 00 00] (0x36B0 = 14000, /4 = 3500)
RPM = 6000: 7E8 [04 41 0C 5D C0 00 00 00] (0x5DC0 = 24000, /4 = 6000)
```
**Формула кодирования:** `raw = RPM * 4`, затем `A = raw >> 8`, `B = raw & 0xFF`
### PID 0x0D — Vehicle Speed
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 0D 00 00 00 00 00]` |
| Response | `7E8 [03 41 0D XX 00 00 00 00]` |
| Formula | `A` (km/h) |
| Min | 0 km/h |
| Max | 255 km/h |
| Bytes | 1 |
**Примеры:**
```
Speed = 0: 7E8 [03 41 0D 00 00 00 00 00]
Speed = 50: 7E8 [03 41 0D 32 00 00 00 00]
Speed = 120: 7E8 [03 41 0D 78 00 00 00 00]
Speed = 200: 7E8 [03 41 0D C8 00 00 00 00]
```
### PID 0x0F — Intake Air Temperature
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 0F 00 00 00 00 00]` |
| Response | `7E8 [03 41 0F XX 00 00 00 00]` |
| Formula | `A - 40` (°C) |
| Min | -40°C |
| Max | 215°C |
| Bytes | 1 |
**Примеры:**
```
Temp = 25°C: 7E8 [03 41 0F 41 00 00 00 00] (0x41 = 65)
Temp = 40°C: 7E8 [03 41 0F 50 00 00 00 00] (0x50 = 80)
```
### PID 0x10 — MAF Air Flow Rate
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 10 00 00 00 00 00]` |
| Response | `7E8 [04 41 10 XX YY 00 00 00]` |
| Formula | `(A * 256 + B) / 100` (g/s) |
| Min | 0 g/s |
| Max | 655.35 g/s |
| Bytes | 2 |
**Примеры:**
```
MAF = 5 g/s: 7E8 [04 41 10 01 F4 00 00 00] (0x01F4 = 500, /100 = 5)
MAF = 25 g/s: 7E8 [04 41 10 09 C4 00 00 00] (0x09C4 = 2500, /100 = 25)
MAF = 100 g/s: 7E8 [04 41 10 27 10 00 00 00] (0x2710 = 10000, /100 = 100)
```
### PID 0x11 — Throttle Position
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 11 00 00 00 00 00]` |
| Response | `7E8 [03 41 11 XX 00 00 00 00]` |
| Formula | `A * 100 / 255` (%) |
| Min | 0% |
| Max | 100% |
| Bytes | 1 |
**Примеры:**
```
Throttle = 0%: 7E8 [03 41 11 00 00 00 00 00]
Throttle = 15%: 7E8 [03 41 11 26 00 00 00 00] (0x26 = 38 → 14.9%)
Throttle = 50%: 7E8 [03 41 11 80 00 00 00 00]
Throttle = 100%: 7E8 [03 41 11 FF 00 00 00 00]
```
### PID 0x20 — Supported PIDs [21-40]
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 20 00 00 00 00 00]` |
| Response | `7E8 [06 41 20 XX XX XX XX 00]` |
**Ответ для поддержки 0x2F:**
```
7E8 [06 41 20 00 00 00 01 00]
```
### PID 0x2F — Fuel Tank Level
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 2F 00 00 00 00 00]` |
| Response | `7E8 [03 41 2F XX 00 00 00 00]` |
| Formula | `A * 100 / 255` (%) |
| Min | 0% |
| Max | 100% |
| Bytes | 1 |
**Примеры:**
```
Fuel = 25%: 7E8 [03 41 2F 40 00 00 00 00]
Fuel = 50%: 7E8 [03 41 2F 80 00 00 00 00]
Fuel = 75%: 7E8 [03 41 2F BF 00 00 00 00]
```
### PID 0x40 — Supported PIDs [41-60]
```
7E8 [06 41 40 00 00 00 01 00] (поддержка 0x5C)
```
### PID 0x5C — Oil Temperature
| Параметр | Значение |
|----------|----------|
| Request | `7DF [02 01 5C 00 00 00 00 00]` |
| Response | `7E8 [03 41 5C XX 00 00 00 00]` |
| Formula | `A - 40` (°C) |
| Min | -40°C |
| Max | 210°C |
| Bytes | 1 |
**Примеры:**
```
Oil Temp = 90°C: 7E8 [03 41 5C 82 00 00 00 00] (0x82 = 130)
Oil Temp = 110°C: 7E8 [03 41 5C 96 00 00 00 00] (0x96 = 150)
```
---
## UDS Protocol (Mode 0x22)
### Формат запроса
```
CAN ID: зависит от ECU (см. таблицу)
Data: [03, 22, PID_H, PID_L, 55, 55, 55, 55]
Пример запроса Boost (PID 0x202A) к Engine ECU:
7E0 [03 22 20 2A 55 55 55 55]
│ │ │ │
│ │ └───┴── PID 0x202A
│ └───────── Service 0x22 (Read Data By ID)
└──────────── Length: 3 bytes
```
### Формат ответа
```
CAN ID: response ID для ECU
Data: [Length, 62, PID_H, PID_L, DataA, DataB, ...]
Пример ответа Boost = 185 kPa:
7E8 [05 62 20 2A 07 3A 00 00]
│ │ │ │ │ │
│ │ └───┴──┴───┴── PID + Data: 0x073A = 1850, *0.1 = 185.0 kPa
│ └────────────── Service 0x62 (positive response)
└───────────────── Length: 5 bytes
```
### Negative Response
Если PID не поддерживается:
```
7E8 [03 7F 22 31 00 00 00 00]
│ │ │ │
│ │ │ └── Error code 0x31 (Request Out of Range)
│ │ └───── Service that failed (0x22)
│ └──────── Negative response indicator (0x7F)
└─────────── Length: 3 bytes
```
---
## UDS PIDs — Engine ECU (0x7E0 → 0x7E8)
### PID 0x202A — Boost Pressure Actual
| Параметр | Значение |
|----------|----------|
| Request | `7E0 [03 22 20 2A 55 55 55 55]` |
| Response | `7E8 [05 62 20 2A XX YY 00 00]` |
| Formula | `(A * 256 + B) * 0.1` (kPa) |
| Min | 0 kPa |
| Max | 400 kPa |
| Bytes | 2 |
**Примеры:**
```
Boost = 100 kPa (атм): 7E8 [05 62 20 2A 03 E8 00 00] (0x03E8 = 1000)
Boost = 150 kPa: 7E8 [05 62 20 2A 05 DC 00 00] (0x05DC = 1500)
Boost = 200 kPa: 7E8 [05 62 20 2A 07 D0 00 00] (0x07D0 = 2000)
Boost = 250 kPa (1.5 bar): 7E8 [05 62 20 2A 09 C4 00 00] (0x09C4 = 2500)
```
### PID 0x2029 — Boost Pressure Target
| Параметр | Значение |
|----------|----------|
| Request | `7E0 [03 22 20 29 55 55 55 55]` |
| Response | `7E8 [05 62 20 29 XX YY 00 00]` |
| Formula | `(A * 256 + B) * 0.1` (kPa) |
| Min | 0 kPa |
| Max | 400 kPa |
| Bytes | 2 |
### PID 0xF423 — Fuel Rail Pressure
| Параметр | Значение |
|----------|----------|
| Request | `7E0 [03 22 F4 23 55 55 55 55]` |
| Response | `7E8 [05 62 F4 23 XX YY 00 00]` |
| Formula | `(A * 256 + B) * 10` (kPa) |
| Min | 0 kPa |
| Max | 25000 kPa (250 bar) |
| Bytes | 2 |
**Примеры:**
```
FRP = 5000 kPa (50 bar): 7E8 [05 62 F4 23 01 F4 00 00] (0x01F4 = 500)
FRP = 15000 kPa (150 bar): 7E8 [05 62 F4 23 05 DC 00 00] (0x05DC = 1500)
FRP = 20000 kPa (200 bar): 7E8 [05 62 F4 23 07 D0 00 00] (0x07D0 = 2000)
```
### PID 0x10C0 — Lambda Actual
| Параметр | Значение |
|----------|----------|
| Request | `7E0 [03 22 10 C0 55 55 55 55]` |
| Response | `7E8 [05 62 10 C0 XX YY 00 00]` |
| Formula | `(A * 256 + B) * 0.000977` (λ) |
| Min | 0.5 λ |
| Max | 2.0 λ |
| Bytes | 2 |
**Примеры:**
```
Lambda = 1.0 (stoich): 7E8 [05 62 10 C0 04 00 00 00] (0x0400 = 1024 → 1.000)
Lambda = 0.8 (rich): 7E8 [05 62 10 C0 03 33 00 00] (0x0333 = 819 → 0.800)
Lambda = 1.1 (lean): 7E8 [05 62 10 C0 04 66 00 00] (0x0466 = 1126 → 1.100)
```
**Формула кодирования:** `raw = lambda / 0.000977`
### PID 0x1456 — Lambda Target
| Параметр | Значение |
|----------|----------|
| Request | `7E0 [03 22 14 56 55 55 55 55]` |
| Response | `7E8 [05 62 14 56 XX YY 00 00]` |
| Formula | `(A * 256 + B) * 0.000977` (λ) |
### PID 0x437C — Torque Actual
| Параметр | Значение |
|----------|----------|
| Request | `7E0 [03 22 43 7C 55 55 55 55]` |
| Response | `7E8 [05 62 43 7C XX YY 00 00]` |
| Formula | `(A * 256 + B) * 0.1` (Nm) |
| Min | 0 Nm |
| Max | 500 Nm |
| Bytes | 2 |
**Примеры:**
```
Torque = 50 Nm: 7E8 [05 62 43 7C 01 F4 00 00] (0x01F4 = 500)
Torque = 200 Nm: 7E8 [05 62 43 7C 07 D0 00 00] (0x07D0 = 2000)
Torque = 350 Nm: 7E8 [05 62 43 7C 0D AC 00 00] (0x0DAC = 3500)
```
### PID 0x39A2 — Wastegate Position Actual
| Параметр | Значение |
|----------|----------|
| Request | `7E0 [03 22 39 A2 55 55 55 55]` |
| Response | `7E8 [05 62 39 A2 XX YY 00 00]` |
| Formula | `(A * 256 + B) * 0.01` (%) |
| Min | 0% |
| Max | 100% |
| Bytes | 2 |
**Примеры:**
```
WG = 0% (closed): 7E8 [05 62 39 A2 00 00 00 00]
WG = 50%: 7E8 [05 62 39 A2 13 88 00 00] (0x1388 = 5000)
WG = 100% (open): 7E8 [05 62 39 A2 27 10 00 00] (0x2710 = 10000)
```
### PID 0x39A3 — Wastegate Position Target
Аналогично 0x39A2, Request: `7E0 [03 22 39 A3 55 55 55 55]`
### PID 0x2004 — Ignition Timing
| Параметр | Значение |
|----------|----------|
| Request | `7E0 [03 22 20 04 55 55 55 55]` |
| Response | `7E8 [05 62 20 04 XX YY 00 00]` |
| Formula | `signed(A * 256 + B) * 0.01` (°) |
| Min | -10° |
| Max | 50° |
| Bytes | 2 (signed) |
**Примеры:**
```
Timing = 0°: 7E8 [05 62 20 04 00 00 00 00]
Timing = 10°: 7E8 [05 62 20 04 03 E8 00 00] (0x03E8 = 1000)
Timing = 25°: 7E8 [05 62 20 04 09 C4 00 00] (0x09C4 = 2500)
Timing = -5°: 7E8 [05 62 20 04 FE 0C 00 00] (0xFE0C = -500 signed)
```
**Формула кодирования (signed):**
```python
raw = int(timing * 100)
if raw < 0:
raw = 0x10000 + raw # Two's complement
A = (raw >> 8) & 0xFF
B = raw & 0xFF
```
### PID 0x200A-0x200D — Timing Correction Cylinders 1-4
| Параметр | Значение |
|----------|----------|
| Request | `7E0 [03 22 20 0A 55 55 55 55]` (Cyl 1) |
| Response | `7E8 [05 62 20 0A XX YY 00 00]` |
| Formula | `signed(A * 256 + B) * 0.01` (°) |
| Min | -15° |
| Max | 5° |
| Bytes | 2 (signed) |
**Примеры (Knock retard usually negative):**
```
Cyl1 TC = 0°: 7E8 [05 62 20 0A 00 00 00 00]
Cyl1 TC = -2°: 7E8 [05 62 20 0A FF 38 00 00] (0xFF38 = -200 signed)
Cyl1 TC = -5°: 7E8 [05 62 20 0A FE 0C 00 00] (0xFE0C = -500 signed)
```
---
## UDS PIDs — Instrument Cluster (0x714 → 0x77E)
### PID 0x22D1 — RPM from Cluster
| Параметр | Значение |
|----------|----------|
| Request | `714 [03 22 22 D1 55 55 55 55]` |
| Response | `77E [05 62 22 D1 XX YY 00 00]` |
| Formula | `(A * 256 + B) / 4` (RPM) |
| Min | 0 RPM |
| Max | 8000 RPM |
| Bytes | 2 |
**Примеры:**
```
RPM = 850: 77E [05 62 22 D1 0D 48 00 00] (0x0D48 = 3400, /4 = 850)
RPM = 3000: 77E [05 62 22 D1 2E E0 00 00] (0x2EE0 = 12000, /4 = 3000)
```
### PID 0x224D — Ambient Light Sensor
| Параметр | Значение |
|----------|----------|
| Request | `714 [03 22 22 4D 55 55 55 55]` |
| Response | `77E [04 62 22 4D XX 00 00 00]` |
| Formula | `A` (0-255, 0=dark, 255=bright) |
| Bytes | 1 |
**Примеры:**
```
Night: 77E [04 62 22 4D 10 00 00 00] (0x10 = 16)
Daytime: 77E [04 62 22 4D E0 00 00 00] (0xE0 = 224)
```
---
## UDS PIDs — DSG Transmission (0x7E1 → 0x7E9)
### PID 0x3816 — Current Gear
| Параметр | Значение |
|----------|----------|
| Request | `7E1 [03 22 38 16 55 55 55 55]` |
| Response | `7E9 [04 62 38 16 XX 00 00 00]` |
| Formula | Lookup table |
| Bytes | 1 |
**Gear mapping:**
```
0x00 = Neutral (0)
0x0C = Reverse (-1)
0x02 = 1st gear
0x04 = 2nd gear
0x06 = 3rd gear
0x08 = 4th gear
0x0A = 5th gear
0x0E = 6th gear
0x10 = 7th gear
```
**Примеры:**
```
Neutral: 7E9 [04 62 38 16 00 00 00 00]
Reverse: 7E9 [04 62 38 16 0C 00 00 00]
3rd gear: 7E9 [04 62 38 16 06 00 00 00]
6th gear: 7E9 [04 62 38 16 0E 00 00 00]
```
### PID 0x3815 — Gear Selector Position (PRNDM)
| Параметр | Значение |
|----------|----------|
| Request | `7E1 [03 22 38 15 55 55 55 55]` |
| Response | `7E9 [04 62 38 15 XX 00 00 00]` |
| Formula | Lookup table |
| Bytes | 1 |
**Mode mapping:**
```
0x00 = P (Park)
0x01 = R (Reverse)
0x02 = N (Neutral)
0x03 = D (Drive)
0x04 = S (Sport)
0x05 = M (Manual)
```
**Примеры:**
```
Park: 7E9 [04 62 38 15 00 00 00 00]
Drive: 7E9 [04 62 38 15 03 00 00 00]
Sport: 7E9 [04 62 38 15 04 00 00 00]
Manual: 7E9 [04 62 38 15 05 00 00 00]
```
### PID 0x1940 — Transmission Oil Temperature
| Параметр | Значение |
|----------|----------|
| Request | `7E1 [03 22 19 40 55 55 55 55]` |
| Response | `7E9 [04 62 19 40 XX 00 00 00]` |
| Formula | `A - 40` (°C) |
| Min | -40°C |
| Max | 150°C |
| Bytes | 1 |
**Примеры:**
```
Temp = 80°C: 7E9 [04 62 19 40 78 00 00 00] (0x78 = 120)
Temp = 100°C: 7E9 [04 62 19 40 8C 00 00 00] (0x8C = 140)
```
---
## Таблица быстрого референса
### OBD2 Request/Response Summary
| PID | Request | Response Example | Value |
|-----|---------|------------------|-------|
| 0x04 | `7DF 02 01 04` | `7E8 03 41 04 80` | 50% load |
| 0x05 | `7DF 02 01 05` | `7E8 03 41 05 82` | 90°C coolant |
| 0x0C | `7DF 02 01 0C` | `7E8 04 41 0C 1F 40` | 2000 RPM |
| 0x0D | `7DF 02 01 0D` | `7E8 03 41 0D 78` | 120 km/h |
| 0x11 | `7DF 02 01 11` | `7E8 03 41 11 40` | 25% throttle |
| 0x2F | `7DF 02 01 2F` | `7E8 03 41 2F 80` | 50% fuel |
| 0x5C | `7DF 02 01 5C` | `7E8 03 41 5C 96` | 110°C oil |
### UDS Request/Response Summary
| ECU | PID | Request | Response Example | Value |
|-----|-----|---------|------------------|-------|
| Engine | 0x202A | `7E0 03 22 20 2A` | `7E8 05 62 20 2A 07 D0` | 200 kPa boost |
| Engine | 0x437C | `7E0 03 22 43 7C` | `7E8 05 62 43 7C 0D AC` | 350 Nm torque |
| Engine | 0x10C0 | `7E0 03 22 10 C0` | `7E8 05 62 10 C0 04 00` | λ=1.0 |
| Cluster | 0x22D1 | `714 03 22 22 D1` | `77E 05 62 22 D1 1F 40` | 2000 RPM |
| DSG | 0x3816 | `7E1 03 22 38 16` | `7E9 04 62 38 16 06` | 3rd gear |
| DSG | 0x3815 | `7E1 03 22 38 15` | `7E9 04 62 38 15 03` | Drive mode |