HID Joystick - For use with X-Plane or other programs that can read HID JoySticks

Dependencies:   USBDevice mbed-rtos mbed

Fork of JoyStick by Ries Twisk

This is a simple Joystick HID that I use for xplane and a home build yoke + paddels, see this forum with the look and feel of it : http://forums.x-plane.org/index.php?showtopic=70041

The analog input are filtered with a LowPass IIR filter and the digital input's will be derived from the analog input and de-bounced.

The analog values are read at a 1Khz interval and to ensure we don't push the USB stack to much at a maximum rate of 20 updates/sec HID data is send over USB only if any values where changed. The JoyStick will send 16Bit analog values as opposite of 8 bit values that are normally used to increase accuracy of the whole system. This is well noticeable within x-plane!

The JoyStick uses the JoyStick copied from Wim Huiskamp and modified to suite my needs and the MBED RTOS libraries for reading analog inputs, sending debug data over USB and sending HID data, 3 threads in total.

Committer:
rvt
Date:
Wed Jun 22 12:50:16 2016 +0000
Revision:
5:a0bb17c379ce
Parent:
4:2cc58c173de8
Latest

Who changed what in which revision?

UserRevisionLine numberNew contents of line
rvt 0:33bc88c4ab31 1 #include "mbed.h"
rvt 0:33bc88c4ab31 2 #include "USBHID.h"
rvt 0:33bc88c4ab31 3 #include "USBJoystick.h"
rvt 0:33bc88c4ab31 4 #include "LowPassFilter.h"
rvt 0:33bc88c4ab31 5 #include "AnalogInFiltered.h"
rvt 5:a0bb17c379ce 6 #include "AutoScale.h"
rvt 4:2cc58c173de8 7 #include "Button.h"
rvt 0:33bc88c4ab31 8 #include "rtos.h"
rvt 0:33bc88c4ab31 9
rvt 1:5b2ab44eb31f 10 // When set, it will send debug data over USB serial
rvt 5:a0bb17c379ce 11 #define TTY_DEBUG false
rvt 0:33bc88c4ab31 12
rvt 0:33bc88c4ab31 13 // Value that defines when to start sending data this prevents the noise sending loads's of data over HID
rvt 5:a0bb17c379ce 14 #define DATA_CHANGE_TRIGGER 8
rvt 0:33bc88c4ab31 15
rvt 1:5b2ab44eb31f 16 // Activity LED for HID data
rvt 1:5b2ab44eb31f 17 #define HIDACTIVITYLED LED3
rvt 0:33bc88c4ab31 18
rvt 0:33bc88c4ab31 19 // Structure that hold's the dataset of the input's
rvt 0:33bc88c4ab31 20 Mutex analogValueMutex;
rvt 0:33bc88c4ab31 21 struct AnalogData {
rvt 5:a0bb17c379ce 22 union {
rvt 5:a0bb17c379ce 23 struct {
rvt 5:a0bb17c379ce 24 uint32_t bit0:1;
rvt 5:a0bb17c379ce 25 uint32_t bit1:1;
rvt 5:a0bb17c379ce 26 uint32_t bit2:1;
rvt 5:a0bb17c379ce 27 uint32_t bit3:1;
rvt 5:a0bb17c379ce 28 uint32_t bit4:1;
rvt 5:a0bb17c379ce 29 uint32_t bit5:1;
rvt 5:a0bb17c379ce 30 uint32_t bit6:1;
rvt 5:a0bb17c379ce 31 uint32_t bit7:1;
rvt 5:a0bb17c379ce 32 uint32_t bit8:1;
rvt 5:a0bb17c379ce 33 uint32_t bit9:1;
rvt 5:a0bb17c379ce 34 uint32_t bit10:1;
rvt 5:a0bb17c379ce 35 uint32_t bit11:1;
rvt 5:a0bb17c379ce 36 uint32_t bit12:1;
rvt 5:a0bb17c379ce 37 uint32_t bit13:1;
rvt 5:a0bb17c379ce 38 uint32_t bit14:1;
rvt 5:a0bb17c379ce 39 uint32_t bit15:1;
rvt 5:a0bb17c379ce 40 uint32_t bit16:1;
rvt 5:a0bb17c379ce 41 uint32_t bit17:1;
rvt 5:a0bb17c379ce 42 uint32_t bit18:1;
rvt 5:a0bb17c379ce 43 uint32_t bit19:1;
rvt 5:a0bb17c379ce 44 uint32_t bit20:1;
rvt 5:a0bb17c379ce 45 uint32_t bit21:1;
rvt 5:a0bb17c379ce 46 uint32_t bit22:1;
rvt 5:a0bb17c379ce 47 uint32_t bit23:1;
rvt 5:a0bb17c379ce 48 uint32_t bit24:1;
rvt 5:a0bb17c379ce 49 uint32_t bit25:1;
rvt 5:a0bb17c379ce 50 uint32_t bit26:1;
rvt 5:a0bb17c379ce 51 uint32_t bit27:1;
rvt 5:a0bb17c379ce 52 uint32_t bit28:1;
rvt 5:a0bb17c379ce 53 uint32_t bit29:1;
rvt 5:a0bb17c379ce 54 uint32_t bit30:1;
rvt 5:a0bb17c379ce 55 uint32_t bit31:1;
rvt 5:a0bb17c379ce 56 };
rvt 5:a0bb17c379ce 57 uint32_t button;
rvt 5:a0bb17c379ce 58 } buttons;
rvt 0:33bc88c4ab31 59 long value1;
rvt 0:33bc88c4ab31 60 long value2;
rvt 0:33bc88c4ab31 61 long value3;
rvt 0:33bc88c4ab31 62 long value4;
rvt 0:33bc88c4ab31 63 } analogData;
rvt 0:33bc88c4ab31 64
rvt 3:0742b0b42ac9 65
rvt 1:5b2ab44eb31f 66
rvt 1:5b2ab44eb31f 67
rvt 1:5b2ab44eb31f 68 /**
rvt 1:5b2ab44eb31f 69 Debug thread to show some values from the system over USB serial.
rvt 1:5b2ab44eb31f 70 Ensure that TTY_DEBUG is defined so that these routines will get activated.
rvt 1:5b2ab44eb31f 71 This is what I do to view the values on OSX within a terminal
rvt 1:5b2ab44eb31f 72 $ cd /dev
rvt 1:5b2ab44eb31f 73 $ ls | grep usbmodem
rvt 1:5b2ab44eb31f 74 cu.usbmodemfa1232
rvt 1:5b2ab44eb31f 75 tty.usbmodemfa1232
rvt 1:5b2ab44eb31f 76 $ screen tty.usbmodemfa1232
rvt 1:5b2ab44eb31f 77 */
rvt 2:ae7a31a3c618 78 const char *byte_to_binary16(int x)
rvt 2:ae7a31a3c618 79 {
rvt 2:ae7a31a3c618 80 static char b[17];
rvt 2:ae7a31a3c618 81 b[0] = '\0';
rvt 2:ae7a31a3c618 82
rvt 2:ae7a31a3c618 83 int z;
rvt 5:a0bb17c379ce 84 for (z = 32768; z > 0; z >>= 1) {
rvt 2:ae7a31a3c618 85 strcat(b, ((x & z) == z) ? "1" : "0");
rvt 2:ae7a31a3c618 86 }
rvt 2:ae7a31a3c618 87
rvt 2:ae7a31a3c618 88 return b;
rvt 2:ae7a31a3c618 89 }
rvt 2:ae7a31a3c618 90
rvt 2:ae7a31a3c618 91
rvt 0:33bc88c4ab31 92 void debug_thread(void const *args)
rvt 0:33bc88c4ab31 93 {
rvt 2:ae7a31a3c618 94 // Serial port for debug data
rvt 2:ae7a31a3c618 95 Serial pc(USBTX, USBRX); // tx, rx
rvt 2:ae7a31a3c618 96
rvt 1:5b2ab44eb31f 97 // Make a local copy
rvt 1:5b2ab44eb31f 98 AnalogData previous;
rvt 0:33bc88c4ab31 99 while (true) {
rvt 1:5b2ab44eb31f 100 // Lock and copy input values
rvt 0:33bc88c4ab31 101 analogValueMutex.lock();
rvt 5:a0bb17c379ce 102 const AnalogData localCopy = analogData;
rvt 0:33bc88c4ab31 103 analogValueMutex.unlock();
rvt 0:33bc88c4ab31 104
rvt 0:33bc88c4ab31 105 // Send to USB
rvt 0:33bc88c4ab31 106 pc.printf("\x1B[0;0H");
rvt 0:33bc88c4ab31 107 pc.printf("Yoke and Pedals!\n\r");
rvt 5:a0bb17c379ce 108 pc.printf("Analog in p20: %d diff: %d \n\r",localCopy.value1,localCopy.value1-previous.value1);
rvt 0:33bc88c4ab31 109 pc.printf("Analog in p19: %d diff: %d \n\r",localCopy.value2,localCopy.value2-previous.value2);
rvt 5:a0bb17c379ce 110 pc.printf("Buttons: %d \n\r",localCopy.buttons.button);
rvt 1:5b2ab44eb31f 111
rvt 1:5b2ab44eb31f 112 // Make local copy so we can show diff version
rvt 5:a0bb17c379ce 113 previous = localCopy;
rvt 0:33bc88c4ab31 114 Thread::wait(1000);
rvt 0:33bc88c4ab31 115 }
rvt 0:33bc88c4ab31 116 }
rvt 3:0742b0b42ac9 117
rvt 0:33bc88c4ab31 118
rvt 2:ae7a31a3c618 119
rvt 0:33bc88c4ab31 120 void hid_thread(void const *args)
rvt 0:33bc88c4ab31 121 {
rvt 1:5b2ab44eb31f 122 //USB HID JoyStick
rvt 1:5b2ab44eb31f 123 USBJoystick joystick;
rvt 1:5b2ab44eb31f 124
rvt 1:5b2ab44eb31f 125 // Activity led for HID data transmissions
rvt 1:5b2ab44eb31f 126 DigitalOut hIDActivity(HIDACTIVITYLED);
rvt 1:5b2ab44eb31f 127
rvt 0:33bc88c4ab31 128 while (true) {
rvt 0:33bc88c4ab31 129 // Wait for analog in to have some data
rvt 1:5b2ab44eb31f 130 hIDActivity=false;
rvt 0:33bc88c4ab31 131 Thread::signal_wait(0x1);
rvt 1:5b2ab44eb31f 132 hIDActivity=true;
rvt 0:33bc88c4ab31 133
rvt 0:33bc88c4ab31 134 // Make a local copy of the data
rvt 0:33bc88c4ab31 135 analogValueMutex.lock();
rvt 5:a0bb17c379ce 136 const AnalogData localCopy = analogData;
rvt 0:33bc88c4ab31 137 analogValueMutex.unlock();
rvt 0:33bc88c4ab31 138
rvt 0:33bc88c4ab31 139 // Update joystick's info
rvt 0:33bc88c4ab31 140 joystick.update(
rvt 4:2cc58c173de8 141 localCopy.value1,
rvt 0:33bc88c4ab31 142 localCopy.value2,
rvt 0:33bc88c4ab31 143 localCopy.value3,
rvt 0:33bc88c4ab31 144 localCopy.value4,
rvt 5:a0bb17c379ce 145 localCopy.buttons.button);
rvt 0:33bc88c4ab31 146
rvt 0:33bc88c4ab31 147 // Wait 50 ms to send a other USB update
rvt 0:33bc88c4ab31 148 Thread::wait(50);
rvt 0:33bc88c4ab31 149 }
rvt 0:33bc88c4ab31 150 }
rvt 0:33bc88c4ab31 151
rvt 0:33bc88c4ab31 152
rvt 0:33bc88c4ab31 153
rvt 0:33bc88c4ab31 154 int main()
rvt 0:33bc88c4ab31 155 {
rvt 5:a0bb17c379ce 156 analogData.buttons.button = 0;
rvt 4:2cc58c173de8 157 analogData.value1=0.;
rvt 4:2cc58c173de8 158 analogData.value2=0.;
rvt 4:2cc58c173de8 159 analogData.value3=0.;
rvt 4:2cc58c173de8 160 analogData.value4=0.;
rvt 4:2cc58c173de8 161
rvt 4:2cc58c173de8 162 Button but5(p5, true, true);
rvt 4:2cc58c173de8 163 Button but6(p6, true, true);
rvt 4:2cc58c173de8 164 Button but7(p7, true, true);
rvt 4:2cc58c173de8 165 Button but8(p8, true, true);
rvt 4:2cc58c173de8 166 Button but9(p9, true, true);
rvt 4:2cc58c173de8 167 Button but10(p10, true, true);
rvt 4:2cc58c173de8 168 Button but11(p11, true, true);
rvt 4:2cc58c173de8 169 Button but12(p12, true, true);
rvt 4:2cc58c173de8 170 Button but13(p13, true, true);
rvt 4:2cc58c173de8 171 Button but14(p14, true, true);
rvt 4:2cc58c173de8 172 Button but15(p15, true, true);
rvt 4:2cc58c173de8 173 Button but16(p16, true, true);
rvt 4:2cc58c173de8 174 Button but17(p17, true, true);
rvt 4:2cc58c173de8 175 Button but21(p21, true, true);
rvt 4:2cc58c173de8 176 Button but22(p22, true, true);
rvt 4:2cc58c173de8 177 Button but23(p23, true, true);
rvt 4:2cc58c173de8 178 Button but24(p24, true, true);
rvt 4:2cc58c173de8 179 Button but25(p25, true, true);
rvt 0:33bc88c4ab31 180
rvt 3:0742b0b42ac9 181 if (TTY_DEBUG) {
rvt 3:0742b0b42ac9 182 Thread _debugThread(debug_thread);
rvt 5:a0bb17c379ce 183 }
rvt 0:33bc88c4ab31 184
rvt 0:33bc88c4ab31 185 // Initialise moving average filters
rvt 5:a0bb17c379ce 186 LowPassFilter lowPassFilter1(new AnalogFilterInterface(),0.99f); // The close the alpha value is to 1, the lower the cut-off frequency
rvt 5:a0bb17c379ce 187 LowPassFilter lowPassFilter2(new AnalogFilterInterface(),0.99f);
rvt 5:a0bb17c379ce 188 LowPassFilter lowPassFilter3(new AnalogFilterInterface(),0.96f);
rvt 5:a0bb17c379ce 189
rvt 5:a0bb17c379ce 190 AutoScale autoScale1(&lowPassFilter1, -32768, 32767, 1.01);
rvt 5:a0bb17c379ce 191 AutoScale autoScale2(&lowPassFilter2, -32768, 32767, 1.01);
rvt 5:a0bb17c379ce 192 AutoScale autoScale3(&lowPassFilter3, -32768, 32767, 1.01);
rvt 0:33bc88c4ab31 193
rvt 0:33bc88c4ab31 194 // Initialise analog input and tell it what fulters to use
rvt 5:a0bb17c379ce 195 AnalogInFiltered ai1(&autoScale1, p20, DATA_CHANGE_TRIGGER);
rvt 5:a0bb17c379ce 196 AnalogInFiltered ai2(&autoScale2, p19, DATA_CHANGE_TRIGGER);
rvt 5:a0bb17c379ce 197 AnalogInFiltered ai3(&autoScale3, p18, DATA_CHANGE_TRIGGER);
rvt 5:a0bb17c379ce 198
rvt 5:a0bb17c379ce 199 Thread _hid_thread(hid_thread);
rvt 0:33bc88c4ab31 200 while (true) {
rvt 0:33bc88c4ab31 201 // Measure analog in's
rvt 5:a0bb17c379ce 202 ai1.setData(0);
rvt 5:a0bb17c379ce 203 ai2.setData(0);
rvt 5:a0bb17c379ce 204 ai3.setData(0);
rvt 4:2cc58c173de8 205
rvt 4:2cc58c173de8 206 but5.measure();
rvt 4:2cc58c173de8 207 but6.measure();
rvt 4:2cc58c173de8 208 but7.measure();
rvt 4:2cc58c173de8 209 but8.measure();
rvt 4:2cc58c173de8 210 but9.measure();
rvt 4:2cc58c173de8 211 but10.measure();
rvt 4:2cc58c173de8 212 but11.measure();
rvt 4:2cc58c173de8 213 but12.measure();
rvt 4:2cc58c173de8 214 but13.measure();
rvt 4:2cc58c173de8 215 but14.measure();
rvt 4:2cc58c173de8 216 but15.measure();
rvt 4:2cc58c173de8 217 but16.measure();
rvt 4:2cc58c173de8 218 but17.measure();
rvt 5:a0bb17c379ce 219
rvt 4:2cc58c173de8 220 but21.measure();
rvt 4:2cc58c173de8 221 but22.measure();
rvt 4:2cc58c173de8 222 but23.measure();
rvt 4:2cc58c173de8 223 but24.measure();
rvt 4:2cc58c173de8 224 but25.measure();
rvt 0:33bc88c4ab31 225
rvt 0:33bc88c4ab31 226 // test of any of the values have been changed, so we only update when data was actually changed
rvt 5:a0bb17c379ce 227 const bool isChanged =
rvt 5:a0bb17c379ce 228 ai1.getIsChanged() ||
rvt 5:a0bb17c379ce 229 ai2.getIsChanged() ||
rvt 5:a0bb17c379ce 230 ai3.getIsChanged() ||
rvt 5:a0bb17c379ce 231 but5.getIsChanged() ||
rvt 5:a0bb17c379ce 232 but6.getIsChanged() ||
rvt 5:a0bb17c379ce 233 but7.getIsChanged() ||
rvt 5:a0bb17c379ce 234 but8.getIsChanged() ||
rvt 5:a0bb17c379ce 235 but9.getIsChanged() ||
rvt 5:a0bb17c379ce 236 but10.getIsChanged() ||
rvt 5:a0bb17c379ce 237 but11.getIsChanged() ||
rvt 5:a0bb17c379ce 238 but12.getIsChanged() ||
rvt 5:a0bb17c379ce 239 but13.getIsChanged() ||
rvt 5:a0bb17c379ce 240 but14.getIsChanged() ||
rvt 5:a0bb17c379ce 241 but15.getIsChanged() ||
rvt 5:a0bb17c379ce 242 but16.getIsChanged() ||
rvt 5:a0bb17c379ce 243 but17.getIsChanged() ||
rvt 5:a0bb17c379ce 244 but21.getIsChanged() ||
rvt 5:a0bb17c379ce 245 but22.getIsChanged() ||
rvt 5:a0bb17c379ce 246 but23.getIsChanged() ||
rvt 5:a0bb17c379ce 247 but24.getIsChanged() ||
rvt 5:a0bb17c379ce 248 but25.getIsChanged();
rvt 0:33bc88c4ab31 249 if (
rvt 5:a0bb17c379ce 250 isChanged
rvt 5:a0bb17c379ce 251 ) {
rvt 0:33bc88c4ab31 252 // Copy analog data to global data
rvt 0:33bc88c4ab31 253 analogValueMutex.lock();
rvt 5:a0bb17c379ce 254 analogData.buttons.bit0 = but5.getData();
rvt 5:a0bb17c379ce 255 analogData.buttons.bit1 = but6.getData();
rvt 5:a0bb17c379ce 256 analogData.buttons.bit2 = but7.getData();
rvt 5:a0bb17c379ce 257 analogData.buttons.bit3 = but8.getData();
rvt 5:a0bb17c379ce 258 analogData.buttons.bit4 = but9.getData();
rvt 5:a0bb17c379ce 259 analogData.buttons.bit5 = but10.getData();
rvt 5:a0bb17c379ce 260 analogData.buttons.bit6 = but11.getData();
rvt 5:a0bb17c379ce 261 analogData.buttons.bit7 = but12.getData();
rvt 5:a0bb17c379ce 262 analogData.buttons.bit8 = but13.getData();
rvt 5:a0bb17c379ce 263 analogData.buttons.bit9 = but14.getData();
rvt 5:a0bb17c379ce 264 analogData.buttons.bit10 = but15.getData();
rvt 5:a0bb17c379ce 265 analogData.buttons.bit11 = but16.getData();
rvt 5:a0bb17c379ce 266 analogData.buttons.bit12 = but17.getData();
rvt 5:a0bb17c379ce 267 analogData.buttons.bit13 = but21.getData();
rvt 5:a0bb17c379ce 268 analogData.buttons.bit14 = but22.getData();
rvt 5:a0bb17c379ce 269 analogData.buttons.bit15 = but23.getData();
rvt 5:a0bb17c379ce 270 analogData.buttons.bit16 = but24.getData();
rvt 5:a0bb17c379ce 271 analogData.buttons.bit17 = but25.getData();
rvt 4:2cc58c173de8 272
rvt 2:ae7a31a3c618 273 analogData.value1 = ai1.getData();
rvt 2:ae7a31a3c618 274 analogData.value2 = ai2.getData();
rvt 5:a0bb17c379ce 275 analogData.value3 = ai3.getData();
rvt 4:2cc58c173de8 276 analogData.value4 = 0.;
rvt 0:33bc88c4ab31 277 analogValueMutex.unlock();
rvt 0:33bc88c4ab31 278
rvt 0:33bc88c4ab31 279 // Signal that data has been changed
rvt 0:33bc88c4ab31 280 _hid_thread.signal_set(0x1);
rvt 0:33bc88c4ab31 281 }
rvt 0:33bc88c4ab31 282 Thread::wait(1);
rvt 0:33bc88c4ab31 283 }
rvt 0:33bc88c4ab31 284 }
rvt 0:33bc88c4ab31 285