I bought an old Thrustmater F16 FLCS gameport joystick and want to convert it to a USB stick, using a Leonardo board. The buttons/hats use SPI and the two pots for Pitch and Bank will obviously connect to two analog inputs.
I found this code for Teensy http://forum.il2sturmovik.com/topic/1790-there-any-better-joystick-ms-sidewinder-force-feedback-2/#entry46472 which obviously relies on the “Joystick” code embedded in that board, so can’t be used with the Leonardo but might give some ideas:
/* USB FLCS Grip
You must select Joystick from the "Tools > USB Type" menu
*/
// Buttons are muxed into shift registers, use the SPI protocol to read them
#include <SPI.h>
const int slaveSelectPin = 0;
unsigned int buttonInputs1; // data read from SPI
unsigned int buttonInputs2;
unsigned int buttonInputs3;
// Use some macros to clean things up
#define S3 !(buttonInputs1 & 0x80) /* Pinky Switch */
#define TG1 !(buttonInputs1 & 0x40) /* Trigger 1 */
#define TG2 !(buttonInputs1 & 0x20) /* Trigger 2 */
#define S1 !(buttonInputs1 & 0x10) /* Nose Wheel Steering */
#define S4 !(buttonInputs1 & 0x08) /* Paddle Switch */
#define S2 !(buttonInputs1 & 0x04) /* Pickle */
#define H1D !(buttonInputs2 & 0x80) /* Trim */
#define H1R !(buttonInputs2 & 0x40)
#define H1U !(buttonInputs2 & 0x20)
#define H1L !(buttonInputs2 & 0x10)
#define H4U !(buttonInputs2 & 0x08) /* CMS */
#define H4L !(buttonInputs2 & 0x04)
#define H4D !(buttonInputs2 & 0x02)
#define H4R !(buttonInputs2 & 0x01)
#define H3D !(buttonInputs3 & 0x80) /* DMS */
#define H3R !(buttonInputs3 & 0x40)
#define H3U !(buttonInputs3 & 0x20)
#define H3L !(buttonInputs3 & 0x10)
#define H2D !(buttonInputs3 & 0x08) /* TMS */
#define H2R !(buttonInputs3 & 0x04)
#define H2U !(buttonInputs3 & 0x02)
#define H2L !(buttonInputs3 & 0x01)
// setup() runs once on boot
void setup() {
// set the slaveSelectPin as an output:
pinMode (slaveSelectPin, OUTPUT);
// start the SPI library:
SPI.begin();
// configure the joystick to manual send mode. This gives precise
// control over when the computer receives updates, but it does
// require you to manually call Joystick.send_now().
Joystick.useManualSend(true);
}
// loop() runs for as long as power is applied
void loop() {
// take the SS pin low to select the chip
digitalWrite(slaveSelectPin,LOW);
// send a value of 0 to read the SPI bytes
buttonInputs1 = SPI.transfer(0x00);
buttonInputs2 = SPI.transfer(0x00);
buttonInputs3 = SPI.transfer(0x00);
// take the SS pin high to de-select the chip:
digitalWrite(slaveSelectPin,HIGH);
// Write to joystick buttons
Joystick.button(1, TG1);
Joystick.button(2, S2);
Joystick.button(3, S3);
Joystick.button(4, S4);
Joystick.button(5, S1);
Joystick.button(6, TG2);
Joystick.button(7, H2U);
Joystick.button(8, H2R);
Joystick.button(9, H2D);
Joystick.button(10, H2L);
Joystick.button(11, H3U);
Joystick.button(12, H3R);
Joystick.button(13, H3D);
Joystick.button(14, H3L);
Joystick.button(15, H4U);
Joystick.button(16, H4R);
Joystick.button(17, H4D);
Joystick.button(18, H4L);
//Joystick.button(19, H1U);
//Joystick.button(20, H1R);
//Joystick.button(21, H1D);
//Joystick.button(22, H1L);
// Determine Joystick Hat Position
int angle = -1;
if (H1U) {
if (H1R) {
angle = 45;
} else if (H1L) {
angle = 315;
} else {
angle = 0;
}
} else if (H1D) {
if (H1R) {
angle = 135;
} else if (H1L) {
angle = 225;
} else {
angle = 180;
}
} else if (H1R) {
angle = 90;
} else if (H1L) {
angle = 270;
}
Joystick.hat(angle);
// Because setup configured the Joystick manual send,
// the computer does not see any of the changes yet.
// This send_now() transmits everything all at once.
Joystick.send_now();
}
I also found this code for using a Leonardo as a USB Joystick http://www.imaginaryindustries.com/blog/?p=80 and that works as expected and sends random output with nothing connected. I have no idea how to translate the Teensy code and merge it with the Leonardo code to achieve what I want though, so was wondering if there’s anyone here with some understanding of Arduino code who could help me?
I don’t really know where to start but in USBAPI.h, the main joystick-related code seems to be:
// Joystick
// Implemented in HID.cpp
// The list of parameters here needs to match the implementation in HID.cpp
typedef struct JoyState // Pretty self explanitory. Simple state to store all the joystick parameters
{
uint8_t xAxis;
uint8_t yAxis;
uint8_t zAxis;
uint8_t xRotAxis;
uint8_t yRotAxis;
uint8_t zRotAxis;
uint8_t throttle;
uint8_t rudder;
uint8_t hatSw1;
uint8_t hatSw2;
uint32_t buttons; // 32 general buttons
} JoyState_t;
class Joystick_
{
public:
Joystick_();
void setState(JoyState_t *joySt);
};
extern Joystick_ Joystick;
and in HID.cpp:
// Joystick
// Usage: Joystick.move(inputs go here)
//
// The report data format must match the one defined in the descriptor exactly
// or it either won't work, or the pc will make a mess of unpacking the data
//
Joystick_::Joystick_()
{
}
#define joyBytes 13 // should be equivalent to sizeof(JoyState_t)
void Joystick_::setState(JoyState_t *joySt)
{
uint8_t data[joyBytes];
uint32_t buttonTmp;
buttonTmp = joySt->buttons;
data[0] = buttonTmp & 0xFF; // Break 32 bit button-state out into 4 bytes, to send over USB
buttonTmp >>= 8;
data[1] = buttonTmp & 0xFF;
buttonTmp >>= 8;
data[2] = buttonTmp & 0xFF;
buttonTmp >>= 8;
data[3] = buttonTmp & 0xFF;
data[4] = joySt->throttle; // Throttle
data[5] = joySt->rudder; // Steering
data[6] = (joySt->hatSw2 << 4) | joySt->hatSw1; // Pack hat-switch states into a single byte
data[7] = joySt->xAxis; // X axis
data[8] = joySt->yAxis; // Y axis
data[9] = joySt->zAxis; // Z axis
data[10] = joySt->xRotAxis; // rX axis
data[11] = joySt->yRotAxis; // rY axis
data[12] = joySt->zRotAxis; // rZ axis
//HID_SendReport(Report number, array of values in same order as HID descriptor, length)
HID_SendReport(3, data, joyBytes);
// The joystick is specified as using report 3 in the descriptor. That's where the "3" comes from
}