tsmspace
Published © GPL3+

4 joystick game controller

4 joysticks allows for easy access to 6 analog axis control to be used in space games, or 8 axis for advanced coordination of the turret.

IntermediateShowcase (no instructions)2,011
4 joystick game controller

Things used in this project

Hardware components

Arduino Nano R3
Arduino Nano R3
×1
Adafruit analog 2 axis thumbstick breakout
×4
Adafruit Tactile Button switch (6mm) x 20 pack
×1
Perma-Proto Breadboard Half Size
Perma-Proto Breadboard Half Size
×1
Adafruit Universal Proto-board PCBs 2cm x 8cm - 3 Pack
×1
Adafruit Universal Proto-board PCBs 3cm x 7cm - 3 Pack
×1
Hook Up Wire Kit, 22 AWG
Hook Up Wire Kit, 22 AWG
×1
3/16 plywood board 4"x6"
×1
USB-A to Mini-USB Cable
USB-A to Mini-USB Cable
×1
#4 1/2inch screws
×30

Hand tools and fabrication machines

Soldering iron (generic)
Soldering iron (generic)
Solder Wire, Lead Free
Solder Wire, Lead Free
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
Wire Stripper & Cutter, 18-10 AWG / 0.75-4mm² Capacity Wires
hack saw
drill
small philips screwdriver
hand saw

Story

Read more

Schematics

picture schematic

Here is the basic wiring diagram

front of board

just a picture to show the organization of the board

back of board

a picture to show the back of the board.

installation instructions

Hopefully, if you've already built or otherwise acquired this config of controller, this will walk you through the software installation.

Code

my build-specific sketch

Arduino
This is simply my specific sketch config for the vjoyserialfeeder recommended sketch. The pin numbers are already set, and my project uses pull-down resistors, also requiring a minor sketch edit. -- IMPORTANT -- you will also need "ibus.h" and "ibus.cpp" from the vjoyserial respository.
#include "ibus.h"

// //////////////////
// Edit here to customize

// How often to send data?
#define UPDATE_INTERVAL 10 // milliseconds

// 1. Analog channels. Data can be read with the Arduino's 10-bit ADC.
// This gives values from 0 to 1023.
// Specify below the analog pin numbers (as for analogRead) you would like to use.
// Every analog input is sent as a single channel.
#define NUM_ANALOG_INPUTS 8
byte analogPins[] = {A0, A1, A2, A3, A4, A5, A6, A7}; // element count MUST be == NUM_ANALOG_INPUTS

// 2. Digital channels. Data can be read from Arduino's digital pins.
// They could be either LOW or HIGH.
// Specify below the digital pin numbers (as for digitalRead) you would like to use.
// Every pin is sent as a single channel. LOW is encoded as 0, HIGH - as 1023
#define NUM_DIGITAL_INPUTS 12
byte digitalPins[] = {2,3,4,5,6, 7,8,9,10,11,12,13}; // element count MUST be == NUM_DIGITAL_INPUTS

// 3. Digital bit-mapped channels. Sending a single binary state as a 16-bit
// channel is pretty wasteful. Instead, we can encode one digital input
// in each of the 16 channel bits.
// Specify below the digital pins (as for digitalRead) you would like to send as
// bitmapped channel data. Data will be automatically organized in channels.
// The first 16 pins will go in one channel (the first pin goes into the LSB of the channel).
// The next 16 pins go in another channel and so on
// LOW pins are encoded as 0 bit, HIGH - as 1.
#define NUM_DIGITAL_BITMAPPED_INPUTS 0
byte digitalBitmappedPins[] = {}; // element count MUST be == NUM_DIGITAL_BITMAPPED_INPUTS

// Define the appropriate analog reference source. See
// https://www.arduino.cc/reference/en/language/functions/analog-io/analogreference/
#define ANALOG_REFERENCE DEFAULT

// Define the baud rate
#define BAUD_RATE 115200

// /////////////////

#define NUM_CHANNELS ( (NUM_ANALOG_INPUTS) + (NUM_DIGITAL_INPUTS) + (15 + (NUM_DIGITAL_BITMAPPED_INPUTS))/16 )

IBus ibus(NUM_CHANNELS);

void setup()
{
  analogReference(ANALOG_REFERENCE); // use the defined ADC reference voltage source
  Serial.begin(BAUD_RATE);           // setup serial
}

void loop()
{
  int i, bm_ch = 0;
  unsigned long time = millis();

  ibus.begin();

  // read analog pins - one per channel
  for(i=0; i < NUM_ANALOG_INPUTS; i++)
    ibus.write(analogRead(analogPins[i]));

  // read digital pins - one per channel
  for(i=0; i < NUM_DIGITAL_INPUTS; i++)
    ibus.write(digitalRead(digitalPins[i]) == HIGH ? 1023 : 0);

  // read digital bit-mapped pins - 16 pins go in one channel
  for(i=0; i < NUM_DIGITAL_BITMAPPED_INPUTS; i++) {
  int bit = i%16;
  if(digitalRead(digitalBitmappedPins[i]) == HIGH)
  bm_ch |= 1 << bit;

  if(bit == 15 || i == NUM_DIGITAL_BITMAPPED_INPUTS-1) {
  // data for one channel ready
  ibus.write(bm_ch);
  bm_ch = 0;
  }
  }

  ibus.end();

  time = millis() - time; // time elapsed in reading the inputs
  if(time < UPDATE_INTERVAL)
    // sleep till it is time for the next update
    delay(UPDATE_INTERVAL  - time);
}

8joy.json

JSON
this is my specific vjoyserialfeeder profile. If you build exactly my controller, and use my sketch, then this profile will work automatically. all you need to do is "import" the profile in vjoyserialfeeder
{"Autoconnect":false,"DefaultProfile":"8joy","MinimizeToTray":false,"Profiles":[{"Key":"8joy","Value":{"COMPort":"COM3","FailsafeTime":500,"FailsafeUpdateRate":100,"LuaScript":null,"Mappings":[{"__type":"AxisMapping:#vJoySerialFeeder","Channel":1,"Axis":0,"Parameters":{"Center":538,"Deadband":4,"Expo":0,"Failsafe":-1,"Invert":true,"Max":1015,"Min":9,"Symmetric":true}},{"__type":"AxisMapping:#vJoySerialFeeder","Channel":0,"Axis":1,"Parameters":{"Center":526,"Deadband":4,"Expo":0,"Failsafe":-1,"Invert":true,"Max":1015,"Min":9,"Symmetric":true}},{"__type":"AxisMapping:#vJoySerialFeeder","Channel":2,"Axis":3,"Parameters":{"Center":501,"Deadband":4,"Expo":0,"Failsafe":-1,"Invert":true,"Max":1017,"Min":1,"Symmetric":true}},{"__type":"AxisMapping:#vJoySerialFeeder","Channel":3,"Axis":4,"Parameters":{"Center":511,"Deadband":4,"Expo":0,"Failsafe":-1,"Invert":true,"Max":1017,"Min":1,"Symmetric":true}},{"__type":"AxisMapping:#vJoySerialFeeder","Channel":4,"Axis":5,"Parameters":{"Center":522,"Deadband":4,"Expo":0,"Failsafe":-1,"Invert":true,"Max":1018,"Min":0,"Symmetric":true}},{"__type":"AxisMapping:#vJoySerialFeeder","Channel":5,"Axis":2,"Parameters":{"Center":499,"Deadband":4,"Expo":0,"Failsafe":-1,"Invert":false,"Max":1018,"Min":0,"Symmetric":true}},{"__type":"AxisMapping:#vJoySerialFeeder","Channel":6,"Axis":7,"Parameters":{"Center":494,"Deadband":4,"Expo":0,"Failsafe":-1,"Invert":true,"Max":1014,"Min":0,"Symmetric":true}},{"__type":"AxisMapping:#vJoySerialFeeder","Channel":7,"Axis":6,"Parameters":{"Center":476,"Deadband":4,"Expo":0,"Failsafe":-1,"Invert":false,"Max":1008,"Min":0,"Symmetric":true}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":9,"Button":0,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":11,"Button":1,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":8,"Button":2,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":10,"Button":3,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":17,"Button":4,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":18,"Button":5,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":16,"Button":6,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":19,"Button":7,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":12,"Button":8,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":13,"Button":9,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":15,"Button":10,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}},{"__type":"ButtonMapping:#vJoySerialFeeder","Channel":14,"Button":11,"Parameters":{"Failsafe":0,"Trigger":false,"TriggerDuration":200,"TriggerEdge":0,"invert":false,"notch":false,"thresh1":511,"thresh2":0}}],"Protocol":0,"ProtocolConfiguration":"","SerialParameters":{"BaudRate":0,"DataBits":0,"Parity":0,"StopBits":0},"UseCustomSerialParameters":false,"VJoyInstance":"vJoy.1"}}],"Version":"1.5.0.0","WebSocketEnabled":false,"WebSocketPort":40000}

vjoyserialfeeder

takes a serial data input and converts it to a joystick input. Follow the directions on this page to install vjoy, and vjoyserialfeeder. You can use my 8joy.json or it's easy to simply make your own profile in vjoyserialfeeder.

Credits

tsmspace
0 projects • 0 followers

Comments