📜 ⬆️ ⬇️

Reverse engineering of Parktronic protocol. Dance of little bits

Hi, Habr!

In an attempt to reduce all the vital performance of your car on one screen of the head unit, it was the turn to connect the parking sensors. Many will argue - even cheap PDCs have their own screen, why output the data somewhere else? Yes, just do not want to put an extra screen in the cabin, and there is a reason to dig in the gland ...

In this article I will try to describe the techniques and tools for reverse engineering of an undocumented protocol for the exchange of two pieces of iron between each other.

From the content of some publications , it seems that, firstly, it is worth choosing parking sensors with a radio channel between the main unit and the screen, and secondly, nothing complicated in the exchange protocols is expected. Hmm ... well, yes. It turned out to be half true.
')
The presence of a radio channel between the main unit and the “display unit” suggests that the exchange protocol between them will be simple and consistent with the transfer of the measured distances in an explicit form. If the screen would cling directly, then, most likely, all the logic would be implemented in one chip and commands like “light this pixel / segment and go out the same” would go to the screen, without being able to get the measured distances directly. Okay. Inspired, we go to the “largest cybermarket” and buy the cheapest set of parking sensors with airplanes in the logo. Bought, and then it began ...

Step one. Opening and reading sent data


To begin, we will determine the method of data transmission over the air. Having opened the main unit, we find there a round chip R433A with a completely standard strapping. There is no receiver in the main unit, therefore, the data transmission channel is one-way, with the only possible modulation for R433 OOK . Those. If there is a high level of digital signal (logical “one”), the transmitter broadcasts a carrier at 433.92 MHz. In the absence of a high level - the transmitter is silent. On the receiver side, decoding occurs in a similar way in the display unit: if the receiver sees the carrier, it gives a high level, if it does not, it gives a low one. Yes, the receiver also perfectly catches all the interference from the parking sensors of the neighbor , so its sensitivity is greatly underestimated. And, as a rule, a checksum is added to the transmitted data (this fact is still useful to us below).

We will need a digital oscilloscope recorder (simple USB oscilloscopes, such as DiSco, are convenient). We find the path that goes from the main microcontroller of the “brains” to the transmitter in the main unit or from the receiver to the microcontroller in the display unit, find any “minus” track, solder wires to them, connect an oscilloscope, look:



This repetitive package sends the main unit immediately when power is applied. With careful consideration, it is possible to make an extremely non-obvious conclusion that the premise consists of three parts:


Find out how the bits are coded in the second and third parts. The alternation of wide and narrow pulses is similar to the Manchester code , which is used, in particular, on Ethernet networks. But Manchester’s two broad impulses cannot be separated by narrow ones. Therefore, noting that a wide pause always follows a narrow “pause” and vice versa (with a total pulse duration + pause = 1 ms), suppose a simpler one — the pulse width directly encodes a logical unit (narrow pulse) or zero (wide pulse).

Step two. Pen decoding


So, we have 32 bits of data and 4 sensors. It is logical to assume that 8 bits are allocated to each sensor, plus, probably, a checksum (we still remember the checksum!). And we need to understand how distance to obstacles is encoded in these bits and so on. To begin with, turn off all sensors and manually write the resulting bit sequence from the oscilloscope readings:

10011100 10011100 10011101 01000100

hmm ... it is clear that nothing is clear. In the absence of sensors, it would be logical to receive all ones or all zeros. Nothing like this here. We connect sensor A, sending it to the void (readings - "infinity"):

10011100 10011100 10010011 01001100

changed the last two bytes. In the 4th byte 1 bit changed. Apparently, it is he who shows the presence of sensor A? And in the 3rd byte 3 bits changed. Moreover, if we consider them as a separate 3-bit number written from the low to high bit, we can see that it has increased by one: 011 + 1 = 100. And this unit is added to the 4th byte. From here two preliminary conclusions:


Let's try to disconnect sensor A and connect sensor B:

10011100 10011110 01011101 01000100

changed 2nd and 3rd bytes. The sixth bit of the second byte (counting from zero) seems to be the “presence of sensor B”. The lower 4 bits of the third byte - also the checksum, also increased by one, which appeared in the second byte. But she appeared there not in the zero and not in the fourth, but in the sixth bit! Already getting interesting. Try simultaneously sensors A and B to "infinity":

10011100 10011110 01010011 01001100

yes, something cleared up. We have the second byte, as in the parcel “sensor B only” and the fourth byte from the parcel “sensor A only”. But in the third byte there are two 4-bit parts from the two previous packages. The assumption about 4-bit checksums begins to be confirmed? The only unusual thing is that the checksum is stuck in the middle of the package.

Let us now try to put an obstacle in front of sensor A (disabling B) at a distance of, say, 90 cm:

10011100 10011100 10011111 01000110

About how, the “presence of sensor A” bit no longer shows us the presence of sensor A. But in the last 4 bits a bit appeared in another place. And the checksum has changed dramatically. Although if you compare with the original parcel without sensors: 1111-1011 = 100, and the last 4 bits: 0110-0010 = 100. Hurray! Coincided!

Nevertheless, it becomes clear later that manually copying these bits from the oscilloscope readings is hard and there is a high probability of making a mistake. Therefore…

Step three. Decoding feet in the microcontroller


We have a microcontroller. Arduino or just AVR on breadboard, whatever. We have it, who does not like him to collect all the data for the head unit. Therefore, it's time to write a program to decode the parcel from the PDC and transfer this parcel through the terminalka to the computer to simplify the further process of reversing.

Since the level of signals from the PDC is standard 5 volts, the connection to the AVR for debugging is very simple - with a wire to any unspecialized leg (hmm ... maybe I shouldn't have crossed out in the header in vain?).

The program source is available on github . The decoding is handled by the PCINT3_vect interrupt handler function in line 119 and on. The rest of the program does a lot of other interesting things, maybe someday I'll write an article about it. In the meantime, I will describe in brief the algorithm for decoding the parcel from parking sensors.

The current AVRok almost every foot can hang interruption, which will be triggered every time you change the level at the entrance. Those. each time when going from 0 to 5 volts and each time when going back from 5 to 0. Thus, it is enough to detect the time between the interrupt and activate the internal state using a timer. There can be several states: waiting for the first 5 pulses, waiting for a wide pulse, waiting for a pause, waiting for the first 16 bits (followed by decoding depending on the pulse duration), waiting for the pause, waiting for the second 16 bits, waiting for the final pulse, transition to the initial state. And all this is implemented in the interrupt handler, it takes literally a few clocks each time and does not occupy the main loop at all (although it takes a separate timer, but this is fixable).

The resulting UART device produces decoded values ​​to the terminalka of the computer directly in the form of 4 bytes. To simplify the subsequent analysis, open Excel and write a macro:

Habr can highlight BASIC?
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 


generating from 4 hex bytes like this (colors and signatures, of course, I already added):



it became very clear that the “sensor presence” bits are indeed available for all 4 sensors and affect the checksums, respectively. And the disappearance of the sensor bit A for some indications is due to something else.

Possessing all the above-described tools, we empirically get a table by sensor A:



Well, everything is very nice looming:

Collect a similar table on sensor B:



here it is more interesting:


Sensor queue C:



I'm starting to think like a Chinese:


On the sensor D to collect a detailed table has become lazy, so:



Well, all the hypotheses were confirmed. The first 4 bits of the last byte encode tens of centimeters of sensor D.

To test, we simulate several combinations of sensors A and B:



yes, everything is the same.

At this stage, we can fully decode the distances for each sensor, including units of centimeters. And the presence / absence of sensors. Maybe this is enough? Hm Something else seems to be undersampled ...

Step Four. CRC (Chinese Redundancy Check) calculation


So, what we already know about local checksums:


We note the currently known membership in the sample of any arbitrary readings:



Let's try to sum up the first line, take the columns of tens of centimeters of sensors B, C and D:

1110 + 0111 + 0011 = 11000

hmm, and the checksum in the third byte 0111. And what if minus one?

1110 + 0111 + 0011 - 1 = 10111

matches if you drop the extra bit. Check in other lines:

1110 + 0111 + 0011 - 1 = 10111 (oh, it all happened again)
0101 + 0111 + 0011 - 1 = 0111 (here without dropping)
1111 + 1100 + 1100 - 1 = 100110 (here already two bits overflowed)
0001 + 0101 + 0011 - 1 = 1000 (without dropping)

Hurray, everything coincided!
We still have unmarked columns. They probably belong to the second checksum, so let's try to sum up:

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

mda, not enough in common with the second checksum. Let's see how much you need to subtract to coincide:

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

I have already seen it somewhere ... well, yes, the first checksum! The dependence is simple - from the second CS, it is necessary to take away what we have discarded as overflow when calculating the first CS, only xor'ennoe from 11. That is, discarding 00 (nothing) from the first COP, from the second subtracting 11, etc.

Whew, like everything. There are two unused bits left, but they seem to be always one.

Step five. Radio cleaning


In general, I am not a supporter of the use of radio channels anywhere. The air is already decently polluted, so that it will all work in places (geographically) rather unstable. Therefore, let us deal with the fact that we will eject the receiver and transmitter from the PDC by connecting the base unit, the display unit and our microcontroller by wires. Why do I mention the display unit, although I was not going to put it? And because of the tweeter. Still, the transfer from the PDC base unit to our microcontroller, there is decoding, then sending to the head unit, there again decoding and rendering will make an uncritical, but noticeable lag in the distance display. Therefore, the display unit will remain in the depths of tidy and will obviously squeak faster (although in the future, maybe I will make my microcontroller squeak).

It would be possible not to bathe and connect all the blocks with wires just like in debug mode, directly. However, throwing a miserable 5 volt TTL through the whole car, believe me, is not a good idea. Therefore, we solder into all three devices of the MAX485 microcircuit, implementing the transfer via a much more reliable RS-485 interface. In general, something like that (sorry for the unwashed flux). Base unit:



on the place of the white circle in the upper right corner of the board there was an R433A chip, the Q11 transistor and the resistor, instead of which the wires were soldered, were also removed from its piping. And in the free space it was possible to arrange the microcircuit so that the legs fell on the negative contact and several other suitable contacts. Since the base unit is always a transmitter, the DE and RE legs can be permanently closed by +5 volts. Lines A and B of the RS485 interface are connected to an additional terminal.

Display Unit:



Well, here in general beauty, MAX485 soldered almost like a native instead of the standing RF83C receiver chip . The DO data output legs and the minus GND, the DE and RE legs, coincided, since this part is always the receiver, they are planted on the ground. The rest required just one jumper.

Works as before:



I’ll probably post a picture of my own microcontroller in the article about the rest of the KMENevoBT functionality from the github.

Finally, the full decoding code of the parcel from the PDC from the debugging program in Delphi:

Well, do not hit, it's just a debugging code on the knee
 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; 



Step Six. findings


Perhaps at some point it was worth to abandon further excavations and order the same parking sensors from Ebay, which the Italian from the forum was picking up on the first link, but I liked the parking sensors themselves. It works very quickly and accurately. I had to finish, already out of principle.
What the Chinese smoked, developing such a protocol, is unclear.

Lyrical digression
I had a chance several years ago to unearth the diagnostic protocol for Ford cars with the brains of EEC-IV. These are such brains on the basis of Intel's set of 8096, it seems. In general, antiquity is at the level of a little bit of 8086. It was put on Fords from the mid 80s and up to the beginning of the 2000s, somewhere from the beginning of the 90s there was implemented the Data Communication Link Diagnostic Protocol (DCL). So, most likely due to performance limitations, it was impossible to make an exchange protocol with asynchronous data exchange, like the COM port of a computer. Therefore, the protocol was synchronous. This meant that when the diagnostic was activated, the brains started sending “frames” along the data line, consisting of words that are strictly synchronized in time. To communicate with the brains, it was necessary to send their words to the specified intervals between the received words with an accuracy of tens of microseconds. No computer with a COM port provided the required accuracy of sending bytes. I had to do on the microcontroller ...
Hmm, so what am I ... Ah, here. In that case, such an interesting exchange protocol was justified by the limitations of the chip. In the case of this PDC, I do not see any logic of just such a “dance” bit. What prevented to arrange, for example, 4x4 bits of tens of centimeters, then 4 bits of units of centimeters, 4 more bits for expanding up to 5 bits of sensors B and C and any service bits and at the end the checksum of all bits (just the sum, without distortions, as a last resort standard CRC8).


By the way, knowing the exchange protocol, you can apply this parking sensor not only on the car, but, for example, on a homemade robot. Yes, there are separate ultrasonic sensors for robots, but here there are four of them at once and they are read by one leg of the arduin, albeit with a delay of several milliseconds.

All read all the best!

PS I wanted to add a survey about whether to write about the excavation of the protocol of the brain for gas injection KME Nevo Pro. There, several other techniques were used ... But I think, rather, it is necessary to ask about "do we need such car searches at Habré?".

Source: https://habr.com/ru/post/249409/


All Articles