{"id":797,"date":"2022-01-27T09:47:39","date_gmt":"2022-01-27T07:47:39","guid":{"rendered":"https:\/\/immerhax.com\/?p=797"},"modified":"2022-05-27T14:14:00","modified_gmt":"2022-05-27T12:14:00","slug":"reverse-engineering-the-bkm-15r","status":"publish","type":"post","link":"https:\/\/immerhax.com\/?p=797","title":{"rendered":"Reverse engineering the BKM-15R"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\"><strong>This post is still being worked on, but for the sake of sharing, I&#8217;ve already made it public so people can start trying some of it. I will update it as I figure out more stuff&#8230;<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>You can find a quick C implementation of this written for the Linux terminal at https:\/\/github.com\/skumlos\/bkm-15r-app<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>You can also find an implementation for ESP32+ENC28J60 at https:\/\/github.com\/skumlos\/bkm-15rduino<\/strong><\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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 &#8220;weird&#8221; inbetween unit, switching from the serial RS422 transmission of the BKM-10R remotes, to Ethernet\/LAN connections, and apparently &#8220;quickly&#8221; 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&#8217;s map the BKM-15R and figure out if we can emulate it!<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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&#8217;t interfere with the communication at all, we simply get to see everything going through the interface in question.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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. <strong>Everything below relates to remote and monitor being in peer-to-peer mode.<\/strong> 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When the remote starts up in peer-to-peer mode, it first ARP&#8217;s &#8220;192.168.0.1&#8221;. The remotes standard IP address is 192.168.0.100. By default the monitors&#8217; IP is 192.168.0.1. The monitor broadcasts some information about it, like model number and such from time to time.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">When the remote gets a positive ARP response, it opens up a TCP socket to the monitor on port <strong><code>53484<\/code><\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"packet-format\">Packet format<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Each packet from remote to monitor starts with a header like this, top part is the &#8220;decoded&#8221;, bottom raw bytes:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">0x03 0x0B 'SONY' 0x00 0x00 0x00 0xB0 0x00 0x00 &lt;PAYLOAD_LENGTH&gt; &lt;PAYLOAD&gt; \n0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 &lt;PAYLOAD_LENGTH&gt; &lt;PAYLOAD&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The monitor responds with a packet whose header is:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">0x03 0x0B 'SONY' 0x00 0x00 0x01 0xB0 0x00 0x00 &lt;PAYLOAD_LENGTH&gt; &lt;PAYLOAD&gt;\n0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 &lt;PAYLOAD_LENGTH&gt; &lt;PAYLOAD&gt;<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><code>&lt;PAYLOAD_LENGTH&gt;<\/code>  is the length in bytes of the payload. Note that <code>&lt;PAYLOAD_LENGTH&gt;<\/code> can be zero, and thus there is no data returned.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"payloads\">Payloads<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">The payloads are ASCII based, meaning everything, even numbers, are encoded in ASCII. Note that the &#8220;commands&#8221; in the payload are generally separated by <code>0x20<\/code>, meaning ASCII space.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"status-probing\">Status probing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Remote:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;HEADER> 'STATget' 0x20 'CURRENT' 0x20 0x35 0x00\n<mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x12<\/mark> 0x53 0x54 0x41 0x54 0x67 0x65 0x74 0x20 0x43 0x55 0x52 0x52 0x45 0x4E 0x54 0x20 0x35 0x00<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Red is the header part, <code>0x12<\/code> last in the header is the payload length (<code>0x12<\/code> = 18 bytes after the header).<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Monitor will then return something like this:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;HEADER&gt; 'STATret' 0x20 'CURRENT' 0x20 <strong>8000<\/strong> 0x20 <strong>0000<\/strong> 0x20 <strong>0000<\/strong> 0x20 <strong>0000<\/strong> 0x20 <strong>0000<\/strong>\n<span class=\"has-inline-color has-vivid-red-color\">0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x28<\/span> 0x53 0x54 0x41 0x54 0x72 0x65 0x74 0x20 0x43 0x55 0x52 0x52 0x45 0x4E 0x54 0x20 <strong><span class=\"has-inline-color has-black-color\">0x38 0x30 0x30 0x30<\/span><\/strong> 0x20 <strong><span class=\"has-inline-color has-black-color\">0x30 0x30 0x30 0x30<\/span><\/strong> <span class=\"has-inline-color has-black-color\">0x20<\/span><span class=\"has-inline-color has-vivid-cyan-blue-color\"> <\/span><strong><span class=\"has-inline-color has-black-color\">0x30 0x30 0x30<\/span><\/strong> <strong>0x30<\/strong> 0x20 <strong>0x30 0x30 0x30 0x30<\/strong> 0x20 <strong>0x30 0x30 0x30 0x30<\/strong><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Red is the header part, <code>0x28<\/code> is the payload length (<code>0x12<\/code> = 40 bytes after the header). Bold parts are the status words. From this it can be seen that the monitors status words are <code>0x8000<\/code> <code>0x0000<\/code> <code>0x0000<\/code> <code>0x0000<\/code> <code>0x0000<\/code>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">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:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x28<\/mark> 0x53 0x54 0x41 0x54 0x72 0x65 0x74 0x20 0x43 0x55 0x52 0x52 0x45 0x4E 0x54 0x20 <strong>0x38 0x30 0x30 0x32<\/strong> 0x20 <strong>0x30 0x30 0x30 0x30<\/strong> 0x20 <strong>0x30 0x30 0x30 0x30<\/strong> 0x20 <strong>0x30 0x30 0x30 0x30<\/strong> 0x20 <strong>0x30 0x30 0x30 0x30<\/strong><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So the status words are now <code>0x8002<\/code> <code>0x0000<\/code> <code>0x0000<\/code> <code>0x0000<\/code> <code>0x0000<\/code>. Thus it can be seen that, just like on the BKM-10R, the status bits are OR&#8217;ed together. By going through all the buttons I have deduced the following status bits:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">Status word 0:\n0x8000 - Power On (CRT On)\n0x0400 - Scanmode (Underscan)\n0x0200 - Horizontal Delay\n0x0100 - Vertical Delay\n0x0080 - Monochrome\n0x0040 - Char mute (Char Off)\n0x0020 - Marker Mode\n0x0010 - External Sync\n0x0008 - Aperture On\n0x0004 - Chroma Up\n0x0002 - Aspect Ratio (16:9)\n\nStatus word 1:\nUnknown \/ unused\n\nStatus word 2:\n0x0100 + 0x0080 - Menu is showing (both seem to be on) \n0x0040 - Color Temp\n0x0020 - Comb\n0x0010 - Blue Only\n0x0004 - R Cutoff\n0x0002 - G Cutoff\n0x0001 - B Cutoff\n\nStatus word 3:\n0x0080 - Phase Manual\n0x0040 - Chroma Manual\n0x0020 - Brightness Manual\n0x0010 - Contrast Manual\n\nStatus word 4:\nUnknown \/ unused<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"status-changing\">Status changing<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">When a button is pressed, the monitor issues a status change &#8216;STATset&#8217; packet plus the button name plus the word &#8216;TOGGLE&#8217; (except for degauss) with spaces between each &#8220;part&#8221;. So for instance aspect ratio toggle sends:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;HEADER&gt; 'STATset' 0x20 'ASPECT' 0x20 'TOGGLE'  \n<span class=\"has-inline-color has-vivid-red-color\">0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x15<\/span> 0x53 0x54 0x41 0x54 0x73 0x65 0x74 0x20 0x41 0x53 0x50 0x45 0x43 0x54 0x20 0x54 0x4F 0x47 0x47 0x4C 0x45<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"> The monitor replies with the standard header, with no payload, so <code>&lt;PAYLOAD_LENGTH&gt;<\/code> is zero:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x01 0xB0 0x00 0x00 0x00<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So by pressing all the buttons, the list of possible status button names are (most quite self explanatory):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\"POWER\" - Power\n\"DEGAUSS\" - Degauss\n\"SCANMODE\" - Scanmode (Underscan)\n\"HDELAY\" - Horizontal delay\n\"VDELAY\" - Vertical delay\n\"MONOCHR\" - Monochrome\n\"APERTURE\" - Aperture\n\"COMB\" - Comb\n\"CHARMUTE\" - Char Off\n\"COLADJ\" - Color Temp.\n\"ASPECT\" - Aspect ratio (16:9)\n\"EXTSYNC\" - External sync\n\"BLUEONLY\" - Blue only\n\"RCUTOFF\" - R Cutoff\n\"GCUTOFF\" - G Cutoff\n\"BCUTOFF\" - B Cutoff\n\"MARKER\" - Marker mode\n\"CHROMAUP\" - Chroma up\n\n\"MANPHASE\" - Manual phase\n\"MANCHR\" - Manual chroma\n\"MANBRT\" - Manual brightness\n\"MANCONT\" - Manual contrast <\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"navigation-input\">Navigation\/input<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">The navigation buttons and input keypad are split into another &#8220;group&#8221; of buttons, the INFO group. Presses are announced <code>&lt;HEADER&gt; 'INFObutton' 0x20 &lt;BUTTON_NAME&gt; 0x20<\/code>. So for instance button &#8216;1&#8217; is announced like this:<\/p>\n\n\n\n<pre id=\"block-b132de62-dc15-43a7-80f1-f39ae5edb2a4\" class=\"wp-block-preformatted\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x0D<\/mark> 0x49 0x4E 0x46 0x4F 0x62 0x75 0x74 0x74 0x6F 0x6E 0x20 0x31 0x20<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Note the second last byte, <code>0x31<\/code>, which is ASCII for &#8216;1&#8217;. Button 2 is then the same packet, except for the second last <code>0x31<\/code> byte instead being <code>0x32<\/code>, so (difference marked in blue):<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\"><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-red-color\">0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x0D<\/mark> 0x49 0x4E 0x46 0x4F 0x62 0x75 0x74 0x74 0x6F 0x6E 0x20 <strong><mark style=\"background-color:rgba(0, 0, 0, 0)\" class=\"has-inline-color has-vivid-cyan-blue-color\">0x32<\/mark><\/strong> 0x20<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Button 3 is <code>0x33<\/code>, and so on for 0-9. The remaining button names are:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">\"ENTER\" - Ent at numeric keys<br>\"DELETE\" - Del at numeric keys<br>\"MENU\" - Menu<br>\"MENUENT\" - Menu Enter<br>\"MENUUP\" - Up<br>\"MENUDOWN\" - Down<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">So for example <code>'INFObutton' 0x20 'ENTER' 0x20<\/code> when pressing &#8220;Ent&#8221; at the numeric keys.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"knobs\">Knobs<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">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 &#8220;INFOknob&#8221;, name of the knob, and a designation of how far it has turned. An example turning the Chroma knob slowly clockwise:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;HEADER&gt; 'INFOknob' 0x20 'R' 0x20 'CHROMA' 0x20 '96\/1\/25'\n<span class=\"has-inline-color has-vivid-red-color\">0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x19<\/span> 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<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">I am still somewhat unsure of how to interpret the output, except that the <code>96<\/code> is static, the next part (<code>1<\/code>) can be small or &#8220;large&#8221; (like 7), usually depending on how fast one turns the knob, positive values are clockwise, negative values counterclockwise. The last part (<code>25<\/code>) 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.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Here is an example of the brightness knob turning counterclockwise at about the same pace as above:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">&lt;HEADER&gt; 'INFOknob' 0x20 'R' 0x20 'BRIGHTNESS' 0x20 '96\/-2\/21'\n<span class=\"has-inline-color has-vivid-red-color\">0x03 0x0B 0x53 0x4F 0x4E 0x59 0x00 0x00 0x00 0xB0 0x00 0x00 0x1E<\/span> 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<\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">The knobs&#8217; names are:<\/p>\n\n\n\n<pre class=\"wp-block-preformatted\">'R PHASE' - Phase<br>'R CHROMA' - Chroma<br>'R BRIGHTNESS' - Brightness<br>'R CONTRAST' - Contrast<\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"memory-card\">Memory card<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Haven&#8217;t gotten to that yet&#8230;<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"wrap-up\">Wrap-up<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">All in all its a pretty simple protocol that can be reproduced in almost any &#8220;normal&#8221; programming language and on any platform that supports raw TCP sockets on a LAN connection.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Any questions, comments or whatever, send me an email at martin@hejnfelt.com<\/p>\n","protected":false},"excerpt":{"rendered":"<p>This post is still being worked on, but for the sake of sharing, I&#8217;ve already made it public so people can start trying some of it. I will update it as I figure out more stuff&#8230; 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 &hellip; <a href=\"https:\/\/immerhax.com\/?p=797\" class=\"more-link\">Continue reading <span class=\"screen-reader-text\">Reverse engineering the BKM-15R<\/span><\/a><\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"closed","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[1],"tags":[],"class_list":["post-797","post","type-post","status-publish","format-standard","hentry","category-uncategorized"],"_links":{"self":[{"href":"https:\/\/immerhax.com\/index.php?rest_route=\/wp\/v2\/posts\/797","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/immerhax.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/immerhax.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/immerhax.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/immerhax.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=797"}],"version-history":[{"count":33,"href":"https:\/\/immerhax.com\/index.php?rest_route=\/wp\/v2\/posts\/797\/revisions"}],"predecessor-version":[{"id":846,"href":"https:\/\/immerhax.com\/index.php?rest_route=\/wp\/v2\/posts\/797\/revisions\/846"}],"wp:attachment":[{"href":"https:\/\/immerhax.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=797"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/immerhax.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=797"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/immerhax.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=797"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}