--- author: Akbar Rahman date: Tue, 04 Aug 2020 15:20:13 +0100 title: Repurposing Racing Wheel Pedals tags: [ g27, sim_racing ] uuid: 0f09200e-fd50-451b-aae1-1117a8a704db ---

Repurposing Racing Wheel Pedals

I have a Logitech G27 I don't use much. I wondered if I could use it for anything else. I could.

The Pinout of the Connector

The first thing I had to do was figure out what each pin did on the DE-9 connector, and which ones I should care about. This was done easily after I took off the top plastic casing thing by poking the three 100k Ohm potentiometers and the connector in the right places at the right times:

pin function
1,4 ground
6 clutch pedal
7 brake pedal
8 accelerator pedal
9 voltage in

Reading the Values of the Pots

I'm using an Arduino to read the pots and then do something with the values. I very dirtily wired pin 4 on the pedals to GND on a Arduino Uno, pin 9 to 5V, and pins 6,7,8 to A0, A1, and A2. I used a basic sketch to check that everything is good:
Show/hide test_sketch.ino
 void setup() {
	Serial.begin(9600);
}

void loop() {
	Serial.println(analogRead(A2));
	delay(20);
}
I noticed that the minimum and maximum values read by the Uno were quite far off 0 and 1024, like they should be, and voltage was being lost on the way to and from the potentiometers. Since the pedals have to be calibrated every time you plug them in, I assume this is normal and spat out this code:
Show/hide sketch_aug02a.ino
// sensor pins
int sa = A0;
int sb = A1;
int sc = A2;

// minimum values detected by the sensors
int mina = 1025;
int minb = 1025;
int minc = 1025;

// maximum values detected by the sensors
int maxa = 512;
int maxb = 512;
int maxc = 512;

// raw values of the sensors
int rva, rvb, rvc;

// calculated values of the sensors (between 0 and 1, this is the value sent to computer)
float cva, cvb, cvc;

void setup() {
	Serial.begin(9600);
}

void loop() {
	rva = analogRead(sa);
	rvb = analogRead(sb);
	rvc = analogRead(sc);

	if (rva < mina) mina = rva;
	if (rvb < minb) minb = rvb;
	if (rvc < minc) minc = rvc;

	if (rva > maxa) maxa = rva;
	if (rvb > maxb) maxb = rvb;
	if (rvc > maxc) maxc = rvc;

	cva = (float)(rva-mina)/(float)(maxa-mina);
	cvb = (float)(rvb-minb)/(float)(maxb-minb);
	cvc = (float)(rvc-minc)/(float)(maxc-minc);

	Serial.print('[');
	Serial.print(cva); Serial.print(',');
	Serial.print(cvb); Serial.print(',');
	Serial.print(cvc);
	Serial.print(']');
	Serial.println();
	delay(20); 
}

Actually Making the Numbers Do Something

This is where you can make the pedals do fun things. I reworked another piece of code I wrote to do a similar thing to quickly create a script that reads the values sent by the Arduino, and then simulate pressing a key combination. The only thing I've done with this is set push-to-talk to ctrl-shift-alt-1. I don't know what else I could use this for, maybe temporarily muting particular things, like music.
Show/hide pedalboard.py
 #!/usr/bin/env python3

import sys
import json
import time
from enum import Enum

import keyboard
import serial

class KeyState(Enum):
    UP = 0
    DOWN = 1

STATES = [KeyState.UP] * 3
THRESHOLD = 0.8
MACROS = ['ctrl+shift+alt+1', 'ctrl+shift+alt+2', 'ctrl+shift+alt+3']

def get_args():
    """ Get command line arguments """

    import argparse
    parser = argparse.ArgumentParser()
    parser.add_argument('device')
    return parser.parse_args()


def main(args):
    """ Entry point for script """
    while True:
        try:
            kb = serial.Serial(port=args.device, baudrate=9600)
            while True:
                handle(json.loads(kb.readline()))
        except serial.serialutil.SerialException as e:
            print(e)
            print("Failed to connect to device... trying again")
            time.sleep(1)
        except Exception as e:
            print(e)
    return 0

def handle(data):
    global STATES

    states = [KeyState.DOWN if value > THRESHOLD else KeyState.UP for value in data]
    r = [handle_state_change(i, states[i]) if states[i] != STATES[i] else None for i in range(len(STATES))]
    STATES = states
    return r

def handle_state_change(key, newstate):
    print(f"{key} {newstate}")
    return keyboard.press(MACROS[key]) if newstate == KeyState.DOWN else keyboard.release(MACROS[key])


if __name__ == '__main__':
    try:
        sys.exit(main(get_args()))
    except KeyboardInterrupt:
        sys.exit(0)