From aba1d476ae27639fa16051af379df5fe21330d84 Mon Sep 17 00:00:00 2001 From: AndunHH <76771247+AndunHH@users.noreply.github.com> Date: Thu, 27 Jun 2024 21:11:46 +0200 Subject: [PATCH] Implemented an encoder wheel for e.g. zooming (#31) Implemented an encoder wheel for e.g. zooming based on the idea by https://github.com/JoseLuisGZA/ErgonoMouse. Use ROTARY_AXIS to choose which axis is replaced by the encoder. When ROTARY_AXIS == 0, the encoder library is not needed in ARDUINO IDE. PlatformIO is loading it via platformio.ini --- README.md | 13 +++-- fritzing/spacemouse.fzz | Bin 45549 -> 59820 bytes platformio.ini | 14 ++++- spacemouse-keys/config_sample.h | 38 +++++++++++++- spacemouse-keys/encoderWheel.cpp | 77 ++++++++++++++++++++++++++++ spacemouse-keys/encoderWheel.h | 4 ++ spacemouse-keys/spacemouse-keys.ino | 19 ++++++- 7 files changed, 152 insertions(+), 13 deletions(-) create mode 100644 spacemouse-keys/encoderWheel.cpp create mode 100644 spacemouse-keys/encoderWheel.h diff --git a/README.md b/README.md index cf32e55..2b62b97 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,7 @@ This repository for the source code is based on the work by [TeachingTech](https - [x] Semi-Automatic calibration methods - [x] Support for keys, that can trigger functions on your pc - [x] Support for "kill-keys", that disable translation or rotation directly in the mouse - -Coming up: -- [ ] Support for wheel to zoom (merging from [JoseLuizGZA](https://github.com/JoseLuisGZA/ErgonoMouse/)) +- [x] Support for an encoder wheel to zoom Wanted, with unclear solution ... ? - [ ] Reverse Direction and Speed options in 3dConnexion Software is not working, because our spacemouse is not accepting this settings. @@ -211,10 +209,11 @@ This code is the combination of multiple works by others: The basis is fdmakara's four joystick movement logic, with jfedor/BennyBWalker's HID SpaceMouse emulation. The four joystick logic sketch was setup for the joystick library instead of HID, so elements of this were omitted where not needed. The outputs were jumbled no matter how Teaching Tech plugged them in, so Teaching Tech spent a lot of time adding debugging code to track exactly what was happening. On top of this, Teching Tech has added more control of speed/direction and comments/links to informative resources to try and explain what is happening in each phase. 8. Code to include meassured min and max values for each Joystick by Daniel_1284580 (In Software Version V1 and newer) 9. Improved code to make it more userfriendly by Daniel_1284580 (In Software Version V2 and newer) -10. Improved Code, improved comments and added written tutorials in comments, by [LivingTheDream](https://www.printables.com/de/model/883967-tt-spacemouse-v2-lid-with-mounting-for-4-mx-switch/): Implemented new algorithm "modifier function" for better motioncontrol by Daniel_1284580 (In Software Version V3) -11. Moved the Deadzone detection into the inital ADC conversion and calculate every value everytime and use the modifier for better seperation between the access, by Andun_HH. -12. Added two additional buttons integrated into the knob to kill either translation or rotation at will and prevent unintended movements, by JoseLuisGZA. -13. Coming up: Added Encoder to use with a wheel on top of the main knob an simulate pulls on any of the axis (main use is simulating zoom like the mouse wheel), by [JoseLuizGZA](https://github.com/JoseLuisGZA/ErgonoMouse/). +10. Improved Code, improved comments and added written tutorials in comments, by [LivingTheDream](https://www.printables.com/de/model/883967-tt-spacemouse-v2-lid-with-mounting-for-4-mx-switch/files) Implemented new algorithm "modifier function" for better motioncontrol by Daniel_1284580 (In Software Version V3) +11. Moved the Deadzone detection into the inital ADC conversion and calculate every value everytime and use the modifier for better seperation between the access, By Andun_HH. +12. Added two additional buttons integrated into the knob to kill either translation or rotation at will and prevent unintended movements, by JoseLuisGZA and AndunHH. +13. Added Encoder to use with a wheel on top of the main knob an simulate pulls on any of the axis (main use is simulating zoom like the mouse wheel), by [JoseLuizGZA](https://github.com/JoseLuisGZA/ErgonoMouse/) and rewritten by AndunHH. + # License Because TeachingTech published his source code on Printables under this license, it also applies here: diff --git a/fritzing/spacemouse.fzz b/fritzing/spacemouse.fzz index e06e4b05db05ec68990037795919e07cea56db79..17e35b90eac647148600fc34541ba6c7b5897325 100644 GIT binary patch delta 25866 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 delta 11484 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 diff --git a/platformio.ini b/platformio.ini index cb0825f..2c03ce1 100644 --- a/platformio.ini +++ b/platformio.ini @@ -1,12 +1,22 @@ +; PlatformIO Project Configuration File +; +; Build options: build flags, source filter +; Upload options: custom upload port, speed and extra flags +; Library options: dependencies, extra library storages +; Advanced options: extra scripting +; +; Please visit documentation for the other options and examples +; https://docs.platformio.org/page/projectconf.html + [env:micro] platform = atmelavr board = micro framework = arduino extra_scripts = pre:set_hwids.py -# send on enter with a cr lf typo and show this output in the console: -monitor_filters = send_on_enter +monitor_filters = send_on_enter monitor_eol = CRLF monitor_echo = yes +lib_deps = paulstoffregen/Encoder@^1.4.4 [platformio] src_dir = spacemouse-keys diff --git a/spacemouse-keys/config_sample.h b/spacemouse-keys/config_sample.h index 146243d..faac966 100644 --- a/spacemouse-keys/config_sample.h +++ b/spacemouse-keys/config_sample.h @@ -45,6 +45,7 @@ // 6: Report velocity and keys after possible kill-key feature // 7: Report the frequency of the loop() -> how often is the loop() called in one second? // 8: Report the bits and bytes send as button codes +// 9: Report details about the encoder wheel, if ROTARY_AXIS > 0 #define STARTDEBUG 0 // Second calibration: Tune Deadzone @@ -152,7 +153,7 @@ // Keys Support // See below for examples // How many keys are there in total? -#define NUMKEYS 4 +#define NUMKEYS 0 // Define the pins for the keys on the arduino // KEYLIST must be empty "{ }" if NUMKEYS = 0, i.e. no key support // The first pins from KEYLIST may be reported via HID @@ -160,7 +161,7 @@ { 15, 14, 16, 10 } // How many keys reported? -#define NUMHIDKEYS 4 +#define NUMHIDKEYS 0 // In order to define which key is assigned to which button, the following list must be entered in the BUTTONLIST below @@ -238,3 +239,36 @@ #endif #define DEBOUNCE_KEYS_MS 200 // time in ms which is needed to allow a new button press + +// ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------- +// ENCODER WHEEL +// Needs the encoder library by Paul Stoffregen. +// There may be an additional encoder wheel to replace one of the velocities +#define ROTARY_AXIS 0 + +// Axis to replace with encoder +// 0. None -> disable this feature completely +// 1. transX +// 2. transY (zoom in "Forward / Backward" Zoom Direction configuration) +// 3. transZ (simulates zoom in "Up / Down" Zoom Direction configuration) +// 4. rotX +// 5. rotY +// 6. rotZ +// (Those are the positions in the velocity array +1, as defined in calibration.h) + +// Define the encoder pins +#define ENCODER_CLK 2 +#define ENCODER_DT 3 +// swap those two pins to change direction of encoder + +// To calculate a velocity from the encoder position, the output is faded over so many loop() iterations, as defined in #ECHOES +// Small number = short duration of zooming <-> Big Number = longer duration of zooming +// Compare this number with the update frequency of the script, reported by debug=7: +// If ECHOES = frequency: the zoom is faded for 1 second. +#define ECHOES 200 + +// Strength of the simulated pull +// Recommended range: 0 - 350 +// Reason for max=350: The HID Interface reports logical max as +350, see hidInterface.h +// Recommended strength = 200 +#define SIMSTRENGTH 200 diff --git a/spacemouse-keys/encoderWheel.cpp b/spacemouse-keys/encoderWheel.cpp new file mode 100644 index 0000000..fd6b6e7 --- /dev/null +++ b/spacemouse-keys/encoderWheel.cpp @@ -0,0 +1,77 @@ +/* + * This code gives the space mouse the ability to include an encoder wheel. + * The encoder gives positions but the space mouse reports velocities. + * Therefore, we calculate a filtered derivative from the encoder position and + * replace the desired velocity from the original space mouse. + * + * Based on the idea by JoseLuisGZA, rewritten by Andun HH + */ + +#include +#include "config.h" +#if ROTARY_AXIS > 0 +#include "encoderWheel.h" + +// Include Encoder library by Paul Stoffregen +#include + +Encoder myEncoder(ENCODER_CLK, ENCODER_DT); + +int32_t previousEncoderValue = 0; +int32_t newEncoderValue; // Store encoder readings +int32_t delta = 0; // Tracks encoder increments when turned + +int zoomIterator = ECHOES; // Counter for echoing the delta through a number of loops for a smoother zoom animation +float simpull; // calculated velocity of the encoder wheel + +void initEncoderWheel() +{ + // Read initial value from encoder + newEncoderValue = myEncoder.read(); +} + +/// @brief Calculate the encoder wheel and update the result in the velocity array +/// @param velocity Array with the velocity, which gets updated at position ROTARY_AXIS-1 +/// @param debug Generate a debug output if debug=9 +void calcEncoderWheel(int16_t *velocity, int debug) +{ + static int factor = 100; // + // read encoder + newEncoderValue = myEncoder.read(); + if (newEncoderValue != previousEncoderValue) + { + // position changed, how much? + delta = newEncoderValue - previousEncoderValue; + previousEncoderValue = newEncoderValue; + zoomIterator = 0; + } + + // Distribute encoder delta through the echoes in the loop and based on simulated axis chosen by the user + // Faded intensity for echoing the encoder reading. + if (zoomIterator < ECHOES) + { + factor = 100 - ((zoomIterator * 100) / ECHOES); // factor shall be percent: between 0 and 100 + simpull = (factor * SIMSTRENGTH) / 100 * delta; + zoomIterator++; // iterate + // add the velocity of the encoder wheel to one of the 6 axis + // the ROTARY_AXIS definition is one above the array definition used for the velocity array (see calibration.h) + // Therefore ROTARY_AXIS-1 is used to change the velocity value + velocity[ROTARY_AXIS - 1] = velocity[ROTARY_AXIS - 1] + simpull; + } + else + { + // fading has ended + simpull = 0; + } + if (debug == 9) + { + // create debug output + Serial.print("Enc Val: "); + Serial.print(newEncoderValue); + Serial.print(", factor: "); + Serial.print(factor); + Serial.print(", simpull: "); + Serial.println(simpull); + } +} +#endif // whole file is only implemented #if ROTARY_AXIS > 0 \ No newline at end of file diff --git a/spacemouse-keys/encoderWheel.h b/spacemouse-keys/encoderWheel.h new file mode 100644 index 0000000..66ad0e1 --- /dev/null +++ b/spacemouse-keys/encoderWheel.h @@ -0,0 +1,4 @@ +// Header file for the encoderWheel.cpp + +void initEncoderWheel(); +void calcEncoderWheel(int16_t* velocity, int debug); \ No newline at end of file diff --git a/spacemouse-keys/spacemouse-keys.ino b/spacemouse-keys/spacemouse-keys.ino index ac291d1..af1bda4 100644 --- a/spacemouse-keys/spacemouse-keys.ino +++ b/spacemouse-keys/spacemouse-keys.ino @@ -24,6 +24,11 @@ // header for HID emulation of the spacemouse #include "hidInterface.h" +#if ROTARY_AXIS > 0 + // if an encoder wheel is used + #include "encoderWheel.h" +#endif + // the debug mode can be set during runtime via the serial interface int debug = STARTDEBUG; @@ -92,6 +97,10 @@ void setup() { // Read idle/centre positions for joysticks. readAllFromJoystick(centerPoints); delay(100); + +#if ROTARY_AXIS > 0 + initEncoderWheel(); +#endif } int rawReads[8], centered[8]; @@ -105,6 +114,7 @@ int maxVals[8] = MAXVALS; int tmpInput; // store the value, the user might input over the serial + void loop() { //check if the user entered a debug mode via serial interface if (Serial.available()) { @@ -202,6 +212,11 @@ void loop() { velocity[ROTZ] = 0; } +#if ROTARY_AXIS > 0 + // If an encoder wheel is used, calculate the velocity of the wheel and replace one of the former calculated velocities + calcEncoderWheel(velocity, debug); +#endif + // Invert directions if needed #if INVX > 0 velocity[TRANSX] = velocity[TRANSX] * -1; @@ -236,7 +251,8 @@ void loop() { // if the kill-key feature is enabled, rotations or translations are killed=set to zero #if (NUMKILLKEYS == 2) - if (keyVals[KILLROT] == LOW) { // check for the raw keyVal and not keyOut, because keyOut is only 1 for a single iteration. keyVals has inverse Logic due to pull-ups + if (keyVals[KILLROT] == LOW) { + // check for the raw keyVal and not keyOut, because keyOut is only 1 for a single iteration. keyVals has inverse Logic due to pull-ups // kill rotation velocity[ROTX] = 0; velocity[ROTY] = 0; @@ -264,7 +280,6 @@ void loop() { // Daniel_1284580 noticed the 3dconnexion tutorial was not working the right way so they got changed send_command(velocity[ROTX], velocity[ROTY], velocity[ROTZ], velocity[TRANSX], velocity[TRANSY], velocity[TRANSZ], keyOut, debug); #endif - if (debug == 7) { // update and report the at what frequency the loop is running