diff --git a/README.md b/README.md index 5286681..799087d 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,5 @@ # Open Source Spacemouse with Keys -Repository for the space mouse based on four joysticks with an addition for keys (not jet in the picture) +Repository for the space mouse based on four joysticks with an addition for keys and an encoder wheel. ![overview](pictures/overview.jpg) @@ -11,8 +11,8 @@ This repository for the source code is based on the work by [TeachingTech](https - [x] Support for "kill-keys", that disable translation or rotation directly in the mouse - [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. +Wanted features: +- [ ] Reverse Direction and Speed options in 3dConnexion Software is not working, because our spacemouse is not accepting this settings. Purchasing the [electronics](#electronics) and [printing some parts](#printed-parts) is not scope of this repository. We start with the software. @@ -104,6 +104,35 @@ spacemouse.bootloader.file=caterina/Caterina-promicro16.hex - Teaching Tech followed the instructions here from [nebhead](https://gist.github.com/nebhead/c92da8f1a8b476f7c36c032a0ac2592a) with two key differences: - Changed the word 'DaemonBite' to 'Spacemouse' in all references. - Changed the VID and PID values as per jfedor's instructions: vid=0x256f, pid=0xc631 (SpaceMouse Pro Wireless (cabled)) +#### "pins_arduino.h" not found +[Taken from](https://github.com/AndunHH/spacemouse/issues/19#issue-2355907540) +> Windows 11, 2.3.2 Arduino IDE, AVR 1.8.6 +> +>I followed every setup step, but was getting complication error "pins_arduino.h" not found. +> +>Checked the file - indeed, not existing, even though it is imported in core +> +>Its the first time I interact with Arduino and I'm not sure if this was suppose to be auto copied or magic imported, but it didn't +> +> Copying the config from variants\leonardo (in my case, I used the board TeachingTech recommended) to version root (...avr\1.8.6) solved it + +#### Changes for Arduino 2.3.2 +Check this [issue](https://github.com/AndunHH/spacemouse/issues/19#issuecomment-2184967522). +With Arduino IDE 2.3.2 and Arduino AVR board package 1.8.6 , a few changes in text pasted to boards.txt were needed + +``` +#spacemouse.build.variant=promicro +spacemouse.build.variant=leonardo + +#spacemouse.bootloader.file=caterina/Caterina-promicro16.hex +spacemouse.bootloader.file=caterina/Caterina-Leonardo.hex +``` +Also SpaceMouse needs to be added to list of boards supported by Arduino AVR platform in package_index.json, so that you can select it in IDE. +``` + { + "name": "SpaceMouse" + }, +``` ## Cloning the github repo Clone the github repo to your computer: Scroll-Up to the green "<> Code" Button and select, if you wish to clone or just download the code. diff --git a/fritzing/spacemouse.fzz b/fritzing/spacemouse.fzz index 17e35b9..0ebfa76 100644 Binary files a/fritzing/spacemouse.fzz and b/fritzing/spacemouse.fzz differ diff --git a/pictures/fritzing-electronics.png b/pictures/fritzing-electronics.png index 6cf7ec8..fc80a18 100644 Binary files a/pictures/fritzing-electronics.png and b/pictures/fritzing-electronics.png differ diff --git a/spacemouse-keys/calibration.cpp b/spacemouse-keys/calibration.cpp index 3c45411..fefc0ca 100644 --- a/spacemouse-keys/calibration.cpp +++ b/spacemouse-keys/calibration.cpp @@ -179,7 +179,131 @@ void updateFrequencyReport() { Serial.print("Frequency: "); Serial.print(iterationsPerSecond); Serial.println(" Hz"); - lastFrequencyUpdate = millis(); // reset timer - iterationsPerSecond = 0; // reset iteration counter + lastFrequencyUpdate = millis(); // reset timer + iterationsPerSecond = 0; // reset iteration counter + } +} + +// Calibrate (=zero) the space mouse +// numIterations: How many readings are taken to calculate the mean. Suggestion: 500 iterations, they take approx. 480ms. +// With debugFlag = true, a suggestion for the dead zone is given on the serial interface to save to the config.h +// It's blocking other functions in the meantime +// returns true, if no warnings occured. +bool busyZeroing(int *centerPoints, uint16_t numIterations, boolean debugFlag) +{ + bool noWarningsOccured = true; + if (debugFlag == true) + { + Serial.println(F("Zeroing Joysticks...")); + } + int act[8]; // actual value + uint32_t mean[8] = {0, 0, 0, 0, 0, 0, 0, 0}; // Array to count all values during the averaging + int minValue[8]; // Array to store the minimum values + int maxValue[8]; // Array to store the maximum values + for (int i = 0; i < 8; i++) + { + minValue[i] = 1023; // Set the min value to the maximum possible value + maxValue[i] = 0; // Set the max value to the minimum possible value + } + + // measure duration + unsigned int long start, end; + start = millis(); + + uint16_t count; + + for (count = 0; count < numIterations; count++) + { + readAllFromJoystick(act); + for (uint8_t i = 0; i < 8; i++) + { + // Add to mean + mean[i] = mean[i] + act[i]; + // Update the minimum and maximum values for dead zone evaluation + if (act[i] < minValue[i]) + { + minValue[i] = act[i]; + } + if (act[i] > maxValue[i]) + { + maxValue[i] = act[i]; + } + } + } + + int16_t deadZone[8]; + int16_t maxDeadZone = 0; + // calculating average by dividing the mean by the number of iterations + for (uint8_t i = 0; i < 8; i++) + { + centerPoints[i] = mean[i] / count; + deadZone[i] = maxValue[i] - minValue[i]; + if (deadZone[i] > maxDeadZone) + { + // get maximum deadzone independet of axis + maxDeadZone = deadZone[i]; + } + + // a dead zone above the following value will be warned + #define DEADZONEWARNING 10 + // a centerpoint below or above those values will be warned (512 +/- 128) + #define CENTERPOINTWARNINGMIN 384 + #define CENTERPOINTWARNINGMAX 640 + + if (deadZone[i] > DEADZONEWARNING || centerPoints[i] < CENTERPOINTWARNINGMIN || centerPoints[i] > CENTERPOINTWARNINGMAX) + { + noWarningsOccured = false; + } + } + + // report everything, if with debugFlag + if (debugFlag) + { + Serial.println(F("## Min- Mean - Max -> Dead Zone")); + for (int i = 0; i < 8; i++) + { + Serial.print(axisNames[i]); + Serial.print(" "); + Serial.print(minValue[i]); + Serial.print(" - "); + Serial.print(centerPoints[i]); + Serial.print(" - "); + Serial.print(maxValue[i]); + Serial.print(" -> "); + Serial.print(deadZone[i]); + Serial.print(" "); + if (deadZone[i] > DEADZONEWARNING) + { + Serial.print(F(" Attention! Moved axis?")); + } + if (centerPoints[i] < CENTERPOINTWARNINGMIN || centerPoints[i] > CENTERPOINTWARNINGMAX) + { + Serial.print(F(" Attention! Axis in idle?")); + } + Serial.println(""); + } + end = millis(); + Serial.println(F("Using mean as zero position...")); + Serial.print(F("Suggestion for config.h: ")); + Serial.print(F("#define DEADZONE ")); + Serial.println(maxDeadZone); + Serial.print(F("This took ")); + Serial.print(end - start); + Serial.print(F(" ms for ")); + Serial.print(count); + Serial.println(F(" iterations.")); + } + return noWarningsOccured; +} + +// define an array for reading the analog pins of the joysticks +int pinList[8] = PINLIST; + +// Function to read and store analogue voltages for each joystick axis. +void readAllFromJoystick(int *rawReads) +{ + for (int i = 0; i < 8; i++) + { + rawReads[i] = analogRead(pinList[i]); } } \ No newline at end of file diff --git a/spacemouse-keys/calibration.h b/spacemouse-keys/calibration.h index 6cd5a8a..a486311 100644 --- a/spacemouse-keys/calibration.h +++ b/spacemouse-keys/calibration.h @@ -34,3 +34,9 @@ bool isDebugOutputDue(); // update and report the function to learn at what frequency the loop is running void updateFrequencyReport(); + +// Calibrate (=zero) the space mouse +bool busyZeroing(int *centerPoints, uint16_t numIterations, boolean debugFlag); + +// Function to read and store analogue voltages for each joystick axis. +void readAllFromJoystick(int *rawReads); \ No newline at end of file diff --git a/spacemouse-keys/config_sample.h b/spacemouse-keys/config_sample.h index aad666f..87d600e 100644 --- a/spacemouse-keys/config_sample.h +++ b/spacemouse-keys/config_sample.h @@ -1,12 +1,12 @@ // The user specific settings, like pin mappings or special configuration variables and sensitivities are stored in config.h. -// Please adjust your settings and save it as config.h +// Please adjust your settings and save it as --> config.h <-- ! // Calibration instructions // Follow this file from top to bottom to calibrate your space mouse // Default Assembly when looking from above on top of the space mouse // -// back +// back resulting axis // C Y+ // | . // B--+--D X-...Z+...X+ @@ -35,8 +35,9 @@ // Debugging (You can send the number over the serial interface, whenever you whish) // -1: Debugging off. Set to this once everything is working. -// 0: Debugging level doesn't change +// 0: Nothing... // 1: Output raw joystick values. 0-1023 raw ADC 10-bit values +// 11: Calibrate / Zero the spacemouse and get a dead-zone suggestion (This is also done on every startup in the setup()) // 2: Output centered joystick values. Values should be approx -500 to +500, jitter around 0 at idle. // 20: semi-automatic min-max calibration // 3: Output centered joystick values. Filtered for deadzone. Approx -350 to +350, locked to zero at idle, modified with a function. @@ -46,22 +47,24 @@ // 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 + +#define STARTDEBUG 0 // Can also be set over the serial interface, while the programm is running! // Second calibration: Tune Deadzone // Deadzone to filter out unintended movements. Increase if the mouse has small movements when it should be idle or the mouse is too senstive to subtle movements. -// Set debug = 2. Don't touch the mouse but observe the values. They should be nearly to zero. Every value around zero which is noise or should be neglected afterwards is in the following deadzone. -#define DEADZONE 3 // Recommended to have this as small as possible for V2 to allow smaller knob range of motion. +// Semi-automatic: Set debug = 11. Don't touch the mouse and observe the automatic output. +// Manual: Set debug = 2. Don't touch the mouse but observe the values. They should be nearly to zero. Every value around zero which is noise or should be neglected afterwards is in the following deadzone. +#define DEADZONE 3 // Recommended to have this as small as possible to allow full range of motion. -// Third calibration: getting min and max values +// Third calibration: Getting MIN and MAX values // Can be done manual (debug = 2) or semi-automatic (debug = 20) // Semi-automatic (debug=20) // 1. Set debug = 20 // 2. Compile the sketch, upload it and wait for confirmation in the serial console. // 3. Move the spacemouse around for 15s to get a min and max value. // 4. Verify, that the minimums are around -400 to -520 and the maxVals around +400 to +520. -// (repeat or check again, if you have small values!) -// 5. Copy the output from the console into your config.h (below the manual instructions) +// (repeat or check again, if you have too small values!) +// 5. Copy the output from the console into your config.h // Manual min/max calibration (debug = 2) // Recommended calibration procedure for min/max adc levels @@ -94,22 +97,21 @@ // Fourth calibration: Sensitivity // Independent sensitivity multiplier for each axis movement. Use degbug mode 4 or use for example your cad program to verify changes. -// eg use lower value like 0.5 to make axis more sensitive, use higher value like 5 to make it less sensitive -// The Values you can change (those Values worked for me, you can or should change them to your preferences): +// E.g. use lower value like 0.5 to make axis more sensitive, use higher value like 5 to make it less sensitive // Recommended calibration procedure for sensitivity -// 1. Make sure modFunc is on level 0!! Change debug to level 4 and upload sketch. Then open Serial Monitor. You will see Values TX, TY, TZ, RX, RY, RZ +// 1. Make sure modFunc is on level 0, see below. Upload the sketch. Then open serial monitor, type 4 and hit enter. You will see Values TX, TY, TZ, RX, RY, RZ // 2. Start moving your spacemouse. You will notice values changing. -// 3. Starting with TX try increasing this value as much as possible by moving your spacemouse around. If you get around 350 thats great. If not change pos_transX_sensitivy and reupload sketch. Repeat until it is around 350 for maximum motion. +// 3. Starting with TX try increasing this value as much as possible by moving your spacemouse around. If you get around 350 thats great. If not change TRANSX_SENSITIVITY and reupload sketch. Repeat until it is around 350 for maximum motion. // 4. Repeat steps 3 for TY,TZ,RX,RY,RZ // 5. Verification: Move the Jockstick in funny ways. All you should get for eather TX,TX,TZ,RX,RY,RZ should be aprox. between -350 to 350. // 6. You have finished sensitivity calibration. You can now test your spacemouse with your favorite program (e.g. Cad software, Slicer) -// 7. Aftermath: You notice the movements are hard to control. Try using Modification Functions (have a look at the beginning of the sketch) [I like level 3 the most. Experiment to find your favorite function] +// 7. Aftermath: You notice the movements are hard to control. Try using Modification Functions [I like level 3 the most. Experiment to find your favorite function] #define TRANSX_SENSITIVITY 2 #define TRANSY_SENSITIVITY 2 #define POS_TRANSZ_SENSITIVITY 0.5 -#define NEG_TRANSZ_SENSITIVITY 5 //I want low sensitiviy for down, therefore a high value. +#define NEG_TRANSZ_SENSITIVITY 5 // I want low sensitiviy for down, therefore a high value. #define GATE_NEG_TRANSZ 15 // gate value, which negative z movements will be ignored (like an additional deadzone for -z). #define GATE_ROTX 15 // Value under which rotX values will be forced to zero #define GATE_ROTY 15 // Value under which roty values will be forced to zero @@ -119,7 +121,7 @@ #define ROTY_SENSITIVITY 1.5 #define ROTZ_SENSITIVITY 2 -// Modifier Function +// Fifth calibration: Modifier Function // Modify resulting behaviour of spacemouse outputs the suppres small movements around zero and enforce big movements even more. // (This function is applied on the resulting velocities and not on the direct input from the joysticks) // This should be at level 0 when starting the calibration! @@ -132,12 +134,13 @@ #define MODFUNC 0 // ------------------------------------------------------------------------------------ -// Direction +// Sixth Calibration: Direction // Modify the direction of translation/rotation depending on preference. // This should be done, when you are done with the pin assignment. // The default 0 is here for x, y and z orientation as to the picture in the readem // The suggestion in the comments is to accomodate the 3dConnexion Trainer "3Dc" -// Switch between true/false as desired. +// Switch between 0 or 1 as desired + #define INVX 0 // pan left/right // 3Dc: 0 #define INVY 0 // pan up/down // 3Dc: 1 #define INVZ 0 // zoom in/out // 3Dc: 1 @@ -146,16 +149,14 @@ #define INVRZ 0 // Rotate around Z axis (twist left/right) // 3Dc: 1 //Switch Zooming with Up/Down Movement -//DISCLAIMER: This will make your spacemouse work like in the original code from TeachingTech, but if you try the 3DConnexion tutorial in the Spacemouse Home Software you will notice it won't work. -#define SWITCHYZ 0 //change to true for switching movement +#define SWITCHYZ 0 // change to 1 to switch Y and Z axis // ------------------------------------------------------------------------------------ // Keys Support // See below for examples -// How many keys are there in total? +// How many keys are there in total? (0=no keys, feature disabled) #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 #define KEYLIST \ { 15, 14, 16, 10 } @@ -182,12 +183,13 @@ #define SM_ROT 26 // Key "Rotate" // BUTTONLIST must have the as many elemets as NUMHIDKEYS -// That means: BUTTONLIST must be empty "{ }" if NUMHIDKEYS = 0 // The keys from KEYLIST are assigned to buttons here: #define BUTTONLIST { SM_FIT, SM_T, SM_R, SM_RCW } + // ------------------------------------------------------------------------------------ -// Kill-Key Feature: Are there buttons to set the translation or rotation to zero? +// Kill-Key Feature +// Are there buttons to set the translation or rotation to zero? // How many kill keys are there? (disabled: 0; enabled: 2) #define NUMKILLKEYS 0 // usually you take the last two buttons from KEYLIST as kill-keys @@ -206,7 +208,6 @@ * KILLROT and KILLTRANS don't matter... KILLROT 0 and KILLTRANS 0 */ - /* Example for three usual buttons and no kill-keys * There are three keys in total: NUMKEYS 3 * The keys which shall be reported to the pc are connected to pin 15, 14 and 16 diff --git a/spacemouse-keys/spacemouse-keys.ino b/spacemouse-keys/spacemouse-keys.ino index 4fb15c6..ecab145 100644 --- a/spacemouse-keys/spacemouse-keys.ino +++ b/spacemouse-keys/spacemouse-keys.ino @@ -72,16 +72,6 @@ int modifierFunction(int x) { return (int)round(result); } -// define an array for reading the analog pins of the joysticks -int pinList[8] = PINLIST; - -// Function to read and store analogue voltages for each joystick axis. -void readAllFromJoystick(int *rawReads) { - for (int i = 0; i < 8; i++) { - rawReads[i] = analogRead(pinList[i]); - } -} - void setup() { // setup the keys e.g. to internal pull-ups #if NUMKEYS > 0 @@ -96,8 +86,10 @@ void setup() { delay(100); Serial.setTimeout(2); // the serial interface will look for new debug values and it will only wait 2ms // Read idle/centre positions for joysticks. - readAllFromJoystick(centerPoints); - delay(100); + + // zero the joystick position 500 times (takes approx. 480 ms) + // during setup() we are not interested in the debug output: debugFlag = false + busyZeroing(centerPoints, 500, false); #if ROTARY_AXIS > 0 initEncoderWheel(); @@ -115,7 +107,6 @@ 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()) { @@ -140,6 +131,14 @@ void loop() { debugOutput1(rawReads, keyVals); } + if (debug == 11) + { + // calibrate the joystick + // As this is called in the debug=11, we do more iterations. + busyZeroing(centerPoints, 2000, true); + debug = -1; // this only done once + } + // Subtract centre position from measured position to determine movement. for (int i = 0; i < 8; i++) { centered[i] = rawReads[i] - centerPoints[i];