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."
- 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 tilt vertically?
→ 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 or Wi-Fi)
- 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
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.
I printed all the parts, assembled them with purchased hardware (stepper motors, A4988 drivers, GT2 belts, shafts, bearings, Arduino Uno, threaded insert kits), and connected the Arduino to my computer.
It moved. But it had five problems:
Problem 1: Gear holes too small for threaded insertsThreaded 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.
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.
Problem 5: Counterweight insufficient The tilt arm uses a counterweight on the opposite side of the pivot to balance the tube's weight. With the 0.5m tube, even the heaviest counter weight I could fit wasn't enough — the motor was fighting gravity on every tilt cycle.
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(or receiving data from somewhere else).
Pattern control in TouchDesigner 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, one per 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.
Unreal Engine 5.5 — DMX Integration
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) |