Galileo: Building a Fully Custom 3D Navigation Controller — Part 1: Hardware and the MAKMOUSE CAD Software
by MAKAO in Circuits > Electronics
562 Views, 4 Favorites, 0 Comments
Galileo: Building a Fully Custom 3D Navigation Controller — Part 1: Hardware and the MAKMOUSE CAD Software
If you've ever used a 3Dconnexion SpaceMouse, you know the pitch: a puck under your left hand that orbits, pans, and zooms your CAD viewport while your right hand keeps working the regular mouse. It's a genuinely good idea. It's also a closed box — you get the button layout 3Dconnexion gave you, the profiles 3Dconnexion's driver ships with, and whatever level of per-application customization their software happens to expose that year.
I wanted the opposite: a device where every button, every gesture, and every LED is mine to define, per application, down to the exact modifier-key combination it sends. That's Galileo — a custom PCB built around an STM32G431, paired with a Windows companion app I wrote myself called MAKMOUSE.
This project is big enough that it won't fit in one article. This first part covers the electronics — the schematic, the component choices, and the reasoning (and trade-offs) behind them — plus a tour of what MAKMOUSE can already do. A follow-up part will go deeper into the software architecture and cover the enclosure, which is close to done but not quite there yet.
Setting the Requirements
Before opening KiCad, the requirement list looked roughly like this:
- Continuous 2-axis input for pan/orbit, plus a separate continuous control for zoom
- A handful of physical buttons, each independently programmable
- Some form of on-device status feedback, both visual (LED) and informational (a small display)
- USB-C, enumerating as a native USB HID device — no separate USB-UART bridge chip
- A form factor small enough to sit under one hand
- A programming/debug path that doesn't interfere with the USB port end users actually plug in
- Full per-application awareness on the PC side, switching behavior automatically based on which program has focus
Downloads
The Concept: Joystick + Ring, Not a 6-DOF Puck
The obvious "correct" way to build a SpaceMouse clone is a floating cap on a 6-degree-of-freedom sensor — optical or capacitive — that reads force and torque in all three axes simultaneously. That's what every commercial SpaceMouse actually does, and it's why you can pan, orbit, and zoom in one continuous, fluid hand motion.
Galileo doesn't do that, deliberately. Instead it combines:
- a 2-axis analog thumb joystick (pan/orbit, mode-selected by holding a side button)
- a separate rotary encoder ring around it (dedicated to zoom)
- three independent push buttons
Honest trade-off: this is a real capability gap, not just a cosmetic difference. You cannot orbit and zoom in the same continuous gesture the way a true 6-DOF sensor allows — pan and orbit share the same two physical axes and are switched between with a held button, and zoom lives on its own control. What you get in exchange is a BOM built almost entirely from parts that cost cents to a few dollars each, are trivially hand-solderable, and have zero proprietary black-box sensor to source or fail. For a one-off, repairable, fully-understood build, that's a trade I was happy to make.
The Brain: STM32G431KBTx
Everything is driven by an STM32G431KBTx in a 32-pin LQFP. It gives the design:
- Native USB (no external USB-UART bridge needed to enumerate as a HID device)
- Enough ADC channels for two joystick axes
- A hardware timer with quadrature encoder mode, so the rotary encoder is decoded entirely in hardware with no software debouncing or polling loop
- An I2C peripheral for the OLED
- Plenty of spare GPIO for three buttons, BOOT0, and reset
Honest aside: this is arguably more MCU than a HID peripheral like this strictly needs — a smaller, cheaper G0 or F0 part could handle two ADC channels, one encoder timer, and an I2C OLED with room to spare. Standardizing on one MCU family across projects (shared firmware, one toolchain, one set of habits) is a perfectly reasonable justification for the extra headroom, and the G431 leaves plenty of margin for whatever Galileo grows into later — but if raw BOM cost were the only criterion, this wouldn't be the cheapest correct answer.
Power and USB-C
USB1 is a full USB-C receptacle, but wired as USB 2.0 only — D+/D− go straight to the MCU's native USB pins (PA11/PA12) and to ESD protection, with no high-speed data-role negotiation. CC1 and CC2 each get a 5.1 kΩ pull-down to ground, which is exactly what's needed to declare the device as a default-current USB-C sink — enough for any USB-C host or cable to recognize and power it correctly, without needing a PD controller.
A few other details worth calling out:
- ESD protection: a USBLC6-2SC6 sits directly on D+/D−, cheap insurance for a connector that gets plugged and unplugged by hand regularly.
- Regulation: VBUS feeds an AMS1117-3.3 LDO down to the 3.3V rail. I've flagged AMS1117 as an anti-pattern before on always-on, DIN-rail-mounted gear, where its mediocre quiescent current and ~1.1V dropout genuinely matter. Neither of those downsides matters here — Galileo only draws power while it's plugged into a USB port, so quiescent current is a non-issue, and there's more than enough headroom between 5V and 3.3V. For a hand-solderable, dead-cheap, drop-in part on a bus-powered peripheral, AMS1117 is simply the right tool for this job.
- A dedicated 4-pin header (CON1: GND / SWCLK / SWDIO / 3.3V) handles programming and debugging over SWD, kept completely separate from the USB-C port. The end user's USB connection never needs to expose a debug/bootloader interface.
The Joystick
The pan/orbit input is a two-axis analog thumb joystick — twin potentiometers on V and H axes, feeding PF0 and PF1 directly into the MCU's ADC. There's no analog RC filtering stage on those lines; noise rejection is left to firmware-side oversampling, which is a reasonable call for a slow-moving, human-driven analog signal like this and keeps the BOM one component shorter.
One detail worth noting for anyone building on top of this: the joystick module has its own integrated push-to-click switch (SEL+/SEL−), and it's deliberately left unconnected in this design. That's a free fourth button sitting right there if a future revision wants it.
The Zoom Ring
Zoom lives on its own dedicated rotary encoder (ENC1), wired into a timer channel that supports hardware quadrature decoding on the G431. That means direction and step count come from the timer peripheral with no CPU polling involved — the firmware just reads a counter. This is the physical control behind the "Sensitivity – zoom (ring)" slider you'll see in MAKMOUSE later in this article.
Three Programmable Side Buttons
SW1, SW2, and SW3 are plain push buttons, each pulled to GND through a 10kΩ resistor and driven to 3.3V when pressed — about as simple and reliable as a digital input gets, with debouncing handled in firmware. These map directly to Side Button 1/2/3 in MAKMOUSE, and as you'll see below, each one can independently be a 3D-navigation hold-action, a one-shot keyboard shortcut, or an arbitrary held key+mouse-button combo.
Status OLED and the I2C Bus
A small I2C OLED module hangs off the MCU's I2C peripheral, with its own 4.7kΩ pull-ups (R4/R5) sized correctly for 3.3V operation. Exactly what gets drawn on it — active profile name, connection state, a button cheat-sheet — is firmware territory I'll cover once the device is fully assembled and running end to end.
The RGB Status LED
A single WS2812B addressable LED (driven from PB0) gives at-a-glance status feedback. It's powered from VBUS (5V) rather than the 3.3V rail — worth a quick honest note, since the MCU's GPIO only drives that data line to 3.3V, which is technically under the WS2812B's nominal logic-high threshold when it's running at 5V. In practice, over a short wire run to a single pixel, this is a very common shortcut that works reliably far more often than not; a proper level shifter is the textbook-correct answer, and it's exactly the kind of thing I'd revisit if this were driving a longer strip instead of one status pixel.
The live preview in MAKMOUSE actually mirrors this LED — the colored ring around the on-screen 3D model (blue for one profile, green for another in the screenshots below) tracks whatever color the physical LED is showing, so you can tell which profile is active without looking at the screen.
Reset, BOOT0, and Bring-up
Two more buttons round out the MCU section: BTN1 on NRST and BTN2 on BOOT0, each debounced with a 1µF capacitor. Having BOOT0 broken out to a button (rather than a jumper you need tweezers for) means dropping into the system bootloader for firmware updates is a two-button chord, not a disassembly job — a small thing that saves real time during active development.
Putting It on a Board
The whole thing lands on a round PCB — the joystick and encoder ring share the center, with the two buttons and a set of connectors around the edge, and the SWD header off to one side.
Meet MAKMOUSE
The PC side is a Windows app I wrote called MAKMOUSE. A few things it already does, based on where it stands today:
Per-application profiles. Profiles (Altium, Chrome, Fusion 360 in the screenshots) match against one or more .exe names, switch automatically with the focused window, and one profile can be set as default. A profile can list several executables — useful for an app family (Fusion360.exe + Inventor.exe under one profile) or for covering more than one Chromium-based browser under a single "Chrome" profile.
MAKMOUSE —Buttons and Gestures Tab, Fusion 360 Profile
Per-button flexibility. Each of the three side buttons is independently configured as one of:
- 3D navigation (hold) — Pan or Orbit, active for as long as the button is held (or toggled on/off, if you'd rather not hold it)
- Keyboard shortcut — a key sent once on press
- Hold keys/button — an arbitrary modifier+key combination plus an optional mouse button, held until release or toggled
In the Fusion 360 profile shown, Button 1 toggles Pan, Button 2 holds Shift+A together with the left mouse button, and Button 3 sends "S" once per press. In the Chrome profile, the same three physical buttons do something completely different — Button 1 is Orbit, Button 2 sends Shift+A, Button 3 toggles Ctrl+Shift+H while holding the middle mouse button. Same hardware, per-app remapping — this is the whole point of building it myself.
MAKMOUSE — Buttons and Gestures Tab, Chrome Profile
3D navigation gestures. Orbit, Pan, and Zoom each get their own row: a hold-key combination, an optional hold-button, an interaction style (cursor-drag or mouse-wheel), and an axis. In effect, MAKMOUSE emulates whatever mouse-plus-modifier gesture the target application already uses natively for navigation, rather than requiring official 3D-mouse driver support baked into that application. The practical upshot: this isn't limited to the handful of CAD packages 3Dconnexion officially supports — if an app can be navigated with a modifier-drag combo (and most can), it can get a Galileo profile. A "Direct control (3D add-in)" option is also present for applications that do expose a native add-in hook, so the fallback (drag emulation) and the "proper" path (native axis control) can coexist depending on what the target app supports.
MAKMOUSE — 3D Navigation Tab
Sensitivity and behavior. Separate sensitivity sliders for the joystick ("knob") and the encoder ring ("zoom"), plus Invert X/Y, Swap X/Y, and a few gesture-quality-of-life options (re-anchoring at the screen edge, centering the cursor before a gesture starts, leaving it where the gesture ended).
A real HID monitor. The Monitor tab shows live button state, the raw HID report bytes, a parsed X/Y axis view, and a scrolling event log of every report, parsed mouse-move delta, and profile switch. This is the kind of diagnostic tooling most commercial configuration software doesn't bother exposing — genuinely useful both for developing the firmware and for anyone troubleshooting their own setup later.
MAKMOUSE — Monitor Tab
A panic switch. Settings includes a global panic hotkey (Ctrl+Alt+F12 by default) that disables MAKMOUSE outright. If a profile is ever misbehaving and sending input you didn't want, you don't need to unplug anything — one keystroke and it's off.
MAKMOUSE — Settings Tab
One thing worth flagging honestly: the "Number of side buttons" setting already supports configuring up to six, and the live-preview panel happily shows a 6-button layout — ahead of the current Galileo 3.0 hardware, which physically has three. That's either headroom for a future revision or evidence the software was designed device-agnostic from day one; either way, it's a nice sign the app isn't hard-wired to this one board.
MAKMOUSE — 6-button Live Preview
There's also a "Continuous gestures" section (mouse-move and scroll, each triggered by a held key/button combination) whose exact behavior I don't want to over-explain from screenshots alone — that's a good candidate for the software-architecture deep dive in Part 2, along with the actual HID report format and how profile-switching detects the foreground process.
Galileo Vs. the Commercial Alternatives
The honest picture: a real SpaceMouse gives you simultaneous 6-DOF motion and, for supported applications, navigation profiles that work the moment you plug it in — no manual gesture mapping required. Galileo can't match the simultaneous-axis feel, and every new application profile is manual work up front. What it does give you is the ability to make literally any Windows application — including ones 3Dconnexion has never heard of, like a browser — respond to a physical control, with every button, gesture, and LED color defined by you and nobody else. For $0 in software licensing and a BOM that costs a fraction of even the cheapest commercial option, that felt like the right trade for a project I fully intend to keep customizing.
What's next
The enclosure is close but not finished, so Part 2 will cover the physical build once it is: final assembly, real photos of the populated board, and a deeper look at how MAKMOUSE actually talks to the device — HID report structure, profile-switching logic, and the gesture-emulation layer that turns joystick motion into an Orbit command inside Fusion 360.
Installation Files
Installation files are available for download on GitHub.
https://github.com/makao1253/MAKMOUSE-CAD-Software