От завершения CaRadio к новой идее
В предыдущих статьях я подробно рассказывал о разработке собственной автомобильной магнитолы CaRadio. Постепенно проект дошёл до состояния, когда им уже можно было полноценно пользоваться в автомобиле. После дальней поездки, во время которой я испытывал магнитолу в реальных условиях, оставалось исправить несколько недочётов. Одним из них была работа со звуком при использовании CarPlay через адаптер Carlinkit. Проблема заключалась в обработке навигационных подсказок. Когда навигатор начинал воспроизводить голосовую подсказку, CaRadio получала соответствующее событие и могла уменьшить громкость музыки. Но вот определить момент окончания подсказки мне так и не удалось. В результате музыка либо возвращалась к прежней громкости слишком рано, либо оставалась приглушённой дольше необходимого. Пришлось использовать обходное решение с временными задержками. После нескольких экспериментов удалось подобрать вариант, который достаточно хорошо работал на практике. На этом первая версия CaRadio была фактически завершена. Магнитола работала, CarPlay подключался, музыка воспроизводилась, навигация функционировала. Более того, эта реализация продолжает работать до сих пор. Но я прекрасно понимал, что проблема не решена правильно. Вместо обработки реального события окончания навигационной подсказки программа просто ожидала определённое время. Именно этот небольшой технический компромисс в итоге привёл меня к совершенно новому направлению разработки.
Попытка разобраться с протоколом Carlinkit
Хотя первая версия CaRadio уже работала, проблема с определением окончания навигационных подсказок не давала мне покоя. Я решил вернуться к изучению протокола Carlinkit и попытаться найти более правильное решение.
К этому моменту я уже достаточно хорошо разобрался в обмене данными между CaRadio и адаптером. Однако оставались вопросы относительно некоторых команд и событий. Возможно, Carlinkit передавал необходимую информацию, но я просто не знал, как её интерпретировать. Не исключал я и того, что сам адаптер не предоставлял нужных событий.
Я снова начал искать документацию, исходные коды и результаты реверс-инжиниринга. На этот раз меня интересовали не только команды обмена с Carlinkit, но и внутренняя работа самого адаптера — в частности, его взаимодействие с iPhone.
Постепенно поиски вышли за рамки первоначальной задачи. Я всё чаще находил материалы, посвящённые нативному подключению CarPlay без использования готовых адаптеров. Информации было немного, она встречалась отдельными фрагментами, но именно тогда я снова обратил внимание на аппаратную аутентификацию MFi.
В какой-то момент у меня возник вопрос: если разобраться в механизме аутентификации и обмене данными непосредственно с iPhone, получится ли вообще обойтись без Carlinkit?
MFi и аппаратная аутентификация
В процессе изучения CarPlay я снова столкнулся с упоминаниями MFi — программы Apple Made for iPhone. Для определённых сценариев взаимодействия с устройствами Apple используется специальный чип аутентификации. Он позволяет выполнить необходимые криптографические операции, не раскрывая закрытые ключи непосредственно программному обеспечению. Идея использовать такой чип для собственной реализации CarPlay казалась интересной. Но возникал вполне очевидный вопрос: где его взять? Отдельные микросхемы MFi не относятся к обычным компонентам, которые можно просто выбрать в каталоге электронных деталей и заказать в любом количестве. Зато у меня уже был адаптер Carlinkit U2W Mini. И я решил посмотреть, что находится внутри.
Разборка Carlinkit U2W Mini
После разборки адаптера я начал изучать его плату. Меня интересовали не столько процессор и память, сколько компоненты, отвечающие за взаимодействие с iPhone. В результате удалось обнаружить необходимый чип аутентификации. Это было важное открытие: теперь у меня имелась не только информация о существовании подобных микросхем, но и реальный компонент, с которым можно было экспериментировать. Однако сначала я решил пойти немного другим путём. Если получить доступ к операционной системе самого адаптера, можно было бы изучить его программное обеспечение и, возможно, разобраться в протоколе значительно быстрее.
Попытка получить доступ через UART
На плате U2W Mini я нашёл интерфейс UART и попытался использовать его для подключения к адаптеру. Часть задачи удалось решить довольно быстро. Адаптер передавал данные через последовательный интерфейс, и я мог наблюдать его вывод. Но полноценного двустороннего соединения получить не удалось. Несмотря на то что данные от адаптера поступали, отправляемые ему команды не приводили к ожидаемому результату. Я пробовал разобраться в причинах, но быстро получить доступ к операционной системе не получилось. Продолжать тратить время на это направление тогда не хотелось, поэтому я временно отложил эксперименты с UART. Впрочем, разборка адаптера уже принесла результат: я знал, где находится MFi-чип, и мог вернуться к нему позже.
Поиск информации о Native CarPlay
После неудачной попытки получить доступ к операционной системе U2W Mini я продолжил искать информацию. Но теперь меня интересовал уже не только протокол Carlinkit. Постепенно появилась другая идея: а обязательно ли вообще использовать Carlinkit? Ведь если разобраться в механизмах подключения iPhone и получить доступ к необходимому чипу аутентификации, теоретически можно попробовать реализовать CarPlay непосредственно на Raspberry Pi. Это означало бы, что готовый адаптер больше не нужен в качестве посредника между iPhone и моей магнитолой. Проблема заключалась в недостатке информации. В открытом доступе встречались отдельные фрагменты документации, обсуждения протоколов, примеры взаимодействия с MFi и некоторые результаты исследований. Но собрать из этого полноценную рабочую реализацию было далеко не просто. Я потратил немало времени, постепенно собирая информацию буквально по крупицам. Тем не менее найденные материалы позволяли предположить, что реализация Native CarPlay на Raspberry Pi вполне возможна. И тогда я снова вспомнил о проекте LIVI.
Неожиданное открытие в LIVI
С LIVI я был знаком ещё со времени предыдущих экспериментов с CarPlay. Этот проект уже помогал мне разобраться в некоторых особенностях взаимодействия с адаптерами и стал одним из источников информации при разработке CaRadio. Решив посмотреть, что изменилось с момента моего последнего знакомства с его исходниками, я снова открыл репозиторий. И обнаружил то, чего совершенно не ожидал. В LIVI уже появилась реализация Native CarPlay. Причём соответствующие изменения были совсем свежими. Насколько я мог судить по истории проекта, они появились буквально в течение нескольких недель, пока я занимался испытаниями CaRadio и последующими доработками. Получилась довольно забавная ситуация. Я потратил много времени на поиск разрозненной информации, изучал возможные способы аутентификации, разбирал Carlinkit и пытался получить доступ к его операционной системе. А в это время в уже знакомом мне проекте появилась реализация именно того, к чему я постепенно пришёл самостоятельно. Разумеется, это не означало, что теперь достаточно скопировать несколько файлов и получить полностью работающую систему. Предстояло разобраться в исходном коде, подключить собственное оборудование, проверить взаимодействие с iPhone и адаптировать необходимые компоненты под свои задачи. Но теперь у меня была гораздо более конкретная отправная точка.
От идеи к новому проекту
На этом этапе первоначальная задача уже заметно изменилась. Всё начиналось с попытки исправить обработку навигационных подсказок в CaRadio, но теперь передо мной открывалась возможность попробовать реализовать собственное подключение CarPlay без использования готового адаптера.
После обнаружения реализации Native CarPlay в LIVI я решил начать с аппаратной части. Необходимый MFi-чип уже был найден на плате разобранного Carlinkit U2W Mini. Оставалось разобраться, как подключить его к Raspberry Pi.
К счастью, у микросхемы оказалось совсем немного выводов. Определить питание удалось довольно быстро, а изучение документации на процессор V851 позволило окончательно убедиться, какие контакты используются для шины I²C.
Я припаял к нужным контактам тонкие проводки и подключил их к шине I²C Raspberry Pi 3B+.
Затем запустил сканирование шины.
И практически сразу увидел то, на что рассчитывал, — адрес MFi-чипа.
Это был небольшой, но очень важный результат. Теперь микросхема, извлечённая из готового адаптера, определялась Raspberry Pi как отдельное I²C-устройство. Следующим шагом предстояло разобраться в командах аутентификации и проверить, получится ли использовать её для непосредственного подключения iPhone.
Именно с этого момента началась практическая работа над новым проектом, который впоследствии получил название CaRaBack — от CaRadio и Backend.
Но обнаружить MFi-чип на шине было только первым шагом. Впереди оставалась куда более сложная задача — заставить iPhone установить соединение с моей собственной реализацией CarPlay.
Об этом и пойдёт речь в следующей статье.