{"id":683,"date":"2013-09-11T11:13:05","date_gmt":"2013-09-11T15:13:05","guid":{"rendered":"http:\/\/www.logicalzero.com\/blog\/?p=683"},"modified":"2014-08-08T21:35:10","modified_gmt":"2014-08-09T01:35:10","slug":"check-out-my-atari-key-pad-part-3-making-it-work","status":"publish","type":"post","link":"http:\/\/www.logicalzero.com\/blog\/?p=683","title":{"rendered":"Check Out my (Atari Key-)Pad, Part 3: Making it Work"},"content":{"rendered":"<p><a href=\"http:\/\/www.flickr.com\/photos\/27076997@N00\/9613666281\/\" target=\"_blank\"><img loading=\"lazy\" class=\"alignleft size-full wp-image-700\" style=\"padding-right: 1em;\" alt=\"Keypad Hookup\" src=\"http:\/\/www.logicalzero.com\/blog\/wp-content\/uploads\/2013\/09\/keypad-hookup.jpg\" width=\"150\" height=\"150\" \/><\/a><em><strong>Note:<\/strong> for some reason, something in the combination of the code listings in this post, the syntax highlighter I&#8217;m using (Crayon), and my theme causes rendering problems in some browsers. I apologize if parts are difficult to read. I&#8217;m looking into it.<\/em><\/p>\n<p>To recap: In <a href=\"http:\/\/www.logicalzero.com\/blog\/?p=608\" title=\"Check Out my (Atari Key-)Pad, Part 1: Hardware Archeology\">part 1<\/a>, I figured out how the CX85 keypad works and how it&#8217;s wired; in <a href=\"http:\/\/www.logicalzero.com\/blog\/?p=627\" title=\"Check Out my (Atari Key-)Pad, Part 2: HID Hacking\">part 2<\/a>, I made a change to the Arduino&#8217;s HID library that will give me more control over the keypad emulation. Now, to write a sketch to drive it!<\/p>\n<p>Hooking the keypad up to an Arduino Leonardo was trivially easy. I used a male DB9 on a ribbon cable, the serial port from an old computer. A simple breakout board (the most complex part of the setup) just widens the 2&#215;5 header connector enough to fit across the gap of a breadboard, DIP style. I wired it to the Arduino somewhat out of order:<br \/>\n<!--more--><br \/>\n<center><\/p>\n<table style=\"background-color: white; color: black; text-align: center;\">\n<tbody>\n<tr>\n<th>DB9 Pin<\/th>\n<th>Name<\/th>\n<th>Arduino Pin<\/th>\n<\/tr>\n<tr>\n<td>1<\/td>\n<td><code>OUTA<\/code><\/td>\n<td>3<\/td>\n<\/tr>\n<tr>\n<td>2<\/td>\n<td><code>OUTB<\/code><\/td>\n<td>6<\/td>\n<\/tr>\n<tr>\n<td>3<\/td>\n<td><code>OUTC<\/code><\/td>\n<td>4<\/td>\n<\/tr>\n<tr>\n<td>4<\/td>\n<td><code>OUTD<\/code><\/td>\n<td>7<\/td>\n<\/tr>\n<tr>\n<td>5<\/td>\n<td><code>OUTE<\/code><\/td>\n<td>5<\/td>\n<\/tr>\n<tr>\n<td>6<\/td>\n<td><code>DATA_AV<\/code><\/td>\n<td>2<\/td>\n<\/tr>\n<tr>\n<td>7<\/td>\n<td><code>VCC<\/code><\/td>\n<td>+5V<\/td>\n<\/tr>\n<tr>\n<td>8<\/td>\n<td><code>GND<\/code><\/td>\n<td>GND<\/td>\n<\/tr>\n<tr>\n<td>9<\/td>\n<td colspan=\"2\"><em>(No connection)<\/em><\/td>\n<\/tr>\n<\/tbody>\n<\/table>\n<p><\/center><\/p>\n<p>This order was simply the tidiest layout on my little breadboard; I didn&#8217;t bother getting the <code>OUT*<\/code> pins in sequence. The only critical pin is <code>DATA_AV<\/code>, attached to Arduino pin 2; an interrupt handler listening to changes on that pin does all the work. It&#8217;s worth mentioning that pins 3-5 are all part of the same <a href=\"http:\/\/www.arduino.cc\/en\/Reference\/PortManipulation\" target=\"_blank\">port<\/a> on an Arduino Uno (specifically, <code>PORTD<\/code>), which I used for my initial experiments. The Leonardo uses a different microcontroller with a weirder layout of ports, so I&#8217;m just reading the inputs with old <code>digitalRead()<\/code>, but the consecutive pins are still useful for iterating.<\/p>\n<p>The Arduino sketch itself is moderately simple. As I mentioned earlier, it uses an interrupt handler (<code>handleKeypress()<\/code>)attached to pin 2 to actually handles the key presses on the keypad and send the corresponding USB keyboard scan codes. Note that the values sent as the first argument to <code>sendKey()<\/code> are the codes that the keyboard uses to represent the keys, not the characters.<\/p>\n<pre class=\"lang:arduino decode:true \" title=\"AtariKeypad.ino\" >\/*\r\n Atari CX85 Keypad to USB\r\n David Stokes, 2013\r\n http:\/\/www.logicalzero.com\/blog\/?cat=59\r\n*\/\r\n\r\n\/\/ The keypad's output for each of its keys\r\nconst byte KEYPAD_DEL =       B00100;\r\nconst byte KEYPAD_YES =       B00101;\r\nconst byte KEYPAD_5 =         B00110;\r\nconst byte KEYPAD_2 =         B00111;\r\nconst byte KEYPAD_ESC =       B01001;\r\nconst byte KEYPAD_NO =        B01100;\r\nconst byte KEYPAD_0 =         B01101;\r\nconst byte KEYPAD_8 =         B01110;\r\nconst byte KEYPAD_PLUSENTER = B01111;\r\nconst byte KEYPAD_4 =         B10100;\r\nconst byte KEYPAD_1 =         B10101;\r\nconst byte KEYPAD_6 =         B10110;\r\nconst byte KEYPAD_3 =         B10111;\r\nconst byte KEYPAD_7 =         B11100;\r\nconst byte KEYPAD_DOT =       B11101;\r\nconst byte KEYPAD_9 =         B11110;\r\nconst byte KEYPAD_MINUS =     B11111;\r\n\r\nstatic byte key;\r\n\r\nvoid handleKeypress() {\r\n  boolean d = digitalRead(2);\r\n\r\n  \/\/ Emergency escape. If pin 13 is LOW, output is inhibited.\r\n  if (!digitalRead(13))\r\n    return;\r\n\r\n  if (!d) {\r\n    \/\/ Pressed! \r\n    \/\/ Read the key pressed.\r\n    key = 0;\r\n    for (byte i=3; i&lt;8; i++)\r\n      key = (key &lt;&lt; 1) | digitalRead(i);\r\n\r\n    switch (key) {\r\n      \/\/ Standard keys: map directly to modern keys\r\n    case KEYPAD_MINUS:\r\n      sendKey(0x56, 0);\r\n      break;\r\n    case KEYPAD_PLUSENTER:\r\n      \/\/ The keypad \"Plus\" key is 0x57, \"Enter\" key is 0x58\r\n      \/\/ Enter is probably more useful.\r\n      sendKey(0x58, 0);\r\n      break;\r\n    case KEYPAD_1:\r\n      sendKey(0x59, 0);\r\n      break;\r\n    case KEYPAD_2:\r\n      sendKey(0x5A, 0);\r\n      break;\r\n    case KEYPAD_3:\r\n      sendKey(0x5B, 0);\r\n      break;\r\n    case KEYPAD_4:\r\n      sendKey(0x5C, 0);\r\n      break;\r\n    case KEYPAD_5:\r\n      sendKey(0x5D, 0);\r\n      break;\r\n    case KEYPAD_6:\r\n      sendKey(0x5E, 0);\r\n      break;\r\n    case KEYPAD_7:\r\n      sendKey(0x5F, 0);\r\n      break;\r\n    case KEYPAD_8:\r\n      sendKey(0x60, 0);\r\n      break;\r\n    case KEYPAD_9:\r\n      sendKey(0x61, 0);\r\n      break;\r\n    case KEYPAD_0:\r\n      sendKey(0x62, 0);\r\n      break;\r\n    case KEYPAD_DOT:\r\n      sendKey(0x63, 0);\r\n      break;\r\n\r\n    \/\/ Non-standard keys, arbitrarily mapped.\r\n    case KEYPAD_DEL:\r\n      \/\/ Send \"Del\" (from the cluster above the arrow keys)\r\n      sendKey(0x4C, 0);\r\n      break;\r\n    case KEYPAD_YES:\r\n      \/\/ Send multimedia keyboard \"Volume Up\"\r\n      sendKey(0x80, 0);\r\n      break;\r\n    case KEYPAD_NO:\r\n      \/\/ Send multimedia keyboard \"Volume Down\"\r\n      sendKey(0x81, 0);\r\n      break;\r\n    case KEYPAD_ESC:\r\n      \/\/ Sends \"Play\/Pause\" key (semi-standard)\r\n      \/\/ BUG: This doesn't actually work; this seem to be 'special' and may \r\n      \/\/ involve multiple bytes.\r\n      sendKey(0xE8, 0);\r\n      break;\r\n    }\r\n  }\r\n  else {\r\n    \/\/ Key released. If the user has pressed more than one key, the keypad\r\n    \/\/ only reports the ID of the last one, so releaseKeys() releases all\r\n    \/\/ keys, not just the one identified by OUT*. \r\n    releaseKeys();\r\n  }\r\n  key = 0;  \r\n}\r\n\r\n\r\nvoid setup(){\r\n  \/\/ The keypad's DATA_AV and OUT* should be attached to pins 2-7.\r\n  for (byte i=2; i&lt;8; i++)\r\n    pinMode(i, INPUT);\r\n\r\n  \/\/ Pin 13 is used as a safety mechanism. If LOW, keypad output is disabled,\r\n  \/\/ just in case a bug causes the Arduino to freak out. This can be safely \r\n  \/\/ removed later.\r\n  pinMode(13, INPUT_PULLUP);\r\n\r\n  \/\/ Keypad's DATA_AV is on pin 2, which corresponds to interrupt 1 on the \r\n  \/\/ Arduino Leonardo.\r\n  attachInterrupt(1, handleKeypress, CHANGE);\r\n}\r\n\r\nvoid loop() {\r\n  \/\/ This space intentionally left blank; the interrupt handler does all \r\n  \/\/ the real work.\r\n}\r\n\r\n\/\/ sendKey() was largely borrowed from:\r\n\/\/ https:\/\/github.com\/una1veritas\/Arduino\/tree\/master\/HID_Keyboard_Advanced\r\nvoid sendKey(byte key, byte modifiers)\r\n{\r\n  KeyReport report = { 0 };  \/\/ Create an empty KeyReport\r\n  report.keys[0] = key;  \/\/ set the KeyReport to key\r\n  report.modifiers = modifiers;  \/\/ set the KeyReport's modifiers\r\n  report.reserved = 1;\r\n  Keyboard.sendReport(&amp;report);  \/\/ send the KeyReport\r\n}\r\n\r\n\/\/ releaseKeys() was separated from sendKey() so that things like auto-repeat\r\n\/\/ still work. \r\nvoid releaseKeys() {\r\n  KeyReport report = { 0 };  \/\/ Create an empty KeyReport\r\n  report.reserved = 0;\r\n  Keyboard.sendReport(&amp;report);  \/\/ send the empty key report\r\n}\r\n<\/pre>\n<p>What&#8217;s next? On the hardware side:<\/p>\n<ul>\n<li> Use a smaller microcontroller board, one that can fit inside the keypad&#8217;s housing. The 9-pin cable is attached by a form of spade connector, so if I can find similar connectors, the entire operation can be done non-destructively.<\/li>\n<li>Use an entirely different microcontroller, one without the Arduino bootloader. The Arduino Leonardo was great for prototyping, but it is much larger and more powerful than I need &#8212; and, at $20, more expensive. I could use a microcontroller like <a href=\"http:\/\/www.atmel.com\/devices\/at90usb82.aspx\" title=\"AVR AT90USB82\" target=\"_blank\">AT90USB82<\/a> with <a href=\"http:\/\/www.fourwalledcubicle.com\/LUFA.php\" title=\"Lightweight USB Framework for AVRs\" target=\"_blank\">LUFA<\/a>. Since all I really need are eight GPIO pins (two for USB, six for reading the keypad), and the device doesn&#8217;t have to respond much faster than its human operator, I could use an even smaller\/cheaper microcontroller with <a href=\"http:\/\/www.obdev.at\/products\/vusb\/index.html\" title=\"V-USB\" target=\"_blank\">software USB emulation<\/a>.<\/li>\n<\/ul>\n<p>On the software side:<\/p>\n<ul>\n<li>Tidy up the sketch. The code isn&#8217;t the cleanest. Seeing it here, I can spot a couple of things I&#8217;d have done slightly differently, but these are moderately trivial. Better to post code I know works.<\/li>\n<li>Get the &#8216;Play\/Pause&#8217; key to work. Maybe this is just a Mac thing, but I&#8217;ve seen a couple of things that imply that some of the farthest &#8216;multimedia&#8217; keys function somewhat differently than the normal ones.<\/li>\n<li>Rewrite for a completely different framework, like LUFA. This would make at least the first point moot.<\/li>\n<\/ul>\n<p>If\/when I continue this project, I&#8217;ll continue posting my progress on the blog!<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Note: for some reason, something in the combination of the code listings in this post, the syntax highlighter I&#8217;m using (Crayon), and my theme causes rendering problems in some browsers. I apologize if parts are difficult to read. I&#8217;m looking into it. To recap: In part 1, I figured out how the CX85 keypad works [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":[],"categories":[59],"tags":[41,16,45,30],"_links":{"self":[{"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/683"}],"collection":[{"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=683"}],"version-history":[{"count":35,"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/683\/revisions"}],"predecessor-version":[{"id":724,"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=\/wp\/v2\/posts\/683\/revisions\/724"}],"wp:attachment":[{"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=683"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=683"},{"taxonomy":"post_tag","embeddable":true,"href":"http:\/\/www.logicalzero.com\/blog\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=683"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}