Исправление CarPlay для Audi A3 8V

Когда я завладел своим рестайловым A3 e-tron, я впервые познакомился с CarPlay на данной модели авто. В целом CarPlay работал нормально и не вызывал вопросов, но со временем я начал замечать неработающие функции.

Речь дальше пойдёт про конкретную голову: Audi A3 8V Sportback e-tron 2017 года, магнитола MIB2 High, прошивка MU1326-MHI2_ER_AU37x_P5089, приборка — Virtual Cockpit (FPK). Внутри это QNX 6.5 на Tegra 3, ARMv7.

Самой основной и бросающейся в глаза неработающей функцией является отсутствие обложек песен на приборной панели, когда музыка воспроизводится с CarPlay (Apple Music, YouTube Music и т. д.).

Пример отсутствующей обложки на Audi A4

В то же время обложки нормально отображаются, если музыка воспроизводится через Bluetooth или с других носителей. По какой-то причине инженеры Audi не доделали этот функционал в CarPlay (и, вероятно, в Android Auto). И последующие обновления MIB2 не добавляли этот функционал.

Вторая функция, которая могла бы работать, но не реализована с завода, — это управление тачем в CarPlay, который находится на крутилке на панели управления мультимедиа.

Центральная крутилка имеет тач-покрытие

Её тоже не реализовали. Возможно, на момент выхода авто ещё не были популярны мультимедиа с тачскрином и решили не делать.

Также недавно увидел, что человек Luka из чата по Virtual Cockpit и MIB в Telegram опубликовал свои доработки по CarPlay для MHI2Q. Он также реализовал вывод на приборную панель манёвров с навигации CarPlay, то есть, проложив маршрут например в Google Maps на CarPlay, вы можете видеть манёвры на приборке и HUD-дисплее.

Сходу его решение напрямую к MHI2 (наша мультимедиа) перенести нельзя. У него стек Cinemo, Qualcomm, а у нас QNX 6.5, Tegra 3, ARMv7. У него драйвер CarPlay на стеке Cinemo, а у нас Harman. А манёвры у него рендерятся прямо на MIB, а у нас такого нет в принципе.

Разница глубже, чем кажется. У Luka протокол iPhone обрабатывает библиотека Cinemo внутри процесса dio_manager, и её функции приёма-передачи можно просто «обернуть» своими — вызвать оригинал и посмотреть на байты. У нас же протоколом занимается драйвер Harman (mm-ipod плюс ipod-drvr-iap2.so), обернуть там нечего: голова заходит в драйвер не по имени функции, а по таблице указателей внутри него. То есть точку входа пришлось искать в самом бинарнике и подменять указатель на функцию приёма пакетов на свою.

В технической части MIB я практически ничего не знаю, но сейчас в нашей жизни активно используется AI, и я решил попробовать доделать и добавить эти фичи в нашу мультимедиа, прибегнув к помощи AI. Для реализации я использовал Claude Opus 5 High.

Для работы я подготовил папку с полным бэкапом прошивки MIB, проект Luka, а также Luka смог декомпилировать java-файлы моей прошивки, по сути получив исходники java-стека. Также для Claude написал простенькие скиллы для подключения к MIB прямо на машине (через network), доступ к shell и т. д.

С реализацией первых двух фич — dpad и обложек песен — Claude справился довольно быстро. Dpad практически с одного захода сделал, а обложки — первую рабочую версию наверно где-то с третьего захода, и то сделал бы быстрее, если бы поняли, что на приборке надо в адаптациях включить Picture_Upload_Download. Потом была доработка обложек: они то с сильной задержкой появлялись, то появлялись артефакты при быстром перелистывании, то, если песня уже играла на телефоне и после подключения CarPlay, обложка не выводилась, и другие ошибки. В машине сидел по паре часов после работы и смотрел, как Claude ищет решения.

Работающие обложки песен на приборной панели

После реализации двух предыдущих фич я захотел реализовать манёвры с навигации CarPlay на виртуальном кокпите. Дал задачу Claude всё проанализировать и попытаться перенести функционал с проекта Luka. Но тут меня ждал небольшой облом. Claude сразу сказал, что рендер у Luka происходит прямо на MIB и там есть отдельный компонент, а у нас такого компонента нет, и у нас в штатной мультимедиа нет вообще анимации для манёвров — в штатной навигации манёвры выводятся статичной картинкой, а у Luka это прямо 3D-графика в реальном времени. У нас в теории тоже можно было бы рендерить на GPU, но в драйвере NVIDIA на этой голове вообще нет компилятора шейдеров: он умеет принимать их только заранее собранными бинарниками под свою версию драйвера, а собрать такие негде.

Отдельная история — как эти манёвры вообще заставить прийти в голову. Драйвер Harman про навигационные сообщения CarPlay не знает ничего: в его таблицах протокола нужной группы сообщений нет, в конфиге тоже. Телефон шлёт манёвры только тем, кто при подключении объявил себя «дисплеем навигации», а объявиться штатными средствами нельзя — такой настройки в прошивке не существует. Пришлось дописывать это объявление прямо в уже собранный пакет представления, побайтово, на лету. После этого iPhone начал слать поток манёвров, которого эта голова не видела ни разу за свою жизнь.

И тут вылезла главная неприятность: примерно через полминуты после подключения CarPlay намертво зависал. Разбирались несколько вечеров, и причина оказалась красивой. У драйвера есть пул всего из шести пакетов на приём. Каждое сообщение о манёвре попадает в обработчик, который про такие сообщения не знает, и возвращает код ошибки. А вызывающий код трактует любой положительный ответ как «пакет забрали, он больше не мой», и в пул его не возвращает. Шесть манёвров — и пул пуст, приём встал навсегда. У Luka такого быть не может в принципе: его библиотека отдаёт готовые байты и никакого пула не показывает. Починка — возвращать пакет в пул самим. После неё шесть минут под нагрузкой без единой просадки.

Claude предложил не рендерить манёвры на MIB, а сделать пререндер на ПК: разбить по кадрам анимацию каждого манёвра, а на MIB останется только проигрывать их с определённой частотой. Благо в репозитории Luka были сами манёвры и инструмент для их рендера.

Claude отрендерил все манёвры, разбил каждый манёвр по кадрам, сделал демку, и я прямо на приборке машины наблюдал анимации манёвров, а Claude спрашивал, какая частота кадров предпочтительнее, и параллельно мониторил производительность. Совместными усилиями пришли к выводу, что достаточно 18 кадров в секунду: на 12 анимация выглядит медленной, а 30 подвешивали MIB и клали всю систему. Где точно проходит граница между ними, мы не мерили — оставили 18 с запасом.

Изначально мы пререндерили только один вид, а у кокпита есть два вида — для большой и малой сцены. Пришлось отрендерить ещё и для малой сцены. Сцены отличаются размерами (328×181 и 210×153 пикселя) и фоном; фон, кстати, пришлось рисовать свой — штатный оказался слишком тёмным и слишком «шумным», чтобы нормально сжиматься. Есть ещё третья сцена — спортивная раскладка кокпита, но у меня e-tron-версия приборки, и там она полноценно не работает, поэтому я это не реализовывал. На данный момент кадров (картинок) отрендерено около 3600 штук — по 1787 на каждую сцену, 47 разных манёвров.

Потом очень долго Claude пытался разобраться с выводом плиток манёвров, постоянно вылезали накладки, также часто зависал CarPlay. На это ушло несколько дней и по четыре часа сидения в машине. Позже надо было ещё протестировать в реальной поездке — тоже пришлось несколько раз проехаться по району.

Забавно, что плитка манёвра на приборке в итоге собирается из двух половинок. Стрелку рисуем мы — это наша картинка на штатной подложке кокпита. А число «600 м» над ней приборка рисует сама: мы отдаём ей расстояние отдельным каналом, текстом. Название улицы мы тоже отправляем, но приборка его почему-то не показывает — это ещё предстоит разобрать.

Реальные поездки дали ещё пачку мелочей, которые за столом не увидишь. Например, iPhone отдаёт не весь маршрут, а скользящее окно из двух манёвров и меняет их местами примерно два раза в секунду — из-за этого стрелка прыгала между настоящим поворотом и заглушкой «двигайтесь прямо». Или расстояние пропадало ближе 200 метров до поворота: оказалось, приборка на этом этапе рисует полосу заполнения вместо цифры. Или потеря GPS посреди маршрута, которую от завершения маршрута не отличить. Всё это ловится только на дороге.

Google Maps в Украине с 2022 года нормально не обновляет карты и строит маршруты очень плохо и против ПДД, но данные про манёвры шлёт, в отличие от Waze. Waze по какой-то причине перестал с недавних пор отправлять данные о манёврах на CarPlay, надеюсь, вскоре вернут это, и тогда заработает и на приборке.

Свой проект я выложил на GitHub: https://github.com/chefranov/mhi2-au37x-carplay

Там наверняка ещё есть много ошибок и т. д. На данные и поддержку навигаторов я никак повлиять не могу: у CarPlay есть свой формат данных, я его читаю и передаю на приборку, а отправлять эти данные или нет — решает приложение навигации.

Сразу оговорюсь: всё это делалось на своей машине и на свой страх и риск. Голова при таких правках монтируется на запись, штатные файлы подменяются, и любой эксперимент здесь — потенциально поездка к дилеру. Всё, что я ставил, откатывается обратно в сток, но если вы не готовы к тому, что мультимедиа перестанет включаться, — лучше не повторять.

Отдельное спасибо Luka: без его разбора приборочной части и без набора манёвров это всё заняло бы в разы больше времени. Java-половина его проекта на нашу голову легла практически один в один — вся возня была вокруг драйвера и графики.

Манёвры на приборной панели с навигатора CarPlay
Демо манёвров

На этом всё, хороших дорог!