I just used some points I earned on Swagbucks to order a discontinued iCade Mobile controller for less than $5 (with Amazon Prime shipping). Currently, the price has gone up to $9, but either way, it’s a deal if you want an iCade circuit to mess with:
I chose the pink one because it was a buck less than the other color. Of course, now it’s going to seem mean to dissect something that “cute.”
The iCade devices, which I have written about before, started out as an April Fool’s joke at Think Geek in 2010. They act like a Bluetooth (or USB) keyboard and some games were written to interpret certain key presses as joystick buttons. Ever since iOS 7, Apple has added official support for game pads so the iCade format is pretty much dead. Still, there are a ton of old apps (over 100) that still support iCade (including Atari’s Greatest Hits and a few other retro emulators).
I plan to dissect mine and use it inside a cheap arcade-style joystick I have, thus allowing me to have something like a Tankstick for iOS (for games that support it), without having to spend any money. I am especially interested in using it for Pinball Arcade and plan to add some buttons on the sides to act as flipper buttons.
I just thought I’d share this, since it’s cheaper to buy this and gut it than to get a cheap Arduino Leonardo type device to hook up via USB adapter cables like my Atari joystick project.
If you get one and hack it in to something, let me know. I’d love to see what you come up with.
P.S. Since 4/15/2014, I have earned over $1419 in Amazon gift cards (and PayPal cash)! Sign up using my link and I get credit: http://swagbucks.com/refer/allenhuffman (Ask me for the tip/howto doc.)
There is a popular set of arcade style joysticks made by a company called X-Arcade. A friend of mine owns one, and he was kind enough to send me a few links with information about these joysticks, and I thought I would share them here.
First, there is an online (Java-based) test program that shows the Tankstick buttons working:
Of particular interest to me is the realization that Tankstick doesn’t appear as a USB joystick at all. Instead, it appears as a keyboard, and sends key presses for the joystick directions, buttons, etc. For models with a trackball (Tankstick+, for instance), I am guessing the trackball appears as a mouse. So, Tankstick is a keyboard/mouse, as opposed to an analog or (as I previously expected) digital joystick.
This means a third layer of support will be needed to create a conversion between Tankstick keyboard presses and the iCade sequences. This would be a very simple Arduino script since it removes any need for reading digital input pins or USB status bits. It does complicate things for me, however, since now I have to learn what it takes to expect data from either a keyboard or joystick (hopefully without requiring the user to switch). My ultimate goal is to have my code “just work” whether the user is wiring up their own controls to the digital inputs, or using a USB host shield and plugging up an analog or digital joystick, or keyboard device like the Tankstick.
Incidentally, the Tankstick still supports the “ancient” (by computer standards) PS/2 style keyboards as well, and those could be read on an Ardunio (or Teensy) using very little add-on hardware and digital pins. It seems a Tankstick-to-iCade converter could be done very easily, so maybe I can spin one of those off as well.
More updates when I have time to get back to this…
This document covers the button layout of the original iCade/iCade Core (same layout) used by the iPad, and the iCade Jr./iCade Mobile used by the iPhone/iPod Touch models. Only with the addition of photos showing the buttons for the iCade Mobile did I begin to understand I have been wrong about how the iCade organizes its buttons. Without one to test and play with, I have just been guessing based on available documentation.
All models provide either a joystick or a 4-way touch pad. For the original iCade (and the cost reduced iCade Core, which is the same unit but without the arcade-looking enclosure), there are eight buttons, organized as two rows of four buttons each. The buttons go red, black, black and white on each row, as if to indicate the group of 2×2 black buttons in the center are different than the red and white ones on the ends. I had been assuming that the main control buttons would be the black ones in the center, similar to the USB joystick I have been experimenting with, which labels the four buttons in the center with the Playstation symbols (square, circle, triangle, smiley face, cute kitty, whatever they are).
With the additional descriptions of the iCade Mobile button layout, I see now this is not the case. I had guessed that the buttons were numbered across, as in the first row being button 1-4 and the second row being button 5-8. This is not the case. Instead, the numbering looks more like this:
1 3 5 7
2 4 6 8
I realized this when I noticed the iCade Mobile was using the four buttons on the left (1-4) for it’s buttons, with 5-8 going to the front index finger buttons. The same was true for the iCade Jr., except buttons 5-8 were located on the back of that unit.
So, the four “primary” buttons for the iCade were 1-4, with two being red and two being black, and the other four (two red, two white) were the extra buttons. This was not what I expected, and it has led me to reorganize my button mapping for the iCade converter project.
There is only one diagram in the iCade documentation that numbers the buttons. Here it is, for the iCade Mobile:
Here it seems they number the directional buttons as 1-4, then the primary buttons as 5-8 and the final four as 9, 0, E1 (enter 1) and E2 (enter 2). With that in mind, I have changed my button definitions.
My arcade joystick has the primary buttons in the center, with the extras as grey buttons to the left and right:
So my arcade joystick thinks it is like a Playstation game pad, with the center color buttons being the right hand buttons, and the grey ones on the left and the right being the four buttons under the fingertips on the front of the gamepad.
If I wanted my joystick to use the same layout as the original iCade, I would have to map my buttons differently. With the joystick now being known as buttons 1-4, the iCade numbering layout looks like this:
1 (5) (7) (9) (E1)
3+4
2 (6) (8) (0) (E2)
I build my iCade array in the order of the 12 buttons:
// This is just used for printing out debug info to the console.
char iCadeDesc[][USB_BTN_COUNT] =
{"Up", "Down", "Left", "Right",
"Btn5", "Btn6", "Btn7", "Btn8",
"Btn9", "Btn0", "BtnE1", "BtnE2"};
So in order to map my joystick over, I created alternate defines for my buttons so they would be easier to figure out:
The group at the end just gives me Playstation-style labels for the buttons I will be using. So, for a gamepad style controller, my mappings would be like this:
Here you can see I assign my L1 button (top left grey) to be button 5 to the iCade, L2 for button 6, and so on. This will map my layout to match the original iCade.
It looks like I am going to need a way to switch the type of joystick being used, since clearly things are different. I will work on this next.
Oh, and I plugged up my Legacy Engineering Atari 2600 USB joystick and found it did not work at all with my converter. (Original company seems to be gone, but here is another recreation.) Instead of stuffing bits in Z1 and Z2, the Atari joystick puts them in the X and Y locations. 0 meaning left, 128 meaning center, and 255 meaning right. They are basically taking a digital joystick and faking out analog positions. The single button is mapped in to Z1 as a bit. (Inside this joystick you can hack on additional buttons, and I bet they fill out other bits in Z1.)
So, I now need to deal with two types of digital layouts (gamepad style, and fighting stick style) and an analog type stick (even one being faked, like the Atari).
2014/03/16 Update: The source code to this is now on GitHub. Check the Arduino link at the top of each page of this site.
2013/02/22 Update: WordPress seems to love to chew up source code, so the sources pasted in are not working properly. I will try to find a solution. Sometimes it works great, other times not at all.
Today, I picked up my Circuits@Home USB Host adapter from the post office. Now I could finally try to put together a converter that would read from a standard USB joystick and send out iCade formatted keyboard characters to my iPad. Since I had never worked with USB before, I was amazed it only took about 30 minutes to figure out. This is a testament to the work done by the folks responsible for the USB Host library code. The inclusion of functioning sample code allowed me to make quick modifications to do what I wanted.
I will post full details soon, but right now, here are some highlights.
For this project, I am using an Arduino Leonardo. Unlike the UNO and most other Arduinos, the Leonardo is capable of acting as a USB HID (human interface device) so it can appear to be a mouse or keyboard to the computer it is plugged in to. In my case, the computer would be an iPad via Apple’s Camera Connector Kit USB port adapter, and the Leonardo would be acting like a keyboard to send iCade keys,
I had previously downloaded the Arduino USB library from GitHub and extracted it in to my Arduino IDE library folder. I discussed my first experience with Arduino libraries in an earlier article when I was trying to get the iTead Studios USB shield to work.
I was able to test the USB by running various example programs that came with the library, specifically USBHidJoystick. Here is an example of that program’s output as I moved the joystick and pressed some buttons:
The joystick I am using is a “Playtech Pro Arcade Fighting Stick” sold on Amazon.com. (As of this posting, the price is $32, but it was about $26 back in December.) Moving the joystick around seemed to change bits in the Z1 value. The four arcade buttons changed bits in Z2. This joystick has a row of seven small control buttons on the top, and four of those buttons (marked Select, Start, L3 and R3) toggled the remaining four bits in Z1.
I do not know how standard USB joysticks are, but I would hope at least the joystick directions and primary buttons would be standardized. The four buttons that were different colors and labeled like Playstation controllers (triangle, circle, X, square) were the high bits of Z2, and the grey surrounding buttons (L1, L2, R1 and R2) were the lower four bits. Perhaps these grey buttons map to the front edge buttons found on console gamepads?
The iCade has a joystick and eight buttons, so I would need to figure out which of the USB buttons I would need to use.
This sample program would be the basis of my experiment. It was made of an Arduino sketch called USBHIDJoystick.pde, and two C++ files — hidjoystickrptparser.cpp and hidjoystickrptparser.h. It seemed the sketch would initialize the USB library, and specify the name of a custom function which would parse the USB data and pull out the joystick related bits. Inside the main loop() was just a call to a task handler function in the library, which I assume is the code responsible for polling and processing incoming USB data.
Inside the hidjoystickrptparser.cpp file, there was a function called OnGamePadChanged() which would print out various values. I planned to comment the print lines out, and just have it call my own button processing code which would parse the set bits rather than read digital input pins. I would just stick the Z1 and Z2 bytes together as a 16-bit integer:
void JoystickEvents::OnGamePadChanged(const GamePadEventData *evt)
{
// Call our joystick handler...
// We are going to combine the two Z1 and Z2 bytes in to a 16-bit value
// for easier parsing...
handleJoystick( (unsigned int)(evt->Z1< <8)|(evt->Z2) );
The GamePadEventData structure, defined in the .h file, contains five byte variables:
uint8_t X, Y, Z1, Z2, Rz;
I would just take the Z1, shift it to the left 8 bits, and OR in the Z2 value, creating a new 16-bit value that held both. In hex, it would look like this: 0xAABB, where AA is Z1 and BB is Z1. Now, inside my new handleJoystick() routine, I could look for those bits rather than scan digital I/O pins.
Instead of using the example sketch, I planned to just merge the USB specific items in to my existing teensy_icade sketch. This would be including some header files, and declaring some variables. The following code was lifted directly from the sample sketch:
// Header files, taken from USBHIDJoystick example.
#include
#include
#include
#include
#include <usb_ch9 .h>
#include
#include
#include
#include</pre>
<address>#include
#include
#include "hidjoystickrptparser.h"
#include #include
#include
#include
// Define some C++ stuff.
USB Usb;
USBHub Hub(&Usb);
HIDUniversal Hid(&Usb);
JoystickEvents JoyEvents;
JoystickReportParser Joy(&JoyEvents);
Inside my setup(), I would need to include the USB specific items. Since I have not found any documentation on the USB library, I can only assume what these functions do. Usb.Init() seems clear enough (though this code still attempts to run even if it fails, which is bad), and the Hid.SetReportParser() seems to be where the calling program passes in a structure containing the handling functions that the USB code will call when it gets a joystick packet. Or something.
// USB initialization stuff.
if (Usb.Init() == -1)
Serial.println("OSC did not start.");
delay( 200 );
if (!Hid.SetReportParser(0, &Joy))
ErrorMessage<uint8_t>(PSTR("SetReportParser"), 1);
After this, the only other bit of code I would be borrowing was to put a call to the USB task handler inside my loop():
// Handle USB
Usb.Task();
Now all I needed to do was pull out my pin reading code from the loop, and make it a separate function which would now be called, not by my loop, but when the USB handler had data to process.
Since my original code used an array of bytes, each representing what pin should be read to indicate the specific direction/button was active, I decided to use the same approach with this. I would use the same button mappings, but alter the defines so that instead of containing a byte value representing a pin number, each one would be a 16-bit value representing which bit was the one for the button. It looks like this:
In my original code, I called them “BTN1_PIN” or “RIGHT_PIN”, but I wanted to change the names to be more clear, and also so a future version might mix both capabilities in the same source code. My array of these items would be updated to hold 16-bit values (instead of bytes), and use the renamed defines:
// Each of these items is a 16-bit value, where the bits represent the 12
// iCade buttons.
unsigned int myPins[USB_BTN_COUNT] =
{UP_USB, DOWN_USB, LEFT_USB, RIGHT_USB,
BTN1_USB, BTN2_USB, BTN3_USB, BTN4_USB,
BTN5_USB, BTN6_USB, BTN7_USB, BTN8_USB};
You can see that I also renamed a count #define to be “USB_BTN_COUNT” instead of “DI_PIN_COUNT”. The other defines I used in the original were removed, since they were just used to error check the user in case they tried to build a version using pins outside of the allowed range.
My iCade array remains the same, though it will need to be customized to map the eight USB buttons to the proper iCade buttons once I figure out what they should be.
Now the real work could begin. I ripped out the entire loop that went through the myPins[] array and made only a few changes. Instead of reading the status of a digital pin in the array, I already knew the status since I was being passed in the bit value. I would just set status to that specific bit:
void handleJoystick(unsigned int buttonMask)
{
/*-------------------------------------------------------------------------*/
// Loop through each Digital Input pin.
for (int thisPin=0; thisPin < USB_BTN_COUNT; thisPin++ )
{
// Read the pin's current status.
unsigned int status = (buttonMask & myPins[thisPin]);
For digital pins, you read each pin and got a LOW or HIGH value. For this, I assume multiple bits could be set at the same time (?), so I wanted to take the buttonMask passed in from the USB code and just test the specific bit pattern for the button in question. I was still looping through all 12 buttons, but now I would be checking the status of a bit pattern to a bit mask in an array, rather than reading a digital pin and storing the status of that pin.
Now status would no longer be LOW or HIGH (0 or 1), but instead would either be 0x0000 or have a specific bit set like 0x0200 or 0x8000. Instead of comparing “status==LOW” I needed to simply check against it being 0. (My Teensy wiring used “active low” so LOW meant the button was pressed, and here the bit pattern being HIGH mean it was pressed.) That change looked like this:
// If pin is Active LOW,
if (status!=0)
{
(I still need to clean up the comments.)
I believe those were the only changes I needed to make, but my first test did not work. It seems my debounce code was causing some kind of issue. Since I assumed the joystick should already be doing denounce before sending out a USB packet with the button status, I tried just commenting out my debounce check and that got everything working. Almost.
The Teensy 2.0 had to be told what kind of USB device it was at compile time. A menu setting would toggle it between USB Serial, or USB Keyboard. With the Leonardo, that option did not appear in the IDE. A quick search revealed I needed to turn on the USB keyboard support in setup():
Keyboard.begin();
Once done, I could open up a text editor (so the “typing” from the Arduino had a place to show up) and move the joystick around and see the results on the screen:
“lv” represents BTN8 pressed then released. “hr” is BTN5. “we” is UP. It worked!
Testing on the iPad was next. Unlike the Teensy 2.0, which could run from the tiny 20mah of power the iPad USB port provided, the Leonardo and USB Host shield would be more demanding. I was not sure what the rules were with using an external power supply on an Arduino — didn’t I read somewhere that you had to change something to prevent problems if it was also connected to a computer’s USB port at the same time?
To be safe, I decided to use a powered USB hub. For testing, my setup looked like this:
JOYSTICK -> Leonardo -> USB Hub -> Mac
I figured all I would have to do is unplug the hub cable from the Mac, and plug it in to the iPad using the Apple Camera Connector kit.
It worked just fine, and I soon found myself testing it on Atari’s Greatest Hits (the first official app to support the iCade) as well as Gridlee (a free arcade game which runs on the MAME emulator).
The buttons were not where they needed to be, so I will need to fix that next.
But for now, I wanted to share my initial progress. I will clean up the code and post it soon.
(Edited on 02/12/2013 to fix the source code formatting, and to add a photo.)
NOTE: There is a native method of compiling code for the Teensy, using a C compiler, but this source code was built using the Teensyduino add-on. It enables the standard Arduino IDE GUI to build code for the Teensy. You have to install the Arduino IDE first, then you install the Teensyduino add-on on top of it.
This is my source code for the Teensy 2.0 iCade/Atari joystick interface. It could easily be made to work on an Arduino that has USB HID support (Arduino Leonardo, Esplora) and enough digital I/O pins. It is a bit bulky because I emit various status messages for testing, but I will at some point provide a stripped down version. I also have a version that uses the Debounce library which appears even smaller, but I like completely self-contained code where possible.
I have not got back to clean this up, so it includes some items commented out and such, but I wanted to provide this for those asking about it.
Or, this code could be modified to run on the new Arduino Esplora, which already has joystick buttons on it and USB HID output to act like a keyboard/mouse to whatever it’s hooked up to:
Keep in mind, the Teensy 2.0 is a low power device and it seems the 20mah of the iPad is enouhg to power it (running in low power mode, no LEDs, etc.). This may not be the case with an Arduino, and they may require hooking up via a powered hub before connecting to the iPad.
//*-----------------------------------------------------------------------------
Teensy iCade Input
by Allen C. Huffman (alsplace@pobox.com)
Monitor digital inputs, then emit a USB keyboard character mapped to an iCade
button depending on the pin status. The character will be the "hold" character
for pin connected (N.O. button push) and "release" character for pin
disconnected (N.O. button released).
Pin 11 is reserved for blinking the onboard LED as a heartbeat "we are alive"
indicator.
This software was written to allow a Teensy 2.0 to interface between arcade
buttons and an iPad via USB and Camera Connector Kit.
2012-12-04 0.0 allenh - Initial version, based on my ArduinoAIDI code.
-----------------------------------------------------------------------------*/
#define VERSION "0.0"
#define LED_OFF
//#include <eeprom .h>
//#include <avr/wdt.h>
/*
iCade keyboard mappings.
See developer doc at: http://www.ionaudio.com/products/details/icade
WE YT UF IM OG
AQ< -->DC
XZ HR JN KP LV
Atari joystick port, looking at the male DB9 on the Atari.
See: http://old.pinouts.ru/Inputs/JoystickAtari2600_pinout.shtml
1 2 3 4 5/ Up Dn Lt Rt PA
6 7 8 9/ Bt +5 Gd PB
*/
/*
The following I/O pins will be used as digital inputs
for each specific iCade function.
*/
#define UP_PIN 0
#define DOWN_PIN 1
#define LEFT_PIN 2
#define RIGHT_PIN 3
#define BTN1_PIN 4
#define BTN2_PIN 5
#define BTN3_PIN 6
#define BTN4_PIN 7
#define BTN5_PIN 8
#define BTN6_PIN 9
#define BTN7_PIN 10
#define BTN8_PIN 12
/*
The following keys are the iCade sequence (hold, release)
for each function. Send "W" to indicate UP, and "E" when
UP is released.
*/
#define UP_KEYS "we"
#define DOWN_KEYS "xz"
#define LEFT_KEYS "aq"
#define RIGHT_KEYS "dc"
#define BTN1_KEYS "yt"
#define BTN2_KEYS "uf"
#define BTN3_KEYS "im"
#define BTN4_KEYS "og"
#define BTN5_KEYS "hr"
#define BTN6_KEYS "jn"
#define BTN7_KEYS "kp"
#define BTN8_KEYS "lv"
#define DI_PIN_COUNT 12 // 12 pins used.
#define DI_PIN_START 1 // First I/O pin.
#define DI_PIN_END 20 // Last I/O pin.
byte myPins[DI_PIN_COUNT] =
{UP_PIN, DOWN_PIN, LEFT_PIN, RIGHT_PIN,
BTN1_PIN, BTN2_PIN, BTN3_PIN, BTN4_PIN,
BTN5_PIN, BTN6_PIN, BTN7_PIN, BTN8_PIN};
char iCadeKeymap[][DI_PIN_COUNT] =
{UP_KEYS, DOWN_KEYS, LEFT_KEYS, RIGHT_KEYS,
BTN1_KEYS, BTN2_KEYS, BTN3_KEYS, BTN4_KEYS,
BTN5_KEYS, BTN6_KEYS, BTN7_KEYS, BTN8_KEYS};
char iCadeDesc[][DI_PIN_COUNT] =
{"Up", "Down", "Left", "Right",
"Btn1", "Btn2", "Btn3", "Btn4",
"Btn5", "Btn6", "Btn7", "Btn8"};
/* We want a very short debounce delay for an arcade controller. */
#define DI_DEBOUNCE_MS 10 // 100ms (1/10th second)
#define LED_PIN 11
#define LEDBLINK_MS 1000
/*---------------------------------------------------------------------------*/
/* For I/O pin status and debounce. */
unsigned int digitalStatus[DI_PIN_COUNT]; // Last set PIN mode.
unsigned long digitalDebounceTime[DI_PIN_COUNT]; // Debounce time.
// unsigned long digitalCounter[DI_PIN_COUNT]; // Times button pressed.
unsigned int digitalDebounceRate = DI_DEBOUNCE_MS; // Debounce rate.
/* For the blinking LED (heartbeat). */
unsigned int ledStatus = LOW; // Last set LED mode.
unsigned long ledBlinkTime = 0; // LED blink time.
unsigned int ledBlinkRate = LEDBLINK_MS; // LED blink rate.
unsigned int pinsOn = 0;
/*---------------------------------------------------------------------------*/
void setup()
{
// Just in case it was left on...
// wdt_disable();
// Initialize the serial port.
Serial.begin(9600);
// Docs say this isn't necessary for Uno.
// while(!Serial) { }
showHeader();
// Initialize watchdog timer for 2 seconds.
// wdt_enable(WDTO_4S);
// LOW POWER MODE!
// Pins default to INPUT mode. To save power, turn them all to OUTPUT
// initially, so only those being used will be turn on. See:
// http://www.pjrc.com/teensy/low_power.html
for (int thisPin = 0; thisPin < DI_PIN_COUNT; thisPin++)
{
pinMode(thisPin, OUTPUT);
}
// Disable Unused Peripherals
ADCSRA = 0;
// Initialize the pins and digitalPin array.
for (int thisPin = 0; thisPin < DI_PIN_COUNT; thisPin++)
{
// Set pin to be digital input using pullup resistor.
pinMode(myPins[thisPin], INPUT_PULLUP);
// Set the current initial pin status.
digitalStatus[thisPin] = HIGH; // digitalRead(thisPin+DI_PIN_START);
// Clear debounce time.
digitalDebounceTime[thisPin] = 0;
// digitalCounter[thisPin] = 0;
}
// Set LED pin to output, since it has an LED we can use.
pinMode(LED_PIN, OUTPUT);
Serial.println("Ready.");
}
/*---------------------------------------------------------------------------*/
void loop()
{
// Tell the watchdog timer we are still alive.
// wdt_reset();
#ifndef LED_OFF
// LED blinking heartbeat. Yes, we are alive.
if ((long)(millis() - ledBlinkTime) >= 0)
{
// Toggle LED.
if (ledStatus == LOW) // If LED is LOW...
{
ledStatus = HIGH; // ...make it HIGH.
}
else
{
ledStatus = LOW; // ...else, make it LOW.
}
// Set LED pin status.
if (pinsOn == 0)
digitalWrite(LED_PIN, ledStatus);
// Reset "next time to toggle" time.
ledBlinkTime = millis() + ledBlinkRate;
}
#endif
// Check for serial data.
if (Serial.available() > 0)
{
// If data ready, read a byte.
int incomingByte = Serial.read();
// Parse the byte we read.
switch (incomingByte)
{
case '?':
showStatus();
break;
default:
break;
}
}
/*-------------------------------------------------------------------------*/
// Loop through each Digital Input pin.
for (int thisPin = 0; thisPin < DI_PIN_COUNT; thisPin++)
{
// Read the pin's current status.
unsigned int status = digitalRead(myPins[thisPin]);
// In pin status has changed from our last toggle...
if (status != digitalStatus[thisPin])
{
// Remember when it changed, starting debounce mode.
// If not currently in debounce mode,
if (digitalDebounceTime[thisPin] == 0)
{
// Set when we can accept this as valid (debounce is considered
// done if the time gets to this point with the status still the same).
digitalDebounceTime[thisPin] = millis() + digitalDebounceRate;
}
// Check to see if we are in debounce detect mode.
if (digitalDebounceTime[thisPin] > 0)
{
// Yes we are. Have we delayed long enough yet?
if ((long)(millis() - digitalDebounceTime[thisPin]) >= 0)
{
// Yes, so consider it switched.
// If pin is Active LOW,
if (status == LOW)
{
// Emit BUTTON PRESSED string.
Serial.print(iCadeDesc[thisPin]);
Serial.print(" pressed (sending ");
Serial.print(iCadeKeymap[thisPin][0]);
Serial.println(" to iCade).");
Keyboard.print(iCadeKeymap[thisPin][0]);
// digitalCounter[thisPin]++;
pinsOn++;
#ifndef LED_OFF
digitalWrite(LED_PIN, HIGH);
#endif
}
else
{
// Emit BUTTON RELEASED string.
Serial.print(iCadeDesc[thisPin]);
Serial.print(" released (sending ");
Serial.print(iCadeKeymap[thisPin][1]);
Serial.println(" to iCade).");
Keyboard.print(iCadeKeymap[thisPin][1]);
if (pinsOn > 0)
pinsOn--;
if (pinsOn == 0)
digitalWrite(LED_PIN, LOW);
}
// Remember current (last set) status for this pin.
digitalStatus[thisPin] = status;
// Reset debounce time (disable, not looking any more).
digitalDebounceTime[thisPin] = 0;
} // End of if ( (long)(millis()-digitalDebounceTime[thisPin]) >= 0 )
} // End of if (digitalDebounceTime[thisPin]>0)
}
else // No change? Flag no change.
{
// If we were debouncing, we are no longer debouncing.
digitalDebounceTime[thisPin] = 0;
}
} // End of (int thisPin=0; thisPin < DI_PIN_COUNT; thisPin++ )
}
/*---------------------------------------------------------------------------*/
void showHeader()
{
int i;
// Emit some startup stuff to the serial port.
Serial.print("iCadeTeensy ");
Serial.print(VERSION);
Serial.println(" by Allen C. Huffman (alsplace@pobox.com)");
Serial.print(DI_PIN_COUNT);
Serial.print(" DI Pins (");
for (i = 0; i < DI_PIN_COUNT; i++)
{
Serial.print(myPins[i]);
Serial.print("=");
Serial.print(iCadeDesc[i]);
Serial.print(" ");
}
Serial.print("), ");
Serial.print(digitalDebounceRate);
Serial.println("ms Debounce.");
}
/*---------------------------------------------------------------------------*/
void showStatus()
{
showDigitalInputStatus();
}
/*---------------------------------------------------------------------------*/
void showDigitalInputStatus()
{
Serial.print("DI: ");
for (int thisPin = 0; thisPin < DI_PIN_COUNT; thisPin++)
{
// Read the pin's current status.
Serial.print(iCadeDesc[thisPin]);
Serial.print("=");
Serial.print(digitalRead(myPins[thisPin]));
Serial.print(" ");
// Serial.print(" (");
// Serial.print(digitalCounter[thisPin]);
// Serial.print(") ");
}
Serial.println("");
}
/*---------------------------------------------------------------------------*/
// End of file.
I recently gained some experience using Arduinos to control inputs in a local haunted house attraction, and that has opened up quite a world of projects. During my research, I learned about the Teensy 2.0, a cheap (starting at $16, http://www.pjrc.com/teensy/index.html) controller that is similar to Arduino, but had more I/O pins than the Arduino Uno, plus had the ability to act like a USB keyboard or mouse. This enables it to send keystrokes or mouse movements/clicks to a computer, as if it were receiving them from an input device.
So, using the $19 version of Teensy 2.0 (that has header pins) and some wires, I was able to connect my Atari Flashback 2 joystick to an iPad via the USB adapter. I wrote a small program that reads the I/O pins of the Teensy 2.0, then sends out USB keyboard commands that emulate the iCade arcade controller.
My project was noted on the Teensy 2.0 project page: