Skip to content

Commit

Permalink
Robust zeroing and dead-zone suggestion (#42)
Browse files Browse the repository at this point in the history
* New debug=11 to recalibrate (=zero) the joysticks.
* Integrated a dead zone suggestion and warnings for non-idle axis
* Updates to README and added FAQ for #19
  • Loading branch information
AndunHH authored Jul 9, 2024
1 parent 930ea19 commit 8ae46ae
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 43 deletions.
35 changes: 32 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
@@ -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)

Expand All @@ -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.

Expand Down Expand Up @@ -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.
Expand Down
Binary file modified fritzing/spacemouse.fzz
Binary file not shown.
Binary file modified pictures/fritzing-electronics.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
128 changes: 126 additions & 2 deletions spacemouse-keys/calibration.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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]);
}
}
6 changes: 6 additions & 0 deletions spacemouse-keys/calibration.h
Original file line number Diff line number Diff line change
Expand Up @@ -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);
51 changes: 26 additions & 25 deletions spacemouse-keys/config_sample.h
Original file line number Diff line number Diff line change
@@ -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+
Expand Down Expand Up @@ -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.
Expand All @@ -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
Expand Down Expand Up @@ -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
Expand All @@ -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!
Expand All @@ -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
Expand All @@ -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 }
Expand All @@ -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
Expand All @@ -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
Expand Down
Loading

0 comments on commit 8ae46ae

Please sign in to comment.