Wireless Windows game controller with NRF24L01

Status
Not open for further replies.

Bodido

New member
There is a big need for good DIY wireless pc gaming input devices.

For quite some time I've been utilizing Teensy 3.1's for my racing simulator. I've used them for pedal inputs, button boxes, and additional steering wheel mounted controls (buttons and rotary encoders.)

What I'd like to do at this point is cut the cord. I would like to eliminate the coiled usb cable I have wrapped around my steering shaft that I'm currently using to connect my buttons and rotary encoders. I'm currently using this code that someone was kind enough to share with me. It works flawlessly for my needs.

Code:
/* Buttons and Rotary Encoders for a Simracing Wheelplate
   as part of a DIY Community Project at virtualracing.org.

  You must select Serial+Keyboard+Mouse+Joystick from the "Tools > USB Type" menu
  Buttons can be wired to Pin 0-12.
  Rotary Encoder A to 13-14, B to 15-16, C to 17-18, D to 19-20.
  Potmeters to Pin 21-22.
  
This is modified and combined code from the supplied library examples: Teensy>USB_Keyboard>Buttons and Encoder>Basic.
Modified by Andre / Februari 2014.
*/

#include <Bounce.h>
#include <Encoder.h>

// Create Bounce objects for each button.  The Bounce object
// automatically deals with contact chatter or "bounce", and
// it makes detecting changes very simple.
Bounce button0 = Bounce(0, 10);
Bounce button1 = Bounce(1, 10);  // 10 = 10 ms debounce time
Bounce button2 = Bounce(2, 10);  // which is appropriate for
Bounce button3 = Bounce(3, 10);  // most mechanical pushbuttons
Bounce button4 = Bounce(4, 10);
Bounce button5 = Bounce(5, 10);  // if a button is too "sensitive"
Bounce button6 = Bounce(6, 10);  // to rapid touch, you can
Bounce button7 = Bounce(7, 10);  // increase this time.
Bounce button8 = Bounce(8, 10);
Bounce button9 = Bounce(9, 10);
Bounce button10 = Bounce(10, 10);
Bounce button11 = Bounce(11, 10);
Bounce button12 = Bounce(12, 10);

Encoder myEncA(13, 14);
Encoder myEncB(15, 16);
Encoder myEncC(17, 18);
Encoder myEncD(19, 20);

void setup() {
  pinMode(0, INPUT_PULLUP);
  pinMode(1, INPUT_PULLUP);
  pinMode(2, INPUT_PULLUP);
  pinMode(3, INPUT_PULLUP);
  pinMode(4, INPUT_PULLUP);
  pinMode(5, INPUT_PULLUP);
  pinMode(6, INPUT_PULLUP);  // Teensy++ LED, may need 1k resistor pullup
  pinMode(7, INPUT_PULLUP);
  pinMode(8, INPUT_PULLUP);
  pinMode(9, INPUT_PULLUP);
  pinMode(10, INPUT_PULLUP);
  pinMode(11, INPUT_PULLUP);
    
  
}
long oldPositionA  = 0;  // Defining the starting postion for the Rotary Encoders
long oldPositionB  = 0;
long oldPositionC  = 0;
long oldPositionD  = 0;


void loop() {
          // Potmeters Pin 21-22
  
          // ENCODERS Pin 13-14, 15-16, 17-18, 19-20
 // Encoder A   
  long newPositionA = myEncA.read();
  if (newPositionA > oldPositionA) {
    oldPositionA = newPositionA;
    Joystick.button(14, 1);
    delay(50);
    Joystick.button(14, 0);
  }
  if (newPositionA < oldPositionA) {
    oldPositionA = newPositionA;
    Joystick.button(15, 1);
    delay(50);
    Joystick.button(15, 0);
  }
  // Encoder B   
  long newPositionB = myEncB.read();
  if (newPositionB > oldPositionB) {
    oldPositionB = newPositionB;
    Joystick.button(16, 1);
    delay(50);
    Joystick.button(16, 0);
  }
  if (newPositionB < oldPositionB) {
    oldPositionB = newPositionB;
    Joystick.button(17, 1);
    delay(50);
    Joystick.button(17, 0);
    }
    // Encoder C   
  long newPositionC = myEncC.read();
  if (newPositionC > oldPositionC) {
    oldPositionC = newPositionC;
    Joystick.button(18, 1);
    delay(50);
    Joystick.button(18, 0);
  }
  if (newPositionC < oldPositionC) {
    oldPositionC = newPositionC;
    Joystick.button(19, 1);
    delay(50);
    Joystick.button(19, 0);
    }
    // Encoder D   
  long newPositionD = myEncD.read();
  if (newPositionD > oldPositionD) {
    oldPositionD = newPositionD;
    Joystick.button(20, 1);
    delay(50);
    Joystick.button(20, 0);
  }
  if (newPositionD < oldPositionD) {
    oldPositionD = newPositionD;
    Joystick.button(21, 1);
    delay(50);
    Joystick.button(21, 0);
  }  
        // BUTTONS Pin 0-12
  
  // Update all the buttons.  There should not be any long
  // delays in loop(), so this runs repetitively at a rate
  // faster than the buttons could be pressed and released.
  button0.update();
  button1.update();
  button2.update();
  button3.update();
  button4.update();
  button5.update();
  button6.update();
  button7.update();
  button8.update();
  button9.update();
  button10.update();
  button11.update();
  button12.update();

  // Check each button for "falling" edge.
  // Type a message on the Keyboard when each button presses
  // Update the Joystick buttons only upon changes.
  // falling = high (not pressed - voltage from pullup resistor)
  //           to low (pressed - button connects pin to ground)
   if (button0.fallingEdge()) {
    Joystick.button(1, 1);
  }
  if (button1.fallingEdge()) {
    Joystick.button(2, 1);
  }
  if (button2.fallingEdge()) {
    Joystick.button(3, 1);
  }
  if (button3.fallingEdge()) {
    Joystick.button(4, 1);
  }
  if (button4.fallingEdge()) {
    Joystick.button(5, 1);
  }
  if (button5.fallingEdge()) {
    Joystick.button(6, 1);
  }
  if (button6.fallingEdge()) {
    Joystick.button(7, 1);
  }
  if (button7.fallingEdge()) {
    Joystick.button(8, 1);
  }
  if (button8.fallingEdge()) {
    Joystick.button(9, 1);
  }
  if (button9.fallingEdge()) {
    Joystick.button(10, 1);
  }
  if (button10.fallingEdge()) {
    Joystick.button(11, 1);
  }
  if (button11.fallingEdge()) {
    Joystick.button(12, 1);
  }
  if (button12.fallingEdge()) {
    Joystick.button(13, 1);
  }

  // Check each button for "rising" edge
  // Type a message on the Keyboard when each button releases.
  // For many types of projects, you only care when the button
  // is pressed and the release isn't needed.
  // rising = low (pressed - button connects pin to ground)
  //          to high (not pressed - voltage from pullup resistor)
   if (button0.risingEdge()) {
    Joystick.button(1, 0);
  }
  if (button1.risingEdge()) {
    Joystick.button(2, 0);
  }
  if (button2.risingEdge()) {
    Joystick.button(3, 0);
  }
  if (button3.risingEdge()) {
    Joystick.button(4, 0);
  }
  if (button4.risingEdge()) {
    Joystick.button(5, 0);
  }
  if (button5.risingEdge()) {
    Joystick.button(6, 0);
  }
   if (button6.risingEdge()) {
    Joystick.button(7, 0);
  }
  if (button7.risingEdge()) {
    Joystick.button(8, 0);
  }
  if (button8.risingEdge()) {
    Joystick.button(9, 0);
  }
  if (button9.risingEdge()) {
    Joystick.button(10, 0);
  }
  if (button10.risingEdge()) {
    Joystick.button(11, 0);
  }
  if (button11.risingEdge()) {
    Joystick.button(12, 0);
  }
  if (button12.risingEdge()) {
    Joystick.button(13, 0);
  }
}

So my hope is that those cheap little nrf24l01 boards are the answer to my needs. I purchased a few of them as well as a few Teensy 3.2's a couple weeks ago. I've played around with them with limited success until I hit this page: https://arduino-info.wikispaces.com/Nrf24L01-2.4GHz-ExampleSketches#bm1 The two bare minimum examples shown there work great for me, including running the transmitter off of battery power. The communication is consistent and reliable, so I believe I'm past any real hardware issues. This is using the current RF24 library from here: http://tmrh20.github.io/RF24/index.html


So my initial goal is to have one battery powered Teensy 3.2 mounted on my steering wheel rim that is connected to my buttons and rotary encoders. I want it to transmit button presses and rotary encoder inputs via the nrf24l01 transciever to another nrf24l01 and Teensy 3.2 that stays plugged into my pc. It will be configured as usb type: Serial+Keyboard+Mouse+Joystick. And show up as a joystick in windows (and iracing, of course, my racing sim of choice).


Once I get that working reliably, I would really like to add additional transmitter Teensies. Myself and many others have more than one wheel rim that we use depending on which car we are simulating. Many serious sim racing addicts have several wheel rims with quick release system like those used in real race cars. It would be nice to be able to turn on a power switch on the wheel rim I intend to use and start racing without having to change anything on the pc side. If I understand correctly, I should be able have up to 6 receiving pipes simultaneously with the RF24 library? If I could configure 6 different wireless controllers that Windows sees as one joystick, I think that would cover my needs, and many other people's needs pretty well.

Beyond that adding analog axis' would be great, as well. People could have multiple wireless gamepads or joysticks that show up as one windows input device with lots of axis' and inputs. This would be very useful to some people, just for windows device id issues alone, not to mention the wireless functionality.

I honestly haven't had to understand much coding at all to get what I currently have going, I've just edited and adapted stuff I've found, but now I'm in a bit over my head. I'm hoping someone can chime in and tell me if what I have planned is feasible. I believe it is. Next, if someone can point to some relevant reading I could do to learn how to do what I'm trying to achieve.

Not asking for someone to give me a fish, but give me a nudge in the right direction of where the fish are, so to speak. ;) I want to learn.

Any thoughts?
 
Status
Not open for further replies.
Back
Top