# iRacing Button Box - Code

• 12-26-2013, 12:32 AM
T.Mihm
iRacing Button Box - Code
I'm using a Teensy 2.0 to build a button box that will be used with iRacing to control most all of the in-car controls and settings in-game. The project will eventually be expanded to around 120 control wired up in a 12x10 grid and utilizing push-button momentaries, a couple on-off toggles, a few rotary encoders, and a pair of 12-position rotaries.

Right now, I'm just familiarizing myself with the Arduino software, learning how to make sketches, and taking my time making sure everything works right. I'm using a simple breadboard and wires until everything is working smoothly. Currently, I'm trying to get a 3x3 matrix of momentary switches to work in-game. Once I've got that I'll figured out, I'll expand the project.

I have run across a few problems that I've researched to solve, but I have some more that have popped up in the process.

Here is my code so far. I'm utilizing a keypad matrix.

Code:

``` // Button Box Sketch Initial Prototype // Rev. 1.0 // Travis Mihm // Initialize the integers for the Row and Column Matrix const int numRows = 3;        // Number of Rows is assigned as a constant integer const int numCols = 3;        // Number of Columns is assigned as a constant integer const int debounceTime = 250;  // Set the milliseconds for debounce as a constant integer //Use Keymap to define the character for the buttons when pressed char keymap[numRows][numCols] = // the keymap assignments remain constant characters {     {'1','2','3'},   {'4','5','6'},   {'7','8','9'}   }; //Set array for pins being used const int rowPins[numRows] = {11, 12, 13};          //Assign constant integers relating the number of rows to their corresponding pins const int colPins[numCols] = { 8, 9, 10};  //Assign constant integers relating the number of columns to their corresponding pins //Begin setup procedure void setup() {     Serial.begin(9600);                                  // Start conversation with the serial port     for (int row = 0; row < numRows; row++)              // For loop; Initial condition is the ingeger "row" set at zero. It then tests to see of the row integer is less than                                                         // the numRows integer. If true, the statement {} runs, and the integer "row" is incremented. If false, the for loop                                                         // terminates    {         pinMode(rowPins[row], INPUT);                      // Set row pins as input type         digitalWrite(rowPins[row], HIGH);                  // Turn on Pull-Ups       }     for (int column = 0; column < numCols; column++)      // Same loop with the rows, but for the columns now     {         pinMode(colPins[column], OUTPUT);                  // Set column pins as output pins                                             digitalWrite(colPins[column], HIGH);                // Set all columns inactive initially       }   } // The "for" loops were to set the initial ready-conditions for the board. Now the code begins the body of the loop procedures to output which buttons get pressed void loop() {     char key = getKey();   if( key != 0)     {         Serial.print("You pressed ");         Serial.println(key);       }   } // returns the the key pressed, or 0 if no key is pressed. char getKey() {     char key = 0;     for (int column = 0; column < numCols; column++)     {         digitalWrite(colPins[column], LOW);    //Activate (column number)     for(int row = 0; row < numRows; row++) //Scan rows on this column for a                                           //key press     {         if(digitalRead(rowPins[row]) == LOW) //When key is pressed...         {             delay(debounceTime);                      // Set the time for the debounceTime integer             while(digitalRead(rowPins[row]) == LOW);  // While the row for the column being scanned is still active             key = keymap[row][column];                // The key integer is now whatever was set in the keymap function under the current row and column being scanned               }           }         digitalWrite(colPins[column], HIGH);          // Deactivate the current column       }     return key;                                    // returns the current key intiger to the getKey function. This ends the getKey loop and starts it over again.   }```
1. - First, I'd like to be able to name the buttons more than just one character. I cannot seem to figure out the process for that. I tried using something like "A1" instead of 'A1' as from what I understood, the "" marks were the solution to my problem.

2. - I currently have the debounceTime at 250ms; any less than that, and I get the stray double-press when the momentary switch is released. However, I'd rather use something like a button state change. That will be useful for my toggle switches, as I really want those to act like momentaries (the toggle is really just for looks, to mimic an ignition switch, starter switch, etc. So one would toggle on or off like a momentary, another would only trigger when toggling from off to on, etc.) and I assume would be useful for the encoders as well. But I'm a little confused on how I'd incorporate a button state change into a matrix. Am I just overthinking it?

3. - I'm still a little confused on how I get the button presses recognized by my computer/iRacing. Is this where USB Type comes into play? Would I be able to name the switches through that?

My apologies if this has all been asked recently or are commonly solved around here. I have a head injury that has affected my working memory, and it gets a little tough to remember how I solved a problem, if I had found a solution somewhere, what I was trying to solve in the first place, etc ;) Lots of sticky notes and lists and diagrams drawn out right now. If I need to include a schematic let me know, but it's fairly easy to deduce how it's wired up.

I have not touched the USB Type, it's still set at serial. Will I be able to communicate through iRacing at all with it on Serial, or will I have to change the device type to something else?

-Travis
• 12-26-2013, 03:32 PM
PaulStoffregen
Quote:

Originally Posted by T.Mihm
1. - First, I'd like to be able to name the buttons more than just one character. I cannot seem to figure out the process for that. I tried using something like "A1" instead of 'A1' as from what I understood, the "" marks were the solution to my problem.

Well, using double quotes is part of the solution.

You have "char" as the type, both in defining the array and as the return type from getKey(). You'll need to change those to "const char *", so the array is actually an array of pointers to strings. Then change gerKey() to return a const char *.

Quote:

2. - I currently have the debounceTime at 250ms; any less than that, and I get the stray double-press when the momentary switch is released. However, I'd rather use something like a button state change. That will be useful for my toggle switches, as I really want those to act like momentaries (the toggle is really just for looks, to mimic an ignition switch, starter switch, etc. So one would toggle on or off like a momentary, another would only trigger when toggling from off to on, etc.) and I assume would be useful for the encoders as well. But I'm a little confused on how I'd incorporate a button state change into a matrix. Am I just overthinking it?
You'll probably have to add quite a bit of code for this.

Quote:

3. - I'm still a little confused on how I get the button presses recognized by my computer/iRacing. Is this where USB Type comes into play? Would I be able to name the switches through that?
I know nothing about the iRacing software. If it responds to USB keyboards, you could select Keyboard from Tools > USB Type, and then use these functions to send keystrokes.

http://www.pjrc.com/teensy/td_keyboard.html

This USB keyboard stuff doesn't necessary "name" your switches. You do that. All this keyboard stuff does is let you send the same type of USB communication a normal USB keyboard would send when its buttons are pressed. That's all. You can use it to build things, but how is up to you.
• 12-27-2013, 06:29 AM
T.Mihm
Thanks for the input, Paul!

As always, the minute I decide to post and try and get some help, I figure out about half of it immediately after ;)

I changed the USB Type to Keyboard and then added keyboard.print(key) after the serial.println(key) line. That got me some of what I wanted. I decided to up it a bit and I'm currently running a 6x4 key matrix smoothly (albeit the two cheap momentaries that crapped out right away) with iRacing.

I still need to work out a way to include the toggles using the falling/rising edge, but that is for another night.

-Travis
• 12-31-2013, 12:43 AM
T.Mihm
Here's my current code:

Code:

```// Button Box Sketch Initial Prototype // Rev. 1.2 // Travis Mihm // Initialize the integers for the Row and Column Matrix const int numRows = 12;        // Number of Rows is assigned as a constant integer const int numCols = 7;        // Number of Columns is assigned as a constant integer const int debounceTime = 200;  // Set the milliseconds for debounce as a constant integer //Use Keymap to define the character for the buttons when pressed const char keymap[numRows][numCols] = // the keymap assignments remain constant characters {     {'A','B','C','D','a','y','m'},   {'E','F','G','H','b','z','n'},   {'I','J','K','L','c','0','o'},   {'*','*','*','*','d','1','p'},   {'*','*','*','*','e','2','q'},   {'*','*','*','*','f','3','r'},   {'*','*','*','*','g','4','s'},   {'M','N','O','P','h','5','t'},   {'*','*','*','*','i','6','u'},   {'Q','R','S','T','j','7','v'},   {'*','*','*','*','k','8','w'},   {'U','V','W','X','l','9','x'},   }; //Set array for pins being used const int rowPins[numRows] = {3, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21};          //Assign constant integers relating the number of rows to their corresponding pins const int colPins[numCols] = {10, 9, 8, 7, 6, 5, 4};  //Assign constant integers relating the number of columns to their corresponding pins //Begin setup procedure void setup() {     Serial.begin(9600);                                  // Start conversation with the serial port     for (int row = 0; row < numRows; row++)              // For loop; Initial condition is the ingeger "row" set at zero. It then tests to see of the row integer is less than                                                         // the numRows integer. If true, the statement {} runs, and the integer "row" is incremented. If false, the for loop                                                         // terminates    {         pinMode(rowPins[row], INPUT);                      // Set row pins as input type         digitalWrite(rowPins[row], HIGH);                  // Turn on Pull-Ups       }     for (int column = 0; column < numCols; column++)      // Same loop with the rows, but for the columns now     {         pinMode(colPins[column], OUTPUT);                  // Set column pins as output pins                                             digitalWrite(colPins[column], HIGH);                // Set all columns inactive initially       }   } // The "for" loops were to set the initial ready-conditions for the board. Now the code begins the body of the loop procedures to output which buttons get pressed void loop() {     char key = getKey();   if( key != 0)     {         Serial.print("You pressed ");         Serial.println(key);         Keyboard.press(key);         Keyboard.release(key);       }   } // returns the the key pressed, or 0 if no key is pressed. char getKey() {     char key = 0;     for (int column = 0; column < numCols; column++)     {         digitalWrite(colPins[column], LOW);    //Activate (column number)     for(int row = 0; row < numRows; row++) //Scan rows on this column for a                                           //key press     {         if(digitalRead(rowPins[row]) == LOW) //When key is pressed...         {             delay(debounceTime);                      // Set the time for the debounceTime integer             while(digitalRead(rowPins[row]) == LOW);  // While the row for the column being scanned is still active             key = keymap[row][column];                // The key integer is now whatever was set in the keymap function under the current row and column being scanned               }           }         digitalWrite(colPins[column], HIGH);          // Deactivate the current column       }     return key;                                    // returns the current key intiger to the getKey function. This ends the getKey loop and starts it over again. }```
However, I still can't seem to get the double quotations to work and name each point on the matrix a string of characters. I was wondering if I could use the KEY_A, KEY_UP, etc. instead of individual characters. But if I simply substitute those in nothing works, and I haven't entirely figured out how to use them. Paul, which lines of code do I need to add the const qualifier to to make that work? I think I've gone into the twisted world of over-thinking things outside my scope ;)

-Travis
• 12-31-2013, 01:35 AM
PaulStoffregen
For starters, change this:

Code:

`const char keymap[numRows][numCols] = // the keymap assignments remain constant characters`
to this

Code:

`const char * keymap[numRows][numCols] = // the keymap assignments remain constant characters`
and this

Code:

```char getKey() {   char key = 0;```
to this

Code:

```const char * getKey() {   const char * key = 0;```
• 12-31-2013, 02:45 AM
T.Mihm
Here's the error report I received:

Code:

``` Arduino: 1.0.5 (Windows 7), Board: "Teensy 2.0" C:\Program Files (x86)\Arduino\hardware\tools\avr\bin\avr-g++ -c -g -Os -Wall -fno-exceptions -ffunction-sections -fdata-sections -mmcu=atmega32u4 -DF_CPU=16000000L -MMD -DUSB_VID=null -DUSB_PID=null -DARDUINO=105 -DTEENSYDUINO=117 -felide-constructors -std=c++0x -DUSB_HID -DLAYOUT_US_ENGLISH -IC:\Program Files (x86)\Arduino\hardware\teensy\cores\teensy C:\Users\T5C87~1.MIH\AppData\Local\Temp\build1437071008571646853.tmp\ButtonBox1_2.cpp -o C:\Users\T5C87~1.MIH\AppData\Local\Temp\build1437071008571646853.tmp\ButtonBox1_2.cpp.o ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2:30: error: invalid conversion from 'char' to 'const char*' ButtonBox1_2.ino: In function 'void loop()': ButtonBox1_2:75: error: invalid conversion from 'const char*' to 'char'```
The crazy huge long line of invalid conversion from 'char' to 'const char*' was because I hadn't changed the 's to "s. Once I changed that, I got an entirely different and strange set of error codes.

If I could just fix the issue I'm having with the buttons being mapped to a capital letter ending up being recognized by iracing as a Shift+letter and then not functioning properly in the simulator, I'd be happy.

What other options do I have here? Could I replace those characters with a different character?

-Travis
• 01-23-2014, 05:33 PM
T.Mihm
I just wanted to update this post on the finished project.

After a break to work on some other things, I came back to the project with a new outlook. I decided to use the Keypad Library this time, and instead use the keypad.set_key1 etc approach to how the buttons correlate to key presses.

Code:

```//*Button*Box*Sketch*Secondary*Prototype //*Rev*2.0 //*Travis*Mihm //*Use*the*Keypad*Library #include*<Keypad.h> const byte ROWS = 5; // Five Row const byte COLS = 12; // Twelve Column char keys[ROWS][COLS] = { ** ****{KEY_A,KEY_B,KEY_C,KEY_D,KEY_E,KEY_F,KEY_G,KEY_H,KEY_I,KEY_J,KEY_K,KEYPAD_0}, ****{KEY_L,KEY_M,KEY_N,KEY_O,KEY_P,KEY_Q,KEY_R,KEY_S,KEY_T,KEY_U,KEY_V,KEYPAD_1}, ****{KEY_W,KEY_X,KEY_Y,KEY_Z,KEY_1,KEY_2,KEY_3,KEY_4,KEY_5,KEY_6,KEY_7,KEYPAD_2}, ****{KEY_8,KEY_9,KEY_0,KEY_F1,KEY_F2,KEY_F3,KEY_F4,KEY_F5,KEY_F6,KEY_F7,KEY_F8,KEYPAD_3}, ****{KEY_F9,KEY_F10,KEY_F11,KEY_F12,KEY_TILDE,KEY_MINUS,KEY_EQUAL,KEY_SEMICOLON,KEY_QUOTE,KEY_SLASH,KEY_SPACE,KEYPAD_4}, ** }; byte rowPins[ROWS] = {9, 8, 7, 6, 5}; // Connect row pins of the button box byte colPins[COLS] = {10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21}; // Connect column pins of the button box Keypad keypad = Keypad ( makeKeymap(keys), rowPins, colPins, ROWS, COLS ); void setup(){ **Serial.begin(9600); ** **Keyboard.begin(); } void loop(){ **char key = keypad.getKey(); ** **if (key != NO_KEY){ **** ****Serial.println("You pressed a key!"); **** ****Keyboard.set_key1(key); ****Keyboard.send_now(); ****Keyboard.set_key1(0); ****Keyboard.send_now(); **** **} ** }```
This code works great with my button box, and the Keypad library really simplified things.

-Travis