Introduction: The Starry Sky Table Lamp
The Starry Sky Table Lamp is a kinetic light sculpture that bridges digital fabrication with traditional craftsmanship. By integrating 3D printing, laser cutting, and woodworking, the project features a custom-built mechanical assembly powered by a rotary motor and Arduino microcomputer. The design is anchored by a central ring-light and crowned with a high-density panel of 684 LEDs, creating a dynamic, rotating visual experience.
Supplies
- 15 inch Plywood stick
- 1/8″ Acrylic Board_black
- 1/8″ Acrylic Board_white
- 1/8″ Acrylic Board_Transparent
- LED strips
- LED Pad 16*16
- Arduino Board
- Wires
- Automobile bearing
- Stepper Motor
- Cardboard
Step 1: Design of Mechanical Gear System and Lamp Body Ring Structure for Laser Cutting
I designed the gear models in Adobe Illustrator and used the Epilog Fusion Pro (36” x 24”) for laser cutting
Step 2: Testing the Stepper Motor and Gears
- Adafruit STSPIN220 Stepper Motor Driver Breakout Board
- Stepper motor - NEMA-17 size - 200 steps/rev, 12V 350mA
Step 3: Soldered the Circuit and Coding the Control Buttons.
Codes:
#include <Adafruit_NeoPixel.h>
#include <Adafruit_STSPIN220.h>
#ifdef __AVR__
#include <avr/power.h>
#endif
// ==========================================
// CONFIGURATION
// ==========================================
// — LED PINS & SETTINGS —
#define LED_BUTTON_PIN 12
#define LED_PIXEL_PIN 8
#define NUM_LEDS 428
#define RAINBOW_SPEED 10 // Lower is faster (ms between updates)
// — MOTOR PINS & SETTINGS —
// Motor Control Pins
const int DIR_PIN = 2;
const int STEP_PIN = 3;
const int MODE1_PIN = 4;
const int MODE2_PIN = 5;
const int EN_FAULT_PIN = 6;
const int STBY_RESET_PIN = 7;
const int SPEED_PIN = A0; // Potentiometer
const int stepsPerRevolution = 200; // Standard Stepper
// ==========================================
// OBJECTS & VARIABLES
// ==========================================
// Define the NeoPixel object
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIXEL_PIN, NEO_GRB + NEO_KHZ800);
// Define Stepper object
Adafruit_STSPIN220 myStepper(stepsPerRevolution, STEP_PIN, DIR_PIN,
MODE1_PIN, MODE2_PIN, EN_FAULT_PIN, STBY_RESET_PIN);
// LED Variables
int ledMode = 0; // 0=Off, 1=Dim, 2=Bright, 3=Rainbow
int lastButtonState = HIGH;
long firstPixelHue = 0;
unsigned long lastRainbowUpdate = 0; // Timer for animation
// Motor Variables
int currentSpeed = 0;
unsigned long lastSpeedPrint = 0; // NEW: Timer for printing speed
void setup() {
Serial.begin(115200); // Make sure your Serial Monitor matches this number!
// — SETUP LEDS —
pinMode(LED_BUTTON_PIN, INPUT_PULLUP);
strip.begin();
strip.show(); // Initialize off
// — SETUP MOTOR —
// Set microstepping mode to 1/16 steps (smoother)
myStepper.setStepMode(STSPIN220_STEP_1_16);
Serial.println(“System Ready: LEDs & Motor Combined.”);
}
void loop() {
// ==========================================
// 1. LED BUTTON LOGIC
// ==========================================
int currentButtonState = digitalRead(LED_BUTTON_PIN);
if (currentButtonState == LOW && lastButtonState == HIGH) {
ledMode++;
if (ledMode > 3) ledMode = 0;
// Handle Static Modes (One-time update)
if (ledMode == 0) {
strip.clear();
strip.show();
Serial.println(“LED: OFF”);
}
else if (ledMode == 1) {
strip.setBrightness(10);
strip.fill(strip.Color(255, 255, 0)); // Yellow
strip.show();
Serial.println(“LED: Yellow (Low)”);
}
else if (ledMode == 2) {
strip.setBrightness(80);
strip.fill(strip.Color(255, 255, 0)); // Yellow
strip.show();
Serial.println(“LED: Yellow (High)”);
}
else if (ledMode == 3) {
strip.setBrightness(80);
Serial.println(“LED: Rainbow Mode”);
}
delay(50); // Small debounce
}
lastButtonState = currentButtonState;
// ==========================================
// 2. LED ANIMATION (If in Rainbow Mode)
// ==========================================
if (ledMode == 3) {
// Non-blocking timer: Only update if enough time has passed
if (millis() – lastRainbowUpdate > RAINBOW_SPEED) {
lastRainbowUpdate = millis();
drawRainbowHorizon();
}
}
// ==========================================
// 3. MOTOR LOGIC
// ==========================================
// Read Potentiometer
int sensorValue = analogRead(SPEED_PIN);
currentSpeed = map(sensorValue, 0, 1023, 0, 120); // Map to 0-120 RPM
// — NEW: PRINT SPEED EVERY 500ms —
if (millis() – lastSpeedPrint > 500) {
lastSpeedPrint = millis();
Serial.print(“Motor Speed: “);
Serial.print(currentSpeed);
Serial.println(” RPM”);
}
if (currentSpeed > 0) {
myStepper.setSpeed(currentSpeed);
// Take 1 step per loop iteration
myStepper.step(1);
}
}
// — HELPER FUNCTION: RAINBOW —
void drawRainbowHorizon() {
// Update Hue
firstPixelHue += 256;
for(int i=0; i<strip.numPixels(); i++) {
int pixelHue = firstPixelHue + (i * 65536L / strip.numPixels());
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(pixelHue)));
}
strip.show();
}
Step 4: Assembled All Laser-cut Pieces on the Wood Stick
Step 5: Created the Lamp’s Base
Step 6: Created the Lampshades
Step 7: Put Everything Together
Step 8: Next Step
Trial and Error
The most significant challenge was power distribution. Driving both the motor and the high-density array of 684 LEDs simultaneously exceeded the current capacity of my initial single-Arduino setup. While the system could handle the ring light and low-speed motor rotation, activating the main LED panel caused a voltage drop that crashed the system (brownout). To resolve this, I isolated the loads by introducing a second Arduino dedicated solely to controlling the LED matrix.
My next steps are to optimize the electrical system and design a housing unit that seamlessly integrates the internal electronics with an external control panel.

