>
Diagnostics over IP
Последнее изменение:
ISO 13400 (DoIP — Diagnostics over IP) — стандарт диагностики автомобилей через Ethernet. Используется в современных автомобилях для высокоскоростной диагностики и обновления ПО. ScanDoc поддерживает DoIP через встроенный Ethernet-адаптер.
Перед использованием команд DoIP необходимо настроить параметры через SET_CONFIG.
| Параметр | Значение | Описание | По умолчанию |
|---|---|---|---|
| ISO13400_SOURCE_ADDR | 0x8100 | Логический адрес тестера (SA). Обычно 0x0E00-0x0EFF. | 0x0E00 |
| ISO13400_TARGET_ADDR | 0x8101 | Логический адрес ЭБУ (TA). Зависит от автомобиля. | — |
| ISO13400_ECU_IP_ADDR | 0x8102 | IP-адрес шлюза/ЭБУ (4 байта, big-endian) | — |
| ISO13400_ECU_TCP_PORT | 0x8103 | TCP-порт для подключения | 13400 |
| ISO13400_T_TCP_INITIAL | 0x8104 | Таймаут неактивности до routing activation (мс) | 2000 |
| ISO13400_T_TCP_GENERAL | 0x8105 | Общий таймаут неактивности TCP (мс) | 300000 |
| ISO13400_T_DIAG_MSG | 0x8106 | Таймаут ожидания диагностического ответа (мс) | 2000 |
| ISO13400_ACTIVATION_TYPE | 0x8107 | Тип активации маршрутизации (0 — default, 1 — WWH-OBD) | 0 |
Отправляет широковещательный UDP-запрос для обнаружения автомобилей с DoIP в локальной сети. Результаты сохраняются во внутреннем буфере и доступны через ISO13400_GET_VEHICLE_INFO.
| IoctlID | 0x8110 |
| pInput | NULL или unsigned long* — таймаут поиска (мс) |
| pOutput | unsigned long* — количество найденных автомобилей |
#include "j2534_dll.hpp"
unsigned long DeviceID; // Получен от PassThruOpen
unsigned long timeout = 3000; // 3 секунды на поиск
unsigned long vehicleCount = 0;
long ret;
ret = PassThruIoctl(DeviceID, ISO13400_DISCOVER_VEHICLES, &timeout, &vehicleCount);
if (ret == STATUS_NOERROR)
{
printf("Найдено автомобилей: %lu\n", vehicleCount);
}
val result = j2534.ptIoctl(deviceID, ISO13400_DISCOVER_VEHICLES, 3000, null)
if (result.status == STATUS_NOERROR) {
Log.i("DoIP", "Найдено автомобилей: ${result.outputValue}")
}
from ctypes import *
timeout = c_ulong(3000) # 3 секунды
vehicle_count = c_ulong(0)
ret = j2534.PassThruIoctl(device_id, ISO13400_DISCOVER_VEHICLES, byref(timeout), byref(vehicle_count))
if ret == 0:
print(f"Найдено автомобилей: {vehicle_count.value}")
uint timeout = 3000;
uint vehicleCount;
int ret = J2534.PassThruIoctl(deviceId, ISO13400_DISCOVER_VEHICLES, ref timeout, out vehicleCount);
if (ret == 0)
Console.WriteLine($"Найдено автомобилей: {vehicleCount}");
Возвращает информацию о найденном автомобиле: VIN, логический адрес, IP-адрес шлюза. Вызывается после ISO13400_DISCOVER_VEHICLES.
| IoctlID | 0x8111 |
| pInput | unsigned long* — индекс автомобиля (0..N-1) |
| pOutput | DOIP_VEHICLE_INFO* — структура с информацией |
typedef struct {
char VIN[18]; // VIN-код (17 символов + '\0')
unsigned short LogicalAddr; // Логический адрес шлюза
unsigned char GID[6]; // Group Identification (опционально)
unsigned char EID[6]; // Entity Identification (MAC-адрес)
unsigned long IPAddr; // IP-адрес (big-endian)
} DOIP_VEHICLE_INFO;
#include "j2534_dll.hpp"
unsigned long DeviceID;
unsigned long index = 0; // Первый найденный автомобиль
DOIP_VEHICLE_INFO vehicleInfo;
long ret;
ret = PassThruIoctl(DeviceID, ISO13400_GET_VEHICLE_INFO, &index, &vehicleInfo);
if (ret == STATUS_NOERROR)
{
printf("VIN: %s\n", vehicleInfo.VIN);
printf("Логический адрес: 0x%04X\n", vehicleInfo.LogicalAddr);
printf("IP: %d.%d.%d.%d\n",
(vehicleInfo.IPAddr >> 24) & 0xFF,
(vehicleInfo.IPAddr >> 16) & 0xFF,
(vehicleInfo.IPAddr >> 8) & 0xFF,
vehicleInfo.IPAddr & 0xFF);
}
val result = j2534.ptGetVehicleInfo(deviceID, 0) // Первый автомобиль
if (result.status == STATUS_NOERROR) {
Log.i("DoIP", "VIN: ${result.vin}")
Log.i("DoIP", "Логический адрес: 0x${result.logicalAddr.toString(16).uppercase()}")
Log.i("DoIP", "IP: ${result.ipAddrString}")
}
from ctypes import *
class DOIP_VEHICLE_INFO(Structure):
_fields_ = [
("VIN", c_char * 18),
("LogicalAddr", c_ushort),
("GID", c_ubyte * 6),
("EID", c_ubyte * 6),
("IPAddr", c_ulong)
]
index = c_ulong(0)
vehicle_info = DOIP_VEHICLE_INFO()
ret = j2534.PassThruIoctl(device_id, ISO13400_GET_VEHICLE_INFO, byref(index), byref(vehicle_info))
if ret == 0:
ip = vehicle_info.IPAddr
print(f"VIN: {vehicle_info.VIN.decode()}")
print(f"Логический адрес: 0x{vehicle_info.LogicalAddr:04X}")
print(f"IP: {(ip >> 24) & 0xFF}.{(ip >> 16) & 0xFF}.{(ip >> 8) & 0xFF}.{ip & 0xFF}")
[StructLayout(LayoutKind.Sequential)]
public struct DOIP_VEHICLE_INFO {
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 18)]
public byte[] VIN;
public ushort LogicalAddr;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] GID;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 6)]
public byte[] EID;
public uint IPAddr;
}
uint index = 0;
DOIP_VEHICLE_INFO vehicleInfo;
int ret = J2534.PassThruIoctl(deviceId, ISO13400_GET_VEHICLE_INFO, ref index, out vehicleInfo);
if (ret == 0)
{
Console.WriteLine($"VIN: {Encoding.ASCII.GetString(vehicleInfo.VIN).TrimEnd('\0')}");
Console.WriteLine($"Логический адрес: 0x{vehicleInfo.LogicalAddr:X4}");
var ip = vehicleInfo.IPAddr;
Console.WriteLine($"IP: {(ip >> 24) & 0xFF}.{(ip >> 16) & 0xFF}.{(ip >> 8) & 0xFF}.{ip & 0xFF}");
}
Устанавливает TCP-соединение с шлюзом автомобиля. IP-адрес и порт должны быть предварительно настроены через SET_CONFIG или получены из ISO13400_GET_VEHICLE_INFO.
| IoctlID | 0x8112 |
| pInput | NULL (использует параметры из SET_CONFIG) или DOIP_CONNECT_PARAMS* |
| pOutput | NULL |
#include "j2534_dll.hpp"
unsigned long ChannelID; // Получен от PassThruConnect для ISO13400
long ret;
// Параметры уже настроены через SET_CONFIG
ret = PassThruIoctl(ChannelID, ISO13400_CONNECT_TCP, NULL, NULL);
if (ret == STATUS_NOERROR)
{
printf("TCP-соединение установлено\n");
}
else
{
char error[256];
PassThruGetLastError(error);
printf("Ошибка подключения: %s\n", error);
}
val result = j2534.ptIoctl(channelID, ISO13400_CONNECT_TCP, 0, null)
if (result.status == STATUS_NOERROR) {
Log.i("DoIP", "TCP-соединение установлено")
} else {
Log.e("DoIP", "Ошибка TCP-подключения: ${result.status}")
}
ret = j2534.PassThruIoctl(channel_id, ISO13400_CONNECT_TCP, None, None)
if ret == 0:
print("TCP-соединение установлено")
else:
print(f"Ошибка TCP-подключения: {ret}")
int ret = J2534.PassThruIoctl(channelId, ISO13400_CONNECT_TCP, IntPtr.Zero, IntPtr.Zero);
if (ret == 0)
Console.WriteLine("TCP-соединение установлено");
else
Console.WriteLine($"Ошибка TCP-подключения: {ret}");
Отправляет запрос Routing Activation для получения доступа к диагностике. Тип активации задаётся параметром ISO13400_ACTIVATION_TYPE.
| IoctlID | 0x8113 |
| pInput | NULL или unsigned long* — тип активации (0 — default, 1 — WWH-OBD) |
| pOutput | unsigned long* — код ответа от шлюза |
| 0x00 | Routing activation denied — Unknown source address |
| 0x01 | Routing activation denied — No available socket |
| 0x02 | Routing activation denied — Socket already active |
| 0x03 | Routing activation denied — Missing authentication |
| 0x04 | Routing activation denied — Rejected confirmation |
| 0x05 | Routing activation denied — Unsupported activation type |
| 0x10 | Routing successfully activated |
| 0x11 | Routing will be activated — confirmation required |
#include "j2534_dll.hpp"
unsigned long ChannelID;
unsigned long activationType = 0; // Default
unsigned long responseCode = 0;
long ret;
ret = PassThruIoctl(ChannelID, ISO13400_ACTIVATE_ROUTING, &activationType, &responseCode);
if (ret == STATUS_NOERROR)
{
if (responseCode == 0x10)
printf("Маршрутизация активирована успешно\n");
else
printf("Код ответа: 0x%02X\n", responseCode);
}
val result = j2534.ptIoctl(channelID, ISO13400_ACTIVATE_ROUTING, 0, null)
if (result.status == STATUS_NOERROR) {
if (result.outputValue == 0x10) {
Log.i("DoIP", "Маршрутизация активирована успешно")
} else {
Log.w("DoIP", "Код ответа: 0x${result.outputValue.toString(16)}")
}
}
from ctypes import *
activation_type = c_ulong(0) # Default
response_code = c_ulong(0)
ret = j2534.PassThruIoctl(channel_id, ISO13400_ACTIVATE_ROUTING, byref(activation_type), byref(response_code))
if ret == 0:
if response_code.value == 0x10:
print("Маршрутизация активирована успешно")
else:
print(f"Код ответа: 0x{response_code.value:02X}")
uint activationType = 0; // Default
uint responseCode;
int ret = J2534.PassThruIoctl(channelId, ISO13400_ACTIVATE_ROUTING, ref activationType, out responseCode);
if (ret == 0)
{
if (responseCode == 0x10)
Console.WriteLine("Маршрутизация активирована успешно");
else
Console.WriteLine($"Код ответа: 0x{responseCode:X2}");
}
| Код | Описание | Возможные причины и решения |
|---|---|---|
| STATUS_NOERROR | Функция выполнена успешно | — |
| ERR_DEVICE_NOT_CONNECTED | Нет соединения с адаптером |
|
| ERR_NOT_SUPPORTED | DoIP не поддерживается |
|
| ERR_TIMEOUT | Таймаут операции |
|
| ERR_INVALID_CHANNEL_ID | Недействительный идентификатор канала |
|
| ERR_FAILED | Неопределённая ошибка |
|
#include "j2534_dll.hpp"
#include <stdio.h>
int DoIPDiagnosticSession(void)
{
unsigned long DeviceID, ChannelID;
long ret;
// 1. Открываем устройство
ret = PassThruOpen(NULL, &DeviceID);
if (ret != STATUS_NOERROR) return ret;
// 2. Проверяем поддержку Ethernet
SCONFIG cfg[1];
SCONFIG_LIST cfgList = {1, cfg};
cfg[0].Parameter = ETHERNET_NDIS_SUPPORTED;
ret = PassThruIoctl(DeviceID, GET_DEVICE_INFO, &cfgList, NULL);
if (ret != STATUS_NOERROR || cfg[0].Value == 0)
{
printf("DoIP не поддерживается\n");
PassThruClose(DeviceID);
return -1;
}
// 3. Поиск автомобилей
unsigned long timeout = 3000;
unsigned long vehicleCount = 0;
ret = PassThruIoctl(DeviceID, ISO13400_DISCOVER_VEHICLES, &timeout, &vehicleCount);
if (ret != STATUS_NOERROR || vehicleCount == 0)
{
printf("Автомобили не найдены\n");
PassThruClose(DeviceID);
return -1;
}
printf("Найдено автомобилей: %lu\n", vehicleCount);
// 4. Получаем информацию о первом автомобиле
unsigned long index = 0;
DOIP_VEHICLE_INFO vehicleInfo;
ret = PassThruIoctl(DeviceID, ISO13400_GET_VEHICLE_INFO, &index, &vehicleInfo);
if (ret != STATUS_NOERROR)
{
PassThruClose(DeviceID);
return ret;
}
printf("VIN: %s\n", vehicleInfo.VIN);
// 5. Настраиваем параметры DoIP
SCONFIG doipCfg[3];
SCONFIG_LIST doipCfgList = {3, doipCfg};
doipCfg[0].Parameter = ISO13400_SOURCE_ADDR;
doipCfg[0].Value = 0x0E00;
doipCfg[1].Parameter = ISO13400_TARGET_ADDR;
doipCfg[1].Value = vehicleInfo.LogicalAddr;
doipCfg[2].Parameter = ISO13400_ECU_IP_ADDR;
doipCfg[2].Value = vehicleInfo.IPAddr;
// 6. Открываем канал ISO 13400
ret = PassThruConnect(DeviceID, ISO13400_PS, 0, 0, &ChannelID);
if (ret != STATUS_NOERROR)
{
PassThruClose(DeviceID);
return ret;
}
ret = PassThruIoctl(ChannelID, SET_CONFIG, &doipCfgList, NULL);
if (ret != STATUS_NOERROR)
{
PassThruDisconnect(ChannelID);
PassThruClose(DeviceID);
return ret;
}
// 7. Устанавливаем TCP-соединение
ret = PassThruIoctl(ChannelID, ISO13400_CONNECT_TCP, NULL, NULL);
if (ret != STATUS_NOERROR)
{
printf("Ошибка TCP-подключения\n");
PassThruDisconnect(ChannelID);
PassThruClose(DeviceID);
return ret;
}
// 8. Активируем маршрутизацию
unsigned long activationType = 0;
unsigned long responseCode = 0;
ret = PassThruIoctl(ChannelID, ISO13400_ACTIVATE_ROUTING, &activationType, &responseCode);
if (ret != STATUS_NOERROR || responseCode != 0x10)
{
printf("Ошибка активации маршрутизации: 0x%02X\n", responseCode);
PassThruDisconnect(ChannelID);
PassThruClose(DeviceID);
return ret;
}
printf("DoIP-соединение установлено!\n");
// 9. Теперь можно отправлять диагностические запросы
// через PassThruWriteMsgs / PassThruReadMsgs
// Закрываем соединение
PassThruDisconnect(ChannelID);
PassThruClose(DeviceID);
return 0;
}
suspend fun connectDoIP(): Boolean {
// 1. Открываем устройство
val openResult = j2534.ptOpen(null)
if (openResult.status != STATUS_NOERROR) return false
val deviceID = openResult.deviceId
// 2. Поиск автомобилей
val discoverResult = j2534.ptIoctl(deviceID, ISO13400_DISCOVER_VEHICLES, 3000, null)
if (discoverResult.status != STATUS_NOERROR || discoverResult.outputValue == 0) {
Log.e("DoIP", "Автомобили не найдены")
j2534.ptClose(deviceID)
return false
}
Log.i("DoIP", "Найдено автомобилей: ${discoverResult.outputValue}")
// 3. Получаем информацию о первом автомобиле
val infoResult = j2534.ptGetVehicleInfo(deviceID, 0)
if (infoResult.status != STATUS_NOERROR) {
j2534.ptClose(deviceID)
return false
}
Log.i("DoIP", "VIN: ${infoResult.vin}")
// 4. Открываем канал и настраиваем параметры
val connectResult = j2534.ptConnect(deviceID, ISO13400_PS, 0u, 0u)
if (connectResult.status != STATUS_NOERROR) {
j2534.ptClose(deviceID)
return false
}
val channelID = connectResult.channelId
val params = listOf(
PtConfig(ISO13400_SOURCE_ADDR, 0x0E00u),
PtConfig(ISO13400_TARGET_ADDR, infoResult.logicalAddr.toUInt()),
PtConfig(ISO13400_ECU_IP_ADDR, infoResult.ipAddr)
)
j2534.ptIoctl(channelID, SET_CONFIG, params.size, params.toByteArray())
// 5. TCP-подключение
val tcpResult = j2534.ptIoctl(channelID, ISO13400_CONNECT_TCP, 0, null)
if (tcpResult.status != STATUS_NOERROR) {
Log.e("DoIP", "Ошибка TCP-подключения")
j2534.ptDisconnect(channelID)
j2534.ptClose(deviceID)
return false
}
// 6. Активация маршрутизации
val routingResult = j2534.ptIoctl(channelID, ISO13400_ACTIVATE_ROUTING, 0, null)
if (routingResult.status != STATUS_NOERROR || routingResult.outputValue != 0x10) {
Log.e("DoIP", "Ошибка активации: 0x${routingResult.outputValue.toString(16)}")
j2534.ptDisconnect(channelID)
j2534.ptClose(deviceID)
return false
}
Log.i("DoIP", "DoIP-соединение установлено!")
return true
}
from ctypes import *
def connect_doip():
# 1. Открываем устройство
device_id = c_ulong()
ret = j2534.PassThruOpen(None, byref(device_id))
if ret != 0:
return False
# 2. Поиск автомобилей
timeout = c_ulong(3000)
vehicle_count = c_ulong(0)
ret = j2534.PassThruIoctl(device_id, ISO13400_DISCOVER_VEHICLES, byref(timeout), byref(vehicle_count))
if ret != 0 or vehicle_count.value == 0:
print("Автомобили не найдены")
j2534.PassThruClose(device_id)
return False
print(f"Найдено автомобилей: {vehicle_count.value}")
# 3. Получаем информацию о первом автомобиле
index = c_ulong(0)
vehicle_info = DOIP_VEHICLE_INFO()
ret = j2534.PassThruIoctl(device_id, ISO13400_GET_VEHICLE_INFO, byref(index), byref(vehicle_info))
if ret != 0:
j2534.PassThruClose(device_id)
return False
print(f"VIN: {vehicle_info.VIN.decode()}")
# 4. Открываем канал ISO 13400
channel_id = c_ulong()
ret = j2534.PassThruConnect(device_id, ISO13400_PS, 0, 0, byref(channel_id))
if ret != 0:
j2534.PassThruClose(device_id)
return False
# 5. Настраиваем параметры DoIP
config = (SCONFIG * 3)()
config[0].Parameter = ISO13400_SOURCE_ADDR
config[0].Value = 0x0E00
config[1].Parameter = ISO13400_TARGET_ADDR
config[1].Value = vehicle_info.LogicalAddr
config[2].Parameter = ISO13400_ECU_IP_ADDR
config[2].Value = vehicle_info.IPAddr
config_list = SCONFIG_LIST()
config_list.NumOfParams = 3
config_list.ConfigPtr = config
ret = j2534.PassThruIoctl(channel_id, SET_CONFIG, byref(config_list), None)
if ret != 0:
j2534.PassThruDisconnect(channel_id)
j2534.PassThruClose(device_id)
return False
# 6. TCP-подключение
ret = j2534.PassThruIoctl(channel_id, ISO13400_CONNECT_TCP, None, None)
if ret != 0:
print("Ошибка TCP-подключения")
j2534.PassThruDisconnect(channel_id)
j2534.PassThruClose(device_id)
return False
# 7. Активация маршрутизации
activation_type = c_ulong(0)
response_code = c_ulong(0)
ret = j2534.PassThruIoctl(channel_id, ISO13400_ACTIVATE_ROUTING, byref(activation_type), byref(response_code))
if ret != 0 or response_code.value != 0x10:
print(f"Ошибка активации: 0x{response_code.value:02X}")
j2534.PassThruDisconnect(channel_id)
j2534.PassThruClose(device_id)
return False
print("DoIP-соединение установлено!")
return True
public bool ConnectDoIP()
{
// 1. Открываем устройство
uint deviceId;
int ret = J2534.PassThruOpen(null, out deviceId);
if (ret != 0) return false;
// 2. Поиск автомобилей
uint timeout = 3000;
uint vehicleCount;
ret = J2534.PassThruIoctl(deviceId, ISO13400_DISCOVER_VEHICLES, ref timeout, out vehicleCount);
if (ret != 0 || vehicleCount == 0)
{
Console.WriteLine("Автомобили не найдены");
J2534.PassThruClose(deviceId);
return false;
}
Console.WriteLine($"Найдено автомобилей: {vehicleCount}");
// 3. Получаем информацию о первом автомобиле
uint index = 0;
DOIP_VEHICLE_INFO vehicleInfo;
ret = J2534.PassThruIoctl(deviceId, ISO13400_GET_VEHICLE_INFO, ref index, out vehicleInfo);
if (ret != 0)
{
J2534.PassThruClose(deviceId);
return false;
}
Console.WriteLine($"VIN: {Encoding.ASCII.GetString(vehicleInfo.VIN).TrimEnd('\0')}");
// 4. Открываем канал ISO 13400
uint channelId;
ret = J2534.PassThruConnect(deviceId, ISO13400_PS, 0, 0, out channelId);
if (ret != 0)
{
J2534.PassThruClose(deviceId);
return false;
}
// 5. Настраиваем параметры DoIP
var configs = new SCONFIG[3];
configs[0] = new SCONFIG { Parameter = ISO13400_SOURCE_ADDR, Value = 0x0E00 };
configs[1] = new SCONFIG { Parameter = ISO13400_TARGET_ADDR, Value = vehicleInfo.LogicalAddr };
configs[2] = new SCONFIG { Parameter = ISO13400_ECU_IP_ADDR, Value = vehicleInfo.IPAddr };
var configList = new SCONFIG_LIST { NumOfParams = 3, ConfigPtr = configs };
ret = J2534.PassThruIoctl(channelId, SET_CONFIG, ref configList, IntPtr.Zero);
if (ret != 0)
{
J2534.PassThruDisconnect(channelId);
J2534.PassThruClose(deviceId);
return false;
}
// 6. TCP-подключение
ret = J2534.PassThruIoctl(channelId, ISO13400_CONNECT_TCP, IntPtr.Zero, IntPtr.Zero);
if (ret != 0)
{
Console.WriteLine("Ошибка TCP-подключения");
J2534.PassThruDisconnect(channelId);
J2534.PassThruClose(deviceId);
return false;
}
// 7. Активация маршрутизации
uint activationType = 0;
uint responseCode;
ret = J2534.PassThruIoctl(channelId, ISO13400_ACTIVATE_ROUTING, ref activationType, out responseCode);
if (ret != 0 || responseCode != 0x10)
{
Console.WriteLine($"Ошибка активации: 0x{responseCode:X2}");
J2534.PassThruDisconnect(channelId);
J2534.PassThruClose(deviceId);
return false;
}
Console.WriteLine("DoIP-соединение установлено!");
return true;
}