Skip to content

Commit

Permalink
Add files via upload
Browse files Browse the repository at this point in the history
  • Loading branch information
ChibiDemon authored Dec 16, 2023
1 parent 771a6b1 commit 0e37324
Show file tree
Hide file tree
Showing 12 changed files with 613 additions and 2 deletions.
50 changes: 50 additions & 0 deletions AdvancedConfig.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
//Advanced settings, only for the pros XD

#define LOOP_TIME 4 //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.

//Comm defines, no touchy
#define COMM_SERIAL 0
#define COMM_BTSERIAL 1

//Encoding
#define ENCODING 1
#define ENCODE_LEGACY 0
#define ENCODE_ALPHA 1

//Finger indeces (not used for legacy)
#define PINKY_IND 4
#define RING_IND 3
#define MIDDLE_IND 2
#define INDEX_IND 1
#define THUMB_IND 0

//Automatically set ANALOG_MAX depending on the microcontroller
#if defined(__AVR__)
#define ANALOG_MAX 1023
#elif defined(ESP32)
#define ANALOG_MAX 4095
#endif


//ANALOG_MAX OVERRIDE:
//uncomment and set as needed (only touch if you know what you are doing)
//#define ANALOG_MAX 4095

#ifndef ANALOG_MAX
#error "This board doesn't have an auto ANALOG_MAX assignment, please set it manually by uncommenting ANALOG_MAX OVERRIDE!"
#endif

//Filtering and clamping analog inputs
#define CLAMP_ANALOG_MAP true //clamp the mapped analog values from 0 to ANALOG_MAX

// Enable and set min and max to match your sensor's expected raw value range
// This discards any spurious values outside of the useful range
#define CLAMP_FLEXION false //clamp the raw flexion values
#define CLAMP_MIN 0 //the minimum value from the flexion sensors
#define CLAMP_MAX ANALOG_MAX //the maximum value from the flexion sensors

// You must install RunningMedian library to use this feature
// https://www.arduino.cc/reference/en/libraries/runningmedian/
#define ENABLE_MEDIAN_FILTER false //use the median of the previous values, helps reduce noise
#define MEDIAN_SAMPLES 20
74 changes: 74 additions & 0 deletions Encoding.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
/*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;
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":""
);
return stringToEncode;
}

//legacy decoding
void decodeData(char* stringToDecode, int* hapticLimits){
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
//Serial.println("Haptic: "+ (String)hapticLimits[0] + " " + (String)hapticLimits[1] + " " + (String)hapticLimits[2] + " " + (String)hapticLimits[3] + " " + (String)hapticLimits[4] + " ");
}

int getArgument(char* stringToDecode, char command){
char* start = strchr(stringToDecode, command);
if (start == NULL)
return -1;
else
return atoi(start + 1);
}

#endif
13 changes: 13 additions & 0 deletions ICommunication.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
//Interface for communication

class ICommunication {

public:
virtual bool isOpen() = 0;

virtual void start() = 0;

virtual void output(char* data) = 0;

virtual bool readData(char* input) = 0;
};
21 changes: 21 additions & 0 deletions LICENSE
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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.
6 changes: 4 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,2 +1,4 @@
# VRHapticGlove
Critical Making's take on the Lucid gloves open source project
# lucidgloves-firmware


Check the [Wiki](https://github.com/LucidVR/lucidgloves/wiki/Firmware-Setup-and-Customization-Tutorial/) for instructions on how to set it up.
37 changes: 37 additions & 0 deletions SerialBTCommunication.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
//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(){
Serial.begin(115200);
m_SerialBT.begin(BTSERIAL_DEVICE_NAME);
Serial.println("The device started, now you can pair it with bluetooth!");
m_isOpen = true;
}

void output(char* data){
m_SerialBT.print(data);
}

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
30 changes: 30 additions & 0 deletions SerialCommunication.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
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;
}
};
83 changes: 83 additions & 0 deletions _main.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#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

ICommunication* comm;
int loops = 0;
void setup() {
#if COMMUNICATION == COMM_SERIAL
comm = new SerialCommunication();
#elif COMMUNICATION == COMM_BTSERIAL
comm = new BTSerialCommunication();
#endif
comm->start();

setupInputs();

#if USING_FORCE_FEEDBACK
setupServoHaptics();
#endif

}

void loop() {
if (comm->isOpen()){
#if USING_CALIB_PIN
bool calibButton = getButton(PIN_CALIB) != INVERT_CALIB;
if (calibButton)
loops = 0;
#else
bool calibButton = false;
#endif

bool calibrate = false;
if (loops < CALIBRATION_LOOPS || ALWAYS_CALIBRATING){
calibrate = true;
loops++;
}

int* fingerPos = getFingerPositions(calibrate, calibButton);
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

bool menuButton = getButton(PIN_MENU_BTN) != INVERT_MENU;

comm->output(encode(fingerPos, 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() >= 10) {
decodeData(received, hapticLimits);
writeServoHaptics(hapticLimits);
}
}
#endif
delay(LOOP_TIME);
}
}
11 changes: 11 additions & 0 deletions gesture.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
bool 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){
return (flexion[INDEX_IND] + flexion[THUMB_IND]) / 2 <= ANALOG_MAX/2 ? 0:1;
}

bool triggerGesture(int *flexion){
return flexion[INDEX_IND]<=(ANALOG_MAX/2)?0:1;
}
54 changes: 54 additions & 0 deletions haptics.ino
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
#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++){
scaledLimits[i] = 180.0f - hapticLimits[i] / 1000.0f * 180.0f;
}
}

//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
Loading

0 comments on commit 0e37324

Please sign in to comment.