Wireless usb Nunchuck controller project (24NRFL01)

Hi everyone,

For quite a while I have been working on my USB HID Nunchuk project, designed for VR purposes.
Also discussed earlier in this topic

Goal
The aim of the project is to use 2 Wii Nunchuks (left and right hand) as substitute for a keyboard and mouse or game controller while playing Skyrim with my Oculus Rift. Thus creating one serious immersive game experience! Believe me. The choice for the Nunchuk is based on its ergonomic shape, that is has a joystick, physical buttons and an accelerometer.

How it works
At this moment the concept works by using an Arduino Pro Micro (connected directly to the Nunchuk) which reads and interprets the I2C signal that are outputted by the Nunchuk (using this sketch). Because of software limitations, I make use one Arduino for each Nunchuck .
The axis of the joystick emulates w, d, a and s, the buttons emulate the mouse buttons and the accelerometers are used for ie. swinging the sword, casting spells and blocking with your shield

Problem
The biggest issue I am experiencing are the wires. It has been a hassle to route the USB cables from the pc to the Arduino’s, each time I wanted to use them. After many hours of desk research and developing, on how to deal with the wires, I stumbled upon the NRF24L01 module, a nifty little 2.4GHz module , commonly being used in ordinary devices as keyboards and mice. I already have two Arduino’s ‘talking’ to each other using a basic sketch, a LED, and a switch, so I know it works!

My question
The next step is to use the NRF24L01 module to send the readily interpreted I2C signals from the Arduino (connected to the Nunchuck) to another Arduino, connected to the pc via usb. (see chart below).

My skill level is novice, but I am confident this is feasible. I would like to share my findings with the community and reach out for some guidance on how to edit my sketch in order to send the readily interpreted I2C data over the NRF24L01 module to another Arduino.

My code

#include <Keyboard.h>
#include <Mouse.h>
#include <Wire.h>
#include "ArduinoNunchuk.h"

int joyKeyDown = (-1);
int buttonDown = (-1);

ArduinoNunchuk nunchuk = ArduinoNunchuk();

void setup()
{
  Serial.begin(19200);
  nunchuk.init();
}

void loop()
{
  int newJoyKeyDown = (-1);
  int newButtonDown = (-1);

  nunchuk.update();

  //// Joystick

  // joystick left
  if (nunchuk.analogX <= 70)
  {
    newJoyKeyDown = 'a';
  }

  // joystick right
  else if (nunchuk.analogX >= 170)
  {
    newJoyKeyDown = 'd';
  }

  // joystick up
  if (nunchuk.analogY >= 170)
  {
    newJoyKeyDown = 'w';
  }

  // joystick down
  else if (nunchuk.analogY <= 70)
  {
    newJoyKeyDown = 's';
  }

  // Z-Button
  if (nunchuk.zButton == 1)
  {
    newJoyKeyDown = 'q';
  }

  // C-Button
  if (nunchuk.cButton == 1)
  {
    newJoyKeyDown = 'e';
  }

  // Both C- and Z-Buttons
  if (nunchuk.zButton == 1 && nunchuk.cButton == 1)
  {
    newButtonDown = MOUSE_RIGHT;
  }

  // Accelerometers
  if (nunchuk.accelY >= 1000 || nunchuk.accelX >= 1000)
  {
    newButtonDown = MOUSE_RIGHT;
    delay(25);
  }

  // if a key is down and we have a new key to press
  if ( (newJoyKeyDown > 0) && (newJoyKeyDown != joyKeyDown) )
  {
    if ( joyKeyDown > 0 )Keyboard.release(joyKeyDown);
    Keyboard.press(newJoyKeyDown);                        // press new key
    joyKeyDown = newJoyKeyDown;                           // remember this key

  }
  else if ( (newJoyKeyDown < 0) && (joyKeyDown > 0) ) {
    // no keys down, release pressed key
    Keyboard.release(joyKeyDown);
    joyKeyDown = (-1);
  }

  //For mouse functions
  if ( (newButtonDown > 0) && (newButtonDown != buttonDown) )
  {
    if ( buttonDown > 0 )Mouse.release(buttonDown);
    Mouse.press(newButtonDown);                        // press mouse
    buttonDown = newButtonDown;                           // remember this key

  }
  else if ( (newButtonDown < 0) && (buttonDown > 0) ) {
    // no keys down, release pressed key
    Mouse.release(buttonDown);
    buttonDown = (-1);
  }
}

You don't say what you need guidance with.

Have a look at this Simple nRF24L01+ Tutorial.

If you envisage having a separate Arduino and nRF24 for each Nunchuck then the concepts in my second example would probably be best as it would avoid problems with data collisions that would arise if both Nunchucks can send messages in an uncontrolled fashion.

...R

Robin2:
You don't say what you need guidance with.

Have a look at this Simple nRF24L01+ Tutorial.

...R

Thanks for your reply and link to your tutorial, nice write up!
I see, you're right. I'll clarify my question.

What I seek guidance with, is how to edit my code, so I can send the readily interpreted I2C data over to the other Arduino, using the NRF24L01.

Klavkjir:
What I seek guidance with, is how to edit my code, so I can send the readily interpreted I2C data over to the other Arduino, using the NRF24L01.

That is still rather woolly.

I assume you have a function in your Arduino code that takes data from the Nunchuck.

Can you put all of the items of data into an array or a struct? If so then your nRF24 function just needs to send the array (or struct).

I reckon it will be simpler if you send all the values every time even if some of them have not changed.

Can you give an example of all the data values that come from a Nunchuck - I don't know anything about them.

...R

Robin2:
I assume you have a function in your Arduino code that takes data from the Nunchuck.

Can you put all of the items of data into an array or a struct? If so then your nRF24 function just needs to send the array (or struct).

I reckon it will be simpler if you send all the values every time even if some of them have not changed.

Can you give an example of all the data values that come from a Nunchuck - I don't know anything about them.

The I2C data from the Nunchuck is read out by the Nunchuk library. It spits out:

-a value between 0 and 255 for the joysticks (linear) so for example joystick X-axis left is 0 and right is 255.
-a value between 0 and 1024 for the accelerometers (x, y and z-axis)
-a value between 0 and 1 for the buttons.

As shown in the sketch, I have assigned values outputted by the Nunchuck to keyboard/mouse functions.

Will it be possible to transmit the outputted values by the Nunchuck through the NRF24 module? In other words; if I press a button (value then changes from 0 to 1), can this value then be transmitted over by the NRF24 module? And how do I incorporate this step in both the transmitter and the receiver sketch?

I hope this has been a more elaborate description of what I am trying to achieve.

Your program has several pieces like this

  if (nunchuk.analogX <= 70)
  {
    newJoyKeyDown = 'a';
  }

To be able to transmit the data wirelessly you need to save the nunchuk values to variables rather than apply them to key values. The assignation to key values should take place in the Arduino that receives the wireless messages. So something like this

nunXval = nunchuk.analogX;
nunYval = nunchuk.analogY;
nunVbtn = nunchuk.zButton;
// etc

However that style makes sending the data inconvenient so I suggest you save the values into an array with enough elements for all of the nunchuk values - something like

int nunValues[12]; // not sure if 12 is the correct number
nunValues[0] = nunchuk.analogX;
nunValues[1] = nunchuk.analogY;
nunValues[2] = nunchuk.zButton;
// etc

And then you can use the nR24 function to send the array nunValues[] and to receive it at the other end.

...R

First of all many thanks for taking the time to help me out. Much appreciated!
I have tried to rewrite my code according to your suggestions and I came up with this. It compiles in the IDE, but does it make any sense to you?

Robin2:
And then you can use the nR24 function to send the array nunValues[] and to receive it at the other end.

I don't quite understand how to send this array. Do you have an advice for me on how to use this piece of code in my sketch?

#include <Wire.h>
#include "ArduinoNunchuk.h"
#include <SPI.h>
#include <nRF24L01.h>
#include <RF24.h>

//For the nRF24L01
#define CE_PIN   9
#define CSN_PIN 10
RF24 radio(9, 10); // NRF24L01 uses SPI pins + Pin 9 and 10
const uint64_t pipe = 0xE6E6E6E6E6E6; // Needs to be the same for communicating between 2 NRF24L01
//---------------

ArduinoNunchuk nunchuk = ArduinoNunchuk();

void setup()
{
  //For the Nunchuk
  nunchuk.init();

  //For the nRF24L01
  radio.begin(); // Start the NRF24L01
  radio.openWritingPipe(pipe); // Get NRF24L01 ready to transmit
}

void loop()
{
  nunchuk.update();
  int nunValues[6]; //
  nunValues[0] = nunchuk.analogX;
  nunValues[1] = nunchuk.analogY;
  nunValues[2] = nunchuk.cButton;
  nunValues[3] = nunchuk.zButton;
  nunValues[4] = nunchuk.accelX;
  nunValues[5] = nunchuk.accelY;
  nunValues[6] = nunchuk.accelZ;
}

This is slightly wrong

  int nunValues[6]; //
  nunValues[0] = nunchuk.analogX;
  nunValues[1] = nunchuk.analogY;
  nunValues[2] = nunchuk.cButton;
  nunValues[3] = nunchuk.zButton;
  nunValues[4] = nunchuk.accelX;
  nunValues[5] = nunchuk.accelY;
  nunValues[6] = nunchuk.accelZ;

because you have created an array with space for 6 values and then you are writing 7 values.

The examples in my nRF24 tutorial send arrays so you should be able to adapt them to your need.

...R

Robin2:
Because you have created an array with space for 6 values and then you are writing 7 values.

Do you mean I can solve this by changing "int nunValues[6]" to "int nunValues[7]", because there are 7 values?

I am confused. I have read your tutorial a couple of times now, but it's still not clear to me how to send the array. I see you use char dataToSend[10] = "Message 0"; to change dataToSend into a value? And later on using dataToSend [8] to send the array?

Will it be possible to use Radio.write to send the array below? Or am I totally missing the point?

void updateMessage() {
  // so you can see that new data is being sent
  nunchuk.update();
  int nunValues[7]; //
  nunValues[0] = nunchuk.analogX;
  nunValues[1] = nunchuk.analogY;
  nunValues[2] = nunchuk.cButton;
  nunValues[3] = nunchuk.zButton;
  nunValues[4] = nunchuk.accelX;
  nunValues[5] = nunchuk.accelY;
  nunValues[6] = nunchuk.accelZ;
}

I see you use char dataToSend[10] = "Message 0"; to change dataToSend into a value?

That statement declares a variable, dataToSend, as an array of chars, and assigns it a value. It does NOT change dataToSend into a value.

And later on using dataToSend [8] to send the array?

You do NOT specify a position in the array when sending the array, unless you only want to send part of the array.

Will it be possible to use Radio.write to send the array below?

Yes.

OP, you don’t seem to be making much of an effort at coming up with a solution for yourself. @Robin2’s example nRF24L01+ code provides a more than adequate starting point that you can modify for your own needs.

If you do a little work and dig into the RF24 library, you’ll see (for example) the prototype for the ‘write()’ method:

bool write( const void* buf, uint8_t len );

Since the buffer pointer is a ‘void *’, you don’t even need to cast the pointer used for your own data. It can point to an array, a struct, anything! You just need to tell the method how many bytes to send (the second parameter). Read up on the sizeof() compile-time operator. You're not going to be spoon-fed everything.

Klavkjir:
Do you mean I can solve this by changing "int nunValues[6]" to "int nunValues[7]", because there are 7 values?

What do you think?

...R