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.

Files at this revision

API Documentation at this revision

Comitter:
rvt
Date:
Thu Mar 10 14:05:02 2016 +0000
Parent:
3:0742b0b42ac9
Child:
5:a0bb17c379ce
Commit message:
expanded to read more digital ports for my new panel

Changed in this revision

Button.cpp Show annotated file Show diff for this revision Revisions of this file
Button.h Show annotated file Show diff for this revision Revisions of this file
USBJoystick.cpp Show annotated file Show diff for this revision Revisions of this file
USBJoystick.h Show annotated file Show diff for this revision Revisions of this file
main.cpp Show annotated file Show diff for this revision Revisions of this file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Button.cpp	Thu Mar 10 14:05:02 2016 +0000
@@ -0,0 +1,26 @@
+#include "Button.h"
+
+
+Button::Button(PinName pin, bool reversed, bool pullUp) {
+    _digitalIn = new DigitalIn(pin);
+    _digitalIn->mode(pullUp?PullUp:PullDown);
+    _lastValue = _digitalIn;
+    _reversed = reversed;
+}
+
+Button::~Button() {
+}
+
+void Button::measure () {
+    _value = _digitalIn->read();
+}
+
+bool Button::getData() {
+    return _value ^_reversed;
+}
+
+bool Button::getIsChanged() {
+    bool lv = _lastValue;
+    _lastValue = _value;
+    return _value ^ lv;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Button.h	Thu Mar 10 14:05:02 2016 +0000
@@ -0,0 +1,27 @@
+#ifndef BUTTON_H
+#define BUTTON_H
+
+#include "mbed.h"
+
+class Button {
+    private:
+        DigitalIn   *_digitalIn;
+        bool _isChanged;
+        bool _lastValue;
+        bool _value;
+        bool _reversed;
+    public:   
+        /**
+        filter : Failter chain
+        pin : Analog input to read
+        */
+        Button(PinName pin, bool reversed, bool pullUp);
+        ~Button();
+                
+        void measure();
+        bool getData();
+        bool getIsChanged();
+
+};
+
+#endif
\ No newline at end of file
--- a/USBJoystick.cpp	Tue Feb 10 13:48:20 2015 +0000
+++ b/USBJoystick.cpp	Thu Mar 10 14:05:02 2016 +0000
@@ -20,27 +20,14 @@
 #include "stdint.h"
 #include "USBJoystick.h"
 
-bool USBJoystick::update(int16_t t, int16_t r, int16_t x, int16_t y, uint8_t button) {
-             HID_REPORT report;
+bool USBJoystick::update(int16_t t, int16_t r, int16_t x, int16_t y, uint32_t button) {
    _t = t;
    _r = r;   
    _x = x;
    _y = y;
    _button = button;     
 
-   // Fill the report according to the Joystick Descriptor
-   report.data[0] = _t & 0xff;
-   report.data[1] = (_t >> 8) & 0xff;
-   report.data[2] = _r & 0xff;
-   report.data[3] = (_r >> 8) & 0xff;
-   report.data[4] = _x & 0xff;
-   report.data[5] = (_x >> 8) & 0xff;
-   report.data[6] = _y & 0xff;
-   report.data[7] = (_y >> 8) & 0xff;
-   report.data[8] = (_button & 0xff);                                      
-   report.length = 9;
-   
-   return send(&report);
+   return USBJoystick::update();
 }
  
 bool USBJoystick::update() {
@@ -55,8 +42,11 @@
    report.data[5] = (_x >> 8) & 0xff;
    report.data[6] = _y & 0xff;
    report.data[7] = (_y >> 8) & 0xff;
-   report.data[8] = (_button & 0xff);                                      
-   report.length = 9;
+   report.data[8] =  (_button >> 24) & 0xff;
+   report.data[9] =  (_button >> 16) & 0xff;
+   report.data[10] = (_button >>  8) & 0xff;
+   report.data[11] = _button & 0xff;
+   report.length = 12;
 
    return send(&report);
 }
@@ -77,7 +67,7 @@
      return update();
 }
 
-bool USBJoystick::button(uint8_t button) {
+bool USBJoystick::button(uint32_t button) {
      _button = button;
      return update();
 }
@@ -89,7 +79,7 @@
    _r = 0;    
    _x = 0;                       
    _y = 0;     
-   _button = 0x00;
+   _button = 0x00000000;
 }
 
 
@@ -154,11 +144,11 @@
 // Buttons
                USAGE_PAGE(1), 0x09,            // Buttons
                USAGE_MINIMUM(1), 0x01,         // 1
-               USAGE_MAXIMUM(1), 0x08,         // 4
+               USAGE_MAXIMUM(1), 0x20,         // 32
                LOGICAL_MINIMUM(1), 0x00,       // 0
                LOGICAL_MAXIMUM(1), 0x01,       // 1
                REPORT_SIZE(1), 0x01,
-               REPORT_COUNT(1), 0x08,
+               REPORT_COUNT(1), 0x20,          // 32
                UNIT_EXPONENT(1), 0x00,         // Unit_Exponent (0)
                UNIT(1), 0x00,                  // Unit (None)                                           
                INPUT(1), 0x02,                 // Data, Variable, Absolute
--- a/USBJoystick.h	Tue Feb 10 13:48:20 2015 +0000
+++ b/USBJoystick.h	Thu Mar 10 14:05:02 2016 +0000
@@ -107,7 +107,7 @@
          * @param buttons buttons state
          * @returns true if there is no error, false otherwise
          */
-         bool update(int16_t t, int16_t r, int16_t x, int16_t y, uint8_t buttons);
+         bool update(int16_t t, int16_t r, int16_t x, int16_t y, uint32_t buttons);
 
          /**
          * Write a state of the mouse
@@ -147,7 +147,7 @@
          * @param button button state
          * @returns true if there is no error, false otherwise
          */
-         bool button(uint8_t button);
+         bool button(uint32_t button);
                   
          /*
          * To define the report descriptor. Warning: this method has to store the length of the report descriptor in reportLength.
@@ -161,7 +161,7 @@
          int16_t _r;              
          int16_t _x;                       
          int16_t _y;     
-         uint8_t _button;
+         uint32_t _button;
          
          void _init();                 
 };
--- a/main.cpp	Tue Feb 10 13:48:20 2015 +0000
+++ b/main.cpp	Thu Mar 10 14:05:02 2016 +0000
@@ -3,7 +3,7 @@
 #include "USBJoystick.h"
 #include "LowPassFilter.h"
 #include "AnalogInFiltered.h"
-#include "SimpleButtonDecoder.h"
+#include "Button.h"
 #include "rtos.h"
 
 // When set, it will send debug data over USB serial
@@ -23,16 +23,28 @@
 // Structure that hold's the dataset of the input's
 Mutex analogValueMutex;
 struct AnalogData {
-    bool button1;
-    bool button2;
-    bool button3;
-    bool button4;
-    bool button5;
+    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;
     long value1;
     long value2;
     long value3;
     long value4;
-    long value5;
 } analogData;
 
 
@@ -96,12 +108,11 @@
         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("Analog in p16: %d  diff: %d    \n\r",localCopy.value5,localCopy.value5-previous.value5);
-        pc.printf("Button 1: %d    \n\r",localCopy.button1);
-        pc.printf("Button 2: %d    \n\r",localCopy.button2);
-        pc.printf("Button 3: %d    \n\r",localCopy.button3);
-        pc.printf("Button 4: %d    \n\r",localCopy.button4);
-        pc.printf("Button 5: %d    \n\r",localCopy.button5);
+        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);
 
         // Make local copy so we can show diff version
         memcpy (&previous, &localCopy, sizeof(AnalogData));
@@ -122,7 +133,7 @@
     // Locla copy of analog data
     AnalogData localCopy;
 
-    uint8_t buttons=0;
+    uint32_t buttons=0x00;
     while (true) {
 
 
@@ -136,29 +147,32 @@
         memcpy (&localCopy, &analogData, sizeof(AnalogData));
         analogValueMutex.unlock();
 
-        buttons=0;
-        if (localCopy.button1==true) {
-            buttons=buttons | 0x01;
-        }
-        if (localCopy.button2) {
-            buttons=buttons | 0x02;
-        }
-        if (localCopy.button3) {
-            buttons=buttons | 0x04;
-        }
-        if (localCopy.button4) {
-            buttons=buttons | 0x08;
-        }
-        if (localCopy.button5) {
-            buttons=buttons | 0x10;
-        }
+        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,
-            localCopy.value5,
             buttons);
 
         // Wait 50 ms to send a other USB update
@@ -170,76 +184,120 @@
 
 int main()
 {
-    analogData.value1=0;
-    analogData.value2=0;
-    analogData.value3=0;
-    analogData.value4=0;
-    analogData.value5=0;
+    analogData.value1=0.;
+    analogData.value2=0.;
+    analogData.value3=0.;
+    analogData.value4=0.;
+
+    Button but5(p5, true, true);
+    Button but6(p6, true, true);
+    Button but7(p7, true, true);
+    Button but8(p8, true, true);
+    Button but9(p9, true, true);
+    Button but10(p10, true, true);
+    Button but11(p11, true, true);
+    Button but12(p12, true, true);
+    Button but13(p13, true, true);
+    Button but14(p14, true, true);
+    Button but15(p15, true, true);
+    Button but16(p16, true, true);
+    Button but17(p17, true, true);
+    Button but21(p21, true, true);
+    Button but22(p22, true, true);
+    Button but23(p23, true, true);
+    Button but24(p24, true, true);
+    Button but25(p25, true, true);
 
     if (TTY_DEBUG) {
         Thread _debugThread(debug_thread);
     }    
+
+
     Thread _hid_thread(hid_thread);
 
     // Initialise moving average filters
-    LowPassFilter lowPassFilter1(new AnalogFilterInterface(),0.5f);   // The close the alpha value is to 1, the lower the cut-off frequency
+    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 lowPassFilter3(new AnalogFilterInterface(),0.95f);
-    LowPassFilter lowPassFilter4(new AnalogFilterInterface(),0.95f);
-    LowPassFilter lowPassFilter5(new AnalogFilterInterface(),0.95f);
 
     // Initialise analog input and tell it what fulters to use
-    AnalogInFiltered ai1(&lowPassFilter1, p20, DATA_CHANGE_TRIGGER*4);
-    AnalogInFiltered ai2(&lowPassFilter2, p19, DATA_CHANGE_TRIGGER);
-    AnalogInFiltered ai3(&lowPassFilter3, p18, DATA_CHANGE_TRIGGER);
-    AnalogInFiltered ai4(&lowPassFilter4, p17, DATA_CHANGE_TRIGGER);
-    AnalogInFiltered ai5(&lowPassFilter5, p16, DATA_CHANGE_TRIGGER);
+    AnalogInFiltered ai2(&lowPassFilter1, p19, DATA_CHANGE_TRIGGER);
+    AnalogInFiltered ai1(&lowPassFilter2, p20, DATA_CHANGE_TRIGGER);
     
-    SimpleButtonDecoder but1(&ai1, 49300, DEBOUNCERUNS);
-    SimpleButtonDecoder but2(&ai1, 46800, DEBOUNCERUNS);
-    SimpleButtonDecoder but3(&ai1, 43650, DEBOUNCERUNS);
-    SimpleButtonDecoder but4(&ai1, 32500, DEBOUNCERUNS);
-    SimpleButtonDecoder but5(&ai1, 39100, DEBOUNCERUNS);
-
     while (true) {
         // Measure analog in's
         ai1.measure();
         ai2.measure();
-        ai3.measure();
-        ai4.measure();
-        ai5.measure();
+
+        but5.measure();
+        but6.measure();
+        but7.measure();
+        but8.measure();
+        but9.measure();
+        but10.measure();
+        but11.measure();
+        but12.measure();
+        but13.measure();
+        but14.measure();
+        but15.measure();
+        but16.measure();
+        but17.measure();
         
-        but1.process();
-        but2.process();
-        but3.process();
-        but4.process();
-        but5.process();
+        but21.measure();
+        but22.measure();
+        but23.measure();
+        but24.measure();
+        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();
         if (
-                false
-                || ai2.getIsChanged()
-                || ai3.getIsChanged()
-                || ai4.getIsChanged()
-//                || ai5.getIsChanged()
-                || but1.getIsChanged()
-                || but2.getIsChanged()
-                || but3.getIsChanged()
-                || but4.getIsChanged()
-                || but5.getIsChanged()
+              isChanged
            ) {
             // Copy analog data to global data
             analogValueMutex.lock();
-            analogData.button1 = but1.getIsPressed();
-            analogData.button2 = but2.getIsPressed();
-            analogData.button3 = but3.getIsPressed();
-            analogData.button4 = but4.getIsPressed();
-            analogData.button5 = but5.getIsPressed();
+            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.value1 = ai1.getData();
             analogData.value2 = ai2.getData();
-            analogData.value3 = ai3.getData();
-            analogData.value4 = ai4.getData();
-            analogData.value5 = ai5.getData();
+            analogData.value3 = 0.;
+            analogData.value4 = 0.;
             analogValueMutex.unlock();
 
             // Signal that data has been changed