Skip to content
PNKP237 edited this page Apr 10, 2024 · 8 revisions

Programming for Opel displays

I would like to use this place to document how to send stuff to the displays in more depth, hopefully saving others from spending countless hours on end with a logic analyzer, watching whatever is happening on the bus - not much entertainment to be had there. While reverse engineering efforts by JJToB saved me a lot of time, there is still more to be said on the quirks of various Opel displays. I will repeat some stuff from his research for clarity.

Printing data

Conversion to UTF-16

Text written to the display should be encoded in UTF-16BE. Simple way to achieve that is to keep the upper surrogate byte as 0x0 and the lower surrogate can be whatever ASCII you desire. Proper conversion is not as simple, but you can check "TextHandler.ino" for a memory-safe implementation. Also see "Unrecognized chars" in the "Pitfalls to avoid" section.

Waiting for the display to acknowledge your transmission

After sending the first message (data byte 0 0x10) you need to wait for the display to acknowledge your transmission.

6C1 # 10 6F 40 00 6C 03 10 0F

This usually happens after few milliseconds in the form of a message with identifier 0x2C1:

2C1 # 30 00 00 00 00 00 00 00

Afterwards, data frames can be sent in rapid succession.

Text formatting

These displays have some basic ability to format how the text is displayed on screen. Formatting strings have to be included in each line preceding the text you're printing to the display and as such they count towards the total character count. Each of these "formatting commands" is prefixed with an escape char which is not visible in the browser (0x1B).

[fS_gm - 7 chars, results in regular size text, aligned to the left side of the screen

[fS_dm - 7 chars, results in reduced size text, aligned to the left side of the screen

[cm - 4 chars, results in centered text

Few more which I have not tested - I believe these only apply to GID and CID, possibly formatting in the "Audio" source selection menu:

[lm
[tr31m
[tl34m
[fS_bm

Sending an empty field or clearing a field

In order to clear a field on the screen you can either send a line containing nothing (with specified length of 0) or containing a whitespace, omiting the formatting data (then the length should be 1). Factory radios do it using the latter method. If you don't clear a text field it will clear itself after some time.

Single line displays (TID, BID, 1-line GID)

When sending text strings to a single-line display you can skip sending strings to IDs other than 10 (equivalent to the middle line of a 3-line displays like GID and CID). It is not necessary but it saves some processing time.

Preventing radios from updating the display

It is possible to abuse ISO 15765-2 flow control in order to have the radio stop its transmission before the whole message is sent, which will result in the display not updating with "Aux". Just transmit a message with identifier 0x2C1, with databyte 0 value of 0x32 - this tells the transmitting node to abort its transmission.

2C1 # 32 00 00 00 00 00 00 00

Note that factory radios don't like their messages blocked repeatedly and will call it quits at some point, resulting either in radio rebooting or abrupt shutdown of all devices connected on the bus.

Another approach is to abuse variable frame separation time, which is set by the node meant to receive the message, but if you're faster, you can basically tell the radio to wait up to 127 ms between each frame. The value is set by databyte 2.

2C1 # 30 00 7F 00 00 00 00 00

That buys you enough time to transmit any text to the screen before the radio even sends one of its consecutive frames, meaning its transmission is not recognized by the display as valid.

Pitfalls to avoid

Unrecognized chars

When sending any text to the display you have to account for the fact that in case the letter is not recognized it will not get printed to the display (sic) but the text coming after the unrecognized character will not be displayed as well, even if it is valid. If you were to send a sequence containing three strings meant for the album, title and artist fields on the main audio screen, such as this:

dolor sit amet
Lorem ∆ ipsum
consectetur adipiscing

∆ is an unrecognized char - here's what will show up on the screen:

dolor sit amet
Lorem 
consectetur adipiscing

As you can see, this does not apply to data after the offending string. Other strings are unaffected, even if they were transmitted after the one containing unrecognized chars. In my case I filter out entire ranges of unsupported chars as part of the conversion to UTF-16, but even that has some issues. Character support may vary between the same model of a display with different firmware versions.

Messages containing no unused data are ignored

Let's say that the last part of the message containing text meant to be displayed will contain only useful data:

6C1 # 22 61 00 73 00 68 00 69

In this case we have UTF-16 text (please note that the upper surrogate of "a" (00) was transmitted as part of the previous frame):

0x(00)61 0x0073 0x0068 0x0069

Even if the amount of data and character count have been specified properly, EVERYTHING we have just transmitted would be ignored. Why - no idea. I found that a good workaround for this problem is to add a space in the last text field you're writing to, so there is always some unused data in the last message. Example:

6C1 # 22 61 00 73 00 68 00 69
6C1 # 23 00 20 AA BB CC DD EE
	       ^------------^ unused data

Now that the last message has been padded with an additional whitespace character (0x0020), remember to reflect that change in char count that comes after specifying the field you're writing to (which is why I prepare the whole message before sending anything) and the total payload (first message, databytes 1 and 4). Whatever is in those 5 bytes at the end does not matter but they have to be transmitted. Simple way to check for this is to count how many bytes are to be transmitted (that is required anyway since the first message has to specify total payload in bytes) and check the remainder after dividing it by 7. If it's 0, then add a space at the end.

CAN arbitration and errors

When writing to displays, some collision is to be expected, due to the radio sending its text payload approximately every 5 seconds when printing "Aux" (and more often if you're interacting with the radio or there's some RDS data, of course). Good approach is to watch for the size of the total message payload (message id 0x6C1, databyte 1) and wait for the radio to finish its business before sending your data.


Tested hardware

This "research" has been conducted using following display modules:

  • 2005 Astra H BID
  • 2006 Vectra C GID
  • 2008 Astra H CID
  • 2009 Astra H GID

Most of my testing is done on a '06 VC GID + CD30MP3 (Delphi-Grundig), as that is what I have on hand. Some additional sanity checks before major releases are done with BID+CD30MP3 and, of course, in my vehicle with GID+CD70Navi.

Clone this wiki locally