/rpi-nintendo-classic-controller

Nintendo classic i2c controller driver for Raspberry Pi

Primary LanguageCMIT LicenseMIT

Nintendo classic i2c controller user space driver for Raspberry Pi

Allows connecting NES/SNES classic mini controllers directly via the I2C bus on the GPIO header of the Raspberry Pi. The driver also supports Wii Classic Controller (Pro).

Connecting the controller

Plug side view:

/---------\
|         |
|  1-2-3  |
|  4-5-6  |
|  _____  |
\_/     \_/
Controller Pin Function Pi Pin
1 I2C data 3 (SDA)
2 device detect -
3 +3.3V 1 (+3.3V)
4 GND 6 (GND)
5 not connected -
6 I2C clock 5 (SCL)

One way to connect the wires without breaking the plug of your controller is to use one of many "nunchuck" breakout boards sold by hobby electronic stores. Some of them seem to be out of stock by now so an alternative would be to sacrifice a socket part from an snes/nes classic extension cable which are currently common and inexpensive.

Enabling i2c on Raspberry Pi

This can be done with the bundled configuration tool raspi-config under "Interfacing Options" > "I2C". If you want to do it manually, it comes down to:

  • Adding dtparam=i2c_arm=on line to /boot/config.txt
  • Loading i2c_dev module (sudo modprobe i2c_dev for the current session and adding i2c_dev line to /etc/modules to load on boot)

Installing required packages

sudo apt-get install build-essential cmake libi2c-dev i2c-tools

Testing the connection

When properly connected it controller should be now visible at address 0x52 and all other addresses should be empty.

$ sudo i2cdetect -y 1
     0  1  2  3  4  5  6  7  8  9  a  b  c  d  e  f
00:          -- -- -- -- -- -- -- -- -- -- -- -- -- 
10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
50: -- -- 52 -- -- -- -- -- -- -- -- -- -- -- -- -- 
60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 
70: -- -- -- -- -- -- -- -- 

Compiling the driver

cmake .
make

Testing the driver

sudo ./i2c-classic-controller -d

If the controller is correctly connected it should display the button events:

Detected device: 01 00 A4 20 01 01
buttons:    A           

Driver options

  • -y <n> by default the driver is using /dev/i2c-1 for the i2c bus connection, which corresponds to the bus exposed on Pi pins 3/5. This option allows using other buses.
  • -f <n> event polling frequency. The default is 60 per second and can be changed in range 1 - 1000.
  • -d print controller events to the standard output
  • -a report analog stick events. By default the driver will ignore the analog input which is not presnet on the classic controllers. It seems that the classic console controllers reuse the Wii Classic Controller Pro hardware, so they cannot be distinguished.
  • -a6 report also the analog triggers. The triggers also report separate digital events, so enabling this may confuse some configuration software. Also in Wii Classic Controller Pro those buttons are not even analog so only 2 values are reported. It is possible that the original Wii Classic Controller (which I don't own) uses true analog output for those.
  • -h use full 8-bit accuracy readout for analog input. The snes/nes classic consoles use this mode even though the controllers don't have analog sticks. This option may be required for some third party controllers which only implement the 8-bit accuracy readout.

Installing

sudo make install
sudo systemctl enable nintendo-i2c-controller

This will install a systemd service which will run the driver on startup. Additionally a udev rule will be set to remove ID_INPUT_KEY property from the uinput node which prevents some software (specifically Kodi) to detect the controller as a keyboard.

Connecting more than one controller

All Nintendo i2c controllers use the same static i2c address which means only one can be connected to an i2c bus at a time. Raspberry Pi 2/3 SoC has 2 buses but bus 0 is used for internal purposes. Pi 4 has more i2c busses however this functionality does not seem to be explored much so I did not try to use it. Either way this driver theoretically would support multiple controllers when one instance of the driver is run for each port (using the -y bus id param).