Home ยป Euro Truck Simulator 2 Arduino controller

Euro Truck Simulator 2 Arduino controller

by Bits and Droids
0 comment
Euro truck simulator 2 toggle box

A friend of mine is a huge Euro truck simulator 2 fan. When his birthday came up, it was a no-brainer that I wanted to create his personal truck macro pad. Integrated with his favorite mechanical key switches and a custom touch.

This was actually the first time that I’ve ever incorporated a button matrix in my final design. This is also the part where I want to focus on in this article.

The keypad

A keypad/button matrix works a bit like an old pirate treasure map. If we plot 2 lines, we’re able to locate where the treasure is buried.

Middle left button pressed
Upper right button pressed


We will outsource most of the work to the Keypad library. This library makes it really easy to create a quick working keypad with minimal coding. The approach they use is that the buttons act as a floodgate of sorts. One side of the button (i.e., the rows) will be made HIGH by our Arduino. So an electrical signal tries to run through the button but is physically blocked. The other side will be LOW by default. The moment we press the button, the HIGH side will be low, and the low side will be HIGH until we close it again. Our library scans which row is HIGH/LOW and cross-references this with the columns. It basically creates a visual map as we did above, only based on LOW HIGH states.

Limitations

We will use this controller for truck simulators, so there isn’t really a need to create a macro pad that can press multiple buttons at once. If we would press certain button combinations we encounter a limitation of this basic keypad approach.

If we press all 3 buttons on the first row our logic starts to break. There is no clear path to the button we’ve pressed since all the lines are wide open. This could be avoided with the use of diodes but for now, I’m going to look at the basic approach.

The code

The libraries used are:

Arduino Playground – Keypad Library
GitHub – NicoHood/HID: Bring enhanced HID functions to your Arduino!

Lets start by looking at the full code and then break it down. First, we take a look at the example sketch that is included with the library.

/* @file MultiKey.ino
|| @version 1.0
|| @author Mark Stanley
|| @contact mstanley@technologist.com
||
|| @description
|| | The latest version, 3.0, of the keypad library supports up to 10
|| | active keys all being pressed at the same time. This sketch is an
|| | example of how you can get multiple key presses from a keypad or
|| | keyboard.
|| #
*/

#include <Keypad.h>

const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the kpd

Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );

unsigned long loopCount;
unsigned long startTime;
String msg;


void setup() {
    Serial.begin(9600);
    loopCount = 0;
    startTime = millis();
    msg = "";
}


void loop() {
    loopCount++;
    if ( (millis()-startTime)>5000 ) {
        Serial.print("Average loops per second = ");
        Serial.println(loopCount/5);
        startTime = millis();
        loopCount = 0;
    }

    // Fills kpd.key[ ] array with up-to 10 active keys.
    // Returns true if there are ANY active keys.
    if (kpd.getKeys())
    {
        for (int i=0; i<LIST_MAX; i++)   // Scan the whole key list.
        {
            if ( kpd.key[i].stateChanged )   // Only find keys that have changed state.
            {
                switch (kpd.key[i].kstate) {  // Report active key state : IDLE, PRESSED, HOLD, or RELEASED
                    case PRESSED:
                    msg = " PRESSED.";
                break;
                    case HOLD:
                    msg = " HOLD.";
                break;
                    case RELEASED:
                    msg = " RELEASED.";
                break;
                    case IDLE:
                    msg = " IDLE.";
                }
                Serial.print("Key ");
                Serial.print(kpd.key[i].kchar);
                Serial.println(msg);
            }
        }
    }
}  // End loop

The code I made looks like this. It basically copies almost all the logic of the example sketch and fits in the logic to register a gamepad press.

#include <HID-Project.h>
#include <HID-Settings.h>
#include <Keypad.h>


#define ENABLE_PULLUPS
const byte ROWS = 3; //four rows
const byte COLS = 3; //three columns
byte keys[ROWS][COLS] = {
{1,2,3},
{4,5,6},
{7,8,9},
};
const byte toggle3A = 9;
const byte toggle3B = 16;
const byte toggle2A = 15;
const byte toggle2B = 14;
const byte looseButton = 8;
byte rowPins[ROWS] =  { 4, 3, 2}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {5,6,7}; //connect to the column pinouts of the kpd

Keypad kpd = Keypad( makeKeymap(keys), rowPins, colPins, ROWS, COLS );




void setup() {

    pinMode(toggle3A, INPUT_PULLUP);
    pinMode(toggle3B, INPUT_PULLUP);
       pinMode(toggle2A, INPUT_PULLUP);
    pinMode(toggle2B, INPUT_PULLUP);
       pinMode(looseButton, INPUT_PULLUP);

    Gamepad.begin();
}


void loop() {
    byte toggle3AState = digitalRead(toggle3A);
    if(toggle3AState == LOW){
      Gamepad.press(10);

    }
     else if(toggle3AState == HIGH){
       Gamepad.release(10);

    }
    byte toggle3BState = digitalRead(toggle3B);
    
    if(toggle3BState == LOW){
      Gamepad.press(11);

    } else if(toggle3BState == HIGH){
       Gamepad.release(11);

    }
    
    if(digitalRead(toggle2A) == LOW){
      Gamepad.press(12);
    }
    if(digitalRead(toggle2B) == LOW){
      Gamepad.press(13);

    }
    if(digitalRead(looseButton) == LOW){
      Gamepad.press(16);

    }  else if(digitalRead(looseButton) == HIGH){
      Gamepad.release(16);
      
    }


    if(digitalRead(toggle2A) == HIGH){
      Gamepad.release(12);

    }
    if(digitalRead(toggle2B) == HIGH){
      Gamepad.release(13);

    }
 
    Gamepad.write();
    


    // Fills kpd.key[ ] array with up-to 10 active keys.
    // Returns true if there are ANY active keys.
    if (kpd.getKeys())
    {
        for (int i=0; i<LIST_MAX; i++)   // Scan the whole key list.
        {
            if ( kpd.key[i].stateChanged )   // Only find keys that have changed state.
            {
                switch (kpd.key[i].kstate) {  // Report active key state : IDLE, PRESSED, HOLD, or RELEASED
                    case PRESSED:{
                      Gamepad.press(kpd.key[i].kchar);
                      Gamepad.write();
                      break;
                    }
     
                    case HOLD: break;
                    

                    case RELEASED:{
                      Gamepad.release(kpd.key[i].kchar);
                      Gamepad.write();
                      break;
                    }
      
                    case IDLE: break;
 
                } 

            }
        }
    }
    
}  // End loop

The parts where we need to make the first adjustments are the pins where the rows/columns are connected and what each button needs to register. By default, the example links chars to the keypad while we want to send integers. Why integers? Because the Gamepad library takes integers to determine which button is pressed.
These buttons represent the default buttons recognized by windows.

There can be 32 buttons connected to a single device in the windows device manager.

//------------EXAMPLE-CODE-------------------

//THE CODE IN THE EXAMPLE USES 4 ROWS WhILE WE ONLy NEED 3
const byte ROWS = 4; //four rows
const byte COLS = 3; //three columns

//WE WANT INTEGERS INSTEAD OF THESE CHARS
char keys[ROWS][COLS] = {
{'1','2','3'},
{'4','5','6'},
{'7','8','9'},
{'*','0','#'}
};

//OUR ROWS/COLUMNS ARE CONNECTED TO DIFFERENT PINS
byte rowPins[ROWS] = {5, 4, 3, 2}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {8, 7, 6}; //connect to the column pinouts of the kpd

//------------OUR-OWN-IMPLEMENTATION-------------------

//WE CHANGE THE ROWS TO 3
const byte ROWS = 3; //four rows
const byte COLS = 3; //three columns

//INTEGERS SMALLER THAN 255 CAN BE STORED AS BYTES TO SAVE SPACE
byte keys[ROWS][COLS] = {
{1,2,3},
{4,5,6},
{7,8,9},
};

//CHANGE THESE PINS TO THE CORRECT ONES YOU'RE USING
byte rowPins[ROWS] =  { 4, 3, 2}; //connect to the row pinouts of the kpd
byte colPins[COLS] = {5,6,7}; //connect to the column pinouts of the kpd

You may also like

Leave a Comment

This website uses cookies to improve your experience. We'll assume you're ok with this, but you can opt-out if you wish. Accept