docs: СЫРОЙ КОД AndrOBD — 10 лет продакшена, золотой стандарт ELM/BT

This commit is contained in:
Repinoid
2026-05-27 10:19:20 +03:00
parent afd4cce53a
commit f95b3fd9f2
+165
View File
@@ -543,3 +543,168 @@ outputStream.flush();
| **Kotlin-интеграция** | AndroidOBD + CarScanApp | Чистый API, современный код |
| **PID/DTC парсинг** | AndrOBD | Все формулы, все режимы |
| **KWP2000 (VW)** | KWP Logger | Если Phaeton на старом протоколе |
---
## Часть 3: СЫРОЙ КОД AndrOBD — золотой стандарт (1993 ⭐, 10 лет в продакшене)
### 3.1. Bluetooth SPP подключение (BtCommService.java)
```java
final UUID SPP_UUID = UUID.fromString("00001101-0000-1000-8000-00805F9B34FB");
// Стандартный socket
if (secure) {
tmp = device.createRfcommSocketToServiceRecord(SPP_UUID);
} else {
tmp = device.createInsecureRfcommSocketToServiceRecord(SPP_UUID);
}
// FALLBACK: reflection RFCOMM channel 1 (для китайских клонов!)
catch (IOException e) {
Class<?> clazz = mmSocket.getRemoteDevice().getClass();
Method m = clazz.getMethod("createRfcommSocket", Integer.TYPE);
sockFallback = (BluetoothSocket) m.invoke(mmSocket.getRemoteDevice(), 1);
mmSocket = sockFallback;
mmSocket.connect(); // пробуем снова
}
```
### 3.2. 🚨 КРИТИЧЕСКИ: 500мс пауза после BT connect
```java
// AndrOBD issue #233 — без этой паузы Android теряет данные!
Thread.sleep(500); // CRITICAL: Fix for Android Bluetooth timing
// Только после этого запускаем worker thread
mBtWorkerThread = new BtWorkerThread(socket, socketType);
mBtWorkerThread.start();
```
### 3.3. Инициализация ELM327 (точный порядок)
```java
private void initialize() {
setStatus(STAT.INITIALIZING);
// 1. Кастомные init-команды (если есть)
cmdQueue.addAll(customInitCommands);
// 2. Установить протокол (AUTO или конкретный)
pushCommand(CMD.SETPROT, preferredProtocol.ordinal()); // ATSP<n>
// 3. Инициализировать адаптивный тайминг
mAdaptiveTiming.initialize(); // ATAT1
// 4. Ускорить протокол (убрать пробелы и переводы строк)
pushCommand(CMD.SETSPACES, 0); // ATS0
pushCommand(CMD.SETLINEFEED, 0); // ATL0
// 5. Выключить эхо
pushCommand(CMD.ECHO, 0); // ATE0
}
// → ждёт "ELM" ответ → затем 0100 (запрос поддерживаемых PID)
```
### 3.4. Адаптивный таймаут (ГЕНИАЛЬНО)
```java
// Константы
ELM_TIMEOUT_MAX = 1000 // мс — максимум
ELM_TIMEOUT_DEFAULT = 200 // мс — стартовое значение
ELM_TIMEOUT_RES = 4 // мс — шаг изменения
ELM_TIMEOUT_MIN = 12 // мс — минимум
// На каждый NODATA → увеличить таймаут на 4мс
// На каждый успех → уменьшить таймаут на 4мс (но не ниже learned_min)
void adapt(boolean increaseTimeout) {
if (increaseTimeout) {
if (elmMsgTimeout + 4 < 1000)
setElmMsgTimeout(elmMsgTimeout + 4);
} else {
if (elmMsgTimeout - 4 >= learnedMin)
setElmMsgTimeout(elmMsgTimeout - 4);
}
}
// Каждое изменение отправляет: ATST<timeout/4>
```
**Диапазон: 12мс → 1000мс, шаг 4мс. Старт: 200мс.**
### 3.5. Обработка ответов (handleTelegram)
```java
// 1. Фильтр эха
if (lastTxMsg.equalsIgnoreCase(bufferStr))
return 0; // проигнорировать
// 2. Определить тип ответа
switch (getResponseId(bufferStr)) {
case PROMPT: // ">" — конец ответа
case MODEL: // "ELM" → вызвать initialize()
case SEARCHING: // идёт поиск
case NODATA: // нет данных
case ERROR: // ошибка
case NOCONN: // "UNABLE"
case BUSERROR: // ошибка шины
// ... ещё 10+
}
```
### 3.6. Обработка ошибок (production-grade)
```
BUS ERROR / UNABLE / CAN ERROR:
→ DISCONNECTED
→ переставить в очередь последнюю команду
→ сбросить протокол (ATSP)
→ переинициализировать тайминг
→ закрыть протокол (ATPC)
DATA ERROR / RX ERROR / BUFFER FULL:
→ WARM START (ATWS) — мягкий перезапуск
NO DATA:
→ увеличить адаптивный таймаут
→ переустановить протокол
STOPPED:
→ переставить последнюю команду в очередь
```
### 3.7. Мульти-фрейм (ISO-TP для VIN и длинных ответов)
```java
// Формат: "014" = 1 строка, 14 hex символов
// "0:4902015756..." — первая строка с длиной
// "1:5A5A314B5A41..." — продолжение
if (buffer[0] == '0' && buffer.length == 3) {
charsExpected = Integer.valueOf(bufferStr, 16) * 2; // 0x14 = 20 байт
lastRxMsg = "";
responsePending = true;
}
if (bufferStr.indexOf(':') >= 0) {
lastRxMsg += bufferStr.substring(idx + 1);
}
if (lastRxMsg.length() >= charsExpected) {
result = handleDataMessage(lastRxMsg); // готово
}
```
### 3.8. Итоговый процесс подключения (хронология)
```
1. BT socket connect()
2. Thread.sleep(500) ← КРИТИЧЕСКИ! Без этого — потеря данных.
3. BtWorkerThread.start()
4. StreamHandler.run() — побайтовое чтение
5. ATSP0 → ATAT1 → ATS0 → ATL0 → ATE0
6. Ждём "ELM" → статус INITIALIZED
7. 0100 → опрос поддерживаемых PID
8. ECU detect → выбор ECU → готов к работе
```