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
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
Testing the Stepper Motor and Gears
- Adafruit STSPIN220 Stepper Motor Driver Breakout Board
- Stepper motor - NEMA-17 size - 200 steps/rev, 12V 350mA
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();
}
Assembled All Laser-cut Pieces on the Wood Stick
Created the Lamp’s Base
Created the Lampshades
Put Everything Together
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.