Подопытным кроликом станет мотоцикл CFMOTO 450MT (IBEX), или, как его ласково прозвали мужики в гаражах - "сифа":

https://cfmoto.ca/

В первой части статьи мы посмотрим на архитектурные схемы различных мотоциклов, поймем как обычно принято соединять блоки в сеть.
Во второй части подключимся к CAN-шине, разберемся какие сообщения по ней путешествуют, накидаем заготовку DBC-файла с описанием фреймов и сигналов.
В третьей части достучимся UDS-диагностикой до блоков мотоцикла, научимся читать и толковать ошибки, читать и редактировать конфигурацию.

Для лучшего понимания происходящего в статье могут понадобиться базовые знания о CAN-шине, протоколе UDS и файловом формате DBC. На хабре уже есть много статей на эту тему, кроме них рекомендуются материалы от CSS Electronics в качестве сжатого ликбеза: CAN, UDS, DBC. Историческую справку о зарождении и развитии CAN можно изучить на сайте ЦРУ, посвященном CAN: can-cia.org.

Cервисная документация CFMOTO размещена на американском сайте компании cfmotousa.com.

В качестве основных инструментов традиционно выступят связка библиотек python-can + can-isotp для работы с CAN-шиной и софт SavvyCAN для анализа логов и визуализации сигналов. Интерфейс для подключения подходит любой, который переваривает библиотека python-can.

Материал опубликован в ознакомительных и образовательных целях.

Часть 1. Архитектура мотоциклетных CAN-шин

Слово архитектура звучит громковато для системы из 3 блоков управления, но с чего-то надо начинать. Перед нами схема для CFMOTO 450MT:

CFMOTO 450MT (2024)
CFMOTO 450MT (2024)

Data Link Connector - это разъем для подключения диагностики. ECU управляет работой двигателя, ABS заведует тормозами и трекшн-контролем (TCS), а Dashboard (или Cluster) - это приборная панель. Пунктиром выделен опциональный блок телематики T-Box. На подопытном мотоцикле его не нашлось, теоретически он должен находится под правой боковой панелью бака.

В целом, такой связки из трех блоков достаточно для решения минимального набора современных задач. Инжектор вписывается в эконормы и обходится без вытягивания подсоса для прогрева, ABS и простой трекшн-контроль делают поездки безопаснее, а приборная панель выводит множество данных и ее можно подружить со смартфоном.

К слову, так же выглядит состав блоков для Royal Enfield Himalayan 450, конкурента "сифы" с идентичным объемом двигателя: ECU, ABS, приборка и опциональная телематика.

Если взять старшего брата CFMOTO 800MT-X, то у него внутри уже поинтереснее:

CFMOTO 800MT-X (2025)
CFMOTO 800MT-X (2025)

Дополнительно появились блоки BCM (Body Control Module) и IMU (Inertial Measurement Unit). Появление BCM интуитивно понятно - потребовался отдельный мозг для управления более навороченной бортовой электроникой, а вот для IMU нужно краткое пояснение.

Внутри IMU есть гироскоп и акселерометр, показания которых сообщают блоку ABS о текущем состоянии мотоцикла: его ускорения по разным осям, наклонен ли он, и в какую сторону, пытается ли встать на дыбы или, наоборот, собирается опрокинуться через переднее колесо. Эта информация крайне полезна для функций безопасности типа "Cornering ABS" и "Cornering Traction Control". Когда мотоцикл наклонен в повороте, ухудшается его контакт с дорогой и требуется гораздо меньше усилий, чтобы заблокировать его колеса при торможении, или сорвать в пробуксовку заднее колесо при ускорении. Можно еще упомянуть функцию "Anti-Wheelie", препятствующую вставанию на дыбы. Применений у IMU много, особенно с точки зрения безопасности.

Говоря о CFMOTO 800MT-X, сложно не упомянуть его австрийского родственника KTM 790 Adventure, с оглядкой на который он создан. CFMOTO и KTM связывают тесные отношения начиная с 2011 года, а с 2023 года сборку KTM 790 Adventure полностью перенесли в Китай.

KTM 790 Adventure (2023)
KTM 790 Adventure (2023)

Родственные мотоциклы отличаются по архитектурной составляющей:

  • много функций выполняется отдельными модулями, часть из которых опциональна к установке: система контроля давления в шинах, обогрев ручек, иммобилайзер и управление фарой;

  • вытекает из первого пункта - отсутствует единый боди-контроллер BCM, его функции разнесены по другим модулям поменьше;

  • блок связи и панель приборов соединяет BroadR-Reach интерфейс, это двухпроводная автомобильная версия Ethernet. BroadR-Reach вышел в свет в 2011 году, а в 2015 году лег в основу IEEE стандарта 100BASE-T1.

Изучая электросхемы других производителей, я наткнулся на интересный пример эволюции, иллюстрирующий внедрение CAN-шины при смене поколений. Речь идет о переходе Honda Africa Twin от поколения CRF1000L к поколению CRF1100L.

Вот схема сети для CRF1000L, которую начали выпускать в 2015 году, всего пару более десяти лет назад:

 Honda CRF1000L Africa Twin (2015)
Honda CRF1000L Africa Twin (2015)

На тот момент Honda не стала применять CAN-шину в данном мотоцикле, при этом опционально он мог быть напичкан довольно сложной электроникой. Например, на нем могла стоять впервые адаптированная для езды по бездорожью автоматическая коробка передач с двумя сцеплениями - DCT. Из схемы можно сделать вывод о том, как технически сложные мотоциклы Honda обходились без CAN-шины:

  • функции, требующие тесного взаимодействия, были реализованы внутри одного блока (PCM + TCM = блок двигателя управлял заодно и коробкой DCT);

  • для компьютерной диагностики применяли K-Line, а без компьютера могли обойтись SCS (Service Check Signal, при замыкании этого пина на массу лампа индикации неисправностей начинает "телеграфировать" номер ошибки);

  • для мультиплексирования данных использовали проприетарный протокол M-TXD (Meter Transmit Data), чтобы не тянуть охапку проводов к приборной панели.

Блок ABS тогда еще был в принципе опционален, он станет обязательным во многих странах только к концу 2010-х.

В следующем поколении, Honda прибавила стопку кубиков к объему двигателя и кардинально поменяла архитектуру мотоцикла:

 Honda CRF1100L Africa Twin (2020)
Honda CRF1100L Africa Twin (2020)

Среди множества отличий:

  • блок управления DCT отпочковался от блока управления двигателем и обрел независимость, между ними появилась выделенная CAN-шина "Powertrain" для обеспечения надежного обмена данными;

  • появилась специальная изолированная CAN-шина для диагностики DIAG, которая подключается через блок двигателя, выполняющего роль шлюза к остальным шинам мотоцикла;

  • появились две раздельные "рамные" CAN-шины: FRM1 соединяет почти все блоки в одну шину, а FRM2 соединяет приборку и дополнительный дисплей.

Новшества в CRF1100L сочетались с консерватизмом - остались на своих местах K-Line и SCS для диагностики некоторых блоков.

Разглядывая схемы Africa Twin, стало интересно, что внутри у ее младшего брата Transalp, вышедшего в свет в 2023 году:

 Honda XL750 Transalp (2023)
Honda XL750 Transalp (2023)

"Транс" занимает место пониже в иерархии Honda и поэтому устроен попроще:

  • для диагностики есть отдельная CAN-шина DIAG, которая проходит через встроенный в блок двигателя шлюз;

  • есть одна "рамная" шина FRM, связывающая между собой все блоки мотоцикла.

На своих местах остались традиционные K-Line и SCS для диагностики некоторых блоков.

Таким образом, с ростом технологической сложности мотоцикла, применение в нем одной или сразу нескольких CAN-шин стало неизбежным. На этом заканчиваем глазеть на схемы и переходим к практике.

Часть 2. Разбор трафика и составление DBC

Подключение и анализ фреймов

Точка подключения у 450MT проще некуда, прямо под седлом нас встречает классический полноразмерный OBDII-разъем со стандартной распиновкой: 6 = CAN-High и 14 = CAN-Low:

Кроме распиновки важно знать битрейт шины, чаще всего это 250 или 500 кбит/с, но иногда встречаются более артхаусные варианты. У CFMOTO битрейт оказался стандартным: 500 кбит/с.

В отличие от автомобиля, на мотоцикле некуда безопасно кинуть ноутбук, пишущий логи во время езды. Поэтому для записи логов на ходу был приобретен отдельный и самостоятельный CAN-логгер, со встроенной АКБ и записью на SD-карту:

Забегая вперед, логгер со своей задачей справился без проблем, но и нагрузка на него была небольшая - одна шина и всего три собеседника на ней. Стационарные логи все равно удобнее записывать и анализировать сразу на ноутбуке.

Подключаем провода, пишем первый лог, изучаем список идентификаторов (ID). Для упрощения дальнейшего реверс-инжиниринга полезно сопоставить каждый из блоков со списком фреймов, который он передает. Это сужает список подозреваемых - например, сигнал RPM скорее всего найдется во фреймах ECU, а сигнал скорости переднего колеса - во фреймах ABS.

Проще всего это делать по очереди, отключая предохранители блоков или скидывая с них разъемы, параллельно отмечая какие фреймы пропадут при следующем включении зажигания. Проделав такую операцию, получаем таблицу фреймов с их отправителями:

| id    | size | cycle time, ms | sender |
|-------|------|----------------|--------|
| 0x110 |    8 |             10 |    ECU |
| 0x111 |    8 |             10 |    ECU |
| 0x120 |    8 |             20 |    CLU |
| 0x123 |    8 |             20 |    ECU |
| 0x12B |    8 |             10 |    ABS |
| 0x12F |    8 |             20 |    ABS |
| 0x151 |    8 |             50 |    CLU |
| 0x153 |    8 |             50 |    CLU |
| 0x154 |    8 |             50 |    CLU |
| 0x163 |    8 |             50 |    CLU |
| 0x251 |    8 |            100 |    CLU |
| 0x2F3 |    8 |            200 |    CLU |
| 0x30E |    8 |            500 |    CLU |
| 0x321 |    8 |            500 |    CLU |
| 0x402 |    8 |           1000 |    CLU |

Итого ECU выдает 3 фрейма, ABS - 2 фрейма, а болтливая приборка занимает все остальные позиции. В некоторых фреймах сразу бросились в глаза циклические счетчики и чексуммы CRC8, посчитанные по алгоритму из SAE J1850.

Составленный в результате реверс-инжиниринга DBC-файл я разместил на гитхабе, возможно он будет дополняться и корректироваться в следующем мотосезоне. Но это не точно.

Таблица комментариев к сигналам из DBC-файла

фрейм

сигнал

комментарий

ECU_h110

engineSpeed

тахометр (об/мин)

engineTemperature

температура двигателя (°C)

fuelRate

мгновенный расход (мкл/с)

clutchLeverPressed

статус сцепления

sideStandUp

статус подножки

neutralSelected

статус нейтрали

ECU_h111

throttle

ручка газа (%)

voltage

напряжение бортсети (В)

CLU_h120

currentGear

включенная передача (0…6)

ECU_h123

torqueRequestDriver

момент, запрошенный водителем через ручку газа (%)

torqueRequestApplied

момент, примененный ECU по факту (%)

ABS_h12B

wheelSpeedFront

скорость переднего колеса (км/ч)

wheelSpeedRear

скорость заднего колеса (км/ч)

torqueLimitTraction

ограничение момента двигателя трекшн-контролем (%)

ABS_h12F

AbsRearDisableAck

подтверждение отключения ABS

TcDisableAck

подтверждение отключения TCS

RegularBraking

флаг обычного торможения/остановки

AbsFrontBraking

флаг срабатывания ABS (перед)

AbsRearBraking

флаг срабатывания ABS (зад)

AbsNotChecked

флаг проверки ABS перед поездкой

CLU_h151

mileageTotal

общий пробег (км)

generalSpeed

скорость мотоцикла (км/ч)

CLU_h153

fuelLevel

уровень топлива (%)

mileageTrip1

пробег поездки 1 (км)

mileageTrip2

пробег поездки 2 (км)

highBeamOn

статус дальнего света

CLU_h154

mileageUntilService

пробег до сервиса (км)

CLU_h163

timeTrip1

время поездки 1 (мин)

timeTrip2

время поездки 2 (мин)

timeTotal

общее время (мин)

CLU_h251

AbsRearDisableCmd

команда отключения ABS

TcDisableCmd

команда отключения TCS

CLU_h30E

pressureFrontWheel

давление TPMS перед (кПа)

pressureRearWheel

давление TPMS зад (кПа)

temperatureFrontWheel

температура TPMS перед (°C)

temperatureRearWheel

температура TPMS зад (°C)

warningFrontWheel

флаг предупреждения TPMS перед

warningRearWheel

флаг предупреждения TPMS зад

Смысл некоторых сигналов достаточно прямолинеен и расписывать отдельно процесс их поиска излишне. Например, к таким сигналам относится highBeamOn: включил дальний свет, посмотрел где и какой бит поменялся, записал. Но есть и любопытные случаи, которые будут описаны подробнее ниже.

Кстати, highBeamOn оказался очень удобен для разметки длинных логов, так как дальний свет кратковременно включается курком на левой ручке. Когда происходило что-то важное, я моргал дальним и потом очень быстро находил этот момент в логе.

Поскольку представленный DBC-файл - это результат гаражного реверс-инжиниринга, его достоверность может хромать.

Для чего может быть полезен DBC-файл?

Подгруженный в SavvyCAN DBC-файл превращает лог из набора байт в осмысленные сигналы, готовые к построению на графике. В частности, вот как выглядит разгон с места до 60 км/ч в обычном городском потоке:

Основные сигналы в динамике
Основные сигналы в динамике

Сигнал engineSpeed содержит RPM и уменьшен в 100 раз, чтобы влезть на график.

Похожие графики можно получить и опрашивая мотоцикл любым OBDII свистком, но у анализа сырых логов есть ряд преимуществ:

  • штатные фреймы могут передаваться чаще, чем происходит опрос диагностикой (вплоть до 100 раз в секунду, в случае 450MT);

  • частый опрос диагностикой создает дополнительную нагрузку на CAN-шину, а обычное прослушивание - нет;

  • записанный лог наиболее полно передает картину происходящего на данный момент в мотоцикле, его можно проиграть обратно в шину и вызвать у блоков соответствующую реакцию;

  • некоторые из сырых сигналов уникальны и попросту недоступны для чтения диагностикой (но может быть и наоборот).

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

Скорость, реальная и завышенная

В таблице присутствуют три разных сигнала скорости: wheelSpeedRear, wheelSpeedFront и generalSpeed. Отличие последней в том, что это именно та скорость, которая выводится на спидометр для водителя. А она, как известно, завышается по ряду причин практических и юридических.

На графике изображен процесс поиска сигнала generalSpeed:

График общей скорости (10, 20, 30 км/ч)
График общей скорости (10, 20, 30 км/ч)

Катаясь по пустырю, я моргал дальним 1 раз, когда видел ровно 10 км/ч на спидометре, 2 раза - 20 км/ч, 3 раза - 30 км/ч. Соотношение вышло один к одному, даже конвертировать не нужно.

С определением точной скорости колес нам поможет любое навигационное приложение с вменяемой индикацией скорости. Когда спидометр 450MT показывает ровно 60 км/ч, навигация показывает 56 км/ч. Используем это отличие для вычисления коэффициента для перевода и построим графики:

Общая скорость vs. скорости колес
Общая скорость vs. скорости колес

Коэффициент для перевода равен 0.05625, не самое популярное значение, но такое же встречается у автомобилей Subaru в проекте opendbc. Почему именно так: изначально сигнал передает значение в м/c с множителем 0.015625 (1/64). Для перевода в км/ч домножаем на 3.6 и получаем искомые 0.05625.

Торможение и ABS

Перед изучением работы ABS, проверим как выглядит торможение без нее. Отключаем систему кнопкой "ABS ON/OFF" на руле, разгоняемся до 20 км/ч и аккуратно притаптываем задний тормоз до блокировки колеса:

Торможение задним тормозом до блокировки колеса (ABS OFF)
Торможение задним тормозом до блокировки колеса (ABS OFF)

Сигнал wheelSpeedRear обвалился до нуля, колесо успешно заблокировано.

Сигналов концевиков заднего и/или переднего тормоза в CAN-шине не нашлось. Судя по электросхемам, концевики только включают стоп-сигнал и больше никуда не подключены, поэтому и некому отправлять их состояние в CAN. Зато нашлись логические сигналы RegularBraking, AbsFrontBraking, AbsRearBraking, соответствующие различным видам торможения.

Повторим тот же эксперимент с задним тормозом, но оставим ABS включенным, для демонстрации работы системы:

Торможение задним тормозом (ABS ON)
Торможение задним тормозом (ABS ON)

Изначально система расценивает торможение обычным (RegularBraking = 1), но при блокировке колеса переходит в режим ABS (AbsRearBraking = 1), активно вмешиваясь в процесс и пульсируя лапкой тормоза. Блокировка заднего колеса не оказывает влияния на общую скорость generalSpeed, передаваемую приборкой, т.к. заднее колесо редко используется для надежной оценки скорости - оно ведущее и часто проскальзывает.

Тормозим передним тормозом, ABS оставим включенным:

Торможение передним тормозом (ABS ON)
Торможение передним тормозом (ABS ON)

Начинается все, как и в прошлый раз, обычным торможением, которое переходит в режим ABS, но только теперь активизируется сигнал переднего колеса (AbsFrontBraking = 1). Его блокировка ожидаемо приводит к небольшому скачку общей скорости generalSpeed.

Использование переднего колеса для оценки скорости это популярная практика для мотоциклов. В "механическом" случае, приборная панель соединяется тросиком с передней ступицей и считает обороты колеса. Достаточно скинуть этот тросик, чтобы заморозить пробег и порадовать следующего владельца мотоцикла.

Разгон и TCS

Система TCS должна детектировать пробуксовку заднего колеса и вмешиваться в работу двигателя, чтобы сбавить обороты и восстановить сцепление колеса с дорогой. Самый логичный путь - прикрыть электронный дроссель. У "сифы" дроссель олдскульный тросиковый, но даже такой двигатель можно придушить, управляя искрой и впрыском топлива.

С самого начала анализа фреймов ECU выделялись два занятных, почти одинаковых сигнала. Оба сигнала переставали быть нулевыми после запуска двигателя, росли вместе с откручиванием ручки газа, но внезапно становились отрицательными на ходу, когда ручка газа была полностью закрыта, что оказалось важной зацепкой.

В поисках информации о блоках ECU от фирмы Bosch попались материалы, описывающие "torque-based" подход к управлению автомобильными двигателями. В простом случае, педаль газа напрямую управляет отдачей двигателя. В случае "torque-oriented" ECU сначала собирает пожелания к целевому крутящему моменту двигателя от всех участников системы. Например, водитель хочет X момента, кондиционер - Y, трекшн-контроль - Z. Все эти воздействия оцениваются ECU и он рассчитывает финальное значение момента, которое будет воплощено в жизнь через открытие дросселя, подачу искры и впрыск топлива.

Характерное поведение подозреваемых сигналов сходится с "torque-based" подходом, поэтому обзываем их соответственно: torqueRequestDriver - это запрос от водителя, а torqueRequestApplied - это то, что ECU реально исполнит. Позже нашелся сигнал ограничения момента torqueLimitTraction, задающий потолок отдачи двигателя при срабатывании TCS.

На графике ниже я выехал на грунтовку, открутил ручку газа примерно наполовину и удерживал, чтобы вызвать пробуксовку:

Срабатывание TCS
Срабатывание TCS

Сигнал fuelRate уменьшен в 100 раз, чтобы влезть на график.

Изначально разгон выглядит стабильным, сигналы torqueRequestDriver и torqueRequestApplied почти идеально повторяют друг-друга, двигатель беспрекословно исполняет команду от ручки газа throttle.

Как только разница скоростей между колес стала больше ~5 км/ч, на момент torqueRequestApplied сверху обрушился потолок в виде torqueLimitTraction, препятствующий дальнейшему набору скорости. При этом положение ручки газа throttle и torqueRequestDriver остаются неизменными - водитель хочет ускоряться дальше, но ограничение срабатывает, и разность скоростей колес уменьшается.

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

После полного закрытия ручки газа и снижения скорости, TCS считает, что кризисная ситуация миновала и снимает ограничение torqueLimitTraction. Сигналы torqueRequestDriver и torqueRequestApplied снова становятся почти одинаковыми и проваливаются ниже нуля, поскольку происходит торможение двигателем.

Расход топлива

Во время движения на приборной панели мотоцикла отображается мгновенный расход топлива. Сама по себе панель не может обладать достаточной информацией для его расчета, стало быть, она ее получает по CAN-шине от ECU. Вопрос в какой форме, и как его сконвертировать в классические л/100 км?

Изначально рассматривались самые разные теории (длительность импульсов форсунок, косвенное вычисление по RPM и нагрузке на двигатель и т.п.), пока круг подозреваемых не сузился до одного сигнала, названного в дальнейшем fuelRate. Данный сигнал содержит посчитанный ECU мгновенный расход топлива в мкл/с. Сперва микролитр звучал абсурдно малой единицей измерения для такого применения, но расчеты указывали именно на него. А позже нашлись упоминания микролитров у производителей Toyota, Volkswagen, Ford.

Чтобы превратить мкл/c в л/100 км, приборке потребуется проделать нехитрую операцию, не забыв про перевод единиц измерения:

\text{Расход(л/100 км)} = \frac{\text{Расход (мкл/c)} * 0.0036}{\text{Скорость(км/ч)}}*100=\frac{\text{Расход (мкл/c)} * 0.36}{\text{Скорость(км/ч)}}

Важно: в расчетах нельзя использовать завышенную скорость со спидометра generalSpeed, это приведет к искаженному результату. Нужна точная скорость переднего колеса wheelSpeedFront.

Посчитаем на практическом примере - на графике ниже я старался плавно поддерживать скорость и радостно моргал дальним четыре раза, когда видел расход ровно 4 л/100 км:

Мгновенный расход топлива
Мгновенный расход топлива

Сигнал fuelRate уменьшен в 10 раз, чтобы влезть на график. Наглядно видна реакция расхода на положение ручки газа.

В точке интереса приблизительные значения fuelRate = 465 мкл/с и wheelSpeedFront = 42 км/ч:

\frac{465(\text{мкл/c})*0.36}{42(\text{км/ч})}=3.98(\text{л/100 км})

Результат близок к искомому значению 4 л/100 км.

Где расход, там и уровень топлива. Сигнал fuelLevel был посчитан подкидыванием резисторов различного номинала, в диапазоне от 10 Ом (полный) до 450 Ом (пустой). Пользуясь значениями уровня топлива и мгновенного расхода можно собирать полезную топливную статистику поездок.

Часть 3. UDS-диагностика и попытка активации функций

В этой части мы переходим от пассивного прослушивания CAN-шины к активной отправке запросов и изучению ответов.

Здесь нужен очевидный дисклеймер о том, что неудачное компостирование мозгов мотоцикла может превратить его в беговел. Если вы новичок - потренируйтесь сначала на блоках с разборки, окирпичив пару штук на столе.

Поиск информации

Предварительно внимательно изучаем сервисную документацию на предмет полезной информации о диагностике. В PDF-файлах нашлись таблицы ошибок для некоторых блоков и описание фирменного софта для диагностики D-SCAN, который работает со своим специализированным интерфейсом.

Софт D-SCAN на момент проведения экспериментов распространялся свободно, но для его работы, естественно, нужны зарегистрированный логин, подключение к интернету и тот самый интерфейс. Впрочем, много полезной информации можно получить и не запуская софт, достаточно внимательно изучить его файлы. Особый интерес представляют текстовые файлы популярных форматов CSV, XML и JSON, в них могут содержаться описания ошибок (DTC), идентификаторы данных (DID) и многое другое.

Чтение и толкование ошибок

Для установления UDS сессии нужно знать два CAN ID - по одному мы передаем запрос, а по другому слушаем ответ. Их можно подобрать самостоятельно или подсмотреть в файлах D-SCAN:

| module | tx id | rx id |
|--------|-------|-------|
| ECU    | 0x7E0 | 0x7E8 |
| ABS    | 0x7E1 | 0x7E9 |
| CLU    | 0x714 | 0x794 |

Блок управления двигателем без проблем читается любым стандартным OBDII-сканером, поэтому его можно пропустить и сразу перейти к проприетарным блокам - панели приборов и блоку ABS.

Для чтения ошибок будем использовать UDS-сервис 0x19 "Read DTC Information", а разобраться с форматом ошибок нам помогут стандарты ISO 15031-6 и SAE J2012.

Ошибки приборной панели

Таблица DTC для приборной панели

DTC

Description

B111716

Vehicle power supply voltage below threshold

B111717

Vehicle power supply voltage above threshold

U007388

CAN bus off fault of BICAN

B110011

SWC Up key short to GND

B111011

SWC Down key short to GND

B112011

SWC Left key short to GND

B113011

SWC Right key short to GND

U220D00

LimpHome DTC

U114087

Lost ABS communicaiton

U118087

Lost TBOX communicaiton

U11C087

Lost BCM communicaiton

U11C487

Lost communication of PEPS

B114016

TPMS Low Battery

Информации о CAN ID и базового понимания UDS достаточно, чтобы самостоятельно стряпать запросы на чтение ошибок и попытаться понять, на что жалуется блок. Потренируемся на приборке и спросим, какие у нее есть ошибки:

-> 19 02 09
<- 59 02 09 C0 73 88 08

Панель сразу возвращает одну единственную ошибку 0xC0738808:

Одна ошибка приборной панели
Одна ошибка приборной панели

Составляем строку "TEXT" слева направо: U - ошибка относится к Network, 0 - ошибка стандартная и ее действительно можно найти в SAE J2012; в документации CFMOTO она называется "CAN bus off fault of BICAN". Тип ошибки - "Bus off", что порождает забавную тавтологию, статус ошибки - ошибка подтверждена.

Возможно она возникла из-за ненадежного подключении к CAN-шине, что довело приборную панель до состояния "Bus off".

Ошибки блока ABS

Таблица DTC блока ABS

DTC

Description

C102100

ABS Control unit failure

C101900

ABS Valve relay failure

C105400

ABS Inlet valve failure Front

C105200

ABS Inlet valve failure Rear

C104900

ABS Outlet valve failure Front

C104800

ABS Outlet valve failure Front Rear

C105900

ABS Voltage High

C105800

ABS Voltage Low

C101500

ABS Pump Failure

C101700

ABS Motor Failure

C103300

ABS WSS ohmic failure Front

C103100

ABS WSS ohmic failure Rear

C103400

ABS WSS plausibility failure Front

C103200

ABS WSS plausibility failure Rear

C103600

WSS long-term signal disturbance (Suspicious) Front

C103700

WSS long-term signal disturbance (Suspicious) Rear

C102400

ABS WSS generic failure

U292200

CAN Bus Off Failure

U292100

CAN Controller generic failure

U292400

CAN Engine Frame failure

U292500

CAN Engine Signal failure

U293400

CAN BCM timeout/DLC failure

U292900

CAN gear frame failure

U293200

CAN gear signal failure

U294400

CAN Meter Info timeout/DLC failure

C110000

Wrong variant in FEE/NVM

C120000

Variant out of Range in FEE/NVM

U293100

Net over voltage

U293000

Net under voltage

Отправляем запрос на чтение ошибок:

-> 19 02 2f 
<- 7f 19 78 
<- 59 02 2f 50 34 00 28 e9 32 00 28

Блок попросил нас подождать (0x78 = "Request Correctly Received - Response Pending"), а затем выдал 2 ошибки 0x50340028 и 0xe9320028:

Две ошибки блока ABS
Две ошибки блока ABS

Что общего у этих двух ошибок:

  • они обе кастомные, из диапазона "Manufacturer Controlled", и их описаний нет в стандартах;

  • они обе не используют Failure Type Byte, информация в базовом DTC полагается достаточной;

  • они обе подтверждены и возникли после последнего сброса памяти ошибок.

Ошибка C1034 означает, что сигнал с переднего датчика скорости валиден по электрическим характеристикам, но не выглядит корректным в сравнении с другими данными, например, передает скорость 10 км/ч, когда блок ABS однозначно считает, что мотоцикл стоит на месте. Если гуглить этот код, то находятся ошибки с похожим описанием у Ducati Monster и Harley-Davidson V-ROD, что однозначно намекает на кровное родство этих мотоциклов с "сифой".

Ошибка U2932 означает, что были проблемы с сигналом "Gear", который скорее всего содержит включенную передачу. Из составленной ранее DBC мы уже знаем, что отправителем этого сигнала является приборная панель, которая как раз падала в состояние "Bus off" с ошибкой U0073.

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

Знакомство с датчиками TPMS

Во время очередного поиска в интернете различных улучшайзингов для мотоцикла, я наткнулся на это:

https://unclewang.net/products/450mt-tire-pressure-sensor-oem

Это штатные датчики давления шин для мотоциклов CFMOTO. Нюанс в том, что с завода они на 450MT никогда не ставились, так как предназначены для более дорогих моделей. На этой же странице лежит набор скриншотов на китайском (легко гуглится и на английском), описывающих процесс прописывания датчиков в приборке мотоцикла.

Только вот делается это через диагностический комплекс D-SCAN, которого у нас нет. Но если поискать в XML-файлах строки со скриншотов, то можно найти кусочки информации, которые склеиваются в рецепт ниже:

1) перевести панель в диагностическую сессию;
2) запросить seed и сгенерировать ответный key;
3) считать и отредактировать битовую маску конфигурации (DID 0xF010);
4) вписать уникальные идентификаторы датчиков (DID 0x0802).

Теоретически этот рецепт выполним своими силами, с помощью Python и подручного CAN-интерфейса. Для проверки на практике из Китая были заказаны два датчика TPMS с партномерами 6WWV-150850-5A01:

Гравировкой на корпус каждого датчика нанесен его ID длиной в 4 байта. В целях конспирации обзовем их 57 B1 XX XX и 57 B8 XX XX. Как показали дальнейшие эксперименты, ID датчиков абсолютно статичны и не меняются во время работы с ними, что потенциально удобно для слежки.

Датчики TPMS - сущности молчаливые и берегущие энергию. Чтобы заставить их передавать данные, нужно поместить их под давление, затем сбросить его. Обойтись без установки датчика в колесо нам поможет народный лайфхак - барокамера из бутылки с ниппелем:

Накачивая и стравливая давление туда-сюда велосипедным насосом, параллельно мониторим BLE эфир на предмет посылок от датчиков. В качестве софта для мониторинга я использовал InsideBlue BLE Tool, в нем было удобно фильтровать датчики по строке "TPMSS" в имени:

Датчики проснулись!
Датчики проснулись!

Обратите внимание, как ID датчиков рифмуются с их BLE MAC-адресами:

57 B1 XX XX = 57:B1:00:00:XX:XX
57 B8 XX XX = 57:B8:00:00:XX:XX

Зная TPMS ID, можно получить BLE MAC, достаточно раздвинуть байты посередине и вставить туда нули. Так ли это для всех датчиков - неизвестно, но эти два оказались именно такими.

Каждая из полезных посылок имеет размер в 17 байт. Попробуем разбить ее на отдельные поля и придать им смысл:

|        ? | TPMS ID  | pressure | temperature | battery? | BLE MAC      |
| XXXXXXXX | 57B1XXXX | 63       | 15          | BB       | 57B10000XXXX |

Первое поле полезной нагрузки не несет, в нем содержится нечто вроде счетчика посылок. После него однозначно определяются: TPMS ID, давление, температура и BLE MAC.

Давлению в 310 kPa на панели соответствовало сырое значение 0x63 (99). Подходящий коэффициент перевода должен быть в районе 3.1. Если прикидывать точнее, то наиболее вероятно он равен 3.125 (800/256), где 800 kPa - это часто встречающийся предел измерения датчиков TPMS, а на 256 легко делить битовым сдвигом. Проверим наш пример:

99 * 3.125 = 309.375\text{(кПа)}

С температурой все еще проще, достаточно перевода в десятичный вид: 0x15 = 21 градусов Цельсия.

Кроме этих данных по-хорошему датчик должен передавать уровень заряда своей батарейки, чтобы оповещать водителя о ее разряде. Возможно уровень содержится в поле "battery?".

Поиск алгоритма расчета ключа

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

В текстовом формате такие алгоритмы хранить не принято по понятным причинам, поэтому искать их нужно в исполняемых файлах.

В процессе поисков был найден DLL-файл с подозрительным названием, намекающим на принадлежность к приборной панели. Можно попробовать дизассемблировать его и извлечь алгоритм расчета, а можно пойти ленивым путем - научиться вызывать функцию и получать ее результат, например, из скрипта на Python. Анализ DLL показал, что внутри есть функция GenerateKeyEx(), а при поиске информации о ней в первых строчках всплывает application note от Vector с подробным описанием:

VKeyGenResultEx GenerateKeyEx
(
    const unsigned char* ipSeedArray,
    unsigned int         iSeedArraySize,
    const unsigned int   iSecurityLevel,
    const char*          ipVariant,
    unsigned char*       iopKeyArray,
    unsigned int         iMaxKeyArraySize,
    unsigned int&        oActualKeyArraySize
);

Алгоритм расчета ключа могут паковать в DLL с подобным стандартизированным API, это позволяет производителю делиться алгоритмом с контрагентами, не открывая его исходники. Например, отдать блок и DLL в сертификационный орган, где может потребоваться доступ к закрытой диагностике для тестов. Кроме софта от Vector, с такими DLL "из коробки" еще умеет работать его китайский конкурент - TSMaster от компании TOSUN.

Поскольку DLL 32-битная, то и работать с ней нужно из 32-битной версией Python. Напишем простой скрипт, вызывающий функцию GenerateKeyEx():

import ctypes

mylib = ctypes.WinDLL("./XXXX.dll")
seed = (ctypes.c_ubyte * 4)(0x12, 0x34, 0x56, 0x78) # seed from the ECU, e.g. 12345678
key = (ctypes.c_ubyte * 4)() # contains the key (after DLL call)
keylength = ctypes.c_uint(4) # contains the actual key length (after DLL call) 

mylib.GenerateKeyEx(
     ctypes.pointer(seed),             # seed from the ECU
     ctypes.c_uint(4),                 # seed length = 4 bytes
     ctypes.c_uint(1),                 # security Level = 1
     ctypes.POINTER(ctypes.c_int)(),   # NULL = No variant string
     ctypes.pointer(key),              # key to send back to the ECU
     ctypes.c_uint(4),                 # max key length = 4 bytes
     ctypes.pointer(keylength),        # actual key length = 4 bytes
)

Убеждаемся, что скрипт действительно импортирует DLL и дергает из нее нужную функцию, дописываем в него взаимодействие с блоком по CAN-шине. И проверяем корректность расчета ключа, не забыв перейти в расширенную диагностическую сессию:

# switch to extended diagnostic session
-> 10 03
<- 50 03 00 32 01 f4

# get seed
-> 27 01
<- 67 01 af e7 ce 25

# send key
-> 27 02 a0 a1 a0 b0
<- 67 02

Ключ действительно подходит! Переходим к следующей операции - активации отображения TPMS-информации. Рецепт состоит из нескольких шагов, для наглядности процесса мы будем выполнять их по очереди, отслеживая реакцию приборки после каждого шага.

Обратите внимание на выделенный участок экрана приборной панели, сейчас там пусто:

Считываем дефолтную конфигурацию, редактируем бит отвечающий за наличие TPMS, записываем обратно:

# read configuration
-> 22 f0 10
<- 62 f0 10 00 00 00 80 04 00 2c 80 00 00 00 00 00 00 00 00

# write configuration
-> 2e f0 10 00 00 00 80 44 00 2c 80 00 00 00 00 00 00 00 00
<- 6e f0 10

После таких операций панель начала ругаться по-китайски, онлайн-переводчик перевел эти иероглифы, как "НЕОБУЧЕНО!":

Похоже теперь панель ждет записи TPMS ID для привязки датчиков. Поскольку один DID содержит сразу оба TPMS ID, пока непонятно, какой из них окажется передним, а какой - задним. Порядок бит тоже неизвестен, поэтому для первой попытки пишем как читаем:

# read TPMS IDs
-> 22 08 02
<- 62 08 02 ff ff ff ff ff ff ff ff

# write TPMS IDs
-> 2e 08 02 57 b1 xx xx 57 b8 xx xx
<- 6e 08 02

По умолчанию поля TPMS ID забиты FF-ами и панель это воспринимает, как признак необученности. После привязки ругательства исчезли и появилась индикация давления и температуры, заполненная прочерками. Панель ждет, когда датчики выйдут на связь:

Наступил момент истины - закидываем датчики в бутылку и по отработанной схеме пробуждаем их:

Вот и все! Привязываем бутылку к мотоциклу и наслаждаемся уникальной возможностью отслеживать давление внутри нее.

По очереди пытая датчики в бутылке, устанавливаем, что 57 B1 XX XX прописался в качестве переднего, а 57 B8 XX XX - заднего. Порядок бит тоже оказался правильным.

Уже после активации и привязки датчиков TPMS, в CAN-шине ожил фрейм 0x30E и в нем обнаружились значения давлений и температур, которые были добавлены в DBC-файл. Возможно эти значения должен слушать опциональный блок телематики, для дальнейшей передачи в приложение пользователя.

Заключение

Хоть в современных мотоциклах и меньше блоков управления, чем в автомобилях, исследовать их не менее интересно.

Мотоциклетная CAN-шина содержит множество полезной информации, которую можно использовать на свое усмотрение - логировать и анализировать поездки, считывать диагностическую информацию и не только.

Комментарии (0)