diff --git a/firmware/lucidgloves-firmware/AdvancedConfig.h b/firmware/lucidgloves-firmware/AdvancedConfig.h index 42316b0..d088ca1 100644 --- a/firmware/lucidgloves-firmware/AdvancedConfig.h +++ b/firmware/lucidgloves-firmware/AdvancedConfig.h @@ -1,4 +1,6 @@ -//Advanced settings, only for the pros XD +#pragma once +#include "src/Util/ConfigUtils.h" +//THIS FILE HAS SOME EXTRA SETTINGS, MAIN SETTINGS ARE IN Config.h #define LOOP_TIME 1 //How much time between data sends (ms), set to 0 for a good time :) #define CALIBRATION_LOOPS -1//How many loops should be calibrated. Set to -1 to always be calibrated. diff --git a/firmware/lucidgloves-firmware/Config.h b/firmware/lucidgloves-firmware/Config.h new file mode 100644 index 0000000..ebfd0f7 --- /dev/null +++ b/firmware/lucidgloves-firmware/Config.h @@ -0,0 +1,158 @@ +#pragma once +#include "src/Util/ConfigUtils.h" +#include "AdvancedConfig.h" + +//LUCIDGLOVES CONFIGURATION SETTINGS :) + +//This is the configuration file, main structure in _main.ino +//CONFIGURATION SETTINGS: +#define COMMUNICATION COMM_SERIAL //Which communication to use. Options are: COMM_SERIAL (usb), COMM_BTSERIAL (bluetooth) +//serial over USB + #define SERIAL_BAUD_RATE 115200 + +//serial over Bluetooth + #define BTSERIAL_DEVICE_NAME "lucidgloves-left" + +//ANALOG INPUT CONFIG +#define USING_SPLAY false //whether or not your glove tracks splay. - tracks the side to side "wag" of fingers. Requires 5 more inputs. +#define USING_MULTIPLEXER true //Whether or not you are using a multiplexer for inputs +#define FLIP_FLEXION false //Flip values from potentiometers (for fingers!) if they are backwards +#define FLIP_SPLAY true //Flip values for splay + + +//Gesture enables, make false to use button override +#define TRIGGER_GESTURE true +#define GRAB_GESTURE true +#define PINCH_GESTURE true + + +//BUTTON INVERT +//If a button registers as pressed when not and vice versa (eg. using normally-closed switches), +//you can invert their behaviour here by setting their line to true. +//If unsure, set to false +#define INVERT_A false +#define INVERT_B false +#define INVERT_JOY false +#define INVERT_MENU false +#define INVERT_CALIB false +//These only apply with gesture button override: +#define INVERT_TRIGGER false +#define INVERT_GRAB false +#define INVERT_PINCH false + + +//joystick configuration +#define JOYSTICK_BLANK true //make true if not using the joystick +#define JOY_FLIP_X false +#define JOY_FLIP_Y false +#define JOYSTICK_DEADZONE 10 //deadzone in the joystick to prevent drift (in percent) + +#define NO_THUMB false //If for some reason you don't want to track the thumb + +#define USING_CALIB_PIN true //When PIN_CALIB is shorted (or it's button pushed) it will reset calibration if this is on. + +#define USING_FORCE_FEEDBACK true //Force feedback haptics allow you to feel the solid objects you hold +#define FLIP_FORCE_FEEDBACK true +#define SERVO_SCALING false //dynamic scaling of servo motors + +#if defined(ESP32) + //(This configuration is for ESP32 DOIT V1 so make sure to change if you're on another board) + //To use a pin on the multiplexer, use MUX(pin). So for example pin 15 on a mux would be MUX(15). + #define PIN_PINKY MUX(12) //These 5 are for flexion + #define PIN_RING MUX(9) + #define PIN_MIDDLE MUX(6) + #define PIN_INDEX MUX(3) + #define PIN_THUMB MUX(0) + #define PIN_JOY_X 33 + #define PIN_JOY_Y 25 + #define PIN_JOY_BTN 26 + #define PIN_A_BTN 27 + #define PIN_B_BTN 14 + #define PIN_TRIG_BTN 12 //unused if gesture set + #define PIN_GRAB_BTN 13 //unused if gesture set + #define PIN_PNCH_BTN 23 //unused if gesture set + #define PIN_CALIB 32 //button for recalibration (You can set this to GPIO0 to use the BOOT button, but only when using Bluetooth.) + #define DEBUG_LED 2 + #define PIN_PINKY_MOTOR 19 //used for force feedback + #define PIN_RING_MOTOR 18 //^ + #define PIN_MIDDLE_MOTOR 5 //^ + #define PIN_INDEX_MOTOR 17 //^ + #define PIN_THUMB_MOTOR 16 //^ + #define PIN_MENU_BTN 34 + + //Splay pins. Only used for splay tracking gloves. Use MUX(pin) if you are using a multiplexer for it. + #define PIN_PINKY_SPLAY MUX(14) + #define PIN_RING_SPLAY MUX(11) + #define PIN_MIDDLE_SPLAY MUX(8) + #define PIN_INDEX_SPLAY MUX(5) + #define PIN_THUMB_SPLAY MUX(2) + + + //Select pins for multiplexers, set as needed if using a mux. You can add or remove pins as needed depending on how many select pins your mux needs. + #define PINS_MUX_SELECT 27, /*S0 pin*/ \ + 14, /*S1 pin*/ \ + 12, /*S2 pin*/ \ + 13 /*S3 pin (if your mux is 3-bit like 74HC4051 then you can remove this line and the backslash before it.)*/ + + #define MUX_INPUT 35 //the input or SIG pin of the multiplexer. This can't be a mux pin. + + //Signal mixing for finger values. Options are: MIXING_NONE, MIXING_SINCOS + //For double rotary hall effect sensors use MIXING_SINCOS. For potentiometers use MIXING_NONE. + #define FLEXION_MIXING MIXING_SINCOS + //Secondary analog pins for mixing flexion values. Only used by MIXING_SINCOS. Use MUX(pin) if you are using a multiplexer for it. + #define PIN_PINKY_SECOND MUX(13) + #define PIN_RING_SECOND MUX(10) + #define PIN_MIDDLE_SECOND MUX(7) + #define PIN_INDEX_SECOND MUX(4) + #define PIN_THUMB_SECOND MUX(1) + +//PINS CONFIGURATION +#elif defined(__AVR__) + //(This configuration is for Arduino Nano so make sure to change if you're on another board) + #define PIN_PINKY A0 + #define PIN_RING A1 + #define PIN_MIDDLE A2 + #define PIN_INDEX A3 + #define PIN_THUMB A4 + #define PIN_JOY_X A6 + #define PIN_JOY_Y A7 + #define PIN_JOY_BTN 7 + #define PIN_A_BTN 8 + #define PIN_B_BTN 9 + #define PIN_TRIG_BTN 10 //unused if gesture set + #define PIN_GRAB_BTN 11 //unused if gesture set + #define PIN_PNCH_BTN 12 //unused if gesture set + #define PIN_CALIB 13 //button for recalibration + #define DEBUG_LED LED_BUILTIN + #define PIN_PINKY_MOTOR 2 //used for force feedback + #define PIN_RING_MOTOR 3 //^ + #define PIN_MIDDLE_MOTOR 4 //^ + #define PIN_INDEX_MOTOR 5 //^ + #define PIN_THUMB_MOTOR 6 //^ + #define PIN_MENU_BTN 8 + + //Splay pins. Only used for splay tracking gloves. Use MUX(pin) if you are using a multiplexer for it. + #define PIN_PINKY_SPLAY MUX(10) + #define PIN_RING_SPLAY MUX(11) + #define PIN_MIDDLE_SPLAY MUX(12) + #define PIN_INDEX_SPLAY MUX(13) + #define PIN_THUMB_SPLAY MUX(14) + + //Select pins for multiplexers, set as needed if using a mux. You can add or remove pins as needed depending on how many select pins your mux needs. + #define PINS_MUX_SELECT 10, /*S0 pin*/ \ + 11, /*S1 pin*/ \ + 12, /*S2 pin*/ \ + 13 /*S3 pin (if your mux is 3-bit like 74HC4051 then you can remove this line and the backslash before it.)*/ + + #define MUX_INPUT A0 //the input or SIG pin of the multiplexer. This can't be a mux pin. + + //Signal mixing for finger values. Options are: MIXING_NONE, MIXING_SINCOS + //For double rotary hall effect sensors use MIXING_SINCOS. For potentiometers use MIXING_NONE. + #define FLEXION_MIXING MIXING_NONE + //Secondary analog pins for mixing. Only used by MIXING_SINCOS. Use MUX(pin) if you are using a multiplexer for it. + #define PIN_PINKY_SECOND MUX(1) + #define PIN_RING_SECOND MUX(3) + #define PIN_MIDDLE_SECOND MUX(5) + #define PIN_INDEX_SECOND MUX(7) + #define PIN_THUMB_SECOND MUX(9) +#endif diff --git a/firmware/lucidgloves-firmware/Encoding.ino b/firmware/lucidgloves-firmware/Encoding.ino deleted file mode 100644 index 5b2ba12..0000000 --- a/firmware/lucidgloves-firmware/Encoding.ino +++ /dev/null @@ -1,105 +0,0 @@ -/*struct inputData { - int* flexion; - int joyX; - int joyY; - bool joyClick; - bool triggerButton; - bool aButton; - bool bButton; - bool grab; - bool pinch; -}; - -struct outputData{ - int* hapticLimits; -}; -*/ - -#if ENCODING == ENCODING_LEGACY -//legacy encoding -char* encode(int* flexion, int joyX, int joyY, bool joyClick, bool triggerButton, bool aButton, bool bButton, bool grab, bool pinch, bool calib, bool menu){ - static char stringToEncode[75]; - - sprintf(stringToEncode, "%d&%d&%d&%d&%d&%d&%d&%d&%d&%d&%d&%d&%d\n", - flexion[0], flexion[1], flexion[2], flexion[3], flexion[4], - joyX, joyY, joyClick, - triggerButton, aButton, bButton, grab, pinch - ); - return stringToEncode; -} -//legacy decoding -void decodeData(char* stringToDecode, int* hapticLimits){ - byte index = 0; - char* ptr = strtok(stringToDecode, "&"); // takes a list of delimiters - while(ptr != NULL) - { - hapticLimits[index] = atoi(ptr); - index++; - ptr = strtok(NULL, "&"); // takes a list of delimiters - } -} -#endif - -#if ENCODING == ENCODE_ALPHA -//alphabetic encoding -char* encode(int* flexion, int joyX, int joyY, bool joyClick, bool triggerButton, bool aButton, bool bButton, bool grab, bool pinch, bool calib, bool menu){ - static char stringToEncode[75]; - int trigger = (flexion[1] > ANALOG_MAX/2) ? (flexion[1] - ANALOG_MAX/2) * 2:0; - #if USING_SPLAY - sprintf(stringToEncode, "A%dB%dC%dD%dE%dF%dG%dP%d%s%s%s%s%s%s%s%s(AB)%d(BB)%d(CB)%d(DB)%d(EB)%d\n", - flexion[0], flexion[1], flexion[2], flexion[3], flexion[4], - joyX, joyY, trigger, joyClick?"H":"", - triggerButton?"I":"", aButton?"J":"", bButton?"K":"", grab?"L":"", pinch?"M":"", menu?"N":"", calib?"O":"", - flexion[5], flexion[6], flexion[7], flexion[8], flexion[9] - ); - #else - sprintf(stringToEncode, "A%dB%dC%dD%dE%dF%dG%dP%d%s%s%s%s%s%s%s%s\n", - flexion[0], flexion[1], flexion[2], flexion[3], flexion[4], - joyX, joyY, trigger, joyClick?"H":"", - triggerButton?"I":"", aButton?"J":"", bButton?"K":"", grab?"L":"", pinch?"M":"", menu?"N":"", calib?"O":"" - ); - #endif - return stringToEncode; -} - -//alpha decoding -void decodeData(char* stringToDecode, int* hapticLimits){ - - //Check if a Z command was received - //Serial.println("Message recieved"); - if (strchr(stringToDecode, 'Z') != NULL) { - //Serial.println("Found Z!"); - bool toReturn = false; - if (strstr(stringToDecode, "ClearData") != NULL) { - clearFlags(); - toReturn = true; - } - if (strstr(stringToDecode, "SaveInter") != NULL) { - saveIntermediate(); - toReturn = true; - } - if (strstr(stringToDecode, "SaveTravel") != NULL) { - saveTravel(); - toReturn = true; - } - - if (toReturn) - return; - } - - hapticLimits[0] = getArgument(stringToDecode, 'A'); //thumb - hapticLimits[1] = getArgument(stringToDecode, 'B'); //index - hapticLimits[2] = getArgument(stringToDecode, 'C'); //middle - hapticLimits[3] = getArgument(stringToDecode, 'D'); //ring - hapticLimits[4] = getArgument(stringToDecode, 'E'); //pinky -} - -int getArgument(char* stringToDecode, char command){ - char* start = strchr(stringToDecode, command); - if (start == NULL) - return -1; - else - return atoi(start + 1); -} - -#endif diff --git a/firmware/lucidgloves-firmware/LICENSE b/firmware/lucidgloves-firmware/LICENSE deleted file mode 100644 index aa29a64..0000000 --- a/firmware/lucidgloves-firmware/LICENSE +++ /dev/null @@ -1,21 +0,0 @@ -MIT License - -Copyright (c) 2021 LucidVR - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to deal -in the Software without restriction, including without limitation the rights -to use, copy, modify, merge, publish, distribute, sublicense, and/or sell -copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in all -copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, -OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE -SOFTWARE. diff --git a/firmware/lucidgloves-firmware/SerialBTCommunication.ino b/firmware/lucidgloves-firmware/SerialBTCommunication.ino deleted file mode 100644 index 3b84540..0000000 --- a/firmware/lucidgloves-firmware/SerialBTCommunication.ino +++ /dev/null @@ -1,43 +0,0 @@ -//only compiles if BTSerial is set because it won't compile for a non-compatible board -#if COMMUNICATION == COMM_BTSERIAL -#include "BluetoothSerial.h" -class BTSerialCommunication : public ICommunication { - private: - bool m_isOpen; - BluetoothSerial m_SerialBT; - - public: - BTSerialCommunication() { - m_isOpen = false; - } - - bool isOpen(){ - return m_isOpen; - } - - void start(){ - m_SerialBT.begin(BTSERIAL_DEVICE_NAME); - #if BT_ECHO - Serial.begin(SERIAL_BAUD_RATE); - Serial.println("The device started, now you can pair it with bluetooth!"); - #endif - m_isOpen = true; - } - - void output(char* data){ - m_SerialBT.print(data); - #if BT_ECHO - Serial.print(data); - Serial.flush(); - #endif - } - - bool readData(char* input){ - /*byte size = m_SerialBT.readBytesUntil('\n', input, 100); - input[size] = NULL;*/ - String message = m_SerialBT.readStringUntil('\n'); - strcpy(input, message.c_str()); - return input != NULL && strlen(input) > 0; - } -}; -#endif diff --git a/firmware/lucidgloves-firmware/SerialCommunication.ino b/firmware/lucidgloves-firmware/SerialCommunication.ino deleted file mode 100644 index 32d27c4..0000000 --- a/firmware/lucidgloves-firmware/SerialCommunication.ino +++ /dev/null @@ -1,30 +0,0 @@ -class SerialCommunication : public ICommunication { - private: - bool m_isOpen; - - public: - SerialCommunication() { - m_isOpen = false; - } - - bool isOpen(){ - return m_isOpen; - } - - void start(){ - //Serial.setTimeout(1000000); - Serial.begin(SERIAL_BAUD_RATE); - m_isOpen = true; - } - - void output(char* data){ - Serial.print(data); - Serial.flush(); - } - - bool readData(char* input){ - byte size = Serial.readBytesUntil('\n', input, 100); - input[size] = NULL; - return input != NULL && strlen(input) > 0; - } -}; diff --git a/firmware/lucidgloves-firmware/_main.ino b/firmware/lucidgloves-firmware/_main.ino deleted file mode 100644 index 4626069..0000000 --- a/firmware/lucidgloves-firmware/_main.ino +++ /dev/null @@ -1,181 +0,0 @@ -#include -#define ALWAYS_CALIBRATING CALIBRATION_LOOPS == -1 - -#define CALIB_OVERRIDE false -#if USING_CALIB_PIN && COMMUNICATION == COMM_SERIAL && PIN_CALIB == 0 && !CALIB_OVERRIDE - #error "You can't set your calibration pin to 0 over usb. You can calibrate with the BOOT button when using bluetooth only. Set CalibOverride to true to override this." -#endif - -bool calibrate = false; -bool calibButton = false; -int* fingerPos = (int[]){0,0,0,0,0,0,0,0,0,0}; - -ICommunication* comm; - -#if ESP32_DUAL_CORE_SET -//std::mutex fingerPosMutex; -ordered_lock* fingerPosLock = new ordered_lock(); -TaskHandle_t Task1; -int threadLoops = 1; -int totalLocks = 0; -int lastMicros = 0; -int fullLoopTime = 0; -int fullLoopTotal = 0; -void getInputs(void* parameter){ - for(;;){ - fullLoopTime = micros() - lastMicros; - fullLoopTotal += fullLoopTime; - lastMicros = micros(); - { - fingerPosLock->lock(); - totalLocks++; - getFingerPositions(calibrate, calibButton); //Save finger positions in thread - - fingerPosLock->unlock(); - } - threadLoops++; - if (threadLoops%100 == 0){ - vTaskDelay(1); //keep watchdog fed - } - delayMicroseconds(1); - } -} -#endif - -int loops = 0; -void setup() { - pinMode(32, INPUT_PULLUP); - pinMode(DEBUG_LED, OUTPUT); - digitalWrite(DEBUG_LED, HIGH); - #if COMMUNICATION == COMM_SERIAL - comm = new SerialCommunication(); - #elif COMMUNICATION == COMM_BTSERIAL - comm = new BTSerialCommunication(); - #endif - comm->start(); - - setupInputs(); - - #if USING_FORCE_FEEDBACK - setupServoHaptics(); - #endif - - #if ESP32_DUAL_CORE_SET - xTaskCreatePinnedToCore( - getInputs, /* Function to implement the task */ - "Get_Inputs", /* Name of the task */ - 10000, /* Stack size in words */ - NULL, /* Task input parameter */ - tskIDLE_PRIORITY, /* Priority of the task */ - &Task1, /* Task handle. */ - 0); /* Core where the task should run */ - #endif -} - - -int lastMainMicros = micros(); -int mainMicros = 0; -int mainMicrosTotal = 0; -int mainloops = 1; - -int target = 0; -bool latch = false; - -void loop() { - mainloops++; - mainMicros = micros() - lastMainMicros; - mainMicrosTotal += mainMicros; - lastMainMicros = micros(); - - if (!digitalRead(27)){ - if (!latch){ - target++; - target %= 5; - - latch = true; - } - } - else - latch = false; - - if (comm->isOpen()){ - #if USING_CALIB_PIN - calibButton = getButton(PIN_CALIB) != INVERT_CALIB; - //Serial.println(getButton(PIN_CALIB)); - if (calibButton) - loops = 0; - #else - calibButton = false; - #endif - - - //bool calibrate = false; - if (loops < CALIBRATION_LOOPS || ALWAYS_CALIBRATING){ - calibrate = true; - loops++; - } - else{ - calibrate = false; - } - - #if !ESP32_DUAL_CORE_SET - getFingerPositions(calibrate, calibButton); - #endif - bool joyButton = getButton(PIN_JOY_BTN) != INVERT_JOY; - - #if TRIGGER_GESTURE - bool triggerButton = triggerGesture(fingerPos); - #else - bool triggerButton = getButton(PIN_TRIG_BTN) != INVERT_TRIGGER; - #endif - - bool aButton = getButton(PIN_A_BTN) != INVERT_A; - bool bButton = getButton(PIN_B_BTN) != INVERT_B; - - #if GRAB_GESTURE - bool grabButton = grabGesture(fingerPos); - #else - bool grabButton = getButton(PIN_GRAB_BTN) != INVERT_GRAB; - #endif - - #if PINCH_GESTURE - bool pinchButton = pinchGesture(fingerPos); - #else - bool pinchButton = getButton(PIN_PNCH_BTN) != INVERT_PINCH; - #endif - - int fingerPosCopy[10]; - int mutexTimeDone; - bool menuButton = getButton(PIN_MENU_BTN) != INVERT_MENU; - { - #if ESP32_DUAL_CORE_SET - int mutexTime = micros(); - //const std::lock_guard lock(fingerPosMutex); - fingerPosLock->lock(); - mutexTimeDone = micros()-mutexTime; - #endif - //memcpy(fingerPosCopy, fingerPos, sizeof(fingerPos)); - for (int i = 0; i < 10; i++){ - fingerPosCopy[i] = fingerPos[i]; - } - #if ESP32_DUAL_CORE_SET - fingerPosLock->unlock(); - #endif - - } - - comm->output(encode(fingerPosCopy, getJoyX(), getJoyY(), joyButton, triggerButton, aButton, bButton, grabButton, pinchButton, calibButton, menuButton)); - #if USING_FORCE_FEEDBACK - char received[100]; - if (comm->readData(received)){ - int hapticLimits[5]; - //This check is a temporary hack to fix an issue with haptics on v0.5 of the driver, will make it more snobby code later - if(String(received).length() >= 5) { - decodeData(received, hapticLimits); - writeServoHaptics(hapticLimits); - } - } - #endif - delay(LOOP_TIME); - } -} diff --git a/firmware/lucidgloves-firmware/haptics.ino b/firmware/lucidgloves-firmware/haptics.ino deleted file mode 100644 index 5162272..0000000 --- a/firmware/lucidgloves-firmware/haptics.ino +++ /dev/null @@ -1,59 +0,0 @@ -#if USING_FORCE_FEEDBACK - -#if defined(ESP32) - #include "ESP32Servo.h" -#else - #include "Servo.h" -#endif - -Servo pinkyServo; -Servo ringServo; -Servo middleServo; -Servo indexServo; -Servo thumbServo; - -void setupServoHaptics(){ - pinkyServo.attach(PIN_PINKY_MOTOR); - ringServo.attach(PIN_RING_MOTOR); - middleServo.attach(PIN_MIDDLE_MOTOR); - indexServo.attach(PIN_INDEX_MOTOR); - thumbServo.attach(PIN_THUMB_MOTOR); -} - -//static scaling, maps to entire range of servo -void scaleLimits(int* hapticLimits, float* scaledLimits){ - for (int i = 0; i < 5; i++){ - #if FLIP_FORCE_FEEDBACK - scaledLimits[i] = hapticLimits[i] / 1000.0f * 180.0f; - #else - scaledLimits[i] = 180.0f - hapticLimits[i] / 1000.0f * 180.0f; - #endif - } - -} - -//dynamic scaling, maps to the limits calibrated from your finger -void dynScaleLimits(int* hapticLimits, float* scaledLimits){ - //will be refactored to take min and max as an argument - - /* this implementation of dynamic scaling relies on the assumption - * that the servo reaches 2/3 of the potentiometer's range, - * and that 0 degrees is geared to the start of the potentiometer. - * Different hardware types may need to handle dynamic scaling differently. - */ - for (int i = 0; i < sizeof(hapticLimits); i++){ - scaledLimits[i] = hapticLimits[i] / 1000.0f * 180.0f; - } -} - -void writeServoHaptics(int* hapticLimits){ - float scaledLimits[5]; - scaleLimits(hapticLimits, scaledLimits); - if(hapticLimits[0] >= 0) thumbServo.write(scaledLimits[0]); - if(hapticLimits[1] >= 0) indexServo.write(scaledLimits[1]); - if(hapticLimits[2] >= 0) middleServo.write(scaledLimits[2]); - if(hapticLimits[3] >= 0) ringServo.write(scaledLimits[3]); - if(hapticLimits[4] >= 0) pinkyServo.write(scaledLimits[4]); -} - -#endif diff --git a/firmware/lucidgloves-firmware/lucidgloves-firmware.ino b/firmware/lucidgloves-firmware/lucidgloves-firmware.ino index 8a1fa69..912cf25 100644 --- a/firmware/lucidgloves-firmware/lucidgloves-firmware.ino +++ b/firmware/lucidgloves-firmware/lucidgloves-firmware.ino @@ -4,159 +4,24 @@ * lucidvrtech.com */ -#include "ConfigUtils.h" -#include "AdvancedConfig.h" +#include "src/Main.h" -//This is the configuration file, main structure in _main.ino -//CONFIGURATION SETTINGS: -#define COMMUNICATION COMM_SERIAL //Which communication protocol to use. Options are: COMM_SERIAL (usb), COMM_BTSERIAL (bluetooth) -//serial over USB - #define SERIAL_BAUD_RATE 115200 - -//serial over Bluetooth - #define BTSERIAL_DEVICE_NAME "lucidgloves-left" - -//ANALOG INPUT CONFIG -#define USING_SPLAY false //whether or not your glove tracks splay. - tracks the side to side "wag" of fingers. Requires 5 more inputs. -#define USING_MULTIPLEXER true //Whether or not you are using a multiplexer for inputs -#define FLIP_FLEXION false //Flip values from potentiometers (for fingers!) if they are backwards -#define FLIP_SPLAY true //Flip values for splay - - -//Gesture enables, make false to use button override -#define TRIGGER_GESTURE true -#define GRAB_GESTURE true -#define PINCH_GESTURE true - - -//BUTTON INVERT -//If a button registers as pressed when not and vice versa (eg. using normally-closed switches), -//you can invert their behaviour here by setting their line to true. -//If unsure, set to false -#define INVERT_A false -#define INVERT_B false -#define INVERT_JOY false -#define INVERT_MENU false -#define INVERT_CALIB false -//These only apply with gesture button override: -#define INVERT_TRIGGER false -#define INVERT_GRAB false -#define INVERT_PINCH false - - -//joystick configuration -#define JOYSTICK_BLANK true //make true if not using the joystick -#define JOY_FLIP_X false -#define JOY_FLIP_Y false -#define JOYSTICK_DEADZONE 10 //deadzone in the joystick to prevent drift (in percent) - -#define NO_THUMB false //If for some reason you don't want to track the thumb - -#define USING_CALIB_PIN true //When PIN_CALIB is shorted (or it's button pushed) it will reset calibration if this is on. - -#define USING_FORCE_FEEDBACK false //Force feedback haptics allow you to feel the solid objects you hold -#define FLIP_FORCE_FEEDBACK true -#define SERVO_SCALING false //dynamic scaling of servo motors - -#if defined(ESP32) - //(This configuration is for ESP32 DOIT V1 so make sure to change if you're on another board) - //To use a pin on the multiplexer, use MUX(pin). So for example pin 15 on a mux would be MUX(15). - #define PIN_PINKY MUX(12) //These 5 are for flexion - #define PIN_RING MUX(9) - #define PIN_MIDDLE MUX(6) - #define PIN_INDEX MUX(3) - #define PIN_THUMB MUX(0) - #define PIN_JOY_X 33 - #define PIN_JOY_Y 25 - #define PIN_JOY_BTN 26 - #define PIN_A_BTN 27 - #define PIN_B_BTN 14 - #define PIN_TRIG_BTN 12 //unused if gesture set - #define PIN_GRAB_BTN 13 //unused if gesture set - #define PIN_PNCH_BTN 23 //unused if gesture set - #define PIN_CALIB 32 //button for recalibration (You can set this to GPIO0 to use the BOOT button, but only when using Bluetooth.) - #define DEBUG_LED 2 - #define PIN_PINKY_MOTOR 19 //used for force feedback - #define PIN_RING_MOTOR 18 //^ - #define PIN_MIDDLE_MOTOR 5 //^ - #define PIN_INDEX_MOTOR 17 //^ - #define PIN_THUMB_MOTOR 16 //^ - #define PIN_MENU_BTN 34 - - //Splay pins. Only used for splay tracking gloves. Use MUX(pin) if you are using a multiplexer for it. - #define PIN_PINKY_SPLAY MUX(14) - #define PIN_RING_SPLAY MUX(11) - #define PIN_MIDDLE_SPLAY MUX(8) - #define PIN_INDEX_SPLAY MUX(5) - #define PIN_THUMB_SPLAY MUX(2) - +/* + * + * THE CONFIG SETTINGS ARE NO LONGER STORED HERE. THEY HAVE BEEN MOVED TO Config.h + * + * You can now change the settings over at config.h before uploading to your gloves. + * + */ - //Select pins for multiplexers, set as needed if using a mux. You can add or remove pins as needed depending on how many select pins your mux needs. - #define PINS_MUX_SELECT 27, /*S0 pin*/ \ - 14, /*S1 pin*/ \ - 12, /*S2 pin*/ \ - 13 /*S3 pin (if your mux is 3-bit like 74HC4051 then you can remove this line and the backslash before it.)*/ - - #define MUX_INPUT 35 //the input or SIG pin of the multiplexer. This can't be a mux pin. - //Signal mixing for finger values. Options are: MIXING_NONE, MIXING_SINCOS - //For double rotary hall effect sensors use MIXING_SINCOS. For potentiometers use MIXING_NONE. - #define FLEXION_MIXING MIXING_SINCOS - //Secondary analog pins for mixing flexion values. Only used by MIXING_SINCOS. Use MUX(pin) if you are using a multiplexer for it. - #define PIN_PINKY_SECOND MUX(13) - #define PIN_RING_SECOND MUX(10) - #define PIN_MIDDLE_SECOND MUX(7) - #define PIN_INDEX_SECOND MUX(4) - #define PIN_THUMB_SECOND MUX(1) - -//PINS CONFIGURATION -#elif defined(__AVR__) - //(This configuration is for Arduino Nano so make sure to change if you're on another board) - #define PIN_PINKY A0 - #define PIN_RING A1 - #define PIN_MIDDLE A2 - #define PIN_INDEX A3 - #define PIN_THUMB A4 - #define PIN_JOY_X A6 - #define PIN_JOY_Y A7 - #define PIN_JOY_BTN 7 - #define PIN_A_BTN 8 - #define PIN_B_BTN 9 - #define PIN_TRIG_BTN 10 //unused if gesture set - #define PIN_GRAB_BTN 11 //unused if gesture set - #define PIN_PNCH_BTN 12 //unused if gesture set - #define PIN_CALIB 13 //button for recalibration - #define DEBUG_LED LED_BUILTIN - #define PIN_PINKY_MOTOR 2 //used for force feedback - #define PIN_RING_MOTOR 3 //^ - #define PIN_MIDDLE_MOTOR 4 //^ - #define PIN_INDEX_MOTOR 5 //^ - #define PIN_THUMB_MOTOR 6 //^ - #define PIN_MENU_BTN 8 +Main mainClass; - //Splay pins. Only used for splay tracking gloves. Use MUX(pin) if you are using a multiplexer for it. - #define PIN_PINKY_SPLAY MUX(10) - #define PIN_RING_SPLAY MUX(11) - #define PIN_MIDDLE_SPLAY MUX(12) - #define PIN_INDEX_SPLAY MUX(13) - #define PIN_THUMB_SPLAY MUX(14) - - //Select pins for multiplexers, set as needed if using a mux. You can add or remove pins as needed depending on how many select pins your mux needs. - #define PINS_MUX_SELECT 10, /*S0 pin*/ \ - 11, /*S1 pin*/ \ - 12, /*S2 pin*/ \ - 13 /*S3 pin (if your mux is 3-bit like 74HC4051 then you can remove this line and the backslash before it.)*/ - - #define MUX_INPUT A0 //the input or SIG pin of the multiplexer. This can't be a mux pin. +void setup(){ + mainClass.setup(); +} - //Signal mixing for finger values. Options are: MIXING_NONE, MIXING_SINCOS - //For double rotary hall effect sensors use MIXING_SINCOS. For potentiometers use MIXING_NONE. - #define FLEXION_MIXING MIXING_NONE - //Secondary analog pins for mixing. Only used by MIXING_SINCOS. Use MUX(pin) if you are using a multiplexer for it. - #define PIN_PINKY_SECOND MUX(1) - #define PIN_RING_SECOND MUX(3) - #define PIN_MIDDLE_SECOND MUX(5) - #define PIN_INDEX_SECOND MUX(7) - #define PIN_THUMB_SECOND MUX(9) -#endif +void loop(){ + mainClass.loop(); +} diff --git a/firmware/lucidgloves-firmware/src/Communication/BTSerialCommunication.cpp b/firmware/lucidgloves-firmware/src/Communication/BTSerialCommunication.cpp new file mode 100644 index 0000000..2df700b --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Communication/BTSerialCommunication.cpp @@ -0,0 +1,37 @@ +#include "BTSerialCommunication.h" + +#if COMMUNICATION == COMM_BTSERIAL + +BTSerialCommunication::BTSerialCommunication() { + m_isOpen = false; +} + +bool BTSerialCommunication::isOpen() { + return m_isOpen; +} + +void BTSerialCommunication::start() { + m_SerialBT.begin(BTSERIAL_DEVICE_NAME); + #if BT_ECHO + Serial.begin(SERIAL_BAUD_RATE); + Serial.println("The device started, now you can pair it with bluetooth!"); + #endif + m_isOpen = true; +} + +void BTSerialCommunication::output(char* data) { + m_SerialBT.print(data); + #if BT_ECHO + Serial.print(data); + Serial.flush(); + #endif +} + +bool BTSerialCommunication::readData(char* input) { + /*byte size = m_SerialBT.readBytesUntil('\n', input, 100); + input[size] = NULL;*/ + String message = m_SerialBT.readStringUntil('\n'); + strcpy(input, message.c_str()); + return input != NULL && strlen(input) > 0; +} +#endif diff --git a/firmware/lucidgloves-firmware/src/Communication/BTSerialCommunication.h b/firmware/lucidgloves-firmware/src/Communication/BTSerialCommunication.h new file mode 100644 index 0000000..c463631 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Communication/BTSerialCommunication.h @@ -0,0 +1,24 @@ +#pragma once +#include "ICommunication.h" +#include "../../Config.h" + +#if COMMUNICATION == COMM_BTSERIAL +#include "BluetoothSerial.h" + +class BTSerialCommunication : public ICommunication { +private: + bool m_isOpen; + BluetoothSerial m_SerialBT; + +public: + BTSerialCommunication(); + + bool isOpen() override; + + void start() override; + + void output(char* data) override; + + bool readData(char* input) override; +}; +#endif diff --git a/firmware/lucidgloves-firmware/ICommunication.ino b/firmware/lucidgloves-firmware/src/Communication/ICommunication.h similarity index 94% rename from firmware/lucidgloves-firmware/ICommunication.ino rename to firmware/lucidgloves-firmware/src/Communication/ICommunication.h index 34ca1f6..45e2dfd 100644 --- a/firmware/lucidgloves-firmware/ICommunication.ino +++ b/firmware/lucidgloves-firmware/src/Communication/ICommunication.h @@ -1,5 +1,6 @@ -//Interface for communication +#pragma once +//Interface for communication class ICommunication { public: diff --git a/firmware/lucidgloves-firmware/src/Communication/SerialCommunication.cpp b/firmware/lucidgloves-firmware/src/Communication/SerialCommunication.cpp new file mode 100644 index 0000000..521dcde --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Communication/SerialCommunication.cpp @@ -0,0 +1,26 @@ +#include "SerialCommunication.h" + +SerialCommunication::SerialCommunication() { + m_isOpen = false; +} + +bool SerialCommunication::isOpen() { + return m_isOpen; +} + +void SerialCommunication::start() { + //Serial.setTimeout(1000000); + Serial.begin(SERIAL_BAUD_RATE); + m_isOpen = true; +} + +void SerialCommunication::output(char* data) { + Serial.print(data); + Serial.flush(); +} + +bool SerialCommunication::readData(char* input) { + byte size = Serial.readBytesUntil('\n', input, 100); + input[size] = NULL; + return input != NULL && strlen(input) > 0; +} diff --git a/firmware/lucidgloves-firmware/src/Communication/SerialCommunication.h b/firmware/lucidgloves-firmware/src/Communication/SerialCommunication.h new file mode 100644 index 0000000..71ceb9f --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Communication/SerialCommunication.h @@ -0,0 +1,20 @@ +#pragma once +#include "ICommunication.h" +#include "../../Config.h" +#include + +class SerialCommunication : public ICommunication { +private: + bool m_isOpen; + +public: + SerialCommunication(); + + bool isOpen() override; + + void start() override; + + void output(char* data) override; + + bool readData(char* input) override; +}; diff --git a/firmware/lucidgloves-firmware/gesture.ino b/firmware/lucidgloves-firmware/src/Controller/Gesture.cpp similarity index 63% rename from firmware/lucidgloves-firmware/gesture.ino rename to firmware/lucidgloves-firmware/src/Controller/Gesture.cpp index 072f815..49bab54 100644 --- a/firmware/lucidgloves-firmware/gesture.ino +++ b/firmware/lucidgloves-firmware/src/Controller/Gesture.cpp @@ -1,11 +1,13 @@ -bool grabGesture(int *flexion){ +#include "Gesture.h" + +bool Gesture::grabGesture(int *flexion){ return (flexion[PINKY_IND] + flexion[RING_IND] + flexion[MIDDLE_IND] + flexion[INDEX_IND]) / 4 <= ANALOG_MAX/2 ? 0:1; } -bool pinchGesture(int *flexion){ +bool Gesture::pinchGesture(int *flexion){ return (flexion[INDEX_IND] + flexion[THUMB_IND]) / 2 <= ANALOG_MAX/2 ? 0:1; } -bool triggerGesture(int *flexion){ +bool Gesture::triggerGesture(int *flexion){ return flexion[INDEX_IND]<=(ANALOG_MAX/2)?0:1; } diff --git a/firmware/lucidgloves-firmware/src/Controller/Gesture.h b/firmware/lucidgloves-firmware/src/Controller/Gesture.h new file mode 100644 index 0000000..bdcc496 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Controller/Gesture.h @@ -0,0 +1,9 @@ +#pragma once +#include "../../AdvancedConfig.h" + +class Gesture { +public: + bool grabGesture(int *flexion); + bool pinchGesture(int *flexion); + bool triggerGesture(int *flexion); +}; diff --git a/firmware/lucidgloves-firmware/src/Controller/Haptics.cpp b/firmware/lucidgloves-firmware/src/Controller/Haptics.cpp new file mode 100644 index 0000000..7fdeea8 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Controller/Haptics.cpp @@ -0,0 +1,39 @@ +#include "Haptics.h" + +#if USING_FORCE_FEEDBACK + +void Haptics::setupServoHaptics() { + pinkyServo.attach(PIN_PINKY_MOTOR); + ringServo.attach(PIN_RING_MOTOR); + middleServo.attach(PIN_MIDDLE_MOTOR); + indexServo.attach(PIN_INDEX_MOTOR); + thumbServo.attach(PIN_THUMB_MOTOR); +} + +void Haptics::scaleLimits(int* hapticLimits, float* scaledLimits) { + for (int i = 0; i < 5; i++) { +#if FLIP_FORCE_FEEDBACK + scaledLimits[i] = hapticLimits[i] / 1000.0f * 180.0f; +#else + scaledLimits[i] = 180.0f - hapticLimits[i] / 1000.0f * 180.0f; +#endif + } +} + +void Haptics::dynScaleLimits(int* hapticLimits, float* scaledLimits) { + for (int i = 0; i < sizeof(hapticLimits); i++) { + scaledLimits[i] = hapticLimits[i] / 1000.0f * 180.0f; + } +} + +void Haptics::writeServoHaptics(int* hapticLimits) { + float scaledLimits[5]; + scaleLimits(hapticLimits, scaledLimits); + if (hapticLimits[0] >= 0) thumbServo.write(scaledLimits[0]); + if (hapticLimits[1] >= 0) indexServo.write(scaledLimits[1]); + if (hapticLimits[2] >= 0) middleServo.write(scaledLimits[2]); + if (hapticLimits[3] >= 0) ringServo.write(scaledLimits[3]); + if (hapticLimits[4] >= 0) pinkyServo.write(scaledLimits[4]); +} + +#endif diff --git a/firmware/lucidgloves-firmware/src/Controller/Haptics.h b/firmware/lucidgloves-firmware/src/Controller/Haptics.h new file mode 100644 index 0000000..7de6b06 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Controller/Haptics.h @@ -0,0 +1,23 @@ +#pragma once +#include "../../Config.h" + +#if defined(ESP32) + #include "ESP32Servo.h" +#else + #include "Servo.h" +#endif + +class Haptics { +private: + Servo pinkyServo; + Servo ringServo; + Servo middleServo; + Servo indexServo; + Servo thumbServo; + +public: + void setupServoHaptics(); + void scaleLimits(int* hapticLimits, float* scaledLimits); + void dynScaleLimits(int* hapticLimits, float* scaledLimits); + void writeServoHaptics(int* hapticLimits); +}; diff --git a/firmware/lucidgloves-firmware/input.ino b/firmware/lucidgloves-firmware/src/Controller/InputManager.cpp similarity index 67% rename from firmware/lucidgloves-firmware/input.ino rename to firmware/lucidgloves-firmware/src/Controller/InputManager.cpp index f1df62f..33c1288 100644 --- a/firmware/lucidgloves-firmware/input.ino +++ b/firmware/lucidgloves-firmware/src/Controller/InputManager.cpp @@ -1,81 +1,16 @@ -#include -#include - -// Requires RunningMedian library by Rob Tillaart -#if (ENABLE_MEDIAN_FILTER || ((INTERFILTER_MODE != INTERFILTER_NONE) && (FLEXION_MIXING != MIXING_NONE))) - #include -#endif - -#if ENABLE_MEDIAN_FILTER - RunningMedian rmSamples[2* NUM_FINGERS] = { - RunningMedian(MEDIAN_SAMPLES), - RunningMedian(MEDIAN_SAMPLES), - RunningMedian(MEDIAN_SAMPLES), - RunningMedian(MEDIAN_SAMPLES), - RunningMedian(MEDIAN_SAMPLES), - RunningMedian(MEDIAN_SAMPLES), - RunningMedian(MEDIAN_SAMPLES), - RunningMedian(MEDIAN_SAMPLES), - RunningMedian(MEDIAN_SAMPLES), - RunningMedian(MEDIAN_SAMPLES) - }; -#endif - -#if ((INTERFILTER_MODE != INTERFILTER_NONE) && (FLEXION_MIXING == MIXING_SINCOS)) - RunningMedian sinSamples[NUM_FINGERS] = { - RunningMedian(INTERFILTER_SAMPLES), - RunningMedian(INTERFILTER_SAMPLES), - RunningMedian(INTERFILTER_SAMPLES), - RunningMedian(INTERFILTER_SAMPLES), - RunningMedian(INTERFILTER_SAMPLES) - }; - - RunningMedian cosSamples[NUM_FINGERS] = { - RunningMedian(INTERFILTER_SAMPLES), - RunningMedian(INTERFILTER_SAMPLES), - RunningMedian(INTERFILTER_SAMPLES), - RunningMedian(INTERFILTER_SAMPLES), - RunningMedian(INTERFILTER_SAMPLES) - }; -#endif - -#define DEFAULT_VREF 1100 -esp_adc_cal_characteristics_t *adc_chars; - -byte selectPins[] = {PINS_MUX_SELECT}; - -int maxFingers[2* NUM_FINGERS] = {0,0,0,0,0,0,0,0,0,0}; -int minFingers[2* NUM_FINGERS] = {ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX}; -int maxTravel[2*NUM_FINGERS] = {ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX}; -#if FLEXION_MIXING == MIXING_SINCOS - int sinMin[NUM_FINGERS] = {ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX}; - int sinMax[NUM_FINGERS] = {0,0,0,0,0}; - - int cosMin[NUM_FINGERS] = {ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX}; - int cosMax[NUM_FINGERS] = {0,0,0,0,0}; - - bool atanPositive[NUM_FINGERS] = {true, true, true, true, true}; - - - int totalOffset1[NUM_FINGERS] = {0,0,0,0,0}; -#endif - - -bool savedInter = false; -bool savedTravel = false; - -void setupInputs(){ +#include "InputManager.h" +InputManager::InputManager() { + // Constructor logic here... +} +void InputManager::setupInputs() { + EEPROM.begin(0x78 + 1); - Serial.begin(115200); //DON'T FORGET TO REMOVE THISSSSS - Serial.println("Setting up input!"); if (isSavedLimits()){ - Serial.println("Is saved limits!"); savedTravel = true; loadTravel(); } if (isSavedIntermediate()){ - Serial.println("IsSavedIntermediate!"); savedInter = true; loadIntermediate(); } @@ -103,15 +38,17 @@ void setupInputs(){ #endif #if USING_MULTIPLEXER - byte selectPins[] = {PINS_MUX_SELECT}; //pinMode(MUX_INPUT, INPUT); - for (int i = 0; i < sizeof(selectPins); i++){ - pinMode(selectPins[i], OUTPUT); + { + byte selectPins[] = {PINS_MUX_SELECT}; + for (int i = 0; i < sizeof(selectPins); i++){ + pinMode(selectPins[i], OUTPUT); + } } #endif } -int analogPinRead(int pin){ +int InputManager::analogPinRead(int pin) { #if USING_MULTIPLEXER if (ISMUX(pin)){ return readMux(UNMUX(pin)); @@ -124,8 +61,11 @@ int analogPinRead(int pin){ #endif } +// Other methods follow the same pattern... + #if USING_MULTIPLEXER -int readMux(byte pin){ +int InputManager::readMux(byte pin) { + byte selectPins[] = {PINS_MUX_SELECT}; int numSelectPins = sizeof(selectPins) / sizeof(selectPins[0]); for (int i = numSelectPins - 1; i > -1; i--){ @@ -137,8 +77,7 @@ int readMux(byte pin){ } #endif -int targetSinMin, targetSinMax, targetSinCurrent, targetCosMin, targetCosMax, targetCosCurrent, targetFlexionMin, targetFlexionMax, targetFlexionCurrent, targetMaxTravel, targetProcessed; -void getFingerPositions(bool calibrating, bool reset){ +void InputManager::getFingerPositions(bool calibrating, bool reset, int* fingerPos) { #if FLEXION_MIXING == MIXING_NONE //no mixing, just linear int rawFingersFlexion[NUM_FINGERS] = {NO_THUMB?0:analogPinRead(PIN_THUMB), analogPinRead(PIN_INDEX), analogPinRead(PIN_MIDDLE), analogPinRead(PIN_RING), analogPinRead(PIN_PINKY)}; @@ -226,16 +165,8 @@ void getFingerPositions(bool calibrating, bool reset){ } for (int i = 0; i +#include "../../Config.h" +#include "../../AdvancedConfig.h" + + +#if (ENABLE_MEDIAN_FILTER || ((INTERFILTER_MODE != INTERFILTER_NONE) && (FLEXION_MIXING != MIXING_NONE))) + #include +#endif + +class InputManager +{ +public: + InputManager(); + void setupInputs(); + int analogPinRead(int pin); + #if USING_MULTIPLEXER + int readMux(byte pin); + #endif + void getFingerPositions(bool calibrating, bool reset, int* fingerPos); + int analogReadDeadzone(int pin); + int getJoyX(); + int getJoyY(); + bool getButton(byte pin); + #if FLEXION_MIXING == MIXING_SINCOS + int sinCosMix(int sinPin, int cosPin, int i); + #endif + void saveTravel(); + void saveIntermediate(); + void clearFlags(); + bool isSavedLimits(); + bool isSavedIntermediate(); + void loadTravel(); + void loadIntermediate(); +private: + // Add your private variables here + bool savedInter = false; + bool savedTravel = false; + + int maxFingers[2* NUM_FINGERS] = {0,0,0,0,0,0,0,0,0,0}; + int minFingers[2* NUM_FINGERS] = {ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX}; + int maxTravel[2*NUM_FINGERS] = {ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX}; + + #if FLEXION_MIXING == MIXING_SINCOS + int sinMin[NUM_FINGERS] = {ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX}; + int sinMax[NUM_FINGERS] = {0,0,0,0,0}; + + int cosMin[NUM_FINGERS] = {ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX, ANALOG_MAX}; + int cosMax[NUM_FINGERS] = {0,0,0,0,0}; + + bool atanPositive[NUM_FINGERS] = {true, true, true, true, true}; + + + int totalOffset1[NUM_FINGERS] = {0,0,0,0,0}; + #endif + + #if ENABLE_MEDIAN_FILTER + RunningMedian rmSamples[2* NUM_FINGERS] = { + RunningMedian(MEDIAN_SAMPLES), + RunningMedian(MEDIAN_SAMPLES), + RunningMedian(MEDIAN_SAMPLES), + RunningMedian(MEDIAN_SAMPLES), + RunningMedian(MEDIAN_SAMPLES), + RunningMedian(MEDIAN_SAMPLES), + RunningMedian(MEDIAN_SAMPLES), + RunningMedian(MEDIAN_SAMPLES), + RunningMedian(MEDIAN_SAMPLES), + RunningMedian(MEDIAN_SAMPLES) + }; + #endif + + #if ((INTERFILTER_MODE != INTERFILTER_NONE) && (FLEXION_MIXING == MIXING_SINCOS)) + RunningMedian sinSamples[NUM_FINGERS] = { + RunningMedian(INTERFILTER_SAMPLES), + RunningMedian(INTERFILTER_SAMPLES), + RunningMedian(INTERFILTER_SAMPLES), + RunningMedian(INTERFILTER_SAMPLES), + RunningMedian(INTERFILTER_SAMPLES) + }; + + RunningMedian cosSamples[NUM_FINGERS] = { + RunningMedian(INTERFILTER_SAMPLES), + RunningMedian(INTERFILTER_SAMPLES), + RunningMedian(INTERFILTER_SAMPLES), + RunningMedian(INTERFILTER_SAMPLES), + RunningMedian(INTERFILTER_SAMPLES) + }; + #endif + + + +}; diff --git a/firmware/lucidgloves-firmware/src/Encoding/AlphaEncoding.cpp b/firmware/lucidgloves-firmware/src/Encoding/AlphaEncoding.cpp new file mode 100644 index 0000000..571e587 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Encoding/AlphaEncoding.cpp @@ -0,0 +1,48 @@ +#include "AlphaEncoding.h" +#include +void AlphaEncoding::encode(OutboundData data, char* stringToEncode){ + int trigger = (data.fingers[1] > ANALOG_MAX/2) ? (data.fingers[1] - ANALOG_MAX/2) * 2:0; + char splayString[30] = ""; + #if USING_SPLAY + sprintf(splayString, "(AB)%d(BB)%d(CB)%d(DB)%d(EB)%d", + data.splay[0], data.splay[1], data.splay[2], data.splay[3], data.splay[4] + ); + #endif + snprintf(stringToEncode, 100, "A%dB%dC%dD%dE%dF%dG%dP%d%s%s%s%s%s%s%s%s%s\n", + data.fingers[0], data.fingers[1], data.fingers[2], data.fingers[3], data.fingers[4], + data.joyX, data.joyY, trigger, data.joyClick?"H":"", + data.triggerButton?"I":"", data.aButton?"J":"", data.bButton?"K":"", data.grab?"L":"", data.pinch?"M":"", data.menu?"N":"", data.calib?"O":"", + splayString); +} +DecodedData AlphaEncoding::decodeData(char* stringToDecode) { + DecodedData decodedData = {}; + + if (strchr(stringToDecode, 'Z') != NULL) { + for (int i = 0; i < NUM_SPECIAL_COMMANDS; i++) { + if (strstr(stringToDecode, SPECIAL_COMMANDS[i]) != NULL) { + decodedData.command = SPECIAL_COMMANDS[i]; + decodedData.fields.specialCommandReceived = true; + return decodedData; + } + } + } + + for (int i = 0; i < NUM_FINGERS; i++) { + int value = getArgument(stringToDecode, 'A' + i); + if (value != -1) { + decodedData.servoValues[i] = value; + decodedData.fields.servoValuesReceived[i] = true; + } + } + + return decodedData; +} + + +int AlphaEncoding::getArgument(char* stringToDecode, char command){ + char* start = strchr(stringToDecode, command); + if (start == NULL) + return -1; + else + return atoi(start + 1); +} diff --git a/firmware/lucidgloves-firmware/src/Encoding/AlphaEncoding.h b/firmware/lucidgloves-firmware/src/Encoding/AlphaEncoding.h new file mode 100644 index 0000000..57ef3e9 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Encoding/AlphaEncoding.h @@ -0,0 +1,12 @@ +#pragma once + +#include "IEncoding.h" +#include "Config.h" + +class AlphaEncoding : public IEncoding { +public: + void encode(OutboundData data, char* stringToEncode) override; + DecodedData decodeData(char* stringToDecode) override; +private: + int getArgument(char* stringToDecode, char command); +}; diff --git a/firmware/lucidgloves-firmware/src/Encoding/IEncoding.h b/firmware/lucidgloves-firmware/src/Encoding/IEncoding.h new file mode 100644 index 0000000..059d8f2 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Encoding/IEncoding.h @@ -0,0 +1,13 @@ +#pragma once +#include "../Util/DataStructs.h" + + + + +// Interface for encoding +class IEncoding { +public: + virtual void encode(OutboundData data, char* stringToEncode) = 0; + + virtual DecodedData decodeData(char* stringToDecode) = 0; +}; diff --git a/firmware/lucidgloves-firmware/src/Encoding/LegacyEncoding.cpp b/firmware/lucidgloves-firmware/src/Encoding/LegacyEncoding.cpp new file mode 100644 index 0000000..56e5852 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Encoding/LegacyEncoding.cpp @@ -0,0 +1,25 @@ +#include "LegacyEncoding.h" +#include +void LegacyEncoding::encode(OutboundData data, char* stringToEncode){ + sprintf(stringToEncode, "%d&%d&%d&%d&%d&%d&%d&%d&%d&%d&%d&%d&%d\n", + data.fingers[0], data.fingers[1], data.fingers[2], data.fingers[3], data.fingers[4], + data.joyX, data.joyY, data.joyClick, + data.triggerButton, data.aButton, data.bButton, data.grab, data.pinch + ); +} + +DecodedData LegacyEncoding::decodeData(char* stringToDecode) { + DecodedData decodedData = {}; + + byte index = 0; + char* ptr = strtok(stringToDecode, "&"); + while(ptr != NULL && index < NUM_FINGERS) + { + decodedData.servoValues[index] = atoi(ptr); + decodedData.fields.servoValuesReceived[index] = true; + index++; + ptr = strtok(NULL, "&"); + } + + return decodedData; +} diff --git a/firmware/lucidgloves-firmware/src/Encoding/LegacyEncoding.h b/firmware/lucidgloves-firmware/src/Encoding/LegacyEncoding.h new file mode 100644 index 0000000..590a5d4 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Encoding/LegacyEncoding.h @@ -0,0 +1,10 @@ +#pragma once + +#include "IEncoding.h" +#include "Config.h" + +class LegacyEncoding : public IEncoding { +public: + void encode(OutboundData data, char* stringToEncode) override; + DecodedData decodeData(char* stringToDecode) override; +}; diff --git a/firmware/lucidgloves-firmware/src/Main.cpp b/firmware/lucidgloves-firmware/src/Main.cpp new file mode 100644 index 0000000..4e67b1a --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Main.cpp @@ -0,0 +1,183 @@ +#include "Main.h" +#include "Communication/SerialCommunication.h" +#include "Communication/BTSerialCommunication.h" +#include "Encoding/AlphaEncoding.h" +#include "Encoding/LegacyEncoding.h" +#include "Util/DataStructs.h" + +#define ALWAYS_CALIBRATING CALIBRATION_LOOPS == -1 +#define CALIB_OVERRIDE false +#if USING_CALIB_PIN && COMMUNICATION == COMM_SERIAL && PIN_CALIB == 0 && !CALIB_OVERRIDE + #error "You can't set your calibration pin to 0 over usb. You can calibrate with the BOOT button when using bluetooth only. Set CalibOverride to true to override this." +#endif + +Main::Main() { + // Constructor code here +} + +void Main::setup() { + pinMode(32, INPUT_PULLUP); + pinMode(DEBUG_LED, OUTPUT); + digitalWrite(DEBUG_LED, HIGH); + + #if COMMUNICATION == COMM_SERIAL + comm = new SerialCommunication(); + #elif COMMUNICATION == COMM_BTSERIAL + comm = new BTSerialCommunication(); + #else + #error "Communication not set." + #endif + + #if ENCODING == ENCODE_ALPHA + encoding = new AlphaEncoding(); + #elif ENCODING == ENCODE_LEGACY + encoding = new LegacyEncoding(); + #else + #error "Encoding not set." + #endif + + + comm->start(); + + input.setupInputs(); + + #if USING_FORCE_FEEDBACK + haptics.setupServoHaptics(); + #endif + + #if ESP32_DUAL_CORE_SET + xTaskCreatePinnedToCore( + Main::getInputsWrapper, /* Function to implement the task */ + "Get_Inputs", /* Name of the task */ + 10000, /* Stack size in words */ + this, /* Task input parameter */ + tskIDLE_PRIORITY, /* Priority of the task */ + &Task1, /* Task handle. */ + 0); /* Core where the task should run */ + #endif +} + +void Main::loop() { + + if (comm->isOpen()){ + + #if USING_CALIB_PIN + data.calib = input.getButton(PIN_CALIB) != INVERT_CALIB; + if (data.calib) + loops = 0; + #else + data.calib = false; + #endif + if (loops < CALIBRATION_LOOPS || ALWAYS_CALIBRATING){ + calibrate = true; + loops++; + } + else{ + calibrate = false; + } + + #if !ESP32_DUAL_CORE_SET + input.getFingerPositions(calibrate, data.calib, fingerPos); + #endif + data.joyClick = input.getButton(PIN_JOY_BTN) != INVERT_JOY; + + #if TRIGGER_GESTURE + data.triggerButton = gesture.triggerGesture(fingerPos); + #else + data.triggerButton = input.getButton(PIN_TRIG_BTN) != INVERT_TRIGGER; + #endif + + data.aButton = input.getButton(PIN_A_BTN) != INVERT_A; + data.bButton = input.getButton(PIN_B_BTN) != INVERT_B; + + #if GRAB_GESTURE + data.grab = gesture.grabGesture(fingerPos); + #else + data.grab = input.getButton(PIN_GRAB_BTN) != INVERT_GRAB; + #endif + + #if PINCH_GESTURE + data.pinch = gesture.pinchGesture(fingerPos); + #else + data.pinch = input.getButton(PIN_PNCH_BTN) != INVERT_PINCH; + #endif + + int fingerPosCopy[10]; + int mutexTimeDone; + data.menu = input.getButton(PIN_MENU_BTN) != INVERT_MENU; + { + #if ESP32_DUAL_CORE_SET + int mutexTime = micros(); + //const std::lock_guard lock(fingerPosMutex); + fingerPosLock->lock(); + mutexTimeDone = micros()-mutexTime; + #endif + //memcpy(fingerPosCopy, fingerPos, sizeof(fingerPos)); + for (int i = 0; i < 10; i++){ + fingerPosCopy[i] = fingerPos[i]; + } + #if ESP32_DUAL_CORE_SET + fingerPosLock->unlock(); + #endif + + } + + memcpy(data.fingers, fingerPosCopy, 5 * sizeof(int)); + data.joyX = input.getJoyX(); + data.joyY = input.getJoyY(); + + static char encodedString[100] = {0}; + encoding->encode(data, encodedString); + comm->output(encodedString); + #if USING_FORCE_FEEDBACK + static char received[100]; + if (comm->readData(received)){ + int hapticLimits[5]; + //This check is a temporary hack to fix an issue with haptics on v0.5 of the driver, will make it more snobby code later + if(String(received).length() >= 5) { + DecodedData recievedData = encoding->decodeData(received); + haptics.writeServoHaptics(recievedData.servoValues); + if (recievedData.fields.specialCommandReceived){ + Serial.println("Special command recieved!!!"); + if (recievedData.command == "ClearData") + input.clearFlags(); + else if (recievedData.command == "SaveInter") + input.saveIntermediate(); + else if (recievedData.command == "SaveTravel") + input.saveTravel(); + } + } + } + #endif + #if defined(ESP32) + vTaskDelay(LOOP_TIME); + #else + delay(LOOP_TIME); + #endif + } +} + + +#if ESP32_DUAL_CORE_SET +void Main::getInputs(){ + for(;;){ + { + fingerPosLock->lock(); + input.getFingerPositions(calibrate, data.calib, fingerPos); //Save finger positions in thread + + fingerPosLock->unlock(); + } + threadLoops++; + if (threadLoops%100 == 0){ + vTaskDelay(1); //keep watchdog fed + } + delayMicroseconds(1); + } +} + +void Main::getInputsWrapper(void* _this) { + // Cast the void* parameter back to a Main* and call getInputs on it + static_cast(_this)->getInputs(); +} + +#endif diff --git a/firmware/lucidgloves-firmware/src/Main.h b/firmware/lucidgloves-firmware/src/Main.h new file mode 100644 index 0000000..0fc2796 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Main.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include "Controller/Gesture.h" +#include "Controller/Haptics.h" +#include "../AdvancedConfig.h" +#include "Communication/ICommunication.h" +#include "Encoding/IEncoding.h" +#include "Util/ConfigUtils.h" +#include "Controller/InputManager.h" + + +class Main { +public: + Main(); + void setup(); + void loop(); + + #if ESP32_DUAL_CORE_SET + static void getInputsWrapper(void* _this); + #endif + +private: + + ICommunication* comm; + IEncoding* encoding; + Haptics haptics; + Gesture gesture; + InputManager input; + + OutboundData data = {}; + + bool calibrate = false; + int fingerPos[10] = {0,0,0,0,0,0,0,0,0,0}; + int loops = 0; + + #if ESP32_DUAL_CORE_SET + void getInputs(); + ordered_lock* fingerPosLock = new ordered_lock(); + TaskHandle_t Task1; + int threadLoops = 1; + #endif + +}; diff --git a/firmware/lucidgloves-firmware/ConfigUtils.h b/firmware/lucidgloves-firmware/src/Util/ConfigUtils.h similarity index 83% rename from firmware/lucidgloves-firmware/ConfigUtils.h rename to firmware/lucidgloves-firmware/src/Util/ConfigUtils.h index c1305e6..68e3342 100644 --- a/firmware/lucidgloves-firmware/ConfigUtils.h +++ b/firmware/lucidgloves-firmware/src/Util/ConfigUtils.h @@ -1,14 +1,11 @@ -//Contains the definitions that need to be evaluated before the main config file (lucidgloves-firmware.ino). +//Contains the definitions that need to be evaluated before the main config file (Config.h). //These shouldn't need to be changed. +#pragma once #include #include #include -int lockTime = 0; -int lockLoops = 1; -int lockTimeTotal = 0; -int lockTimeLast = 0; class ordered_lock { std::queue cvar; @@ -17,10 +14,6 @@ class ordered_lock { public: ordered_lock() : locked(false) {}; void lock() { - lockTime = micros() - lockTimeLast; - lockTimeLast = micros(); - lockTimeTotal += lockTime; - lockLoops++; std::unique_lock acquire(cvar_lock); if (locked) { std::condition_variable signal; diff --git a/firmware/lucidgloves-firmware/src/Util/DataStructs.h b/firmware/lucidgloves-firmware/src/Util/DataStructs.h new file mode 100644 index 0000000..473bec2 --- /dev/null +++ b/firmware/lucidgloves-firmware/src/Util/DataStructs.h @@ -0,0 +1,41 @@ +#pragma once +#include "../../AdvancedConfig.h" + +const char* const SPECIAL_COMMANDS[] = { + "SaveInter", + "SaveTravel", + "ClearData" + // Add more commands as needed +}; + +//NUM_SPECIAL_COMMANDS can be defined this way because all char* are pointers of the same size +const int NUM_SPECIAL_COMMANDS = sizeof(SPECIAL_COMMANDS) / sizeof(SPECIAL_COMMANDS[0]); + +struct ReceivedFields { + bool servoValuesReceived[NUM_FINGERS]; + bool specialCommandReceived; +}; + +struct DecodedData { + ReceivedFields fields; + int servoValues[NUM_FINGERS]; + const char* command; +}; + +struct OutboundData { + int fingers[NUM_FINGERS]; + int joyX; + int joyY; + bool joyClick; + bool triggerButton; + bool aButton; + bool bButton; + bool grab; + bool pinch; + bool calib; + bool menu; + + #if USING_SPLAY + int splay[NUM_FINGERS]; + #endif +}; \ No newline at end of file