Attempting to reverse-engineer a checksum for a serial timing device that updates a display. Protocol is partly understood, however the checksum calculation specified does not produce the expected value.
I would like to understand what is happening in this protocol, as I would like to write to the display with my own software and not just sniff the data.
Known parameters:
- RS-485 half duplex
- 8n1
- DATA0...n transmitted unchanged, low nibbles are BCD (0-9)
- (EDIT) this is likely not the case, as the spec I have does not seem to be the correct one. Disregard the following point. ADDR byte and CSUM byte split into two bytes each, hi and lo nibbles might be split between the two bytes, however I'm confused as to why it appears that the ADDR byte is a start flag byte.
- sub_ADDR matches the protocol specification, used to identify the data in the packet to a specific part of the display, and possibly to different displays
- One-way communication (EDIT) one-to-many master-slave likely
- FEC appears to be "repeat the message" as all packets are immediately duplicated in the recorded stream, i.e. 1,1,2,2,3,3..etc.
- Receiver likely "pretends it never happened" if packets are mangled as past data is irrelevant
- (EDIT) All packets are of known length and the lengths do not change in the data set
(EDIT: CRITICAL INFO) Low nibbles of bytes labeled A3-A6 and B3-B6 represent digits 0-9 as BCD. A value of 0x0f would blank the digit on the display.[1] Therefore:
0a 0c 02 00 60 00 80 0b -> "20:00" 28 0c 02 00 60 00 80 -> "20:00" 0a 0c 01 09 65 09 d8 0b -> "19:59" 0a 0c 0f 09 65 09 80 0b -> " 9:59"
- (EDIT) High nibbles of A3-A6 contain flag bits. Known flag bits include bit 6 in DATA2 (A5,B5) which indicates state == running, and bit 5 in DATA2 (A5,B5) which indicates colon == on.
First question: If the FEC is indeed "repeat", does it follow that the designer would incorporate a CSUM? Perhaps the packets that appear to be CSUM are something else not documented?
Packet format:
as described in protocol spec, possibly incorrect or outdated. (EDIT: the protocol spec does appear to be incorrect for my device. Corruption of the data is unlikely)
"X" denotes a nibble that takes a value. "?" denotes a nibble that is used for flag bits.
| lo_ADDR | hi_ADDR | sub_ADDR | DATA_0 | DATA_1 | DATA_2 | DATA_3 | lo_CSUM | hi_CSUM |
| 0x8X | 0x9X | 0x0c | 0x?X | 0x?X | 0x?X | 0x?X | 0xAX | 0xBX |
Some of the fields seem to match; others don't. As seen below, lo and hi ADDR and lo and hi CSUM don't seem to match the description.
Most packets in the stream appear to start with 0x0a and end with 0x0b, although some packets observed don't follow that pattern. Are they flag bytes?
An example of two packets of interest, bytes that work as specified marked ok, ones that don't with !k
A1 A2 A3 A4 A5 A6 A7 A8 B1 B2 B3 B4 B5 B6 B7
------------------------------------------------
!k ok ok ok ok ok !k !k !k ok ok ok ok ok !k
0a 0c 02 00 60 00 80 0b 28 0c 02 00 60 00 80
Straight from Pyserial, appears as follows:
b'\n\x0c\x02\x00`\x00\x80\x0b(\x0c\x02\x00 \x00\x80'
Ten lines of packets, two neighboring packets shown, repeats removed: (edit: added more lines, formatting for clarity)
A1 A2 A3 A4 A5 A6 A7 A8 B1 B2 B3 B4 B5 B6 B7
--------------------- state == running --------------
----------------------(A3, B3 <= 0x09)---------------
0a 0c 01 09 65 09 d8 0b .repeat. 28 0c 01 09 65 09 e0 .repeat. ...
0a 0c 01 09 65 08 c0 0b ........ 28 0c 01 09 65 08 80 ...
0a 0c 01 09 65 07 a8 0b ........ 28 0c 01 09 65 07 a0 ...
0a 0c 01 09 65 06 90 0b ........ 28 0c 01 09 65 06 c0 ...
0a 0c 01 09 65 05 f8 0b ........ 28 0c 01 09 65 05 e0 ...
0a 0c 01 09 65 04 e0 0b ........ 28 0c 01 09 65 04 80 ...
0a 0c 01 09 65 03 c8 0b ........ 28 0c 01 09 65 03 a0 ...
0a 0c 01 09 65 02 b0 0b ........ 28 0c 01 09 65 02 c0 ...
0a 0c 01 09 65 01 98 0b ........ 28 0c 01 09 65 01 e0 ...
0a 0c 01 09 65 00 80 0b ........ 28 0c 01 09 65 00 80 ...
0a 0c 01 09 64 09 e0 0b ........ 28 0c 01 09 64 09 80 ...
0a 0c 01 09 64 08 80 0b ........ 28 0c 01 09 64 08 80 ...
0a 0c 01 09 64 07 a0 0b ........ 28 0c 01 09 64 07 80 ...
0a 0c 01 09 64 06 c0 0b ........ 28 0c 01 09 64 06 80 ...
0a 0c 01 09 64 05 e0 0b ........ 28 0c 01 09 64 05 80 ...
0a 0c 01 09 64 04 80 0b ........ 28 0c 01 09 64 04 80 ...
0a 0c 01 09 64 03 a0 0b ........ 28 0c 01 09 64 03 80 ...
0a 0c 01 09 64 02 c0 0b ........ 28 0c 01 09 64 02 80 ...
0a 0c 01 09 64 01 e0 0b ........ 28 0c 01 09 64 01 80 ...
0a 0c 01 09 64 00 80 0b ........ 28 0c 01 09 64 00 80 ...
... ...
0a 0c 01 09 64 04 a0 0b ........ 28 0c 01 09 63 04 80 ...
... ...
-------------------- state == stopped -------------------
------------(high nibble A5, B5 0b110 -> 0b10)-----------
------------(bit 6 in byte A5, B5 == flag bit)-----------
0a 0c 01 09 23 04 a0 0b ........ 28 0c 01 09 23 04 80 ...
... ...
---------------- state == running | stopped -------------
------------(if A3, B3 == 0x0f, then A7, B7 -> 80)-------
------------(A3, B3 == 0x0f == no digit displayed)-------
... ...
0a 0c 0f ** ** ** 80 0b ........ 28 0c 0f ** ** ** 80 ...
... ...
0x0a << 2 == 0x28. Is that just coincidence? Or are some bits handled improperly?
It would appear that A7 and B7 are 8 bit CSUM values. However, when the following CSUM calculation is done as specified in the documentation:
CSUM = lo_ADDR ^ hi_ADDR ^ sub_ADDR ^ DATA_0 ^ DATA_1 ^ DATA_2 ^ DATA_3
0x62 = 0xa ^ 0x0 ^ 0x0c ^ 0x01 ^ 0x09 ^ 0x65 ^ 0x09
0x62 = 0x8 ^ 0x2 ^ 0x0c ^ 0x01 ^ 0x09 ^ 0x65 ^ 0x09
The calculated value doesn't match A7 or B7. However:
"The CSUM byte is then split into 2 bytes, 0xAX (low nibble) and 0xBX (high nibble)"
I've tried https://crccalc.com as well as others, thinking that the calculation in the specification is incorrect, however I can't get any matches on any 8-bit CSUMs that I've tried.
I think I'm missing something obvious, as I'm quite an amateur. I'm really trying to understand what's happening, where I've gone wrong, and not just get my problem solved. I'm now two days into trying to figure this out, and I just can't get it.
Quesion two: Are those fields actually checksums, if they are, how are they calculated?
Question three: If they're not, what are they?
Question four: Why would some packets appear to be missing (or have a different) start and stop flag bytes? (EDIT: Most likely because the specification I have is wrong)
(EDIT) The CSUM calc in the spec does not apply to my data.
Check sum calculation spec, verbatim from documentation:
CSUM = LOW ADDR
CSUM = CSUM XOR'ed with HIGH ADDR
CSUM = CSUM XOR'ed with SUB ADDR
CSUM = CSUM XOR'ed with Data0
..
CSUM = CSUM XOR'ed with DataN (the last data-byte)
The CSUM byte is then split into 2 byte, 0xAn (low nibble) and 0xBn (high nibble)
Some interesting patterns which seem to hold throughout my data set:
- if A3,B3 == 0x0f then A7,B7 == 0x80
- High nibble A5,B5 == 0x6 | 0x2: change does not seem to affect A7,B7
- if A6,B6 == 0x00 then A7,B7 == 0x80
(EDIT) UPDATE: You know, the more I think about it and look around the internet, this system shares many similarities with DMX protocol. This system was made to work with many devices daisy-chained together.
- One-to-many connection, with termination required at the end
- 0x00 break on every line, similar to lo value inter-packet (line?) break, bursts of data about once a second
- possibly no error correction at all
- used to control a "lighting" device (7-segment display)
- others ...?
[1] "The Texas Instruments seven-segment display decoder chips 7446/7447/7448/7449 and 74246/74247/74248/74249 and the Siemens FLH551-7448/555-8448 chips used truncated versions of "2", "3", "4", "5" and "6" for digits A–E. Digit F (1111 binary) was blank." - wikipedia, 7-segment display