USB Emulation

USB 

Haven't you ever wondered how you can just plug a USB flash drive or mouse into any computer and it just works? This section will break down how that happens as well as how this kit can work like a USB Gamepad or keyboard.

A Protocol By Any Other name

USB stands for Universal Serial Bus. What we care about here is the serial part. Serial communication. If we wanted to transmit the word 'information' it would sent one by one. I N F O R M A T I O N , one character or letter at a time. When you use a USB mouse or flash drive, data is sent and received in these ordered chunks, one by one.

What's That?

When you plug a USB device into your computer a number of things happen before you can use it. The computer will ask what kind of device was just plugged in. What it does and what resources it needs. The device will answer and if the computer can it will allocate the necessary resources. Assuming the computer trusts the device. A device might be 'signed' which means its legitimacy is backed by a company or entity. An Xbox Gamepad is backed by Microsoft and is trusted by Windows.

A Special Class Above the Rest

USB devices have sub sets of devices. The one we care about for this kit is the Human Interface Device, or HID, class. This is a group of devices that are used by people. Mice, Keyboard, Flight Sticks, and Exercise Machines are a few. These devices have a VIP pass when it comes to USB. Upon plugging into a computer and being asked what it is. It answers with a unique identifier that the computer will know and will readily accept.

What About the Kit? 

There are two USB ports on this kit. The micro-USB found on the Arduino and the big USB-B found on the kit PCB. The micro-USB can only be used for communication between the Arduino and computer. We've used some pins from the Arduino and routed them to the USB-B socket so we can spoof a Human Interface Device. When you plug the Arduino into your computer all the back and forth happens and we can't control that. But with this other USB socket we can use software to tell the computer whatever we want.

In this case we use the USBkeyboard library to tell the computer that this device is a keyboard. The computer accepts this and now we can use the kit as an input.

Code

Upload this code via the micro-USB then switch to the USB-B socket. All Arduino programming must be through the micro-USB.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
#include "UsbKeyboard.h"

#define BYPASS_TIMER_ISR 1

int leftInputs[] = {A0, A1}; //INPUT_PULLUP reading pins
int leftOutputs[] = {A2, A3, A4}; //Column HIGH or LOW pins
bool leftHeld[2][3] =  { // Variables for presses
  {false, false, false},
  {false, false, false}
};
int leftButtons[2][3] = { //Used to translate  the reading to a real button
  {KEY_ARROW_LEFT, KEY_ARROW_UP, KEY_SPACE},
  {KEY_ARROW_RIGHT, KEY_ARROW_DOWN, KEY_SPACE}
};

/*
int leftButtons[2][3] = {
  {"LL", "LU", "SELECT"},
  {"LR", "LD", "LShoulder"}
};
*/

int rightInputs[] = {10, A5}; //INPUT_PULLUP reading pins
int rightOutputs[] = {11, 12, 13}; //Column HIGH or LOW pins
bool rightHeld[2][3] =  { // Variables for presses
  {false, false, false},
  {false, false, false}
};
int rightButtons[2][3] = {
  {KEY_X, KEY_Y, KEY_SPACE},
  {KEY_A, KEY_B, KEY_SPACE}
};

/*
int rightButtons[2][3] = {
  {"RL", "RU", "START"},
  {"RR", "RD", "RShoulder"}
};
*/

void setup() {
  //Set inputs
  for (int i = 0; i < 2; i++) {
    pinMode(leftInputs[i], INPUT_PULLUP);
    pinMode(rightInputs[i], INPUT_PULLUP);
  }

  //Set outputs
  for (int i = 0; i < 3; i++) {
    pinMode(leftOutputs[i], OUTPUT);
    digitalWrite(leftOutputs[i], HIGH);
    pinMode(rightOutputs[i], OUTPUT);
    digitalWrite(rightOutputs[i], HIGH);
  }

#if BYPASS_TIMER_ISR
  // disable timer 0 overflow interrupt (used for millis)
  TIMSK0 &= !(1 << TOIE0); // ++
#endif

  Serial.begin(9600); //For debugging
}




#if BYPASS_TIMER_ISR
void delayMs(unsigned int ms) {

  for (int i = 0; i < ms; i++) {
    delayMicroseconds(1000);
  }
}
#endif

void loop() {

  UsbKeyboard.update();

  // Left side
  for (int y = 0; y < 3; y++) { //Outputs
    digitalWrite(leftOutputs[y], LOW); //Turn column on
    for (int x = 0; x < 2 ; x++) { //Inputs
      if (!digitalRead(leftInputs[x])) { //Read row
        //Button is pressed
        if (!leftHeld[x][y]) {
          UsbKeyboard.sendKeyStroke(leftButtons[x][y]);
          leftHeld[x][y] = true;
        }
      }
      else {
        leftHeld[x][y] = false;
      }
    }//End of x
    digitalWrite(leftOutputs[y], HIGH); //turn column off
  }//End of y

  // Right side
  for (int y = 0; y < 3; y++) { //Outputs
    digitalWrite(rightOutputs[y], LOW); //Turn column on
    for (int x = 0; x < 2 ; x++) { //Inputs
      if (!digitalRead(rightInputs[x])) { //Read row
        if (!rightHeld[x][y]) {
          UsbKeyboard.sendKeyStroke(rightButtons[x][y]);
          rightHeld[x][y] = true;
        }
      }
      else {
        rightHeld[x][y] = false;
      }
    }//End of y
    digitalWrite(rightOutputs[y], HIGH); //turn column off
  }//End of x

#if BYPASS_TIMER_ISR  // check if timer isr fixed.
  delayMs(20);
#else
  delay(20);
#endif
}

How Does this Work?

There are two clusters of buttons on the RGB Matrix Arcade. Split into the left and right side. What pins they use must be declared first.

Download file Copy to clipboard
1
2
int leftInputs[] = {A0, A1}; //INPUT_PULLUP reading pins
int leftOutputs[] = {A2, A3, A4}; //Column HIGH or LOW pins

The next block stores if any of the buttons have been pressed.

Download file Copy to clipboard
1
2
3
4
bool leftHeld[2][3] =  { // Variables for presses
  {false, false, false},
  {false, false, false}
};

Now this is the most important block. Here we define what buttons will do what key presses.

Download file Copy to clipboard
1
2
3
4
int leftButtons[2][3] = { //Used to translate  the reading to a real button
  {KEY_ARROW_LEFT, KEY_ARROW_UP, KEY_SPACE},
  {KEY_ARROW_RIGHT, KEY_ARROW_DOWN, KEY_SPACE}
};

There are two arrays that hold the key values. Their physical buttons are as follows.

Download file Copy to clipboard
1
2
3
4
int leftButtons[2][3] = {
  {"LL", "LU", "SELECT"},
  {"LR", "LD", "LShoulder"}
};

We can see that the left side left button is mapped to the press of the left arrow key. LL <> KEY_ARROW_LEFT . Below is a list of possible keys to mapped to these positions.

Keys

Here are some key values you can sub in.

Download file Copy to clipboard
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
#define KEY_A       4
#define KEY_B       5
#define KEY_C       6
#define KEY_D       7
#define KEY_E       8
#define KEY_F       9
#define KEY_G       10
#define KEY_H       11
#define KEY_I       12
#define KEY_J       13
#define KEY_K       14
#define KEY_L       15
#define KEY_M       16
#define KEY_N       17
#define KEY_O       18
#define KEY_P       19
#define KEY_Q       20
#define KEY_R       21
#define KEY_S       22
#define KEY_T       23
#define KEY_U       24
#define KEY_V       25
#define KEY_W       26
#define KEY_X       27
#define KEY_Y       28
#define KEY_Z       29
#define KEY_1       30
#define KEY_2       31
#define KEY_3       32
#define KEY_4       33
#define KEY_5       34
#define KEY_6       35
#define KEY_7       36
#define KEY_8       37
#define KEY_9       38
#define KEY_0       39

#define KEY_ENTER   40

#define KEY_SPACE   44

#define KEY_F1      58
#define KEY_F2      59
#define KEY_F3      60
#define KEY_F4      61
#define KEY_F5      62
#define KEY_F6      63
#define KEY_F7      64
#define KEY_F8      65
#define KEY_F9      66
#define KEY_F10     67
#define KEY_F11     68
#define KEY_F12     69

#define KEY_ARROW_LEFT 0x50
#define KEY_ARROW_RIGHT 0x4F
#define KEY_ARROW_UP 0x52
#define KEY_ARROW_DOWN 0x51