Реверс-інжиніринг протоколу парктроника. Танець маленьких біт

Привіт, хабр!
У спробах звести все життєві робочі показники свого автомобіля на один екран головного пристрою дійшла черга і до підключення парктроніка. Багато хто заперечать — адже навіть у дешевих парктроніків є свій екранчик, навіщо виводити дані кудись ще? Так просто зайвий екранчик в салоні ставити не хочеться, і попорпатися в залозі привід є…

У статті постараюся описати прийоми та інструменти для реверс-інжинірингу недокументированного протоколу обміну двох залозок між собою.

Зі змісту деяких публікацій, створюється враження, що, по-перше, варто вибирати парктронік з радіоканалом між основним блоком і екраном, і по-друге, нічого складного в протоколах обміну не очікується. Хм… ну так. Це виявилося правдою наполовину.

Наявність радіоканалу між основним блоком і блоком індикації» дозволяє припустити, що протокол обміну між ними буде простим і послідовним з передачею виміряних відстаней в явному вигляді. Якби екран чіплявся безпосередньо, то, швидше за все, вся логіка була б реалізована в одному чіпі і на екран йшли б уже команди типу «запали ось цей піксель/сегмент і погаси он той», без можливості отримати безпосередньо виміряні відстані. Окей. Натхнені, йдемо в «найбільший кибермаркет» і купуємо там найдешевший комплект парктроника з літаками в логотипі. Купили, і тут почалося…

Крок перший. Розтин і зчитування посилаються даних

Для початку визначимося зі способом передачі даних по радіоканалу. Розкривши основний блок, знаходимо там кругленький чіп R433A з абсолютно стандартної обв'язкою. Приймача в основному блоці немає, отже, канал передачі даних односторонній, з єдино можливою для R433 OOK-модуляцією. Тобто при наявності високого рівня цифрового сигналу (логічної «одиниці») передавач передає в ефір несучу на частоті 433,92 МГц. При відсутності високого рівня — передавач мовчить. З боку приймача в блоці індикації аналогічним чином відбувається декодування: якщо приймач бачить несучу, видає високий рівень, якщо не бачить — видає низький. Так, всі перешкоди від парктроника сусіда приймач так само прекрасно ловить, тому його чутливість сильно знижується. І до передаваних даних, як правило, додається контрольна сума (цей факт нам ще знадобиться нижче).

Нам знадобиться цифровий осцилограф-самописець (зручні прості USB-осцилографи, типу DiSco). Знаходимо стежку, яка йде від головного мікроконтролера «мізків» до передавача в основному блоці або від приймача до мікроконтролеру в блоці індикації, знаходимо будь-яку «мінусову» доріжку, подпаиваем до них проводочки, підключаємо осцилограф, дивимося:



Ось таку повторювану посилку шле основний блок відразу при подачі живлення. При уважному її розгляді можна зробити вкрай неочевидний висновок про те, що посилка складається з трьох частин:

  • Перша частина — очевидно, певна синхронізація, щоб «розбудити» приймач і чіп в блоці індикації. Складається з п'яти імпульсів по 0.4 мс з паузами по 0.4 мс і слідом одного імпульсу тривалістю 1 мс з паузою 2 мс. Запам'ятаємо інформацію про тривалості, вона стане в нагоді для реалізації власного декодера.
  • Друга і треті частини — це безпосередньо дані, закодовані в 16 імпульсів різної ширини в кожній частині (плюс ще один 0.4 мс імпульс в самому кінці).
З'ясуємо, яким чином у другій і третій частині закодовані біти. Чергування широких і вузьких імпульсів схоже на Манчестерський код, який застосовується, зокрема, в мережах Ethernet. Але у манчестера два широких імпульсу не можуть розділятися вузьким. Тому, помітивши, що після широкого імпульсу завжди слід вузька «пауза» і навпаки (при загальній тривалості імпульсу+пауза = 1 мс), припустимо більш просте — ширина імпульсу безпосередньо кодує логічну одиницю (вузький імпульс) або нуль (широкий імпульс).

Крок другий. Декодування ручками

Отже, у нас є 32 біта даних і 4 датчика. Логічно припустити, що на кожен датчик відводиться «трохи менше» 8 біт, плюс, ймовірно, контрольна сума (ми ще пам'ятаємо про контрольну суму!). І нам потрібно зрозуміти, яким чином у цих бітах кодуються відстані до перешкод і все таке. Для початку відключимо всі датчики і вручну з показань осцилографа запишемо отриману послідовність біт:

10011100 10011100 10011101 01000100

хм… зрозуміло, що нічого не зрозуміло. У відсутність датчиків логічно б отримувати всі одиниці або всі нулі. Тут нічого схожого. Підключимо датчик A, направивши його в порожнечу (свідчення — «нескінченність»):

10011100 10011100 10010011 01001100

змінилися останні два байти. В 4-му байті помінявся 1 біт. Мабуть, саме він показує «наявність датчика A»? А в 3-му байті змінилося 3 біта. Причому, якщо розглядати їх як окремий 3-бітне число, записане від молодшого до старшого біту, можна помітити, що воно збільшилося на одиницю: 011+1=100. А ця одиниця — додалася до 4-го байту. Звідси два попередні висновки:

  • у 3-му байті є біти, що відносяться до контрольної сумі,
  • контрольна сума — це проста арифметична сума чого-то з чим-то, ніяких там CRC та інших складнощів,
  • дані в цілому кодуються не байтами, а 4-бітними «нибблами».
Спробуємо відключити датчик A і підключити датчик B:

10011100 10011110 01011101 01000100

змінилися 2-й і 3-й байти. Шостий біт другого байта (рахуючи від нуля), схоже, «наявність датчика B». Молодші 4 біта байта — теж контрольна сума, теж збільшилася на одиницю, що з'явилася у другому байті. Але з'явилася вона там не в нульовому і не в четвертому, а в шостому биті! Вже стає цікаво. Пробуємо одночасно датчики A та B в «нескінченність»:

10011100 10011110 01010011 01001100

так, дещо прояснилося. Маємо другий байт, як в посилці «тільки датчик B» і четвертий байт з посилки «тільки датчик A». А от у третьому байті два 4-бітних частини з двох попередніх посилок. Припущення про 4-бітні контрольні суми починає підтверджуватися? Незвично лише те, що контрольна сума засунута в середину посилки.

Тепер спробуємо виставити перед датчиком A (відключивши B) перешкоду на відстані, скажімо, 90 см:

10011100 10011100 10011111 01000110

про як, біт «наявність датчика A» більше не показує нам наявність датчика A. Але в останніх 4 бітах з'явився біт в іншому місці. І разюче змінилася контрольна сума. Хоча якщо порівняти з вихідною посилкою без датчиків: 1111-1011 = 100, а останні 4 біта: 0110-0010 = 100. Ура! Співпало!

Тим не менш, далі стає зрозуміло, що вручну з показань осцилографа змальовувати ці біти важко і велика ймовірність помилитися. Тому…

Крок третій. Декодування ніжками у мікроконтролері

У нас є мікроконтролер. Ардуїнов або просто AVR на макетке, неважливо. Він у нас є, кому як не йому збирати всі дані для головного пристрою. Тому саме час написати програмку для декодування посилки від парктроника і передачі цієї посилки через терминалку в комп'ютер для подальшого спрощення процесу реверсинга.

Оскільки рівень сигналів від парктроника становить стандартні 5 вольт, то підключення до AVRке для налагодження дуже просте — проводом на будь-яку неспеціалізовану ніжку (хмм… може я даремно закреслив у заголовку?).

Ісходник програми доступний на гітхабі. Декодуванням займається функція-обробник переривання PCINT3_vect у рядку 119 і далі. Інша частина програми робить багато інших цікавих штук, може бути коли-небудь я і про це напишу статтю. А поки опишу коротко алгоритм декодування посилки від парктроника.

У нинішніх AVRок майже на кожну ногу можна повісити переривання, яке буде спрацьовувати кожен раз при зміні рівня на вході. Тобто кожного разу при переході від 0 до 5 вольт і кожен раз при переході назад від 5 до 0. Таким чином, достатньо за допомогою таймера засікати час між спрацьовуваннями переривання і фіксувати внутрішній стан. Станів може бути кілька: очікування перших 5 імпульсів, очікування широкого імпульсу, очікування паузи, очікування перших 16 біт (з подальшим декодуванням в залежності від тривалості імпульсу), очікування паузи, очікування друге 16 біт, очікування фінального імпульсу, перехід в початковий стан. Причому все це реалізовано в обробника переривання, забирає кожен раз буквально лічені такти і зовсім не займає головний цикл (правда, займає окремий таймер, але це поправно).

Вийшло пристрій по UART видає в терминалку комп'ютера декодированные значення безпосередньо у вигляді 4х байт. Для спрощення подальшого аналізу відкриваємо Excel і пишемо макрос:

Хабр вміє підсвічувати бейсік?
Dim lst As Worksheet
Dim s As String
Dim v, i, j As Long

Set lst = ActiveWorkbook.ActiveSheet

For k = 2 To 365
For i = 1 To 8
If i Mod 2 = 0 Then ii = i - 1 Else ii = i + 1
s = "&H0" + Mid(lst.Cells(k, 2).Value, ii, 1)
v = CDec(s)
For j = 0 To 3
If (v And (2 ^ j)) > 0 Then
lst.Cells(k, 3 + (i - 1) * 4 + j) = "1"
Else
lst.Cells(k, 3 + (i - 1) * 4 + j) = "0"
End If
Next j
Next i
Next k


генерує з 4 шістнадцяткових байт ось таке (різнобарв'я і підпису, звичайно, я вже додав сам):



дуже наочно стало видно, що біти «наявність датчика» дійсно є по всім 4-м датчикам і впливають на контрольні суми відповідно. А пропажа біта датчика A при деяких свідченнях зумовлено чимось ще.

Володіючи всім вищеописаним інструментарієм, досвідченим шляхом отримуємо таблицю по датчику A:



ну все дуже непогано вимальовується:
  • Останні 4 біта — десятки сантиметрів датчика A. Причому якщо промоделювати відстані аж до нуля, вийде, що нулю десятків сантиметрів відповідає 1111 і далі по спадній, 10+ см = 1110, 20+ см = 1101, 30+ см = 1100 і т. д. аж до 0011, відповідного 130+ див.
  • Зазначені блідо-рожевим два стовпці по 2 біта відповідають одиницям сантиметрів (зауважте, що для 105, 95 і 85 см біти однакові). Причому в першому стовпці більш старші біти 4-бітного значення. Принцип кодування той же: 0 см = 1111, 1 см = 1110 і т. д. аж до 9 см = 0110
  • Перша контрольна сума залишається незмінною, а ось друга змінюється хитро. Стовпець десятків сантиметрів впливає на суму безпосередньо, а от обидва стовпці одиниць сантиметрів — впливають лише на старші два біти контрольної суми.
Зберемо аналогічну таблицю по датчику B:



тут цікавіше виходить:
  • Два стовпці по два біти, що кодують одиниці сантиметрів, залишаються на своїх місцях (відмічені светлозеленым). Виходить, що, швидше за все, одиниці сантиметрів виводяться кожен раз для найближчого до перешкоди датчика, а десятки сантиметрів — окремо по кожному датчику. Таким чином, на штатному екранчику виводиться відстань до найближчого до перешкоди датчика з точністю до сантиметра і, грубо, відстань до інших датчиків у вигляді смужок перед бампером.
  • Десятки сантиметрів для датчика B виходять також розбитими на два стовпці по два біта (відзначені «среднезеленым»).
  • Звернувшись до інструкції парктроніку, з'ясовуємо, що заявлена максимальна фіксовану відстань до перешкоди — 2.5 метра. А в 4 бітах можна закодувати тільки 1.6 метра. Значить десь є п'ятий біт? І дійсно, промоделировав відстані 1.7 метра і далі, з'ясовуємо, що це другий біт першого байта (відзначений темнозеленым). Таким чином, десятки сантиметрів датчика B кодуються біти в такому порядку (від старшого до молодшого): 2,1,0,16,15.
  • Змінюються значення обох 4-бітних контрольних сум. Отже, біти, пов'язані з показаннями датчика B, впливають на обидві суми.
  • Перша контрольна сума чітко змінюється на одиницю разом зі змінами на одиницю значення десятків сантиметрів. Мабуть інші стовпці, які також включені в цю суму — серед неизменяющихся по датчику B.


Чергу датчика C:



я вже починаю мислити, як китаєць:
  • гоньфень джі ренран суньза ой, тобто одиниці сантиметрів раніше на своїх місцях.
  • Десятки сантиметрів для датчика C закодовані в п'яти бітах, які на цей раз разом, хоч і належать різним байтам (бузковий і темносиреневый). Принцип кодування аналогічний попереднім датчикам.
  • Перша контрольна сума (перші 4 біти) чітко змінюється на одиницю разом зі змінами на одиницю значення десятків сантиметрів. Аналогічно датчику B. Отже, попередній висновок: в першу контрольну суму входять значення десятків сантиметрів датчика B і датчика C (ймовірно, без п'ятого біта) і щось ще. Інтуїція підказує, що це молодші 4 біта останнього байта. Перевіримо нижче.


По датчику D збирати докладну таблицю стало ліниво, тому так:



що ж, всі гіпотези підтвердилися. Перші 4 біти останнього байта кодують десятки сантиметрів датчика D.

Для перевірки промоделюємо кілька сполучень датчиків A і B:



так, усе збігається.

На даному етапі ми можемо повністю декодувати відстані по кожному датчику, включаючи одиниці сантиметрів. Та наявність/відсутність датчиків. Може бути, цього досить? Хм. Здається щось ще недораскопано…

Крок четвертий. Розрахунок CRC (Chinese Redundancy Check)

Отже, що ми вже знаємо про місцеві контрольні суми:
  • Їх дві, по 4 біта, знаходяться чомусь не в останньому, а в третьому байті.
  • Кожна з них є простою арифметичною сумою даних з інших стовпців.
  • Імовірно відома належність деяких біт до конкретних контрольними сумами.


Зазначимо відому на даний момент приналежність на вибірці якихось довільних показів:



спробуємо підсумувати по першому рядку, візьмемо стовпці десятків сантиметрів датчиків B, C і D:

1110 + 0111 + 0011 = 11000

хм, а контрольна сума у третьому байті 0111. А якщо мінус один?

1110 + 0111 + 0011 — 1 = 10111

збігається, якщо відкинути зайвий біт. Перевіримо по інших рядках:

1110 + 0111 + 0011 — 1 = 10111 (ой, тут все повторилося)
0101 + 0111 + 0011 — 1 = 0111 (тут без відкидання)
1111 + 1100 + 1100 — 1 = 100110 (тут аж два біта переповнилося)
0001 + 0101 + 0011 — 1 = 1000 (без відкидання)

ура, все співпало!
У нас залишилися не зазначені стовпці. Ймовірно, вони відносяться до другої контрольної сумі, тому спробуємо підсумувати:

1010 + 1011 + 0011 = 11000
1110 + 0111 + 0101 = 11010
1110 + 0011 + 1000 = 11001
1111 + 1111 + 0111 = 100101
1111 + 1011 + 0111 = 100001

мда, обмаль спільного з другою контрольною сумою. Подивимося, скільки потрібно відняти, щоб збіглося:

1010 + 1011 + 0011 — 10 = 10110
1110 + 0111 + 0101 — 10 = 11000
1110 + 0011 + 1000 — 11 = 10110
1111 + 1111 + 0111 — 01 = 100100
1111 + 1011 + 0111 — 11 = 11110

десь я це вже бачив… а, ну так, у першій контрольної суми! Залежність проста — від другої КС потрібно відняти те, що ми відкинули як переповнення при розрахунку першої КС, тільки xor енное з 11. Тобто відкидаючи 00 (нічого) від першої КС, від другої віднімаємо 11 і т. д.

Уфф, ніби все. Залишилося два незадіяних біта, але вони, схоже, завжди одиниці.

Крок п'ятий. Чистка радіоефіру

А взагалі я не прихильник застосування радіоканалів де попало. Ефір і так пристойно загиджений, так що працювати це все буде місцями (географічними) досить нестабільно. Тому займемося тим, що викинемо з парктроника приймач і передавач, з'єднавши базовий блок, блок індикації і наш мікроконтролер по проводах. Чому я згадую блок індикації, хоча не збирався його ставити? А з-за пищалки. Все-таки передача від базового блоку парктроника в наш мікроконтролер, там декодування, потім пересилання в головний пристрій, там знову декодування і відтворення внесе некритичний, але помітний лад відображення відстаней. Тому блок індикації залишиться в надрах прибирання і буде пищати завідомо швидше (хоча в майбутньому, може бути, змушу пищати свій мікроконтролер).

Можна було б не паритися і з'єднати всі блоки проводочками прямо як в налагоджувальному режимі, безпосередньо. Однак прокидывать через всю машину жалюгідні 5 вольт TTL, повірте мені, не найкраща ідея. Тому впаяем у всі три пристрої мікросхеми MAX485, що реалізують передачу по куди більш надійному інтерфейсу RS-485. Загалом якось так (вибачте за неотмытый флюс). Базовий блок:



на місці крапкою у правому верхньому куті плати стояв чіп R433A, з його обв'язки видалено транзистор Q11 і резистор, замість якого припаяний проводок. А у вільному місці вдалося розташувати микросхемку так, що ніжки потрапили на мінусовій контакт і кілька інших відповідних контактів. Оскільки базовий блок завжди є передавачем, ніжки DE і RE можна постійно замкнути на +5 вольт. Лінії A і B інтерфейсу RS485 виведені на додаткову клему.

Блок індикації:



ну тут взагалі краса, MAX485 впаялась практично як рідна замість стояла мікросхеми приймача RF83C. Збіглися ніжки виходу даних DO і мінусова GND, ніжки DE і RE, оскільки ця частина завжди приймач, посаджені на землю. Інше вимагало всього однієї перемички.

Працює, як і раніше:



фотку власного мікроконтролера, мабуть, опублікую в статті про іншу частину функціоналу KMENevoBT з гитхаба.

Наостанок, код повного декодування посилки від парктроника з налагоджувальною програмки на Delphi:

Ну не бийте, це всього лише налагоджувальний код на коліні
procedure TfrmKMEmul.UpdatePT(a, b, c, d: Byte);
var
cm, la, lb, lc, ld, crc1, crc2: integer;
begin
cm := $F - ( (a shr 2) and $C + (b shr 4) and $3 );
la := (d shr 4) and $F;
lb := ((a and 7) shl 2) + ((b shr 6) and $3);
lc := ((a shr 6) and $3) + ((b and $7) shl 2);
ld := (d) and $F;
crc1 := ((lc and $F + lb and $F + ld) - 1);
crc2 := ((a shr 2) and $F) + ((b shr 2) and $F) + ((d shr 4) and $F);
crc2 := crc2 - ((crc1 shr 4) xor $3);

la := $F - la;
lb := $1F - lb;
lc := $1F - lc;
ld := $F - ld;

lbCM.Caption := IntToStr(cm);
lbA.Caption := Format('%.1f', [la/10]);
lbB.Caption := Format('%.1f', [lb/10]);
lbC.Caption := Format('%.1f', [lc/10]);
lbD.Caption := Format('%.1f', [ld/10]);

if la = $F $2 then lbA.Font.Color := clRed else lbA.Font.Color := clGreen;
if lb = $1F - $4 then lbB.Font.Color := clRed else lbB.Font.Color := clGreen;
if lc = $1F - $4 then lbC.Font.Color := clRed else lbC.Font.Color := clGreen;
if ld = $F $2 then lbD.Font.Color := clRed else lbD.Font.Color := clGreen;

if crc1 and $F = c and $F then lbCRC.Caption := 'OK' else lbCRC.Caption := 'BAD';
if (crc2 and $F = (c shr 4) and $F) then lbCRC2.Caption := 'OK' else lbCRC2.Caption := 'BAD';
end;



Крок шостий. Висновки

Можливо, в якийсь момент варто було відмовитися від подальших розкопок і замовити з Ebay той же парктронік, який расковырял італієць з форуму по першій посиланням, але мені сподобався сам парктронік. Він дуже швидко і точно працює. Довелося добити, вже з принципу.
Що курили китайці, розробляючи ось такий протокол, незрозуміло.

Ліричний відступДовелося мені кілька років тому розкопати протокол діагностики автомобілів Форд з мізками EEC-IV. Це такі мізки на основі интеловского набору 8096, здається. Загалом, старовину на рівні трохи-чи не 8086. Ставилося це на форди з середини 80х і до початку 2000х, десь з початку 90х там був реалізований протокол діагностики Data Link Communication (DCL). Так от, швидше за все із-за обмежень продуктивності неможливо було зробити протокол обміну з асинхронним обміном даними, як COM-порт комп'ютера. Тому протокол був синхронний. Це означало, що при активації діагностики мізки починали слати по лінії даних «кадри», що складаються з суворо синхронізованих за часом байтслів. Для спілкування з мізками необхідно було з точністю до десятків мікросекунд засилати свої слова в зазначені проміжки між прийнятими словами. Ніякий комп'ютер з COM-портом не забезпечував необхідної точності відправки байт. Довелося робити на мікроконтролері…
Хм, так про що я… А, ось. У тому випадку такий цікавий протокол обміну був виправданий обмеженнями чіпа. У випадку з цим парктроніком я не бачу ніякої логіки саме такого «танцю» біт. Що заважало розташувати, наприклад, 4х4 біт десятків сантиметрів, потім 4 біта одиниць сантиметрів, ще 4 біта на розширення до 5 біт датчиків B і C і всякі службові і в кінці контрольна сума всіх біт (просто сума, без перекручень, в крайньому випадку стандартна CRC8).


До речі, знаючи протокол обміну, можна застосувати цей парктронік не тільки на автомобілі, а, приміром, на саморобному роботі. Так, для роботів є окремі ультразвукові датчики, але тут їх відразу чотири і читаються вони однією ніжкою ардуины, хоч і з затримкою в кілька мілісекунд.

Всім дочитавшим всього найкращого!

P. S. Хотів додати опитування щодо писати про розкопки протоколу мізків для газового уприскування KME Nevo Pro. Там дещо інші прийоми використовувалися… Але думаю, швидше треба питати щодо «а чи потрібні на хабре такі околоавтомобильные вишукування?».

Джерело: Хабрахабр

0 коментарів

Тільки зареєстровані та авторизовані користувачі можуть залишати коментарі.