Digitalduino - DIY Arduino Racing Wheel! (With Direct Port Manipulation) PDF

You might also like

Download as pdf or txt
Download as pdf or txt
You are on page 1of 16

Digitalduino

Thursday, January 26, 2012

DIY Arduino Racing Wheel! (with Direct Port Manipulation)


The title says it all: I'm building a racing wheel for my computer. It will use the HDD encoders from a
few posts back to measure how far the pedals have been pressed and the angle of the steering wheel,
six buttons for a six speed shifter(with another button for toggling speed shift vs real shift, how this
works depends on the game being played), and a few auxiliary buttons.

Since that i am now using multiple encoders and interrupts, i am now using direct port manipulation to
read the pin states as it is many times faster than regular digitalReads.

I found a way better way to write the Arduino values to the virtual joystick; PPJoyCom. It is a add-on
type program for the PPJoy virtual joystick driver. PPJoyCom waits for a initialization byte over the
serial port from the Arduino(print a byte value 240+number of channels). then the encoder values
range from 0 to 255(max value of a byte) and are sent to PPJoyCom and written to the joystick. way
easier than a C# app. :) basic sample PPJoyCom arduino sketch below, along with encoder code.

basic flow:
1. arduino reads HDD encoders for position, button states etc. sends the data to the PPJoyCom
program.
3. PPJoyCom program "writes" the values from the Arduino to the virtual joystick.
3. racing game reads the virtual joystick; X axis is turning, Y is throttle, Z is brake, buttons for shifting,
menu keys...

#define Xpin  2
#define Ypin 3
#define Btn1Pin 11
#define Btn2Pin 12

byte Xaxis;
byte Yaxis;
byte Btn1 = 0;
byte Btn2 = 0;

void setup()
{
  Serial.begin(9600);
}
void loop()
{
  Btn1 = digitalRead(Btn1Pin);
  Btn2 = digitalRead(Btn2Pin);

  Xaxis = analogRead(Xpin) / 4;
  Yaxis = analogRead(Ypin) / 4;

  Serial.print(244,BYTE); // 240 + # of channels, 2 Buttons and 2 axis


  Serial.write(0);
  Serial.print(Btn1,BYTE);
  Serial.print(Btn2,BYTE); 
  Serial.print(x,BYTE);
  Serial.print(y,BYTE);
  delay(25);
}

#include <PinChangeInt.h>
#include <PinChangeIntConfig.h>

#define WheelPinA 2
#define WheelPinB 3

#define GasPinA 4
#define GasPinB 5

#define BrakePinA 6
#define BrakePinB 7

#define ClutchPinA 8
#define ClutchPinB 9

#define Gear1pin 10
#define Gear2pin 11
#define Gear3pin 12
#define Gear4pin 13
#define Gear5pin 14 //A0
#define Gear6pin 15 //A1

#define ShiftModepin 16 //A2, real shifting or speed shift


#define Aux1pin 17 //A3
#define Aux2pin 18 //A4
#define Aux3pin 19 //A5

volatile unsigned int Encoders[3] = {0}; //wheel, gas, brake, clutch


volatile boolean ButtonStates[3] = {0}; //current gear, shift mode, aux: 1, 2, or 3

void setup()
{
  //         76543210
  //(PIND & B00001000) check only the pin '1' (in this case digital 3) in this line for low or high, & means
dont check any others
  //would be the same as: PIND & (1 << 3);

  //1111111 checking for true would be 128+64+32+16+8+4+2+1


  //(PIND & B00001000) checking for true would be 8

  //goes from left to right


  //       76543210
  DDRD =  B00000000; //sets 2 through 7 as inputs
  PORTD &= B11111100; //enable pullups on all but tx/rx

  //       ..13...8
  DDRB &= B11000000; //sets  through 13 as inputs excpet for crytsl pins
  PORTB &= B00111111; //enable pullups on all but crystal pins

   //     ..543210
  DDRC = B00000000; //sets analog 0 through 5 as inputs
  PORTC &= B00111111; //enable pullups on all but analog 6 an 7 (6 and 7 arent avaliable on atmega
328)

  PCattachInterrupt(WheelPinA, WheelTrigA, CHANGE);


  PCattachInterrupt(WheelPinB, WheelTrigB, CHANGE);

  PCattachInterrupt(GasPinA, GasTrigA, CHANGE);


  PCattachInterrupt(GasPinB, GasTrigB, CHANGE);

  PCattachInterrupt(BrakePinA, BrakeTrigA, CHANGE);


  PCattachInterrupt(BrakePinB, BrakeTrigB, CHANGE);

  PCattachInterrupt(ClutchPinA, ClutchTrigA, CHANGE);


  PCattachInterrupt(ClutchPinB, ClutchTrigB, CHANGE);

  PCattachInterrupt(Gear1pin, GearItrig, CHANGE);


  PCattachInterrupt(Gear2pin, GearIItrig, CHANGE);
  PCattachInterrupt(Gear3pin, GearIIItrig, CHANGE);
  PCattachInterrupt(Gear4pin, GearIVtrig, CHANGE);
  PCattachInterrupt(Gear5pin, GearVtrig, CHANGE);
  PCattachInterrupt(Gear6pin, GearVItrig, CHANGE);
  PCattachInterrupt(Aux1pin, AuxItrig, CHANGE);
  PCattachInterrupt(Aux2pin, AuxIItrig, CHANGE);
  PCattachInterrupt(Aux3pin, AuxIIItrig, CHANGE);
  PCattachInterrupt(ShiftModepin, ShiftTrig, CHANGE);
  Serial.begin (115200);
}

void loop()
{
  for(int x=0; x<4; x++)
 {
    Serial.print(Encoders[x]);
    Serial.print(","); //delimiter
 }
  for(int x=0; x<4; x++)
 {
    Serial.print(ButtonStates[x]);
    Serial.print(","); //delimiter
 }
  Serial.println();
}

void GearItrig()
{
  ButtonStates[0]=1;
}

void GearIItrig()
{
  ButtonStates[0]=2;
}

void GearIIItrig()
{
  ButtonStates[0]=3;
}

void GearIVtrig()
{
  ButtonStates[0]=4;
}

void GearVtrig()
{
  ButtonStates[0]=5;
}

void GearVItrig()
{
  ButtonStates[0]=6;
}
void ShiftTrig()
{
  ButtonStates[1]=~ButtonStates[1];
}

void AuxItrig()
{
  ButtonStates[2]=1;
}

void AuxIItrig()
{
  ButtonStates[2]=2;
}

void AuxIIItrig()
{
  ButtonStates[3]=3;
}

void WheelTrigA()
{
  // look for a low-to-high on pin A
  if ((PIND & B00000100) == 4)
 {
    // check pin B to see which way encoder is turning
    if ((PIND & B00001000) == 0)
  {
      Encoders[0]  -= 1;         // CW
  }
    else
  {
      Encoders[0]  += 1;         // CCW
  }
 }
  else   // must be a high-to-low edge on pin A                                    
 {
    // check pin B to see which way encoder is turning
    if ((PIND & B00001000) == 8)
  {
      Encoders[0]  -= 1;          // CW
  }
    else
  {
      Encoders[0]  += 1;          // CCW
  }
 }
  //Serial.println (Encoders[0], DEC);        
  // use for debugging - remember to comment out
}
void WheelTrigB()
{
  // look for a low-to-high on pin B
  if ((PIND & B00001000) == 8)
 {
    // check pin A to see which way encoder is turning
    if ((PIND & B00000100) == 4)
  {
      Encoders[0]  -= 1;         // CW
  }
    else
  {
      Encoders[0]  += 1;         // CCW
  }
 }
  // Look for a high-to-low on pin B
  else
 {
    // check pin B to see which way encoder is turning
    if ((PIND & B00000100) == 0)
  {
      Encoders[0]  -= 1;          // CW
  }
    else
  {
      Encoders[0]  += 1;          // CCW
  }
 }
}

void GasTrigA()
{
  // look for a low-to-high on pin A
  if ((PIND & B00010000) == 16)
 {
    // check pin B to see which way encoder is turning
    if ((PIND & B00100000) == 0)
  {
      Encoders[1]  -= 1;         // CW
  }
    else
  {
      Encoders[1]  += 1;         // CCW
  }
 }
  else   // must be a high-to-low edge on pin A                                    
 {
    // check pin B to see which way encoder is turning
    if ((PIND & B00100000) == 32)
  {
      Encoders[1]  -= 1;          // CW
  }
    else
  {
      Encoders[1]  += 1;          // CCW
  }
 }
  //Serial.println (Encoders[1], DEC);        
  // use for debugging - remember to comment out
}
void GasTrigB()
{
  // look for a low-to-high on pin B
  if ((PIND & B00100000) == 32)
 {
    // check pin A to see which way encoder is turning
    if ((PIND & B00010000) == 16)
  {
      Encoders[1]  -= 1;         // CW
  }
    else
  {
      Encoders[1]  += 1;         // CCW
  }
 }
  // Look for a high-to-low on pin B
  else
 {
    // check pin B to see which way encoder is turning
    if ((PIND & B00010000) == 0)
  {
      Encoders[1]  -= 1;          // CW
  }
    else
  {
      Encoders[1]  += 1;          // CCW
  }
 }
}

void BrakeTrigA()
{
  // look for a low-to-high on pin A
  if ((PIND & B01000000) == 64)
 {
    // check pin B to see which way encoder is turning
    if ((PIND & B10000000) == 0)
  {
      Encoders[2]  -= 1;         // CW
  }
    else
  {
      Encoders[2]  += 1;         // CCW
  }
 }
  else   // must be a high-to-low edge on pin A                                    
 {
    // check pin B to see which way encoder is turning
    if ((PIND & B10000000) == 128)
  {
      Encoders[2]  -= 1;          // CW
  }
    else
  {
      Encoders[2]  += 1;          // CCW
  }
 }
  //Serial.println (Encoders[2], DEC);        
  // use for debugging - remember to comment out
}

void BrakeTrigB()
{
  // look for a low-to-high on pin B
  if ((PIND & B10000000) == 128)
 {
    // check pin A to see which way encoder is turning
    if ((PIND & B01000000) == 64)
  {
      Encoders[2]  -= 1;         // CW
  }
    else
  {
      Encoders[2]  += 1;         // CCW
  }
 }
  // Look for a high-to-low on pin B
  else
 {
    // check pin B to see which way encoder is turning
    if ((PIND & B01000000) == 0)
  {
      Encoders[2]  -= 1;          // CW
  }
    else
  {
      Encoders[2]  += 1;          // CCW
  }
 }
}

void ClutchTrigA()
{
  // look for a low-to-high on pin A
  if ((PINB & B00000001) == 1)
 {
    // check pin B to see which way encoder is turning
    if ((PINB & B00000010) == 0)
  {
      Encoders[3]  -= 1;         // CW
  }
    else
  {
      Encoders[3]  += 1;         // CCW
  }
 }
  else   // must be a high-to-low edge on pin A                                    
 {
    // check pin B to see which way encoder is turning
    if ((PINB & B00000010) == 2)
  {
      Encoders[3]  -= 1;          // CW
  }
    else
  {
      Encoders[3]  += 1;          // CCW
  }
 }
  //Serial.println (Encoders[3], DEC);        
  // use for debugging - remember to comment out
}
void ClutchTrigB()
{
  // look for a low-to-high on pin B
  if ((PINB & B00000010) == 2)
 {
    // check pin A to see which way encoder is turning
    if ((PINB & B00000001) == 1)
  {
      Encoders[3]  -= 1;         // CW
  }
    else
  {
      Encoders[3]  += 1;         // CCW
  }
 }
  // Look for a high-to-low on pin B
  else
 {
    // check pin B to see which way encoder is turning
    if ((PINB & B00000001) == 0)
  {
      Encoders[3]  -= 1;          // CW
  }
    else
  {
      Encoders[3]  += 1;          // CCW
  }
 }
}

Jordan at 12:38 PM

Share

20 comments:

Bluntknife April 18, 2012 at 4:47 PM


I've modified your code to work with a single linear pot and it the analog signal always comes up as a
digital one when I check it with the windows game controller.

Any help would be awesome.

[code]
#define Xpin 0
byte Xaxis;
void setup(){
Serial.begin(9600);
}

void loop()
{

Xaxis = analogRead(Xpin) / 4;
Serial.write(241); // 240 + # of channels, 2 Buttons and 2 axis
Serial.write(Xaxis);

delay(25);
}
[/code]
Reply

Jordan April 18, 2012 at 6:02 PM


Oh, just thought of something! in PPjoy you have to setup the buttons/analog inputs correctly. like if you
have:

Serial.write(241);
Serial.write(Xaxis);

then a analog input would need to be set as the first input in PPjoy, not a digital.
http://www.etheli.com/simulator/simulator.html look at first pic here.
Reply

Replies

Bluntknife April 19, 2012 at 5:11 AM


Thanks alot!

That solved the issue.

Been searching for long time for a tutorial for PPjoy with Ardiuno.

Reply

Unknown May 15, 2012 at 10:54 PM


I need some help with something regarding arduino or ppjoy... Iva used your code exactly as it is and axes
work great but buttons keeps flashing in game controller properties.
Do you have a clue what could be?? I tried every code ive found but axes work and buttons dont
Ive set up ppjoy to read all 16 buttons, however button 1 keeps flashing and button 2 doesnt exist.
I hope ive made some sense here. English is not my first lenguaje.
(using windows 7 64)
Tranks!!
Reply

Jordan May 16, 2012 at 5:34 AM


strange, so do the other buttons work? are you using actual buttons on the arduino, or are you just testing
the code? if you are using actual buttons, try taking out the button read and make the arduino always
send one state(high or low) to ppjoy and see if ppjoy lines up with it.
Reply

Ace94 May 27, 2012 at 6:19 PM


Hey, I am having an issue getting the ppjoycom to recognize the serial signal from the arduino. The serial
monitor on the arduino program shows that a signal is coming through. However ppjoycom does not
even register a initiation. It just says "Error 2 opening port" Any ideas?
Reply

Jordan May 27, 2012 at 7:11 PM


that sounds more like something else is using the COM port at the same time. you say "arduino program
shows that a signal is coming through" which im guessing mean you have the serial window open in the
arduino IDE... close that serial window, then try.
Reply

raul May 29, 2013 at 7:53 PM


where do i put the code? should i use both codes or what ?
Reply

Replies

Jordan May 31, 2013 at 5:17 PM


first is an example, second is actual project

Reply

raul June 11, 2013 at 1:24 AM


im having a problem, ppjoy recognice the first button, but not the second one, im using actual buttons.

im ussing pullups because i was haven missreadings on open buttons

[code]
#define B4Pin 10
#define B3Pin 11
#define B2Pin 12
#define B1Pin 13
#define Xpin A5
#define acelPin A0
#define frenoPin A2

byte Xaxis;
byte acel;
byte freno;
byte Bot1;
byte Bot2;
byte Bot3;
byte Bot4;

void setup()
{
Serial.begin(9600);
pinMode(B4Pin,INPUT_PULLUP);
pinMode(B3Pin,INPUT_PULLUP);
pinMode(B2Pin,INPUT_PULLUP);
pinMode(B1Pin,INPUT_PULLUP);
}

void loop()
{

Xaxis = analogRead(Xpin)/8;
acel= analogRead(acelPin)/4;
freno= analogRead(frenoPin)/4;
Bot4=digitalRead(B4Pin);
Bot3= digitalRead(B3Pin);
Bot2= digitalRead(B2Pin);
Bot1=digitalRead(B1Pin);

byte Ib1= !Bot1;


byte Ib2= !Bot2;
byte Ib3= !Bot3;
byte Ib4= !Bot4;

Serial.write(245); // 240 + # of channels, 2 Buttons and 2 axis


//Serial.write(Ib1);
//Serial.write(Ib2);
Serial.write(Ib3);
Serial.write(Ib4);
Serial.write(Xaxis);
Serial.write(acel);
Serial.write(freno);

delay(25);
}

[/code]
Reply

Replies

Jordan June 11, 2013 at 6:22 AM


in serial write, that should be 247, not 245

raul June 11, 2013 at 12:04 PM


yeah i was alittle desperate, i tried everything, but 2 buttons are comented

Jordan June 11, 2013 at 12:06 PM


setup yours just like mine and see if it works, then add stuff

raul June 11, 2013 at 12:09 PM


you mean 2 axis 2 buttons?

Jordan June 11, 2013 at 12:10 PM


yes

raul June 18, 2013 at 11:10 PM


sorry for taking me so long to answer, now buttons dont respond, no problem with pedals.

Serial.write(244); // 240 + # of channels, 2 Buttons and 2 axis

Serial.write(Ib1);
Serial.write(Ib2);
//Serial.write(Ib3);
//Serial.write(Ib4);
//Serial.write(Xaxis);
Serial.write(acel);
Serial.write(freno);

Reply
Gastón Sebastián Marengo September 16, 2013 at 8:31 PM
Just send a Serial.write(0); after the initialization byte (240 + # of channels). It is needed (but not
documented). After that all my axis and buttons worked.

Serial.write(244);
Serial.write(0);
Serial.write(Ib1);
Serial.write(Ib2);
//Serial.write(Ib3);
//Serial.write(Ib4);
//Serial.write(Xaxis);
Serial.write(acel);
Serial.write(freno);
Reply

Replies

Jordan September 16, 2013 at 9:08 PM


thanks, ill add that

Jordan September 16, 2013 at 9:10 PM


Of course, the tuorial doesnt have it, but the project code does, DOH! haha anyways, its fixed
now.

Reply

Giovanni Iezzi December 28, 2013 at 6:29 PM


byte Btn1;
byte Btn2;
byte Xaxis;
void setup(){
Serial.begin(9600);
}
void loop(){
Btn1 = digitalRead(28);
Btn2 = digitalRead(26);
Xaxis = analogRead(A0) / 4;

Serial.write(byte(243));
Serial.write(byte(0));
Serial.write(byte(Xaxis));
Serial.write(byte(Btn1));
Serial.write(byte(Btn2));
delay(25);
}

This code is not recognizing the two buttons, i have changed it a lot of times, but still id doesent work,
any help please?
Reply

Enter your comment...

Comment as: Unknown (Google) Sign out

Publish Preview Notify me

‹ Home ›
View web version

About Me

Jordan
View my complete profile

Powered by Blogger.

You might also like