Forum Rule: Always post complete source code & details to reproduce any issue!
Results 1 to 7 of 7

Thread: iRacing Button Box - Code

  1. #1
    Junior Member
    Join Date
    Nov 2013
    Posts
    6

    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

  2. #2
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,996
    Quote Originally Posted by T.Mihm View Post
    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 *.

    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.

    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.

  3. #3
    Junior Member
    Join Date
    Nov 2013
    Posts
    6
    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

  4. #4
    Junior Member
    Join Date
    Nov 2013
    Posts
    6
    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
    Last edited by T.Mihm; 12-31-2013 at 12:53 AM.

  5. #5
    Senior Member PaulStoffregen's Avatar
    Join Date
    Nov 2012
    Posts
    22,996
    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;

  6. #6
    Junior Member
    Join Date
    Nov 2013
    Posts
    6
    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
    Last edited by T.Mihm; 12-31-2013 at 04:34 AM.

  7. #7
    Junior Member
    Join Date
    Nov 2013
    Posts
    6
    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

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •