> Работа с протоколом диагностики HONDA на OBD1. Хочу поделиться с Вами своими знаниями по этой теме.
 fedorenkoalex92
сообщение 19.02.18 - 12:55
Сообщение #1


Новенький


Группа: .Хондаводы.
Сообщений: 3
Имя: Александр
Город: Кропивницкий
Машина: Honda Civic VTI 92

Репутация: 1



Добрый день всем. Хочу рассказать о своем опыте использования протокола диагностики Honda на OBD1 и поделиться знаниями по этой теме.
Хочу сразу заметить, что все изложенное тестировалось только на европейском Р30 мозгах. В теории все и на других должно быть нормально, так как все отличия что я видел в разного рода программах из сети были не значительны и в основном касались того как считываются данные. В многих примерах первые 5 байт не читались и пропускались, я же их читаю все. Поэтому если во многих примерах вы видите что обороты, например, берут из ячеек 2 и 3, то я беру их с ячеек 7 и 8.

Предыстория.

Итак, началось все с того, что при покупке автомобиля, там была установлена штука, типа бортовика, с экранчиком куда выводилась информация. Внешний вид этой конструкции мне не очень нравился и я решил разобраться в том как все работает и переделать. Было много вариантов как же все сделать. Сначала были варианты немного переписать программу и подключить к другому экрану получше. Были варианты и с разными маленькими OLED, и обычным дисплеем 20*4 и даже с 5 дюймовым Nextion… Это выглядит интересно и можно найти ему достойное место в салоне.Например, можно взять маленький OLED и оно может неплохо стать в место где находятся часы, но часы я трогать не хотел. Сперва был вариант с дисплеем 20 * 4:



И последний вариант который я пробовал это использование дисплея для Ардуино от фирмы Nextion. Выглядело примерно так:



Сейчас, у меня все выглядит так:
- Arduino - общается с ECU, получает от него сырые данные, отправляет по блютуз.
- Устройство на Android, когда оно мне нужно, поставил и использую - подключается к Ардуино, получает данные, обрабатывает и выводит на экран.

Запрос к ECU

Вот так выглядит команда - {0x20,0x05,0x00,0x10,0xCB} - массив с 5 элементов.
Где первая ячейка - команда. В нашем случае это число 32. в НЕХ = 0х20;
Вторая - длина команды - 5 (ячеек то 5);
Третья - сдвиг. Тоесть откуда будем начинать читать. Тут мы начинаем с начала поэтому он будет 0. Дальше он будет 16 (0x10), 32 (0x20) и так дальше;
Четвертая - количество байт, которые будут считаны. Читаем по 16 байт. (в НЕХ = 0х10);
Последняя - это CRC. Считается такой формулой: CRC = 0x0100-(byte[0]+byte[1] +byte[2] +byte[3]);

Всего для считывания данных есть 3 команды.
Код
{0x20,0x05,0x00,0x10,0xCB}; //читаем первые 16 байт
{0x20,0x05,0x10,0x10,0xBB}; //читаем вторые 16 байт
{0x20,0x05,0x20,0x10,0xAB};  //читаем третьи 16 байт


Итак, берем и шлем всю эту штуку в наше ECU.

Код
Serial.write(команда,CMD_SIZE);
Serial.flush();


И ждем ответа. Как дождемся, то записываем ответ в буфер:

Код
for (int i=0; i < BUFFER_SIZE; i++)
{
         buffer_01[i] = Serial.read();
}


где BUFFER_SIZE = 24, а buffer_01 массив размером в BUFFER_SIZE;

Почему 24? Ведь мы просили 16 байт. Дальше рассмотрим как выглядит ответ.

Ответ от ECU

Ответ в нашем случае представляет собой массив размеров 24 байта,
где первые 5 байт - это копия запроса, только уже в десятичной системе:

32, 5, 0, 16, 203 - нам они не нужны

дальше 2 байта это длина ответа не учитывая 5 байт выше(24 - 5 = 19):

0, 19 - это тоже не потребуется
А вот дальше уже идут данные что нам нужны, 16 байт информации, с которой можно получить значения датчиков и прочее. Например будет такое:

5, 95, 0, 0, 0, 0, 0, 0 ,128, 0, 0, 69, 193, 0, 0, 0

И замыкает массив 2 байта - CRC ответа;

Получаем информацию. Что где находиться.

Вначале приведу суммирующую таблицу, где разбираеться ответ от ECU.



Дальше подробней о каждом и параметров. Итак ответ после первого запроса, получили ответ в buffer_01 размером 24 байта.
Буду приводить номер ячейки где находиться и формулу по котором считаеться. Формулы взяты с интернетов, перерыв много форумов и примеров вижу что у всех они однаковые практически.
Дальше будут куски Java кода.

Обороты двигателя
buffer_01[7] и buffer_01[8]

Код
rpm = 1875000/( buffer_01[7] *256 + buffer_01[8]+1);

или
Код
rpm = (1875000 / (buffer_01[7] << 8 | buffer_01[8]));

или
Код
float value = rpmParam1 << 8 & 65280 | rpmParam1 >> 8 & (int) Byte.MAX_VALUE;
rpm = (int) (1875000.0 / (double) value);
rpm = rpm + rpmParam2;


Скорость
buffer_01[9]
без формулы, берем значение как есть

Различные флажки, что включено выключено.
buffer_01[15] - слово с флагами 1;
buffer_01[16] - слово с флагами 2;
buffer_01[17] - слово с флагами 3;
buffer_01[18] - слово с флагами 4;
buffer_01[19] - слово с флагами 5;
buffer_01[20] - слово с флагами 6;
buffer_01[21] - слово с флагами 7;
buffer_01[22] - слово с флагами 8;

В каждой с этих ячеек имееться 8 различных флажков для разных параметров.
Стоит просто перевести значение с любой из ячеек в двоичную систему получим что то типа 0100 0010. Каждый 0 и 1 тут за что-то отвечает. Ниже представлю таблицу. Каждая строка это байт 1 - 8, каждый столбец это номер бита.



Например если взять четвертый байт и 6й бит. (с права на лево) Тут у нас находиться индикатор Check Engine. Тоесть если там будет 1 а не 0 - значить горит CheckEngine.
Проверить каждый с индикаторов можна например маской. Для того же Check Engine это например будет

Код
boolean checkEngine = (word1[18] & 00100000) != 0


По первому ответу это все. Дальше второй. Ответ в buffer_02 размером 24 байта.

Температура ОЖ
buffer_02 [7]

Код
int engineTemp = (int) (155.04149f - (buffer_02 [7] * 3.0414878f) + Math.pow(buffer_02 [7], 2) * 0.03952185f - Math.pow(buffer_02 [7], 3) * 0.00029383913f + Math.pow(buffer_02 [7], 4) * 0.0000010792568f - Math.pow(buffer_02 [7], 5) * 0.0000000015618437f);


Температура воздуха на впуске
buffer_02 [8]

Код
int airTemp = (int) (155.04149f - (buffer_02 [8] * 3.0414878f) + Math.pow(buffer_02 [8], 2) * 0.03952185f - Math.pow(buffer_02 [8], 3) * 0.00029383913f + Math.pow(buffer_02 [8], 4) * 0.0000010792568f - Math.pow(buffer_02 [8], 5) * 0.0000000015618437f);


MAP
buffer_02 [9]

Код
int map = (int) (buffer_02 [9] * 0.716f - 5.0f);


Давление атмосферное (точно не уверен)
buffer_02 [10]

Код
float atmPressure = (float) (buffer_02 [10] * 0.716f - 5.0f);


Положение дроссельной заслонки
buffer_02 [11]

Код
int droccelOpen = ((buffer_02 [11] - 24) / 2);


Датчик O2
buffer_02 [12]

Код
float o2 = (float) (buffer_02 [12] / 51.3f)


Второй датчик O2 - у меня его нет. В теории формула такая же самая.

Напряжение
buffer_02 [14]

Код
float batteryVoltage = (float) buffer_02 [14]/ 10.45f


Нагрузка генератора или что то типа того
buffer_02 [15]

Код
float generatorVoltage = (float) (buffer_02 [15] / 2.55f)


ELD Load
buffer_02 [16]

Код
float eldLoad = 77.06f - (float) (buffer_02 [16] / 2.5371f)


EGR Position
buffer_02 [18]

Код
float egrPos = (float) (buffer_02 [18] / 51.3f)


С этим тоже все. Дальше третий. Ответ в buffer_03 размером 24 байта.

Кратковременная коррекция
buffer_03[7]

Код
int stCor = (int) ((buffer_03[7] / 128.0f - 1.0f) * 100.0f)


Долгосрочная коррекция
buffer_03[9]

Код
int ltCor = (int) ((buffer_03[9] / 128.0f - 1.0f) * 100.0f)


2 байта время впрыска
buffer_03[11] и buffer_03[12]

Код
double injectionTime = ((buffer_03[11] * 256.0) + buffer_03[12]) / 250.0;


ING (вроде Угол опережения зажигания)
buffer_03[13]

Код
int ign = (int) ((buffer_03[13] - 128.0f) / 2.0f)


ING Limit
buffer_03[14]

Код
int ignLimit = (int) ((buffer_03[14] - 24.0f) / 4.0f);


Дальше идут значения, в которых я еще не очень разобрался)

Valve Idle
buffer_03[15]

Код
float valve = (float) (buffer_03[15] / 2.55f)


Valve EGR
buffer_03[18]

Код
float valve = (float) (buffer_03[18] / 2.55f)


Position Valve EGR
buffer_03[19]

Код
float valve = (float) (valveParam / 2.55f)



С этим все. Дальше поговорим о считывании ошибок.

Ошибки.

Для считывания ошибок есть 2 комманды:

Код
{0x20,0x05,0x40,0x10,0x8B}

и
Код
{0x20,0x05,0x50,0x10,0x7B}


Схема та же самая, шлем, получаем ответ.

Теперь как разобрать где какая ошибка.
Как и ранее у нас будет 16 байт информации что нам нужна, остальное можно не трогать.

В каждом полученом байте из 16 содержится статус сразу о 2ух ошибках. Одна ошибка это пол байта. Вторые пол байта это уже следущая ошибка.

Итак, номера ошибок начинаються с 0.
Допустим берем первый байт из тех что нам нужно.
В первом байте содержиться информация о ошибке 0 и 1
дальше во втором 2 и 3, третьем 4 и 5 и так дальше.

Так я проверяю первый и второй полубайт есть
Код
boolean isError1Valid = ((int) errorWordVal & 15) != 0;

и
Код
boolean isError2Valid = ((int) errorWordVal & 240) != 0;


Вот таблица для примера



В таблице видно ошибку 43.Почему не 42? Потому что номера битов отсчитываются справа налево.
Проверку ошибок как раз удалось проверить в боевых условия. Когда была проблема с топливным фильтром, как раз светилась злосщасная ошибка 43 - Система подачи топлива неисправна. После починки и сброса все стало нормально.

С этим тоже все. У нас есть код ошибки. Теперь нужно просто посмотреть описание ошибки по коду. Например, вот здесь.



В таблице нет ошибки с кодом 0 - ошибка ECU.

Всем спасибо за внимание, надеюсь это будет кому то полезно.

Из проблем сейчас есть такая штука, что на высоких оборотах RPM высчитывается как то не правильно. На данный момент тестирую третью формулу. Может с ней будет получше. Если не поможет буду искать еще варианты.

Так же если Вам будет интересно, то могу еще написать о своей текущей реализации программы(Ардуино + устройство на Андроид связываются по блютуз) и привести схему подключения.

Сообщение отредактировал fedorenkoalex92 - 19.02.18 - 14:10



           
 TOL
сообщение 26.02.18 - 03:23
Сообщение #2


Хондавод
*

Группа: .Хондаводы.
Сообщений: 374
Имя: Анатолий
Город: пригород ПК
Машина: CB+F23a obd2 ecu+T2C5+5lug+16rims

Репутация: 2



вот это мозг ohmy.gif , похвально






®
СВой
           
 timur056
сообщение 12.04.18 - 17:45
Сообщение #3


Новенький


Группа: .Хондаводы.
Сообщений: 1
Имя: Тимур
Город: оренбург
Машина: honda capa

Репутация: нет



А как все это добро подключил к ЭБУ?



           
 TheMike
сообщение 21.06.21 - 17:22
Сообщение #4


Новенький


Группа: .Хондаводы.
Сообщений: 11
Имя: Михаил
Город: Ставрополь
Машина: Honda Accord CE7

Репутация: нет



Для прошивок на основе Crome Gold (протокол QD3):

Тут http://forum.pgmfi.org/viewtopic.php?f=54&t=24512 добрый человек выложил инфу по протоколу QD3.

Сначала подключил ноутбук с Crome через USB-TTL конвертер напрямую на CN2 (перемычка J12 удалена, мозг P06) и диагностика почти сразу заработала (искал правильные настройки соединения). Потом монитором СОМ-порта "подслушал" как идет обмен данными - нужно послать в порт число 70 (0x46) и в ответ получаем 46 байт данных. Как их разбирать - написано по ссылке выше.

Подключил ARDUINO NANO напрямую к мозгу: контакты Arduino RX и TX на разъем CN2.

Прикрепленное изображение


Для дисплея "OLED 0.91 mini display" написал скетч (мой первый для ардуино smile11.gif ) для расчета расхода топлива:


#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <SPI.h>
Adafruit_SSD1306 display(-1);
const int Array_Size = 48; //массив для чтения данных из мозга
const int injrate = 240; //производительность форсунки
byte ecuData[Array_Size];
byte rpmLowRaw = 0; // переменная
byte rpmHighRaw = 0; // переменная
int rpm = 0;
float fuelms = 0; // время впрыска
float fuel = 0; // расход топлива л/час
double TPS = 0; // положение дросселя

void setup()
{
// init display I2C addr 0x3C
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
// очистка буфера дисплея
display.clearDisplay();
// настройка текста дисплея
display.setTextSize(1);
display.setTextColor(WHITE);
display.setCursor(0, 0);
display.println("Honda OBD1 datalogger");
display.println("Crome QD3 protocol");
display.println("TheMike 2021");
//вывод буфера дисплея на экран
display.display();
delay(2000);

// Настройка СОМ порта Arduino
Serial.begin(38400, SERIAL_8N1);

// настройка дисплея
display.clearDisplay();
display.setTextSize(2); //увеличиваем размер текста
display.setTextColor(WHITE);
display.setCursor(0, 0);

}

void loop() {

// очистка массива
for ( int i = 0; i < Array_Size; i++) {
ecuData[i] = 0;
}

Serial.flush();

//отправляем запрос в порт согласно протоколу QD3
Serial.write(70);

display.clearDisplay();
display.setCursor(0, 0);

//задержка для заполнения буфера данными, без нее часто загружался не полный массив данными
delay(100);

//считвает данные из порта в массив
while (Serial.available() > 0) {
for ( int i = 0; i < 45; i++) {
ecuData[i] = Serial.read();
delay(1);
}
}

//обороты двигателя
rpmLowRaw = ecuData[2]; // младший байт оборотов
rpmHighRaw = ecuData[3]; // старший байт оборотов

// если оба байта = 255 то обороты = 0
if ((rpmLowRaw == 255) and (rpmHighRaw == 255)) {
rpm = 0;
}
else {
rpm = floor (1875000 / ((rpmHighRaw * 256) + rpmLowRaw));
};

//время впрыска
rpmLowRaw = ecuData[7]; // младший байт времени впрыска
rpmHighRaw = ecuData[8]; // старший байт времени впрыска

// если оба байта = 0 то и время впрыска = 0
if ((rpmHighRaw == 0) and (rpmLowRaw == 0)) {
fuelms = 0;
}
else {
fuelms = floor (((rpmHighRaw * 256) + rpmLowRaw - 24) / 2.28);
fuelms = fuelms / 100;
};

fuel = floor ((fuelms * injrate * rpm * 0.12) / 600);

rpmLowRaw = ecuData[20]; //скорость в км/час

TPS = (0.472637 * ecuData[24]) - 11.46119; //положение дроссельной заслонки

// если скорость меньше 10 км/час или педаль газа отпущена - показывать расход л/час
// иначе - расход л/100км
if ((rpmLowRaw < 10) or (TPS < 2) ){
fuel = fuel / 100;
display.println(fuel);
display.println("L/H");
}
else {
fuel = fuel / rpmLowRaw;
display.println(fuel);
display.println("L/100km");
};

display.display();
delay(10);

}



Сообщение отредактировал TheMike - 21.06.21 - 17:29



           
 
Теги
Нет тегов для показа


 



    
Текстовая версия · Удалить установленные форумом cookies · Отметить все сообщения прочитанными ·  Сейчас: 19.04.24 - 18:42