11 KiB
Сессия 2026-05-26 — отладка Android-клиента и Mock ELM327
Хронология
08:00 — Старт: пулл изменений с ночи
Ночью на другой машине было сделано:
android/вынесен в отдельный репоgithub.com/Repinoid/elmer-android- Добавлен
tools/mock_elm327.py— TCP-эмулятор ELM327 на порту 35000 web/raw_endpoint.py— стейт-машина, парсинг ответов, LLM-интеграция- Исправлены 3 бага в Android-клиенте (NetworkOnMainThread, URL сервера, фильтр \n)
08:20 — Попытка 1: телефон → mock → сервер
Запущены mock (:35000) и Flask (:5005). Настроен проброс портов через Windows:
netsh interface portproxy add v4tov4 listenport=5005 listenaddress=0.0.0.0 connectport=5005 connectaddress=192.168.18.62
netsh advfirewall firewall add rule name="Elmer Flask" dir=in action=allow protocol=TCP localport=5005
WiFi IP ноутбука: 10.47.183.102.
Телефон подключился к mock (TCP: OK), но команды не шли.
08:30 — Ошибка 1: Server URL захардкожен на obdai.ru
Глянув код клиента в ElmForwardService.kt, обнаружено:
// MainActivity.kt строка 66
putExtra(EXTRA_SERVER_URL, "https://obdai.ru/api/v1/raw-obd")
Телефон слал данные в интернет, а не на локальный Flask.
Исправление: в MainActivity.kt — авто-вывод URL сервера из IP устройства:
val deviceHost = debugHost.split(":")[0]
val localServerUrl = "http://$deviceHost:5005/api/v1/raw-obd"
08:35 — Ошибка 2: Gradle 9.5 слишком новый
CI упал с org.gradle.api.artifacts.SelfResolvingDependency. Причина: AGP 8.2.0 несовместим с Gradle 9.x.
Исправление: в build-apk.yml:
- name: Setup Gradle
uses: gradle/actions/setup-gradle@v4
with:
gradle-version: "8.5"
И создан gradle/wrapper/gradle-wrapper.properties с gradle-8.5-bin.zip.
08:40 — Ошибка 3: Коммиты не в ту ветку
Изначально все изменения ушли в master, но рабочая ветка — relay-only.
Исправление: переключился на relay-only, применил изменения туда, master откатил через git reset --hard && git push --force.
08:45 — Ветка relay-only: добавлена отладка
В ElmForwardService.kt добавлено:
say("🌐 Server: $serverUrl")— показ URL при подключенииsay("← $raw")— каждое сырое сообщение от устройстваsay("→ $cmd")— каждая команда сервера- Обработка ошибок:
⚠️ Server unreachable,⚠️ Bad JSON
09:00 — Ошибка 4: Cleartext HTTP заблокирован
Телефон показал ⚠️ Server down: cleartext.... Android 9+ блокирует HTTP (не-HTTPS) по умолчанию.
Исправление: в AndroidManifest.xml:
android:usesCleartextTraffic="true"
09:05 — Ошибка 5: APK не скачивается с сервера
Телефон открыл http://10.47.183.102:5005, страница загрузилась, но APK — 404.
Flask отдаёт статику из /static/, а ссылка была /app-debug.apk.
Исправление: ссылка изменена на /static/app-debug.apk.
09:10 — Ошибка 6: Приложение не устанавливается поверх
Google Play Protect проверил APK, но «приложение не установлено». Причина: каждый CI-билд генерирует новый debug-keystore → сигнатуры не совпадают → Android блокирует установку поверх.
Исправление: сгенерирован фиксированный debug.keystore (пароль android, alias androiddebugkey) и закоммичен в репо. В CI добавлен шаг:
- name: Setup debug keystore
run: cp debug.keystore ~/.android/debug.keystore
09:15 — Тестовая версия: вместо сервера — локальный протокол
Пользователь потребовал тестовую версию без сервера. Создан TestService.kt:
- TCP-подключение к mock
- Самостоятельная отправка AT-команд (ATZ, ATE0, ATL0, ATSP0, ATH1)
- Чтение VIN (0902)
- Чтение DTC stored (03), pending (07)
- Чтение 10 PID'ов (0105..0107)
- Весь вывод на экран в реальном времени
Интерфейс: зелёная кнопка «🧪 ТЕСТ», версия v0.2.0-test, URL по умолчанию 10.47.183.102:35000.
09:25 — Ошибка 7: Мусор в командах
Mock получил 01070 ATZ вместо ATZ. Причина: клиент слал \r, а mock использовал readline() (ждёт \n). В буфере накопился мусор.
Исправление в клиенте: cmd + "\r\n" вместо cmd + "\r".
Исправление в mock: полная переделка чтения — побайтовый разбор:
# Было: self.rfile.readline()
# Стало: читаем по 1 байту, \r и \n — разделители
ch = self.rfile.read(1)
if ch in (b'\n', b'\r'):
# обработать накопленный буфер
else:
buf += ch
09:30 — 100% успешный тест
Mock лог (чистый!):
🔌 Подключение
📥 ATZ → ELM327 v1.5
📥 ATE0 → OK
📥 ATL0 → OK
📥 ATSP0 → OK
📥 ATH1 → OK
📥 0902 → VIN: WVWZZZ1KZAW123456
📥 03 → DTC: P0301, P0303
📥 07 → DTC: none
📥 0105..07 → 10 PID'ов
🔌 Отключение
18 команд — 18 ответов. Ноль мусора.
Архитектура (текущая)
┌─────────────────────────────────────────────────────┐
│ ТЕСТОВЫЙ РЕЖИМ (работает на 100%) │
│ │
│ Телефон (Android) Ноутбук (WSL) │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ TestService │──TCP──→ │ mock_elm327.py │ │
│ │ │←──TCP── │ :35000 │ │
│ │ ATZ→ATEx→ │ │ │ │
│ │ 0902→03/07 │ │ Фейковые данные: │ │
│ │ PID'ы │ │ VIN, DTC, PID │ │
│ └──────────────┘ └──────────────────┘ │
│ │
├─────────────────────────────────────────────────────┤
│ РАБОЧИЙ РЕЖИМ (клиент готов, сервер частично) │
│ │
│ Телефон Ноутбук │
│ ┌──────────────┐ ┌──────────────────┐ │
│ │ElmForwardSvc │─HTTP→ │ Flask :5005 │ │
│ │ (транспорт) │←─JSON─ │ raw_endpoint.py │ │
│ │ │ │ ↓ стейт-машина │ │
│ │ BT/TCP → │ │ ↓ парсер │ │
│ │ ELM327/mock │ │ ↓ LLM (DeepSeek)│ │
│ └──────────────┘ └──────────────────┘ │
└─────────────────────────────────────────────────────┘
Все ошибки и решения
| # | Ошибка | Причина | Решение |
|---|---|---|---|
| 1 | Телефон не слал команды | server_url = obdai.ru (интернет) | Авто-вывод http://host:5005 |
| 2 | CI build fail | Gradle 9.5 ≠ AGP 8.2 | Закрепить Gradle 8.5 |
| 3 | Коммиты в master | Не переключил ветку | Перенос в relay-only, откат master |
| 4 | Server down: cleartext | Android блокирует HTTP | usesCleartextTraffic="true" |
| 5 | APK 404 на сервере | Flask static path | /static/app-debug.apk |
| 6 | Не устанавливается поверх | Разные debug-ключи | Фиксированный keystore в репо |
| 7 | Мусор 01070 ATZ |
\r vs \r\n + readline() |
Побайтовое чтение в mock + \r\n |
Ключевые файлы
Android (ветка relay-only)
| Файл | Назначение |
|---|---|
TestService.kt |
ТЕСТОВЫЙ — сам гонит протокол, без сервера |
ElmForwardService.kt |
РАБОЧИЙ — транспорт BT/TCP ↔ HTTP |
MainActivity.kt |
UI: кнопки ТЕСТ и Диагностировать |
debug.keystore |
Фиксированный ключ подписи (пароль android) |
build-apk.yml |
CI: Gradle 8.5, сборка debug APK |
Сервер (ветка master)
| Файл | Назначение |
|---|---|
tools/mock_elm327.py |
Эмулятор ELM327 на TCP :35000 |
web/app.py |
Flask сервер :5005 |
web/raw_endpoint.py |
Стейт-машина: парсинг, сессии, LLM |
web/templates/index.html |
Страница загрузки APK |
Команды для запуска
# Ноутбук — терминал 1: mock
python tools/mock_elm327.py
# Ноутбук — терминал 2: сервер
python web/app.py
# Телефон: открыть http://10.47.183.102:5005 → скачать APK → кнопка ТЕСТ
Версии APK
| Версия | Статус | Ключ |
|---|---|---|
| 0.1.0 | Устарела | Случайный |
| 0.2.0-test | На телефоне | Случайный |
| 0.5.0-test | В CI | Фиксированный |
Что дальше (рабочая версия)
- Дописать
raw_endpoint.py— надёжный парсинг + сохранение в БД - Настроить
DEEPSEEK_API_KEYдля LLM - Протестировать цепочку: телефон → сервер → LLM → диагноз
- Подключить к реальному ELM327 в машине (Bluetooth вместо mock)