Reverse engineering the BKM-15R

This post is still being worked on, but for the sake of sharing, I’ve already made it public so people can start trying some of it. I will update it as I figure out more stuff…

You can find a quick C implementation of this written for the Linux terminal at https://github.com/skumlos/bkm-15r-app

You can also find an implementation for ESP32+ENC28J60 at https://github.com/skumlos/bkm-15rduino

So related to the BKM-68X, an almost just as important unit to have when it comes to the BVM-A series, is the remote control, the BKM-15R. The BKM-15R is a “weird” inbetween unit, switching from the serial RS422 transmission of the BKM-10R remotes, to Ethernet/LAN connections, and apparently “quickly” evolved to the BKM-16R and newer for Sonys professional LCD series. As far as I know, only the BVM-A series use the BKM-15R, and nothing else. While the BKM-16R *should* work for BVM-A monitors, allegedly the function buttons of it do not, which is kinda a bummer, even though you *can* reach most if not all, of that functionality through menus. Still not so cool though. So what to do? Let’s map the BKM-15R and figure out if we can emulate it!

So to analyze network traffic, the infinitely awesome WireShark application comes handy. Since we need to listen in on a LAN connection, the easiest way is to find a managed switch that supports Port Mirroring, so a third unit will get the packets coming in and out of the network ports of either where the monitor is attached to the switch, or where the remote is. By doing this, we don’t interfere with the communication at all, we simply get to see everything going through the interface in question.

The remote has two modes, peer-to-peer and LAN, selected by a switch on the back. Peer-to-peer is when the remote is connected directly to the monitor by a network cable (straight through) and LAN mode is the more esoteric version, where the remote lives on a network with other units. The same goes for the monitor. Everything below relates to remote and monitor being in peer-to-peer mode. While I suspect it should work just as well in LAN mode, I have not tested any of that. Everything here is tested with an original BKM-15R and a BVM-A20F1E.

When the remote starts up in peer-to-peer mode, it first ARP’s “192.168.0.1”. The remotes standard IP address is 192.168.0.100. By default the monitors’ IP is 192.168.0.1. The monitor broadcasts some information about it, like model number and such from time to time.

When the remote gets a positive ARP response, it opens up a TCP socket to the monitor on port 53484.

Packet format

Each packet from remote to monitor starts with a header like this, top part is the “decoded”, bottom raw bytes:

0x03 0x0B 'SONY' 0x00 0x00 0x00 0xB0 0x00 0x00 <PAYLOAD_LENGTH> <PAYLOAD> 
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 <PAYLOAD_LENGTH> <PAYLOAD>

The monitor responds with a packet whose header is:

0x03 0x0B 'SONY' 0x00 0x00 0x01 0xB0 0x00 0x00 <PAYLOAD_LENGTH> <PAYLOAD>
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 <PAYLOAD_LENGTH> <PAYLOAD>

<PAYLOAD_LENGTH> is the length in bytes of the payload. Note that <PAYLOAD_LENGTH> can be zero, and thus there is no data returned.

Payloads

The payloads are ASCII based, meaning everything, even numbers, are encoded in ASCII. Note that the “commands” in the payload are generally separated by 0x20, meaning ASCII space.

Status probing

When the remote has connected, it starts continuously asking for the status of the monitor (roughly every 500ms), and the monitor replies with a 5 word status packet, like this:

Remote:

<HEADER> 'STATget' 0x20 'CURRENT' 0x20 0x35 0x00
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x12 0x53 0x54 0x41 0x54 0x67 0x65 0x74 0x20 0x43 0x55 0x52 0x52 0x45 0x4E 0x54 0x20 0x35 0x00

Red is the header part, 0x12 last in the header is the payload length (0x12 = 18 bytes after the header).

Monitor will then return something like this:

<HEADER> 'STATret' 0x20 'CURRENT' 0x20 8000 0x20 0000 0x20 0000 0x20 0000 0x20 0000
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x28 0x53 0x54 0x41 0x54 0x72 0x65 0x74 0x20 0x43 0x55 0x52 0x52 0x45 0x4E 0x54 0x20 0x38 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30

Red is the header part, 0x28 is the payload length (0x12 = 40 bytes after the header). Bold parts are the status words. From this it can be seen that the monitors status words are 0x8000 0x0000 0x0000 0x0000 0x0000.

By pressing the buttons, it can be seen how the status words change depending on what is activated, like if the Aspect Ratio (16:9) button is pressed, the monitor returns:

0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x28 0x53 0x54 0x41 0x54 0x72 0x65 0x74 0x20 0x43 0x55 0x52 0x52 0x45 0x4E 0x54 0x20 0x38 0x30 0x30 0x32 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30 0x20 0x30 0x30 0x30 0x30

So the status words are now 0x8002 0x0000 0x0000 0x0000 0x0000. Thus it can be seen that, just like on the BKM-10R, the status bits are OR’ed together. By going through all the buttons I have deduced the following status bits:

Status word 0:
0x8000 - Power On (CRT On)
0x0400 - Scanmode (Underscan)
0x0200 - Horizontal Delay
0x0100 - Vertical Delay
0x0080 - Monochrome
0x0040 - Char mute (Char Off)
0x0020 - Marker Mode
0x0010 - External Sync
0x0008 - Aperture On
0x0004 - Chroma Up
0x0002 - Aspect Ratio (16:9)

Status word 1:
Unknown / unused

Status word 2:
0x0100 + 0x0080 - Menu is showing (both seem to be on) 
0x0040 - Color Temp
0x0020 - Comb
0x0010 - Blue Only
0x0004 - R Cutoff
0x0002 - G Cutoff
0x0001 - B Cutoff

Status word 3:
0x0080 - Phase Manual
0x0040 - Chroma Manual
0x0020 - Brightness Manual
0x0010 - Contrast Manual

Status word 4:
Unknown / unused

Status changing

When a button is pressed, the monitor issues a status change ‘STATset’ packet plus the button name plus the word ‘TOGGLE’ (except for degauss) with spaces between each “part”. So for instance aspect ratio toggle sends:

<HEADER> 'STATset' 0x20 'ASPECT' 0x20 'TOGGLE'  
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x15 0x53 0x54 0x41 0x54 0x73 0x65 0x74 0x20 0x41 0x53 0x50 0x45 0x43 0x54 0x20 0x54 0x4F 0x47 0x47 0x4C 0x45

The monitor replies with the standard header, with no payload, so <PAYLOAD_LENGTH> is zero:

0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x00

So by pressing all the buttons, the list of possible status button names are (most quite self explanatory):

"POWER" - Power
"DEGAUSS" - Degauss
"SCANMODE" - Scanmode (Underscan)
"HDELAY" - Horizontal delay
"VDELAY" - Vertical delay
"MONOCHR" - Monochrome
"APERTURE" - Aperture
"COMB" - Comb
"CHARMUTE" - Char Off
"COLADJ" - Color Temp.
"ASPECT" - Aspect ratio (16:9)
"EXTSYNC" - External sync
"BLUEONLY" - Blue only
"RCUTOFF" - R Cutoff
"GCUTOFF" - G Cutoff
"BCUTOFF" - B Cutoff
"MARKER" - Marker mode
"CHROMAUP" - Chroma up

"MANPHASE" - Manual phase
"MANCHR" - Manual chroma
"MANBRT" - Manual brightness
"MANCONT" - Manual contrast 

The navigation buttons and input keypad are split into another “group” of buttons, the INFO group. Presses are announced <HEADER> 'INFObutton' 0x20 <BUTTON_NAME> 0x20. So for instance button ‘1’ is announced like this:

0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x0D 0x49 0x4E 0x46 0x4F 0x62 0x75 0x74 0x74 0x6F 0x6E 0x20 0x31 0x20

Note the second last byte, 0x31, which is ASCII for ‘1’. Button 2 is then the same packet, except for the second last 0x31 byte instead being 0x32, so (difference marked in blue):

0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x0D 0x49 0x4E 0x46 0x4F 0x62 0x75 0x74 0x74 0x6F 0x6E 0x20 0x32 0x20

Button 3 is 0x33, and so on for 0-9. The remaining button names are:

"ENTER" - Ent at numeric keys
"DELETE" - Del at numeric keys
"MENU" - Menu
"MENUENT" - Menu Enter
"MENUUP" - Up
"MENUDOWN" - Down

So for example 'INFObutton' 0x20 'ENTER' 0x20 when pressing “Ent” at the numeric keys.

Knobs

The knobs are also part of the INFO group. When a knob is turning, the remote sends a packet about every 150 ms with the string “INFOknob”, name of the knob, and a designation of how far it has turned. An example turning the Chroma knob slowly clockwise:

<HEADER> 'INFOknob' 0x20 'R' 0x20 'CHROMA' 0x20 '96/1/25'
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x19 0x49 0x4E 0x46 0x4F 0x6B 0x6E 0x6F 0x62 0x20 0x52 0x20 0x43 0x48 0x52 0x4F 0x4d 0x41 0x20 0x39 0x36 0x2F 0x31 0x2F 0x32 0x35

I am still somewhat unsure of how to interpret the output, except that the 96 is static, the next part (1) can be small or “large” (like 7), usually depending on how fast one turns the knob, positive values are clockwise, negative values counterclockwise. The last part (25) seems to be saying something about how far it has rotated, but I have seen both values be negative, which makes not so much sense.

Here is an example of the brightness knob turning counterclockwise at about the same pace as above:

<HEADER> 'INFOknob' 0x20 'R' 0x20 'BRIGHTNESS' 0x20 '96/-2/21'
0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x1E 0x49 0x4E 0x46 0x4F 0x6B 0x6E 0x6F 0x62 0x20 0x52 0x20 0x42 0x52 0x49 0x47 0x48 0x54 0x4E 0x45 0x53 0x53 0x20 0x39 0x36 0x2F 0x2D 0x32 0x2F 0x32 0x31

The knobs’ names are:

'R PHASE' - Phase
'R CHROMA' - Chroma
'R BRIGHTNESS' - Brightness
'R CONTRAST' - Contrast

Memory card

Haven’t gotten to that yet…

Wrap-up

All in all its a pretty simple protocol that can be reproduced in almost any “normal” programming language and on any platform that supports raw TCP sockets on a LAN connection.

Any questions, comments or whatever, send me an email at martin@hejnfelt.com

Comments are closed.