I've been wanting to make an arcade cabinet for a while, but for now I've just built this controller which includes a spinner (for moving left and right) and a fire button.
Step #1: Component SelectionFirst, I was thinking about using a potentiometer to detect the spinner rotation but it obviously couldn't be rotated indefinably. Next, I thought rotary encoder, too bad that I don't have one and it isn't a smooth turn. Then, I thought DC motor: it's smooth, tiny, and I have one... yay!
Next was the microcontroller. Initially, I thought Arduino, but I needed an HID-compatible board that I didn't have yet, so I went with a DigiSpark (tiny, cheap, easy to use).
Step #2: 3D ModelingTo have a minimum ergonomic controller, I look for boxes in my stuff to find one that had more or less the right dimension for my hands. Next, I measured it rounded the values and started designing it on SolidWorks.
So after many iteration of schematic to detect to rotation way of the motor I came up with this. I've created a virtual mass to get two phases motor that I can input in the comparator The potentiometer is here to adjust how fast you need to spin the motor in order to press a key. Unfortunately, my motor produces a too low voltage to be picked up by the comparator with the resistor so I added a switch that disconnect the positive to make sure that a slight movement of the motor produce enough voltage so that the LM358 could trigger, next there is a button for the fire key which is using a pulldown resistor. For those wondering if the USB port has enough power to spin the motor, well IT DOESN'T. The motor does not spin you spin it I use it as a sensor here.
Step #4: ProgrammingFirst a bit of backstory the digisparkKeyborad library only allow for one key modifier (like CTRL / SHIFT / ALT) and one normal key (like A/B/C/D) so you need to modify the library to allow still one modifier but multiple key (I modified it to allow 6 keys). Here's how you do it:
Open the DigiKeyboard.h and modify this:
const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x01, // REPORT_COUNT (simultaneous keystrokes)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
};
to this:
const PROGMEM char usbHidReportDescriptor[USB_CFG_HID_REPORT_DESCRIPTOR_LENGTH] = { /* USB report descriptor */
0x05, 0x01, // USAGE_PAGE (Generic Desktop)
0x09, 0x06, // USAGE (Keyboard)
0xa1, 0x01, // COLLECTION (Application)
0x05, 0x07, // USAGE_PAGE (Keyboard)
0x19, 0xe0, // USAGE_MINIMUM (Keyboard LeftControl)
0x29, 0xe7, // USAGE_MAXIMUM (Keyboard Right GUI)
0x15, 0x00, // LOGICAL_MINIMUM (0)
0x25, 0x01, // LOGICAL_MAXIMUM (1)
0x75, 0x01, // REPORT_SIZE (1)
0x95, 0x08, // REPORT_COUNT (8)
0x81, 0x02, // INPUT (Data,Var,Abs)
0x95, 0x06, //REPORT_COUNT (6 simultaneous keystrokes)
0x75, 0x08, // REPORT_SIZE (8)
0x25, 0x65, // LOGICAL_MAXIMUM (101)
0x19, 0x00, // USAGE_MINIMUM (Reserved (no event indicated))
0x29, 0x65, // USAGE_MAXIMUM (Keyboard Application)
0x81, 0x00, // INPUT (Data,Ary,Abs)
0xc0 // END_COLLECTION
};
The line that changes is this one:
0x95, 0x06, //REPORT_COUNT (6 simultaneous keystrokes)
You also need to modify the line:
uchar reportBuffer[1];
To:
uchar reportBuffer[7];
For the simplicity of use I also added a new function just under the function sendKeyPress
void setReportBuffer(uchar NewreportBuffer[7]) {
while (!usbInterruptIsReady()) {
// Note: We wait until we can send keyPress
// so we know the previous keyPress was
// sent.
usbPoll();
_delay_ms(5);
}
memset(reportBuffer, 0, sizeof(reportBuffer));
reportBuffer[0] = NewreportBuffer[0];
reportBuffer[1] = NewreportBuffer[1];
reportBuffer[2] = NewreportBuffer[2];
reportBuffer[3] = NewreportBuffer[3];
reportBuffer[4] = NewreportBuffer[4];
reportBuffer[5] = NewreportBuffer[5];
reportBuffer[6] = NewreportBuffer[6];
usbSetInterrupt(reportBuffer, sizeof(reportBuffer));
}
So that later I can use this in the Arduino code to modify the buffer directly. This brings me to the Arduino code, which can be optimized a lot:
#include "DigiKeyboard.h"
String movingStatus = "static";
void setup() {}
void loop() {
bool leftValue = digitalRead(1);
bool rightValue = digitalRead(2);
bool fire = digitalRead(0);
bool pressFire = false;
bool pressLeft = false;
bool pressRight = false;
if(leftValue && movingStatus == "static") {
pressLeft = true;
pressRight = false;
}
if(movingStatus=="left" && !leftValue) {
pressRight = false;
pressLeft = false;
}
if(rightValue && movingStatus == "static") {
pressRight = true;
pressLeft = false;
}
if(movingStatus=="right" && !rightValue) {
pressRight = false;
pressLeft = false;
}
pressFire = fire;
int i=1;
uchar keybuffer[7] = {0,0,0,0,0,0,0};
if(pressLeft==true) { keybuffer[i]=80; i++; }
if(pressRight==true) { keybuffer[i]=79; i++; }
if(pressFire==true) { keybuffer[0] = MOD_CONTROL_LEFT; }
DigiKeyboard.setReportBuffer(keybuffer);
}
So to explain the code, first I read all these used pins and test if, for example, the left pin is high the variable movingStatus
to "left"
and only change the variable if the pin goes low even if the right is put to high this is because my circuit isn't prefect and work if the motor turn slowly and if the motor turn's faster, for example to the right the comparator will put the right pin high but short after the left pin will be also high (don't know why) so the code prevent that. Next, if we need to go right, we add the right arrow key to buffer and send it to the computer. The fire key is a bit special because for the first player mame use the CTRL key so we need to set the first bit of the buffer to MOD_CONTROL_LEFT
(which is a global variable defined by the DigiKeyboard
library.
Comments