Introduction: DIY Space Mouse for Fusion 360 Using Magnets
This project originated from my curiosity about CAD input devices. I realized that I was struggling to remember navigation combos in Fusion 360 whenever I switched workstations. Having never used a Space mouse before, I decided to explore the possibility of creating a similar device using only the parts I already had on hand.
To make the 3-axis joystick, I used a low-resolution magnetometer and neodymium magnets mounted on a spring system directly inspired form the real 3D connexion Space Mouse. Currently, the device emulates the mouse and keyboard to execute the orbit and pan functions within Fusion 360. There are also two shortcut buttons — one for the home view and another for fitting to the screen.
To address the lack of a native option to assign a keyboard shortcut for views in Fusion 360, we will go over a workaround that involves a custom python add-in.
Supplies
Parts
- 1 × QT Py RP2040 Adafruit
- 1 × TLV493D Triple-Axis Magnetometer Adafruit
- 1 × STEMMA QT / Qwiic JST SH 4-pin Cable - 100mm Long Adafruit
- 2 × Tactile Button switch (6mm) Adafruit
- 3 × Adafruit LED Sequins - Emerald Green (optional) Adafruit
- 4 × Rubber Bumper Feet Adafruit
Hardware
- Round Magnets Amazon
- 6 × (6 × 2mm)
- Springs Amazon
- 3 × Compression 23/64" × 1-3/8"
- 3 × Extension 13/64" × 13/16"
- M2.5 Heat-Set Inserts McMasterCarr
- 24 × 3.4 mm
- M2.5 Socket Head Screws Amazon
- 10 × 3mm
- 3 × 5mm
- 7 × 8mm
- 4 × 12mm
3D printing
Finishing (optional)
- Assorted Grits Sandpaper Amazon
- Glazing and Spot Putty Amazon
- Filler Primer Amazon
- Spray Paint Amazon
- Flat Matte Clear Coat Amazon
Tools
- 3D Printer Amazon
- Soldering Station/Iron. Amazon
- Helping hands Amazon
- Precision Screwdriver Set Amazon
- Wire stripper Amazon
- Sanding block
- Flux, Solder
- Hook up wire
- Flush cutters
- Hobby knife
- Tweezers and pliers
Affiliate links may be included in the parts list. I may receive a small commission at no additional cost to you.
Step 1: Watch the Build Video
The video will give you an overview of the design and build process.
Step 2: 3D Printing
You can grab the included STLs in the page or from Github
⚠️ Few things to keep in mind before printing
- I have designed the holes and fit of the pieces to accommodate tolerances from my 3D printing setup. You might want to verify the heated inserts holes and the fit, particularly between the Base_top.stl and the Base_bottom.stl before proceeding.
- For the Base_bottom.stl, I printed it using a 5% grid infill with 3-4 wall counts. Around layer 64, I paused the print to pack in the steel BB's and add some weight. This step is essential to counteract the tension from the springs prevent the the device from sliding around.
- I suggest to go for 100% infill solid parts from the knob and the three stem parts.
- I included 1 model for the buttons but you obviously need to print it twice.
Attachments
Step 3: Finishing (optional)
If you are not familiar with post-processing techniques for FDM prints, I have some instructions in this project to guide you.
Step 4: Heated Inserts
Once the clear coat has had ample time to dry, I proceeded to install the heated inserts into the designated 24 holes.
Although it may seem excessive for most cases, I prefer incorporating them into my 3D prints. I would rather do this then deal with stripped threads after few adjustments.
Step 5: Assembly _1
Following that, I proceeded to install six mini round magnets (6x2mm) into the designated slot on Stem_top.stl.
Please note that depending on the tolerances of your 3D printer and the magnet manufacturer, you may need to apply some glue to secure them in place. In my case, I was able friction fit them.
Step 6: Assembly _2
Now, we can assemble the spring mechanism and attach it to the knob from the inside.
Begin by inserting the 3 compression springs into the appropriate holes on both Stem_top.stl and Stem_middle.stl. With both pieces compressed together, securely hold them in place while fastening the extension springs to the three sides using 6 x 3mm M2.5 socket head screws.
Once this step is complete, you can insert the entire contraption inside the knob and secure it from the inside using 3 x 5mm M2.5 screws. The Stem_middle.stl features three larger holes, allowing you to pass through the springs and screw in the entire thing to the knob from inside.
Step 7: Wiring _1
I did not plan on this initially. However, it seemed to be a great opportunity to incorporate some LEDs for added visual interest.
I carefully glued and wired 3 Adafruit LED Sequins onto the sides of Stem_bottom.stl. I took care to ensure that they did not interfere with the four screws or the lip protruding from Base_bottom.stl, allowing for proper clearance.
Next, I plugged the Magnetometer and secured over it Stem_bottom.stl to the inside of the knob using 4 x 8mm M2.5 screws.
⚠️ Check the wiring diagram
Attachments
Step 8: Wiring _2
I soldered wires to both switches and carefully measured the length of the shared wire (ground) that needs to fit into the channel around the opening in the middle. Then, I placed the two printed buttons with the thicker part facing up into the slots, followed by the switch it self. Once that was completed, I positioned the Base_middle_plate.stl and guided the remaining 5 wires (3 from the buttons and 2 from the leds) through the opening from inside. Finally, I secured plate using four 4 x 3mm M2.5 screws.
⚠️ Check the wiring diagram
Attachments
Step 9: Wiring _3
Now, we can complete the wiring by soldering the remaining five wires to the RP2040 on the bottom side, ensuring they are placed as far away from the edges as possible (please refer to the image). After soldering the wires, we can neatly tuck the slack into the opening below, aligning the RP2040 snugly in its designated slot.
⚠️ Check the wiring diagram
Attachments
Step 10: Assembly _3
The assembly was finalized by positioning the Base_bottom.stl and securely attaching it to the stem using 3 x 8mm M2.5 screws then to the rest of the body using 4 x 12mm M2.5 screws.
To enhance stability, I affixed 4 rubber feet to the bottom.
Now, we can proceed to upload the code and conduct a test run of the device.
Step 11: Code _1
First thing is to get the Adafruit QT Py RP2040 working in the Arduino IDE. You can follow this guide if you never done this before.
Once the board is working properly you can open the Arduino sketch located here and install the required libraries in your environment.
#include <TinyUSB_Mouse_and_Keyboard.h>
#include <OneButton.h>
#include <Tlv493d.h>
#include <SimpleKalmanFilter.h>
In order for the sketch to compile you will also need to change the USB stack to from Arduino to TinyUSB. You can read more on how to do that here.
Once the board flashed successfully you can unplug, open the serial console and plug it back in.
By default the code prints out to the console x,y,z after initialization.
You can test the knob to see if it moves the mouse cursor and if it's in the right orientation. Incase it's reversed you can invert the values in the code if you don't want to open it and flip the sensor.
Depending on how centred the knob in your build and how the everything fits together, you might need to adjust the magRange and sensitivity values. You can move the knob to the extremities take a look at the x and y values printed on serial console and adjust the ceiling value for magRange.
The "dead zone" or center position is represented by xyThreshold you can adjust this for tighter tolerances to your preference.
int calSamples = 300;
int sensivity = 8;
int magRange = 3;
int outRange = 127; // Max allowed in HID report
float xyThreshold = 0.4; // Center threshold
The shortcut buttons are sent from these 2 functions. You can define new combos if you like using this reference.
// go to home view in Fusion 360 by pressing (CMD + SHIFT + H) shortcut assigned to the custom Add-in command
void goHome()
{
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press(KEY_LEFT_SHIFT);
Keyboard.write('h');
delay(10);
Keyboard.releaseAll();
Serial.println("pressed home");
}
// fit to view by pressing the middle mouse button twice
void fitToScreen()
{
Mouse.press(MOUSE_MIDDLE);
Mouse.release(MOUSE_MIDDLE);
Mouse.press(MOUSE_MIDDLE);
Mouse.release(MOUSE_MIDDLE);
Serial.println("pressed fit");
}
Step 12: Code _2
To get homeview shortcut working you'll need to install the custom Add-in for Fusion.
- In the toolbar, go to UTILITIES > Scripts and Add-Ins.
- Click on the Add-Ins tab. Under My Add-Ins click on the green plus sign and copy the Send home folder located here to that location and click open.
- Make sure Run on Startup is active and click Run.
- You should see a new command > Send To Home View appear under Add-Ins.
- Click on the 3 dots icon > Change Keyboard Shortcut...
- Assign the same keyboard shortcut sent by the button press from the device: cmd+shift+h
Step 13: Done
Thank you for sticking around until the end!
This was a lot of fun to make and I enjoyed the process. There is certainly a lot of improvements to be made. I'd love to hear your suggestions in the comments below.
I have a lot of ideas and I plan on making more projects like these Follow me for more
Grand Prize in the
Magnets Contest
158 Comments
Question 5 days ago
Hi, I made it and it's working super well! But one thing I'd like to know is, do I need to have the Arduino program running all the time to use it? I notice if I close the Arduino program the space mouse will lose all functionality until I open arduino up and go to the serial monitor again. It's not a huge issue, but if I can I'd like to avoid having more apps open on my pc constantly especially since it seems Arduino eats up more resources than fusion360 for some reason.
Question 8 days ago on Introduction
Hello can you please explain in detail how to get the code onto the Adafruit? I am unfortunately a beginner
Best regards Uli
Question 15 days ago
Hello! Its a great project! But I have one question. Does it work on windows 11?
Question 17 days ago
I can't find the "TinyUSB_Mouse_and_Keyboard" library. The only libraries that show up are the 4 below. Please help.
I tried downloading:
Adafruit TinyUSB Library (Didn't work)
ESP32TinyUSB (Didn't work)
TinyUSBSimplePacketComs (Didn't work)
bluemicro_hid (Didn't work)
Every time the error message returns:
C:\Users\adale\AppData\Local\Temp\.arduinoIDE-unsaved2023111-23432-1ytcise.65igl\sketch_dec1a\sketch_dec1a.ino:1:10: fatal error: TinyUSB_Mouse_and_Keyboard.h: No such file or directory
1 | #include <TinyUSB_Mouse_and_Keyboard.h>
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
compilation terminated.
exit status 1
Compilation error: TinyUSB_Mouse_and_Keyboard.h: No such file or directory
Answer 16 days ago
You can download the repo as a zip here https://github.com/cyborg5/TinyUSB_Mouse_and_Keybo...
And then include the zip in arduino IDE
Question 17 days ago
Hallo, und erstmal vielen Dank für die tolle Arbeit, aber ich bekomme es nicht hin sie zum laufen zu kriegen. ich habe alle Bibliotheken hinzugefügt. ich habe mal alle Fehler eingefügt.
In file included from C:\Users\Julien\Documents\Arduino\sketch_dec01c\sketch_dec01c.ino:3:
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h: In function 'void setup()':
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:32:8: error: expected unqualified-id before string constant
32 | extern "C" {
| ^~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:81:20: error: 'callbackFunction' has not been declared
81 | void attachClick(callbackFunction newFunction);
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:82:20: error: 'parameterizedCallbackFunction' has not been declared
82 | void attachClick(parameterizedCallbackFunction newFunction, void *parameter);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:88:26: error: 'callbackFunction' has not been declared
88 | void attachDoubleClick(callbackFunction newFunction);
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:89:26: error: 'parameterizedCallbackFunction' has not been declared
89 | void attachDoubleClick(parameterizedCallbackFunction newFunction, void *parameter);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:95:25: error: 'callbackFunction' has not been declared
95 | void attachMultiClick(callbackFunction newFunction);
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:96:25: error: 'parameterizedCallbackFunction' has not been declared
96 | void attachMultiClick(parameterizedCallbackFunction newFunction, void *parameter);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:102:29: error: 'callbackFunction' has not been declared
102 | void attachLongPressStart(callbackFunction newFunction);
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:103:29: error: 'parameterizedCallbackFunction' has not been declared
103 | void attachLongPressStart(parameterizedCallbackFunction newFunction, void *parameter);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:109:28: error: 'callbackFunction' has not been declared
109 | void attachLongPressStop(callbackFunction newFunction);
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:110:28: error: 'parameterizedCallbackFunction' has not been declared
110 | void attachLongPressStop(parameterizedCallbackFunction newFunction, void *parameter);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:116:30: error: 'callbackFunction' has not been declared
116 | void attachDuringLongPress(callbackFunction newFunction);
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:117:30: error: 'parameterizedCallbackFunction' has not been declared
117 | void attachDuringLongPress(parameterizedCallbackFunction newFunction, void *parameter);
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:171:3: error: 'callbackFunction' does not name a type
171 | callbackFunction _clickFunc = NULL;
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:172:3: error: 'parameterizedCallbackFunction' does not name a type
172 | parameterizedCallbackFunction _paramClickFunc = NULL;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:175:3: error: 'callbackFunction' does not name a type
175 | callbackFunction _doubleClickFunc = NULL;
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:176:3: error: 'parameterizedCallbackFunction' does not name a type
176 | parameterizedCallbackFunction _paramDoubleClickFunc = NULL;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:179:3: error: 'callbackFunction' does not name a type
179 | callbackFunction _multiClickFunc = NULL;
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:180:3: error: 'parameterizedCallbackFunction' does not name a type
180 | parameterizedCallbackFunction _paramMultiClickFunc = NULL;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:183:3: error: 'callbackFunction' does not name a type
183 | callbackFunction _longPressStartFunc = NULL;
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:184:3: error: 'parameterizedCallbackFunction' does not name a type
184 | parameterizedCallbackFunction _paramLongPressStartFunc = NULL;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:187:3: error: 'callbackFunction' does not name a type
187 | callbackFunction _longPressStopFunc = NULL;
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:188:3: error: 'parameterizedCallbackFunction' does not name a type
188 | parameterizedCallbackFunction _paramLongPressStopFunc = NULL;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:191:3: error: 'callbackFunction' does not name a type
191 | callbackFunction _duringLongPressFunc = NULL;
| ^~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:192:3: error: 'parameterizedCallbackFunction' does not name a type
192 | parameterizedCallbackFunction _paramDuringLongPressFunc = NULL;
| ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~
In file included from C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\cores\rp2040/api/HardwareI2C.h:2,
from C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:27,
from C:\Users\Julien\Documents\Arduino\libraries\TLV493D_A1B6-1.0.3\src/Tlv493d.h:38,
from C:\Users\Julien\Documents\Arduino\sketch_dec01c\sketch_dec01c.ino:4:
c:\users\julien\appdata\local\arduino15\packages\rp2040\hardware\rp2040\3.6.1\arduinocore-api\api\hardwarei2c.h:24:1: error: 'namespace' definition is not allowed here
24 | namespace arduino {
| ^~~~~~~~~
In file included from C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:28:
c:\users\julien\appdata\local\arduino15\packages\rp2040\hardware\rp2040\3.6.1\pico-sdk\src\rp2_common\hardware_i2c\include\hardware\i2c.h:21:8: error: expected unqualified-id before string constant
21 | extern "C" {
| ^~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:37:36: error: expected class-name before '{' token
37 | class TwoWire : public HardwareI2C {
| ^
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:39:23: error: expected ')' before '*' token
39 | TwoWire(i2c_inst_t *i2c, pin_size_t sda, pin_size_t scl);
| ~ ^~
| )
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:83:18: error: type 'arduino::Print' is not a base type for type 'setup()::TwoWire'
83 | using Print::write;
| ^~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:93:5: error: 'i2c_inst_t' does not name a type; did you mean 'i2c_hw_t'?
93 | i2c_inst_t *_i2c;
| ^~~~~~~~~~
| i2c_hw_t
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:118:27: error: local class 'class setup()::TwoWire' shall not have static data member 'const uint32_t setup()::TwoWire::TWI_CLOCK' [-fpermissive]
118 | static const uint32_t TWI_CLOCK = 100000;
| ^~~~~~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:42:10: error: 'void setup()::TwoWire::begin()' marked 'override', but does not override
42 | void begin() override;
| ^~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:44:10: error: 'void setup()::TwoWire::begin(uint8_t)' marked 'override', but does not override
44 | void begin(uint8_t address) override;
| ^~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:46:10: error: 'void setup()::TwoWire::end()' marked 'override', but does not override
46 | void end() override;
| ^~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:52:10: error: 'void setup()::TwoWire::setClock(uint32_t)' marked 'override', but does not override
52 | void setClock(uint32_t freqHz) override;
| ^~~~~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:54:10: error: 'void setup()::TwoWire::beginTransmission(uint8_t)' marked 'override', but does not override
54 | void beginTransmission(uint8_t) override;
| ^~~~~~~~~~~~~~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:55:13: error: 'uint8_t setup()::TwoWire::endTransmission(bool)' marked 'override', but does not override
55 | uint8_t endTransmission(bool stopBit) override;
| ^~~~~~~~~~~~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:56:13: error: 'uint8_t setup()::TwoWire::endTransmission()' marked 'override', but does not override
56 | uint8_t endTransmission(void) override;
| ^~~~~~~~~~~~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:58:12: error: 'size_t setup()::TwoWire::requestFrom(uint8_t, size_t, bool)' marked 'override', but does not override
58 | size_t requestFrom(uint8_t address, size_t quantity, bool stopBit) override;
| ^~~~~~~~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:59:12: error: 'size_t setup()::TwoWire::requestFrom(uint8_t, size_t)' marked 'override', but does not override
59 | size_t requestFrom(uint8_t address, size_t quantity) override;
| ^~~~~~~~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:61:12: error: 'size_t setup()::TwoWire::write(uint8_t)' marked 'override', but does not override
61 | size_t write(uint8_t data) override;
| ^~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:62:12: error: 'size_t setup()::TwoWire::write(const uint8_t*, size_t)' marked 'override', but does not override
62 | size_t write(const uint8_t * data, size_t quantity) override;
| ^~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:64:17: error: 'virtual int setup()::TwoWire::available()' marked 'override', but does not override
64 | virtual int available(void) override;
| ^~~~~~~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:65:17: error: 'virtual int setup()::TwoWire::read()' marked 'override', but does not override
65 | virtual int read(void) override;
| ^~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:66:17: error: 'virtual int setup()::TwoWire::peek()' marked 'override', but does not override
66 | virtual int peek(void) override;
| ^~~~
C:\Users\Julien\AppData\Local\Arduino15\packages\rp2040\hardware\rp2040\3.6.1\libraries\Wire\src/Wire.h:67:18: error: 'virtual void setup()::TwoWire::flush()' marked 'override', but does not override
67 | virtual void flush(void) override;
| ^~~~~
In file included from C:\Users\Julien\Documents\Arduino\libraries\TLV493D_A1B6-1.0.3\src/Tlv493d.h:39:
c:\users\julien\documents\arduino\libraries\tlv493d_a1b6-1.0.3\src\util\businterface.h:42:1: error: 'namespace' definition is not allowed here
42 | namespace tlv493d
| ^~~~~~~~~
In file included from c:\users\julien\documents\arduino\libraries\tlv493d_a1b6-1.0.3\src\util\tlv493d_conf.h:36,
from C:\Users\Julien\Documents\Arduino\libraries\TLV493D_A1B6-1.0.3\src/Tlv493d.h:40:
c:\users\julien\documents\arduino\libraries\tlv493d_a1b6-1.0.3\src\util\RegMask.h:41:1: error: 'namespace' definition is not allowed here
41 | namespace tlv493d
| ^~~~~~~~~
c:\users\julien\documents\arduino\libraries\tlv493d_a1b6-1.0.3\src\util\tlv493d_conf.h:56:1: error: 'namespace' definition is not allowed here
56 | namespace tlv493d
| ^~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\TLV493D_A1B6-1.0.3\src/Tlv493d.h:133:9: error: 'tlv493d' does not name a type; did you mean 'Tlv493d'?
133 | tlv493d::BusInterface_t mInterface;
| ^~~~~~~
| Tlv493d
sketch_dec01c:28:1: error: 'voidsetup' was not declared in this scope
28 | voidsetup() {
| ^~~~~~~~~
sketch_dec01c:66:1: error: 'voidloop' was not declared in this scope
66 | voidloop() {
| ^~~~~~~~
sketch_dec01c:106:1: error: 'voidgoHome' was not declared in this scope
106 | voidgoHome() {
| ^~~~~~~~~~
sketch_dec01c:116:1: error: 'voidfitToScreen' was not declared in this scope
116 | voidfitToScreen() {
| ^~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\TLV493D_A1B6-1.0.3\src/Tlv493d.h: At global scope:
C:\Users\Julien\Documents\Arduino\libraries\TLV493D_A1B6-1.0.3\src/Tlv493d.h:74:9: warning: 'setup()::Tlv493d::~Tlv493d() noexcept' used but never defined
74 | ~Tlv493d(void);
| ^
C:\Users\Julien\Documents\Arduino\libraries\OneButton-2.1.0\src/OneButton.h:50:12: warning: 'setup()::OneButton::OneButton(int, boolean, bool)' used but never defined
50 | explicit OneButton(const int pin, const boolean activeLow = true, const bool pullupActive = true);
| ^~~~~~~~~
In file included from C:\Users\Julien\Documents\Arduino\sketch_dec01c\sketch_dec01c.ino:5:
C:\Users\Julien\Documents\Arduino\libraries\SimpleKalmanFilter-0.1.0\src/SimpleKalmanFilter.h:14:3: warning: 'setup()::SimpleKalmanFilter::SimpleKalmanFilter(float, float, float)' used but never defined
14 | SimpleKalmanFilter(float mea_e, float est_e, float q);
| ^~~~~~~~~~~~~~~~~~
C:\Users\Julien\Documents\Arduino\libraries\TLV493D_A1B6-1.0.3\src/Tlv493d.h:73:9: warning: 'setup()::Tlv493d::Tlv493d()' used but never defined
73 | Tlv493d(void);
| ^~~~~~~
exit status 1
'voidsetup' was not declared in this scopeVCTVCR^TGFFFFFFFFFFFFFFFFFFDDDDDDGGGGGGGGVFDGGGGGGGGGGGGGGGGGGGGGGGGGGGD^GDT^GDVDGTVDVV^RD^FFFF"!DSQ
7 weeks ago
I built the spacemouse tonight but I have one issue.
My controls are turned 90 degrees.
So up/down = left/right and left/right is up/down.
Can someone tell me how to adjust the code to get it working the right way?
Reply 24 days ago
// update the filters
xCurrent = xFilter.updateEstimate(mag.getX() - xOffset);
yCurrent = yFilter.updateEstimate(mag.getY() - yOffset);
zCurrent = zFilter.updateEstimate(mag.getZ() - zOffset);
you need to change the xCurrent to yCurrent and so the same for the yCurrent. It should look like this when done:
// update the filters
yCurrent = xFilter.updateEstimate(mag.getX() - xOffset);
xCurrent = yFilter.updateEstimate(mag.getY() - yOffset);
zCurrent = zFilter.updateEstimate(mag.getZ() - zOffset);
Then if you have one moving inverted, ie left is right and right is left, you need to multiply that current by -1. Should look like this:
yCurrent = (xFilter.updateEstimate(mag.getX() - xOffset))*-1;
xCurrent = yFilter.updateEstimate(mag.getY() - yOffset);
zCurrent = zFilter.updateEstimate(mag.getZ() - zOffset);
Notice how the yCurrent values are in parenthesis and there is a *-1 at then end. This well flip the current reading so a negative value will be positive and a positive value will be negative. You should now have left to left and right to right. Do a same thing to the xCurrent if you up and down are inverted.
26 days ago
```
#include<OneButton.h>
#include<Tlv493d.h>
#include<SimpleKalmanFilter.h>
Tlv493d mag = Tlv493d();
SimpleKalmanFilter xFilter(1, 1, 0.2), yFilter(1, 1, 0.2), zFilter(1, 1, 0.2);
// Setup buttons
OneButton button1(27, true);
OneButton button2(24, true);
float xOffset = 0, yOffset = 0, zOffset = 0;
float xCurrent = 0, yCurrent = 0, zCurrent = 0;
int calSamples = 300;
int sensivity = 8;
int magRange = 3;
int outRange = 127;
float xyThreshold = 0.4;
int inRange = magRange * sensivity;
float zThreshold = xyThreshold * 1.5;
bool isOrbit = false;
voidsetup() {
button1.attachClick(goHome);
button1.attachLongPressStop(goHome);
button2.attachClick(fitToScreen);
button2.attachLongPressStop(fitToScreen);
Mouse.begin();
Keyboard.begin();
Serial.begin(9600);
Wire1.begin();
mag.begin(Wire1);
mag.setAccessMode(mag.MASTERCONTROLLEDMODE);
mag.disableTemp();
for (int i = 1; i <= calSamples; i++) {
delay(mag.getMeasurementDelay());
mag.updateData();
xOffset += mag.getX();
yOffset += mag.getY();
zOffset += mag.getZ();
Serial.print(".");
}
xOffset = xOffset / calSamples;
yOffset = yOffset / calSamples;
zOffset = zOffset / calSamples;
Serial.println();
Serial.println(xOffset);
Serial.println(yOffset);
Serial.println(zOffset);
}
voidloop() {
button1.tick();
button2.tick();
delay(mag.getMeasurementDelay());
mag.updateData();
xCurrent = xFilter.updateEstimate(mag.getX() - xOffset);
yCurrent = yFilter.updateEstimate(mag.getY() - yOffset);
zCurrent = zFilter.updateEstimate(mag.getZ() - zOffset);
if (abs(xCurrent) > xyThreshold || abs(yCurrent) > xyThreshold) {
int xMove = map(xCurrent, -inRange, inRange, -outRange, outRange);
int yMove = map(yCurrent, -inRange, inRange, -outRange, outRange);
if (abs(zCurrent) < zThreshold && !isOrbit) {
Keyboard.press(KEY_LEFT_SHIFT);
isOrbit = true;
}
Mouse.press(MOUSE_MIDDLE);
Mouse.move(yMove, xMove, 0);
} else {
Mouse.release(MOUSE_MIDDLE);
if (isOrbit) {
Keyboard.release(KEY_LEFT_SHIFT);
isOrbit = false;
}
}
Keyboard.releaseAll();
Serial.print(xCurrent);
Serial.print(",");
Serial.print(yCurrent);
Serial.print(",");
Serial.print(zCurrent);
Serial.println();
}
voidgoHome() {
Keyboard.press(KEY_LEFT_GUI);
Keyboard.press(KEY_LEFT_SHIFT);
Keyboard.write('h');
delay(10);
Keyboard.releaseAll();
Serial.println("pressed home");
}
voidfitToScreen() {
Mouse.press(MOUSE_MIDDLE);
Mouse.release(MOUSE_MIDDLE);
Mouse.press(MOUSE_MIDDLE);
Mouse.release(MOUSE_MIDDLE);
Serial.println("pressed fit");
}
```
27 days ago
Provided code uses the map() function to interpolate between input current values and output positional mouse movement values. Map() only uses integers as input and output; For a mouse with input range of -3 to 3, there would only be 6 discrete mouse movement values, making fine control using the mouse difficult. The provided code attempts to mitigate this by scaling the input range by a user set sensitivity while keeping the output range high. If you've played video games with a controller, this is similar to maxing out the in-game sensitivity and limiting the range of motion of the joystick, as opposed to reducing the in game sensitivity.
A quick workaround to this I am using is to scale the input current as well as the input range, reusing the sensitivity variable. Letting X be the scalar value, this would ideally lead to X discrete mouse output values per unit current. E.G. let X=2 with an actual input current range of -3 to 3. The map() function now sees input values from -6 to 6, doubling the number of discrete mouse output values. Due to noise, I found a sensitivity value of around 8 works well for this application. I reduced my outrange to 10, and multiply x/yCurrent by the sensitivity in the map() functions. Because I am still using map(), there are in actuality only 20 discrete outputs because of the low outrange magnitude. I will not be at my computer for a bit for testing, but this could probably be fixed by multiplying outrange by sensitivity as well, and dividing the resulting x/yMove values by the sensitivity. I am not sure if interpolating and truncating output values manually would be faster, but using map() is an easy solution for having discrete output values, as a continuous output would probably be affected by sensor noise.
I am not sure if Mouse.h only accepts integer values as well.
Question 4 weeks ago
Mine stops working after a few mins. I am on windows 10. any hints?
Question 3 months ago
Stops working for me as well after a few seconds (Windows 10), it then
messes up my normal mouse an keyboard functionality and I am forced to
restart. Anyone else able to get it working? Using all the parts suggested in the tutorial. Using latest Arduino IDE and Adafruit libraries.I did try increasing the sensitivity and thresholds to no avail.
Answer 3 months ago
I ended up fixing my issue. Narrowed the issue to Keyboard.press(KEY_LEFT_SHIFT); causing the issue. Added code to release the key press if no changes detected to xyz coordinates. Now it no longer crashes and locks up the system. This instructable has been a blast to make! Thanks for sharing this awesome project.
Reply 2 months ago
I'm having this same issue and attempted to add some code to release but it keeps happening. Would you mind sharing some details on what you did to fix it?
Reply 2 months ago
I changed
if (abs(zCurrent) < zThreshold && !isOrbit)
{
Keyboard.press(KEY_LEFT_SHIFT);
isOrbit = true;
}
to
if (abs(zCurrent) < zThreshold && !isOrbit)
{
Keyboard.press(KEY_LEFT_SHIFT);
isOrbit = true;
} else { // fix for windows 10 locking the keyboard and mouse
Keyboard.release(KEY_LEFT_SHIFT);
isOrbit = false;
}
Reply 5 weeks ago
Same issue, I try your fix but doesn't work , maybe because I use mac os.
Reply 4 weeks ago
Ne fonctionne pas non plus sous Windows 11, plante le clavier et reboot obligatoire
4 weeks ago
Hello,
I put money and heart into it. Doesn't work, crashes after a few minutes forcing me to restart my PC under Windows 11.
I am very sad.
My modification of the code Keyboard.release(KEY_LEFT_SHIFT); given in the comments does not work.
Florian
4 months ago
One question, how can I apply with blender?
Reply 4 weeks ago
the orbit and pan shortcuts are flipped so you'd have to edit the code a little
but the buttons wouldn't do anything, you'd have to edit the code further to give them any functionality.