
Project Overview
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.
Background
For my final project, I am developing a custom star light projector for my upcoming baby. The goal is to create a device that casts a gentle, soothing starlight pattern onto the nursery ceiling. I plan to use an Arduino microcontroller to program and control a series of LED strips, creating a simple and calming visual effect. This project is a way for me to combine new technical skills with a personal, heartfelt gift for my baby.
Materials
- 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
- Adafruit STSPIN220 Stepper Motor Driver Breakout Board
- Cardboard
Overall Design Process
1. Design of mechanical gear system and lamp body ring structure for laser cutting




2. Testing the Stepper Motor and gears

3. Soldered the circuit and coding the control buttons.


4. Assembled all laser-cut pieces on the wood stick


5. Created the lamp’s base
6. Created the lampshades


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.
Next Step:
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.
Special Thanks
Becky, Huy& Kyle
Luis and baby
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();
}

