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.

Revision:
5:a0bb17c379ce
Parent:
4:2cc58c173de8
--- a/main.cpp	Thu Mar 10 14:05:02 2016 +0000
+++ b/main.cpp	Wed Jun 22 12:50:16 2016 +0000
@@ -3,44 +3,59 @@
 #include "USBJoystick.h"
 #include "LowPassFilter.h"
 #include "AnalogInFiltered.h"
+#include "AutoScale.h"
 #include "Button.h"
 #include "rtos.h"
 
 // When set, it will send debug data over USB serial
-#define TTY_DEBUG true
+#define TTY_DEBUG false
 
 // Value that defines when to start sending data this prevents the noise sending loads's of data over HID
-#define DATA_CHANGE_TRIGGER 64
+#define DATA_CHANGE_TRIGGER 8
 
 // Activity LED for HID data
 #define HIDACTIVITYLED LED3
 
-// Number of samples taken before a buttons as set to be pressed. 
-// It essentually wait's untel the value is stable enough to be said to be pressed
-#define DEBOUNCERUNS 10
-
-
 // Structure that hold's the dataset of the input's
 Mutex analogValueMutex;
 struct AnalogData {
-    bool but5;
-    bool but6;
-    bool but7;
-    bool but8;
-    bool but9;
-    bool but10;
-    bool but11;
-    bool but12;
-    bool but13;
-    bool but14;
-    bool but15;
-    bool but16;
-    bool but17;
-    bool but21;
-    bool but22;
-    bool but23;
-    bool but24;
-    bool but25;
+    union {
+        struct {
+            uint32_t bit0:1;
+            uint32_t bit1:1;
+            uint32_t bit2:1;
+            uint32_t bit3:1;
+            uint32_t bit4:1;
+            uint32_t bit5:1;
+            uint32_t bit6:1;
+            uint32_t bit7:1;
+            uint32_t bit8:1;
+            uint32_t bit9:1;
+            uint32_t bit10:1;
+            uint32_t bit11:1;
+            uint32_t bit12:1;
+            uint32_t bit13:1;
+            uint32_t bit14:1;
+            uint32_t bit15:1;
+            uint32_t bit16:1;
+            uint32_t bit17:1;
+            uint32_t bit18:1;
+            uint32_t bit19:1;
+            uint32_t bit20:1;
+            uint32_t bit21:1;
+            uint32_t bit22:1;
+            uint32_t bit23:1;
+            uint32_t bit24:1;
+            uint32_t bit25:1;
+            uint32_t bit26:1;
+            uint32_t bit27:1;
+            uint32_t bit28:1;
+            uint32_t bit29:1;
+            uint32_t bit30:1;
+            uint32_t bit31:1;
+        };
+        uint32_t button;
+    } buttons;
     long value1;
     long value2;
     long value3;
@@ -66,26 +81,13 @@
     b[0] = '\0';
 
     int z;
-    for (z = 32768; z > 0; z >>= 1)
-    {
+    for (z = 32768; z > 0; z >>= 1) {
         strcat(b, ((x & z) == z) ? "1" : "0");
     }
 
     return b;
 }
-const char *byte_to_binary8(int x)
-{
-    static char b[9];
-    b[0] = '\0';
 
-    int z;
-    for (z = 128; z > 0; z >>= 1)
-    {
-        strcat(b, ((x & z) == z) ? "1" : "0");
-    }
-
-    return b;
-}
 
 void debug_thread(void const *args)
 {
@@ -93,29 +95,22 @@
     Serial pc(USBTX, USBRX); // tx, rx
 
     // Make a local copy
-    AnalogData localCopy;
     AnalogData previous;
     while (true) {
         // Lock and copy input values
         analogValueMutex.lock();
-        memcpy (&localCopy, &analogData, sizeof(AnalogData));
+        const AnalogData localCopy = analogData;
         analogValueMutex.unlock();
 
         // Send to USB
         pc.printf("\x1B[0;0H");
         pc.printf("Yoke and Pedals!\n\r");
-        pc.printf("Analog in p20: %s %d          \n\r",byte_to_binary16((localCopy.value1 + 32768)),localCopy.value1 + 32768);
+        pc.printf("Analog in p20: %d  diff: %d    \n\r",localCopy.value1,localCopy.value1-previous.value1);
         pc.printf("Analog in p19: %d  diff: %d    \n\r",localCopy.value2,localCopy.value2-previous.value2);
-        pc.printf("Analog in p18: %d  diff: %d    \n\r",localCopy.value3,localCopy.value3-previous.value3);
-        pc.printf("Analog in p17: %d  diff: %d    \n\r",localCopy.value4,localCopy.value4-previous.value4);
-        pc.printf("Button 5: %d    \n\r",localCopy.but5);
-        pc.printf("Button 6: %d    \n\r",localCopy.but6);
-        pc.printf("Button 7: %d    \n\r",localCopy.but7);
-        pc.printf("Button 8: %d    \n\r",localCopy.but8);
-        pc.printf("Button 9: %d    \n\r",localCopy.but9);
+        pc.printf("Buttons: %d    \n\r",localCopy.buttons.button);
 
         // Make local copy so we can show diff version
-        memcpy (&previous, &localCopy, sizeof(AnalogData));
+        previous = localCopy;
         Thread::wait(1000);
     }
 }
@@ -129,14 +124,8 @@
 
     // Activity led for HID data transmissions
     DigitalOut hIDActivity(HIDACTIVITYLED);
-    
-    // Locla copy of analog data
-    AnalogData localCopy;
 
-    uint32_t buttons=0x00;
     while (true) {
-
-
         // Wait for analog in to have some data
         hIDActivity=false;
         Thread::signal_wait(0x1);
@@ -144,36 +133,16 @@
 
         // Make a local copy of the data
         analogValueMutex.lock();
-        memcpy (&localCopy, &analogData, sizeof(AnalogData));
+        const AnalogData localCopy = analogData;
         analogValueMutex.unlock();
 
-        buttons=0x00;
-        buttons = buttons | localCopy.but5 ;
-        buttons = buttons | localCopy.but6 << 1;
-        buttons = buttons | localCopy.but7 << 2;
-        buttons = buttons | localCopy.but8 << 3;
-        buttons = buttons | localCopy.but9 << 4;
-        buttons = buttons | localCopy.but10 << 5;
-        buttons = buttons | localCopy.but11 << 6;
-        buttons = buttons | localCopy.but12 << 7;
-        buttons = buttons | localCopy.but13 << 8;
-        buttons = buttons | localCopy.but14 << 9;
-        buttons = buttons | localCopy.but15 << 10;
-        buttons = buttons | localCopy.but16 << 11;
-        buttons = buttons | localCopy.but17 << 12;
-        buttons = buttons | localCopy.but21 << 13;
-        buttons = buttons | localCopy.but22 << 14;
-        buttons = buttons | localCopy.but23 << 15;
-        buttons = buttons | localCopy.but24 << 16;
-        buttons = buttons | localCopy.but25 << 17;
-
         // Update joystick's info
         joystick.update(
             localCopy.value1,
             localCopy.value2,
             localCopy.value3,
             localCopy.value4,
-            buttons);
+            localCopy.buttons.button);
 
         // Wait 50 ms to send a other USB update
         Thread::wait(50);
@@ -184,6 +153,7 @@
 
 int main()
 {
+    analogData.buttons.button = 0;
     analogData.value1=0.;
     analogData.value2=0.;
     analogData.value3=0.;
@@ -210,23 +180,28 @@
 
     if (TTY_DEBUG) {
         Thread _debugThread(debug_thread);
-    }    
-
-
-    Thread _hid_thread(hid_thread);
+    }
 
     // Initialise moving average filters
-    LowPassFilter lowPassFilter1(new AnalogFilterInterface(),0.95f);   // The close the alpha value is to 1, the lower the cut-off frequency
-    LowPassFilter lowPassFilter2(new AnalogFilterInterface(),0.95f);
+    LowPassFilter lowPassFilter1(new AnalogFilterInterface(),0.99f);   // The close the alpha value is to 1, the lower the cut-off frequency
+    LowPassFilter lowPassFilter2(new AnalogFilterInterface(),0.99f);
+    LowPassFilter lowPassFilter3(new AnalogFilterInterface(),0.96f);
+
+    AutoScale autoScale1(&lowPassFilter1, -32768, 32767, 1.01);
+    AutoScale autoScale2(&lowPassFilter2, -32768, 32767, 1.01);
+    AutoScale autoScale3(&lowPassFilter3, -32768, 32767, 1.01);
 
     // Initialise analog input and tell it what fulters to use
-    AnalogInFiltered ai2(&lowPassFilter1, p19, DATA_CHANGE_TRIGGER);
-    AnalogInFiltered ai1(&lowPassFilter2, p20, DATA_CHANGE_TRIGGER);
-    
+    AnalogInFiltered ai1(&autoScale1, p20, DATA_CHANGE_TRIGGER);
+    AnalogInFiltered ai2(&autoScale2, p19, DATA_CHANGE_TRIGGER);
+    AnalogInFiltered ai3(&autoScale3, p18, DATA_CHANGE_TRIGGER);
+
+    Thread _hid_thread(hid_thread);
     while (true) {
         // Measure analog in's
-        ai1.measure();
-        ai2.measure();
+        ai1.setData(0);
+        ai2.setData(0);
+        ai3.setData(0);
 
         but5.measure();
         but6.measure();
@@ -241,7 +216,7 @@
         but15.measure();
         but16.measure();
         but17.measure();
-        
+
         but21.measure();
         but22.measure();
         but23.measure();
@@ -249,54 +224,55 @@
         but25.measure();
 
         // test of any of the values have been changed, so we only update when data was actually changed
-        bool isChanged = 
-        ai1.getIsChanged() ||
-        ai2.getIsChanged() ||
-        but5.getIsChanged() ||
-        but6.getIsChanged() ||
-        but7.getIsChanged() ||
-        but8.getIsChanged() ||
-        but9.getIsChanged() ||
-        but10.getIsChanged() ||
-        but11.getIsChanged() ||
-        but12.getIsChanged() ||
-        but13.getIsChanged() ||
-        but14.getIsChanged() ||
-        but15.getIsChanged() ||
-        but16.getIsChanged() ||
-        but17.getIsChanged() ||
-        but21.getIsChanged() ||
-        but22.getIsChanged() ||
-        but23.getIsChanged() ||
-        but24.getIsChanged() ||
-        but25.getIsChanged();
+        const bool isChanged =
+            ai1.getIsChanged() ||
+            ai2.getIsChanged() ||
+            ai3.getIsChanged() ||
+            but5.getIsChanged() ||
+            but6.getIsChanged() ||
+            but7.getIsChanged() ||
+            but8.getIsChanged() ||
+            but9.getIsChanged() ||
+            but10.getIsChanged() ||
+            but11.getIsChanged() ||
+            but12.getIsChanged() ||
+            but13.getIsChanged() ||
+            but14.getIsChanged() ||
+            but15.getIsChanged() ||
+            but16.getIsChanged() ||
+            but17.getIsChanged() ||
+            but21.getIsChanged() ||
+            but22.getIsChanged() ||
+            but23.getIsChanged() ||
+            but24.getIsChanged() ||
+            but25.getIsChanged();
         if (
-              isChanged
-           ) {
+            isChanged
+        ) {
             // Copy analog data to global data
             analogValueMutex.lock();
-            analogData.but5 = but5.getData();
-            analogData.but6 = but6.getData();
-            analogData.but7 = but7.getData();
-            analogData.but8 = but8.getData();
-            analogData.but9 = but9.getData();
-            analogData.but10 = but10.getData();
-            analogData.but11 = but11.getData();
-            analogData.but12 = but12.getData();
-            analogData.but13 = but13.getData();
-            analogData.but14 = but14.getData();
-            analogData.but15 = but15.getData();
-            analogData.but16 = but16.getData();
-            analogData.but17 = but17.getData();
-            analogData.but21 = but21.getData();
-            analogData.but22 = but22.getData();
-            analogData.but23 = but23.getData();
-            analogData.but24 = but24.getData();
-            analogData.but25 = but25.getData();
+            analogData.buttons.bit0 = but5.getData();
+            analogData.buttons.bit1 = but6.getData();
+            analogData.buttons.bit2 = but7.getData();
+            analogData.buttons.bit3 = but8.getData();
+            analogData.buttons.bit4 = but9.getData();
+            analogData.buttons.bit5 = but10.getData();
+            analogData.buttons.bit6 = but11.getData();
+            analogData.buttons.bit7 = but12.getData();
+            analogData.buttons.bit8 = but13.getData();
+            analogData.buttons.bit9 = but14.getData();
+            analogData.buttons.bit10 = but15.getData();
+            analogData.buttons.bit11 = but16.getData();
+            analogData.buttons.bit12 = but17.getData();
+            analogData.buttons.bit13 = but21.getData();
+            analogData.buttons.bit14 = but22.getData();
+            analogData.buttons.bit15 = but23.getData();
+            analogData.buttons.bit16 = but24.getData();
+            analogData.buttons.bit17 = but25.getData();
 
             analogData.value1 = ai1.getData();
             analogData.value2 = ai2.getData();
-            analogData.value3 = 0.;
+            analogData.value3 = ai3.getData();
             analogData.value4 = 0.;
             analogValueMutex.unlock();