Skip to the content
PCBKitNew board
2 layers26.9 × 35.2 mmFilter wheel, the printed circuit board in 3D

Filter wheel

xlazam01/OpenWheel

Astronomy Filter Wheel Project

Size
26.9 × 35.2 mm
Copper layers
2
Thickness
1.6 mm
Parts
62
Nets
70
Pads
289
Tracks, vias
381, 181
Pours
1
Routed
Yes
KiCad
KiCad 10.0 (file 20260206)
Created
18 Oct 2026
Updated
7 Nov 2026

Overview

Filter wheel is an open-source printed circuit board by xlazam01, from the xlazam01/OpenWheel repository on GitHub, published under the GPL-3.0 licence. It is a 2-layer board of 26.9 × 35.2 mm with 62 parts on 70 nets, routed with 381 tracks and 181 vias.

It is built around the 24AA64T-I/OT, RT9193-33GB and TMC2209-LA, with the USBLC6-2SC6, TS-1088-AR02016 and MC-311D among its other parts. Its parts include 25 capacitors, 19 resistors, 5 ICs, 3 connectors and 2 diodes. It belongs with the STM32 boards of this library.

Main components

7

README

README.md

OpenWheel

Open-source motorised filter wheel for astrophotography (up to 9 filters; the DIY release takes 4 × 2"). It appears on the PC as a USB serial device, is detected automatically, and is driven from NINA and other ASCOM clients, or from KStars/Ekos through INDI.

OpenWheel DIY filter wheel

Documentation

Build guidestep-by-step pages drawn from the CAD model, also as PDF
Bill of materialsprinted, CNC and bought parts with sources; PCB parts with LCSC numbers (CSV)
User manualmounting, drivers, buttons and LEDs, errors, changing filters, firmware update
DIY notesevery dimension and design decision behind the mechanics
SchematicPCB schematic (PDF); board renders: top, bottom, 3D
Protocolserial commands for your own software
MCUSTM32G0B1 (native USB CDC), PlatformIO + Arduino framework
Motorstepper on a TMC2209 (UART configured)
Positionhall sensor + a different-length magnet per slot; calibration, filter names and focus offsets are stored in a 24AA64 I2C EEPROM
Host protocoltext commands :NN[params]#, 115200 baud, every command is answered
USB IDVID 0x1209 (pid.codes), PID see docs/USB_IDS.md

PCB 3D view PCB top PCB bottom

Controller PCB (V3.1, rendered from hw/filter_wheel.kicad_pcb)

Repository layout

FolderContent
fw/firmware (PlatformIO project, target STM32G0B1)
hw/KiCad schematic and PCB
cad/diy/mechanical models of the DIY release (SolidCraft sources, STEP, 3MF for printing)
driver/ASCOM/ASCOM driver for NINA & co.: core library, COM local server, setup dialog, tests, installer script
driver/INDI/INDI driver for KStars/Ekos on Linux, wheel simulator, tests
tools/gui/small Python/Qt test GUI
tools/hwtest/pytest acceptance tests that run against a real board over USB
tools/manual/generator for the build-guide pages (CAD view DXF → SVG/PDF)
docs/build guide, BOM, user manual, DIY notes, protocol reference, USB IDs
release/one folder per release: firmware binary, PCB fabrication files, CAD exports — latest 1.0.0

Build and flash

text
pio run -d fw -e STM32G0B1                 # build
pio run -d fw -e STM32G0B1 -t upload       # flash over USB (DFU)

Entering the bootloader (DFU):

  • Hold the CCW button while powering on. The CCW button is wired to the BOOT0 pin, so the STM32 starts its built-in USB DFU bootloader instead of the firmware.
  • On a running wheel the command :31# does the same without touching the button (flag in RAM + reset, see docs/BOOTLOADER.md).

Open fw/ (not the repository root) in VS Code with the PlatformIO extension.

Using the wheel

  • Buttons: single click on CW / CCW moves to the next / previous slot, double click homes, a long press on CW starts calibration. Holding CW while powering on also starts calibration; holding CCW while powering on enters DFU (see above).
  • LEDs: status LED 1 blinks while moving; LED 2 blinks the slot number on arrival; both LEDs alternate on an error (read the cause with :33# / :34#).
  • Test GUI: pip install -r tools/gui/requirements.txt && python tools/gui/main.py
  • Hardware tests: python -m pytest tools/hwtest --port COM13

Serial protocol

Full reference with every command: docs/PROTOCOL.md. In short:

  • Link: USB CDC serial port (115200 8N1, the speed is nominal over USB). Do not rely on DTR.

  • Framing: a command is :NN[params]# where NN is a two-digit command number. Bytes outside :...# are ignored; a new : restarts the command. Keep a command to 46 characters and send one at a time (a large burst can overrun the USB receive buffer and lose the #).

  • Every command is answered with one line ending in #:

    AnswerMeaning
    <value>#query result (number, text, or comma separated list)
    OK#set / action accepted (a move answers as soon as it has started)
    ERR,<code>#rejected, see error codes below
  • Slots are numbered from 0. Parameters accept :07 3#, :07,3# and :073#.

  • Identify first. After power-on and each time a host opens the port, only :25# and :32# are accepted; anything else answers ERR,16 until :32# has been sent (protocol 3, firmware 1.4).

Example session:

text
:25#        ->  OK#                                       ping
:33#        ->  ERR,16#                                   not identified yet
:32#        ->  OPENWHEEL,G0B1,FW1.4.0,3,207834924B31#    identify: model, firmware, protocol version, serial
:33#        ->  S,1,0,-1,4,0,3#                           status: idle, slot 0, 4 slots, no error
:07 2#      ->  OK#                                       start moving to slot 2
:33#        ->  S,2,-1,2,4,0,3#                           moving (slot -1), target 2
:07 9#      ->  ERR,7#                                    bad parameter
:33#        ->  S,1,2,-1,4,0,3#                           arrived at slot 2

Main commands:

CommandPurposeAnswer
:25#pingOK#
:32#identify (automatic detection; unlocks all other commands)OPENWHEEL,model,firmware,protocol,serial#
:33#statusS,state,slot,target,slots,error,flags#
:06#current slot, -1 while moving or unknownnumber
:07 N#move to slot NOK#
:20#homeOK#
:30#haltOK#
:35#start calibrationOK#
:16#number of slots found by calibrationnumber
:34# / :34,0#read / clear the sticky device errorE,code# / OK#
:36# / :37 N,name#get all filter names / set the name of slot Na,b,c# / OK#
:38# / :39 N,offset#get all focus offsets / set the offset of slot Na,b,c# / OK#
:40#health: MCU temperature, supplies, driver temperature, warningsH,...# (see docs/PROTOCOL.md)
:41# / :42 N#stall detection: threshold and load readings / set threshold (0 = off)G,...# / OK#
:43# / :44 N#status LED brightness 0..100 % (0 = off) / set itnumber / OK#
:45#live Hall sensor state, also uncalibrated (magnet polarity check)1# switched / 0#
:18# / :19#firmware version / nametext
:31#reboot into the DFU bootloaderOK#

Status fields: state 0 init, 1 idle, 2 moving, 3 homing, 4 calibrating, 5 error; slot is the current slot (-1 while moving or when the position is unknown); target the slot of a move; slots the calibrated slot count (0 = not calibrated); error the sticky error code; flags bit 0 calibrated, bit 1 position known, bit 2 button held at boot, bit 3 EEPROM fault, bit 4 health warning (details with :40#).

Error codes:

CodeMeaningCodeMeaning
0none7bad parameter
1homing timed out8busy (moving, homing or calibrating)
2move timed out9not calibrated
3calibration failed10unknown command
4EEPROM missing or write failed11malformed command
5motor driver (TMC2209) fault12not changeable at runtime
6no hall sensor pulse seen13motor stalled (stall detection)

Codes 1 to 6 and 13 are device faults: the wheel signals them on the LEDs for 10 s and keeps the code until it is cleared with :34,0#. Codes 7 to 12 are only returned in the reply to a rejected command.

Filter names and focus offsets (:36 to :39) are stored in the wheel's EEPROM, in their own region, so they survive a calibration reset and follow the wheel to any PC. A name has up to 15 printable ASCII characters and must not contain , # : or |.

ASCOM driver (NINA and other clients)

driver/ASCOM contains an ASCOM Platform 7 driver (IFilterWheelV3, ProgID ASCOM.OpenWheel.FilterWheel). It finds the wheel by its USB ID (VID 0x1209) and then confirms it with :32#, so there is no COM port to choose.

  • Position is -1 while the wheel moves; faults are reported as ASCOM exceptions with a readable reason.
  • Names and FocusOffsets come from the wheel. The imaging software keeps its own filter list and can import these (NINA: clear its cached filter list after renaming).
  • Extra actions: Status (JSON, including temperatures, supply voltages and warnings), Home, Halt, ClearError, Calibrate (parameter confirm).
  • The setup dialog (gear icon in NINA) edits names and offsets, shows the live status and health (temperatures, supply, warnings) and offers Home, Halt, Clear error and Calibrate. Stall detection is set up there too: Test move measures the motor load and suggests a threshold, Set stores it in the wheel (0 = off). The status LED brightness (0 = off, for dark sites) is set there as well. Magnet polarity shows the live Hall sensor while a builder holds each loose magnet to it before gluing: SWITCHED = south pole at the sensor, that face goes outward (firmware 1.3+, no calibration needed).
  • Health warnings (low supply, driver overtemperature or reset, crystal not running) are written to the log as they appear.
  • It is built so that a silent, garbage-sending or unplugged device never hangs the client: every call is bounded in time, a lost connection is retried in the background, and a move that never ends is halted.
  • Log file: %LOCALAPPDATA%\OpenWheel\driver.log.

Build and register for development (the second command asks for administrator rights itself):

text
dotnet build driver/ASCOM/OpenWheel.Ascom -c Release
OpenWheel.Ascom.Server.exe /regserver          # use /unregserver to remove it

Tests: dotnet test driver/ASCOM/OpenWheel.Core.Tests (simulated misbehaving devices) and conformu conformance ASCOM.OpenWheel.FilterWheel (ConformU).

INDI driver (KStars/Ekos on Linux)

driver/INDI contains indi_openwheel, an INDI::FilterWheel driver for libindi 2.x (Raspberry Pi, Astroberry, StellarMate). It behaves like the ASCOM driver:

  • It finds the wheel by its USB ID and confirms it with :32#.
  • Filter names and focus offsets are stored in the wheel.
  • It shows the status and health of the wheel, and offers Home, Halt, Calibrate and Clear error.
  • Every command is bounded in time.

It comes with a simulated wheel (openwheel_sim) and tests against it. Building, installing and the udev rule are described in driver/INDI/README.md. It has not been tested with the real wheel yet.

Status

The firmware (protocol v2) passes the hardware acceptance tests on a real board. The ASCOM driver passes ConformU 4.5.2 with no errors, warnings or issues (report in driver/ASCOM/conformance/). An Inno Setup installer script exists (driver/ASCOM/installer/) but has not been run yet; NINA field testing is in progress.

Acknowledgements

Many thanks to the authors of the myFilterWheel ASCOM DIY project (Robert Brown, Clive Stachon and contributors, MIT licence). The basic :NN# serial protocol of OpenWheel was inspired by theirs and then extended with new features: acknowledged replies, error codes, a status command and device identification for automatic detection. OpenWheel is an independent project and is not affiliated with myFilterWheel.

Licences

  • Software and firmware: GPL-3.0-or-later, see LICENSE.
  • Hardware design files (hw/, cad/): CERN-OHL-S-2.0, see LICENSE-HARDWARE.
  • Vendored libraries in fw/lib/ keep their own licences (TMCStepper, SparkFun External EEPROM).

Parts

62
QtyValueFootprintReferences
65k1OpenWheel:R_0402_1005MetricR1, R2, R10, R11, R12, R13
5100nFOpenWheel:C_0402_1005MetricC4, C7, C8, C9, C13
510kOpenWheel:R_0402_1005MetricR4, R7, R9, R14, R18
4100nF/50VOpenWheel:C_0402_1005MetricC14, C15, C20, C22
410uF/10VOpenWheel:C_0402_1005MetricC6, C16, C17, C21
31uFOpenWheel:C_0402_1005MetricC2, C3, C10
3M2OpenWheel:MountingHole_2.2mm_M2_DIN965_PadH1, H2, H3
20.11ROpenWheel:R_0805_2012MetricR16, R17
2100nFOpenWheel:C_0402_1005Metric_v2C23, C25
212pFOpenWheel:C_0402_1005MetricC11, C12
2REDOpenWheel:LED_0603_1608MetricD1, D2
1100ROpenWheel:R_0402_1005Metric_v3R19
110kOpenWheel:R_0402_1005Metric_v3R20
110kOpenWheel:R_0402_1005Metric_v4R21
11MOpenWheel:R_0402_1005Metric_v2R3
11k5OpenWheel:R_0402_1005MetricR15
11nFOpenWheel:C_0402_1005Metric_v3C24
122nFOpenWheel:C_0402_1005MetricC5
122nF/50VOpenWheel:C_0402_1005MetricC19
124AA64T-I/OTOpenWheel:SOT-23-5U4
14.7nFOpenWheel:C_0402_1005Metric_v2C1
147uF/10VOpenWheel:C_1206_3216MetricC18
1510ROpenWheel:R_0402_1005MetricR8
1CCW / BOOT0OpenWheel:SW_Push_SPST_NO_Alps_SKRKSW2
1CWOpenWheel:SW_Push_SPST_NO_Alps_SKRKSW3
1HallOpenWheel:JST_PH_B3B-PH-K_1x03_P2.00mm_VerticalJ4
1Logo_WorksOnMySky_13mmOpenWheel:Logo_WorksOnMySky_13mmG1
1MC-311DOpenWheel:USB_C_Receptacle_G-Switch_GT-USB-7010ASVJ1
1MotorOpenWheel:JST_PH_B4B-PH-K_1x04_P2.00mm_VerticalJ3
1RT9193-33OpenWheel:SOT-23-5_v2U2
1SMD0603P050TFOpenWheel:Fuse_0603_1608MetricF1
1STM32G0B1CBTxOpenWheel:LQFP-48_7x7mm_P0.5mmU3
1TAXM8M4RDBCCT2T / 8MHzOpenWheel:Crystal_SMD_3225-4Pin_3.2x2.5mmY1
1TMC2209-LAOpenWheel:QFN-28-1EP_5x5mm_P0.5mm_EP3.75x3.75mm_ThermalViasU5
1USBLC6-2SC6OpenWheel:SOT-23-6U1

Questions

How big is the Filter wheel PCB?

It measures 26.9 × 35.2 mm and has 2 copper layers, 1.6 mm thick.

What parts does the Filter wheel use?

62 parts, built around the 24AA64T-I/OT, RT9193-33GB, TMC2209-LA and USBLC6-2SC6. The full list of its parts is on this page, with links to each part's symbol and footprint where PCBKit's library has them.

Where can I download the Filter wheel KiCad files?

From the xlazam01/OpenWheel repository on GitHub, at the commit this page was made from: the board is hw/filter_wheel.kicad_pcb and its schematic hw/filter_wheel.kicad_sch. PCBKit opens the board in the browser, with nothing to install.

Can I use the Filter wheel design in my own project?

Its repository publishes it under the GPL-3.0 licence: you may reuse it on that licence's terms (keep the author's notice, and for a copyleft licence share your changes alike).

Similar boards

  • 2 layers35 × 42 mmAstroUniBoard, the printed circuit board in 3D

    AstroUniBoard

    xlazam01/AstroUniBoard

    STM32G0B1-based telescope focuser/rotator controller — OnStepX firmware port + KiCad PCB

    146 parts1GPL-3.0
  • 4 layers277.2 × 107.5 mmKatia, the printed circuit board in 3D

    Katia

    ikajdan/katia

    A split keyboard with 6 by 3 column staggered keys and 3 thumb keys, based on the Crkbrd design

    230 parts33CERN-OHL-P-2.0
  • 4 layers50 × 50 mmWireslesslock, the printed circuit board in 3D

    Wireslesslock

    Shehroz30/wireslesslock

    69 partsMIT
  • 2 layers61 × 61 mmJuwei17onstep bck, the printed circuit board in 3D

    Juwei17onstep bck

    xlazam01/Juwei17

    Upgrade of Juwei17 Astronomy Mount

    145 parts9MIT
  • 4 layers130 × 110 mmOpenBeacon2, the printed circuit board in 3D

    OpenBeacon2

    etherkit/OpenBeacon2

    Multimode MEPT based on OpenBeacon

    261 parts15
  • 4 layers40 × 65 mmCCM7, the printed circuit board in 3D

    CCM7

    OzanE1/CCM7

    Yeniay Control Computer M7

    129 partsLGPL-2.1
  • 4 layers40 × 65 mmCC M7, the printed circuit board in 3D

    CC M7

    OzanE1/CCM7A

    CCM7 Assembly

    138 partsLGPL-2.1
  • 2 layers82.1 × 22.5 mmStar pointer, the printed circuit board in 3D

    Star pointer

    dilshan/star-pointer

    DIY virtual electronic finderscope for astronomical telescopes

    39 parts24MIT