1. Saber Light — Moving Head LED Bar
What project is it?
How I Approached It
I started by breaking the idea down into problems I actually needed to solve:
“I want a moving head light that holds a tube light on it."
I listed several mechanical questions based on my core question:
- How do I control two stepper motors at the same time with different speeds and directions?
→ I need a motor driver board. I chose the A4988 — it takes simple step/direction signals from an Arduino and handles the current driving. One driver per four motors maximum, controlled from the microcontroller.
- How does the fixture rotate horizontally (pan)?
→ The base needs to rotate relative to a fixed chassis. I used a gear system connecting the base to the frame, driven by one stepper motor. The motor turns the gear, the gear turns the base.
- How does the tube rotate vertically
(tilt)
?
→ This is harder. The tube is mounted on a swing arm. The second stepper motor drives it through a belt-and-pulley system. The belt connects the motor shaft to the tilt swing arm, so when the motor turns, the arm swings up or down.
The device is a two-axis rotary mechanism. Here's the signal chain from command to motion:
- A command is sent (via serial port)
- The Arduino microcontroller receives it and calculates the required angle and speed
- It sends step and direction signals to the A4988 motor drivers
- The drivers amplify those signals and drive the stepper motors
- Motors rotates a pulley/gear, and a timing belt transfers that rotation to a drive shaft
- The drive shaft is supported by needle bearings (chosen because they handle radial load well in tight spaces and keep friction low)
- The structure rotates around the corresponding axis — one motor controls pan, the other controls tilt.
Installing progress:
All parts were designed in Onshape, a cloud-based parametric CAD tool. I chose Onshape specifically because it's parametric — every dimension is a variable. When I discovered during testing that a hole was too small, I changed one number and re-exported. No redrawing.
Parts I designed:
- Base plate with gear mount
- Chassis frame with motor mounts
- Tilt arm/ swing arm assembly
- Pulley housings
- LED tube mounting bracket
- Counterweight extension arm
Every part was exported as STL and printed on an FDM 3D printer.
Onshape modeling progress:
3D printing progress:
First Prototype: What went wrong
It moved. But it had five problems:
Problem 1 : Threaded inserts need to be heat-set into the plastic. The holes I designed were sized for the insert outer diameter, but didn't account for the melted plastic displacement during heat-setting. The inserts wouldn't seat properly.
Fix: Made the holes bigger.
Problem 2 : Screws couldn't pass through the frame Two mounting points on the chassis didn't have through-holes. I had designed the screw positions but forgot to add clearance holes on the frame plate they pass through.
Fix: Boolean-cut two holes to the chassis.
Problem 3 : Belt chain lacked clearance
The timing belt was rubbing against the inside of the housing during tilt motion. Not enough space between the belt path and the walls.
Fix: Extended the overall length of the machine body to give the belt more room to travel without contact.
Problem 4 : LED tube not secured The 0.5m light tube was just resting in a cradle. When the motors ran, the tube swayed and bounced. At higher speeds it was unusable.
Fix: Designing a proper mounting bracket that clamps the tube using its actual dimensions, or attaching directly to the tube's built-in locking mechanism.
Fix: Extended the counterweight arm to increase the moment arm, allowing a heavier counterweight at a greater distance from the pivot. More torque with the same weight.
All five issues were addressed, and I printed a second prototype with the revised parts.
I wrote the control code using the AccelStepper library. The key challenge: both motors need to run simultaneously at different speeds and swing angles, and the Arduino is single-threaded.
The solution is
runSpeed() — a non-blocking function that advances the motor by one step if enough time has passed since the last step. By calling runSpeed() for both motors in every loop iteration, they effectively run in parallel.Configuration:
Full steps per revolution: 200 (standard 1.8° stepper)
Micro-stepping: 16x (3,200 steps/rev)
Pan swing angle: 100°
Tilt swing angle: 80°
Pan speed: 300 steps/sec
Tilt speed: 300 steps/sec
The motors move the tube. But the tube itself has 36 individually addressable RGB LEDs — it needs color data.
Hardware wiring
Power supply → Art-Net brain box → LED tube (via DMX cable) → done
The Art-Net box sits in the middle. It takes data from my computer over a standard Ethernet cable and converts it into DMX signal that the tube understands. The DMX male connector on the tube plugs into the female side on the Art-Net box.
Network configuration I used Advatek to put everything on the same IP subnet — the Art-Net box, TouchDesigner's output, and the tube's DMX universe. If they're not on the same range, the Art-Net box ignores the data.
Pattern control Inside TouchDesigner, I built mainly a four-node chain:
Pattern — generate any visual (gradient, chase, pulse, audio-reactive, etc.)
LookupTexture — sample that pattern down to exactly 36 RGB values in each LED
LookupChannel - sample that pattern down to exact number of Pan/Tilt values in each LED
DMXFixture POP — map those values to the correct DMX channels
DMXOut POP — send it over Art-Net to the tube
I change a pattern on screen, the tube updates instantly. No rendering, no export, real-time
The physical fixture is one half. The other half is a virtual replica inside Unreal Engine, controlled by the same DMX protocol used in real lighting consoles. This lets me pre-visualize lighting designs before building them physically.
DMX channel map
The fixture uses 110 DMX channels:
| Channels | Function |
|---|---|
| Ch 1 | Pan (0–255 maps to ±270°) |
| Ch 2 | Tilt (0–255 maps to ±135°) |
| Ch 3–110 | 36 RGB LEDs (3 channels each: R, G, B) |
Basically I set number of lights each fixture inside Fixture Matrix Config. I added three attributes as Red, Green Blue under Cell Attributes. I add Pan and Tilt two attributes in the Function. Then, I set them into the right universe/patch inside Fixture Patch.
For code. I wrote the full C++ header (.h) and implementation (.cpp): component hierarchy, pan/tilt rotation logic, per-LED material assignment, and DMX delegate binding.
After that, I let claude-code to learn my LED tube blueprint structure. And based on the learning, claude code generate me new blueprints that would allow fan-shape fixtures with Pan, Tilt, Color. Below are some experiments I played with:
I have three kinds of blueprints: three blades, five blades and six blades. Below are the structure of blueprints: