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:
Wed Jun 22 12:50:16 2016 +0000
Parent:
4:2cc58c173de8
Commit message:
Latest

Changed in this revision

AnalogFilterInterface.cpp Show annotated file Show diff for this revision Revisions of this file
AnalogFilterInterface.h Show annotated file Show diff for this revision Revisions of this file
AnalogInFiltered.cpp Show annotated file Show diff for this revision Revisions of this file
AnalogInFiltered.h Show annotated file Show diff for this revision Revisions of this file
AutoScale.cpp Show annotated file Show diff for this revision Revisions of this file
AutoScale.h Show annotated file Show diff for this revision Revisions of this file
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
LowPassFilter.cpp Show annotated file Show diff for this revision Revisions of this file
LowPassFilter.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
diff -r 2cc58c173de8 -r a0bb17c379ce AnalogFilterInterface.cpp
--- a/AnalogFilterInterface.cpp	Thu Mar 10 14:05:02 2016 +0000
+++ b/AnalogFilterInterface.cpp	Wed Jun 22 12:50:16 2016 +0000
@@ -4,6 +4,7 @@
 {
     _chain = chain;
 }
+
 AnalogFilterInterface::AnalogFilterInterface()
 {
 }
@@ -12,7 +13,8 @@
 {
     _data = data;
 };
-long AnalogFilterInterface::getData()
+
+long AnalogFilterInterface::getData() const
 {
     return _data;
 };
\ No newline at end of file
diff -r 2cc58c173de8 -r a0bb17c379ce AnalogFilterInterface.h
--- a/AnalogFilterInterface.h	Thu Mar 10 14:05:02 2016 +0000
+++ b/AnalogFilterInterface.h	Wed Jun 22 12:50:16 2016 +0000
@@ -18,7 +18,7 @@
         virtual void setData(long data);
 
         // Get the filtered datapoint
-        virtual long getData();
+        virtual long getData() const;
         
         // Get the chained filter
         virtual AnalogFilterInterface * getChain(){return _chain;};
diff -r 2cc58c173de8 -r a0bb17c379ce AnalogInFiltered.cpp
--- a/AnalogInFiltered.cpp	Thu Mar 10 14:05:02 2016 +0000
+++ b/AnalogInFiltered.cpp	Wed Jun 22 12:50:16 2016 +0000
@@ -1,23 +1,18 @@
 #include "AnalogInFiltered.h"
 
 
-AnalogInFiltered::AnalogInFiltered(AnalogFilterInterface *filter, PinName pin, int fuzzyFactor) {
-    _ain = new AnalogIn(pin);
-    _filter = filter;
-    _fuzzyFactor = fuzzyFactor;
-    _lastValue=0;
+AnalogInFiltered::AnalogInFiltered(AnalogFilterInterface *filter, PinName pin, int fuzzyFactor) : _ain(new AnalogIn(pin)), _fuzzyFactor(fuzzyFactor), _filter(filter), _lastValue(0) {
 }
 
 AnalogInFiltered::~AnalogInFiltered() {
     delete(_ain);
-    delete(_filter);
 }
 
-void AnalogInFiltered::measure () {
+void AnalogInFiltered::setData (long d) {
     _filter->setData(_ain->read_u16() - 32768);
 }
 
-long AnalogInFiltered::getData() {
+long AnalogInFiltered::getData() const {
     return _filter->getData();
 }
 
diff -r 2cc58c173de8 -r a0bb17c379ce AnalogInFiltered.h
--- a/AnalogInFiltered.h	Thu Mar 10 14:05:02 2016 +0000
+++ b/AnalogInFiltered.h	Wed Jun 22 12:50:16 2016 +0000
@@ -7,12 +7,12 @@
 /**
 Analog input, this reads a analog value from a PIN and send the data through the filterchain
 */
-class AnalogInFiltered {
+class AnalogInFiltered : public AnalogFilterInterface {
     private:
         AnalogIn *_ain;
+        const int _fuzzyFactor;
+        AnalogFilterInterface   *_filter;
         long   _lastValue;
-        int _fuzzyFactor;
-        AnalogFilterInterface   *_filter;
     public:   
         /**
         filter : Failter chain
@@ -22,11 +22,8 @@
         ~AnalogInFiltered();
         
         // Read a value from analog in
-        void measure ();
-        
-        
-        long getData();
-        
+        virtual void setData(long data);
+        virtual long getData() const;
         
         // Test if the input value is changed based on a offset
         bool getIsChanged();
diff -r 2cc58c173de8 -r a0bb17c379ce AutoScale.cpp
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AutoScale.cpp	Wed Jun 22 12:50:16 2016 +0000
@@ -0,0 +1,46 @@
+#include "AutoScale.h"
+#include <algorithm> 
+
+AutoScale::AutoScale(AnalogFilterInterface *chain,long expectedMin, long expectedMax) :
+    AnalogFilterInterface(chain), _expectedMax(expectedMax), _expectedMin(expectedMin), _currentMax(expectedMin), _currentMin(expectedMax), _current(0), _a(0.), _b(0), _mul(1.0)
+{
+}
+AutoScale::AutoScale(AnalogFilterInterface *chain,long expectedMin, long expectedMax, double multiplier) :
+    AnalogFilterInterface(chain), _expectedMax(expectedMax), _expectedMin(expectedMin), _currentMax(expectedMin), _currentMin(expectedMax), _current(0), _a(0.), _b(0), _mul(multiplier)
+{
+}
+
+AutoScale::~AutoScale()
+{
+}
+
+void AutoScale::setData(long dataPoint)
+{
+    getChain()->setData(dataPoint);
+    _current = getChain()->getData();
+
+    if (_current < _currentMin) {
+        _currentMin = _current;
+        reCalc();
+    }
+    if (_current > _currentMax) {
+        _currentMax = _current;
+        reCalc();
+    }
+    _current = (long)((_a * _current + _b)*_mul);
+
+    _current = std::min(_current, _expectedMax);
+    _current = std::max(_current, _expectedMin);
+}
+
+long AutoScale::getData() const
+{
+    return _current;
+}
+
+void AutoScale::reCalc()
+{
+    _a = (_expectedMax - _expectedMin) / (double)(_currentMax - _currentMin);
+    _b = -((_a * _currentMax) - _expectedMax);
+}
+
diff -r 2cc58c173de8 -r a0bb17c379ce AutoScale.h
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/AutoScale.h	Wed Jun 22 12:50:16 2016 +0000
@@ -0,0 +1,34 @@
+#ifndef AUTOSCALE_H
+#define AUTOSCALE_H
+
+#include "mbed.h"
+#include "AnalogFilterInterface.h"
+
+/**
+Auto scale a analog input to it's desired min/max values
+This is handy of you connect a potentiometer to a analog input where you cannot make the full values, but
+your flight simulator expects full values
+**/
+class AutoScale : public AnalogFilterInterface
+{
+private:
+    const long _expectedMax;
+    const long _expectedMin;
+    long _currentMax;
+    long _currentMin;
+    long _current;
+    double _a;
+    double _b;
+    double _mul;
+public:
+    AutoScale(AnalogFilterInterface *chain,long expectedMin, long expectedMax);
+    AutoScale(AnalogFilterInterface *chain,long expectedMin, long expectedMax, double multiplier);
+    ~AutoScale();
+
+    virtual void setData(long data);
+    virtual long getData() const;
+private:
+    void reCalc();
+};
+
+#endif
\ No newline at end of file
diff -r 2cc58c173de8 -r a0bb17c379ce Button.cpp
--- a/Button.cpp	Thu Mar 10 14:05:02 2016 +0000
+++ b/Button.cpp	Wed Jun 22 12:50:16 2016 +0000
@@ -1,26 +1,30 @@
 #include "Button.h"
 
 
-Button::Button(PinName pin, bool reversed, bool pullUp) {
-    _digitalIn = new DigitalIn(pin);
+Button::Button(PinName pin, bool reversed, bool pullUp) : _digitalIn(new DigitalIn(pin)), _value(false), _reversed(reversed)
+{
     _digitalIn->mode(pullUp?PullUp:PullDown);
-    _lastValue = _digitalIn;
-    _reversed = reversed;
+    _lastValue=_digitalIn;
 }
 
-Button::~Button() {
+Button::~Button()
+{
+    delete(_digitalIn);
 }
 
-void Button::measure () {
-    _value = _digitalIn->read();
+void Button::measure ()
+{
+    _value = _digitalIn->read() ^_reversed;
 }
 
-bool Button::getData() {
-    return _value ^_reversed;
+bool Button::getData() const
+{
+    return _value;
 }
 
-bool Button::getIsChanged() {
-    bool lv = _lastValue;
+bool Button::getIsChanged()
+{
+    const bool lv = _lastValue;
     _lastValue = _value;
     return _value ^ lv;
 }
diff -r 2cc58c173de8 -r a0bb17c379ce Button.h
--- a/Button.h	Thu Mar 10 14:05:02 2016 +0000
+++ b/Button.h	Wed Jun 22 12:50:16 2016 +0000
@@ -6,10 +6,9 @@
 class Button {
     private:
         DigitalIn   *_digitalIn;
-        bool _isChanged;
         bool _lastValue;
         bool _value;
-        bool _reversed;
+        const bool _reversed;
     public:   
         /**
         filter : Failter chain
@@ -19,7 +18,7 @@
         ~Button();
                 
         void measure();
-        bool getData();
+        bool getData() const;
         bool getIsChanged();
 
 };
diff -r 2cc58c173de8 -r a0bb17c379ce LowPassFilter.cpp
--- a/LowPassFilter.cpp	Thu Mar 10 14:05:02 2016 +0000
+++ b/LowPassFilter.cpp	Wed Jun 22 12:50:16 2016 +0000
@@ -14,7 +14,7 @@
     smoothedValue = (getChain()->getData() * (1.0 - _alpha)) + (smoothedValue * _alpha);
 }
 
-long LowPassFilter::getData() {
+long LowPassFilter::getData() const {
     return smoothedValue;
 }
 
diff -r 2cc58c173de8 -r a0bb17c379ce LowPassFilter.h
--- a/LowPassFilter.h	Thu Mar 10 14:05:02 2016 +0000
+++ b/LowPassFilter.h	Wed Jun 22 12:50:16 2016 +0000
@@ -19,7 +19,7 @@
         LowPassFilter(AnalogFilterInterface *chain, double alpha);
         ~LowPassFilter();
         virtual void setData(long data);
-        virtual long getData();
+        virtual long getData() const;
 };
 
 #endif
\ No newline at end of file
diff -r 2cc58c173de8 -r a0bb17c379ce main.cpp
--- 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();