Batman – Lamp

The Batman Lamp is a custom-designed RGB LED lamp based on a Wemos D1 Mini and 37 WS2812 RGB LEDs.

The enclosure was designed specifically for 3D printing and is made entirely from black and white PETG.

The lamp is controlled using a single push button, providing multiple functions such as switching lighting modes and adjusting brightness.

Images

Front


Front (Power off)

The front of the Batman lamp is printed with a 0.2 mm thick black top layer, making the iconic Batman logo clearly visible even when the lamp is turned off. Behind it, two white layers provide additional strength while also acting as a light diffuser. Although the black front slightly reduces the light output, this design creates the impressive illuminated effect shown in the image above when the lamp is switched on.


Custom LED Assembly

The LEDs are mounted on a custom carrier plate shaped like the Batman logo, securely glued in place, and carefully soldered together. (Sharp-eyed viewers may notice a section with different-colored wires—I had to improvise because I ran out of the original wire.)

The complete LED assembly is inserted into the front housing from the rear and secured with screws. This ensures that no electronic components obstruct the LEDs, resulting in even illumination of the logo. The push button and the Wemos controller are installed inside the rear cover of the enclosure.


Features

  • Multiple programmed lighting modes
  • Static colors
  • Color cycling
  • Smooth RGB color transitions
  • Dynamic lighting animations
  • 5 brightness levels
  • Automatic saving of the last settings
  • Single-button operation

Controls

ActionFunction
Short pressSwitch lighting mode
Long pressAdjust brigthness

The last selected lighting mode and brightness level are automatically saved and restored the next time the lamp is powered on.


Elektronics

The lamp uses:

  • Wemos D1 Mini
  • 37x WS2812 RGB LEDs
  • Push button (The push button is soldered onto a 20 × 20 mm perfboard.)
  • 10kΩ resistor

The push button is wired as a pull-up circuit:

  • GPIO5 → 10kΩ → 3,3V
  • GPIO5 → Push button → GND

Screws

QuantityTypePurpose
2xM2 x 5mmPush button PCB
8xM3 x 8mmEnclosure cover / LED holder

Conclusion

I’m very pleased with the final result. The lamp has a clean and elegant appearance, and the contrast between the completely black Batman logo when switched off and the striking illuminated effect when turned on is what makes this design so special to me.

The internal layout is just as satisfying: all electronic components fit neatly inside the enclosure, keeping everything organized while ensuring nothing interferes with the lighting effect.

I hope you enjoy this Batman lamp as much as I do. Have fun building your own!

Arduino Quellcode anzeigen

// Beispiel Arduino Code

#include 
#include 
// by DL4KER www.dl4ker.de
// ================= EINSTELLUNGEN =================
#define LED_PIN       2
#define BUTTON_PIN    5
#define NUM_LEDS      37
#define NUM_PROGRAMS  8
#define NUM_DIM_LEVELS 5

#define LED_TYPE      WS2812B
#define COLOR_ORDER   GRB

CRGB leds[NUM_LEDS];

// 5 Dimmstufen: 20%, 40%, 60%, 80%, 100%
const uint8_t dimLevel[NUM_DIM_LEVELS] = {
  51, 102, 153, 204, 255
};

// ================= SPEICHER =================
uint8_t program;
uint8_t dim;

#define EE_PROGRAM 0
#define EE_DIM     1

// ================= TASTER =================
bool buttonState = HIGH;
bool lastButtonState = HIGH;
bool longDone = false;

unsigned long buttonTime = 0;
unsigned long debounceTime = 0;

#define DEBOUNCE 40
#define LONG_PRESS 700

// ================= ANIMATION =================
unsigned long animTime = 0;

// =================================================
// SETUP
// =================================================
void setup() {

  pinMode(BUTTON_PIN, INPUT_PULLUP);

  EEPROM.begin(8);

  program = EEPROM.read(EE_PROGRAM);
  dim     = EEPROM.read(EE_DIM);

  if (program >= NUM_PROGRAMS) program = 0;
  if (dim >= NUM_DIM_LEVELS) dim = NUM_DIM_LEVELS - 1;

  FastLED.addLeds(
    leds, NUM_LEDS
  );

  FastLED.setBrightness(dimLevel[dim]);
  FastLED.clear();
  FastLED.show();

  randomSeed(analogRead(A0));

  animTime = millis();
}

// =================================================
// LOOP
// =================================================
void loop() {

  handleButton();

  switch (program) {

    case 0: randomProgram(); break;
    case 1: colorPulse();    break;
    case 2: fixedColor(CRGB::Blue);   break;
    case 3: fixedColor(CRGB::Red);    break;
    case 4: fixedColor(CRGB::Green);  break;
    case 5: fixedColor(CRGB::White);  break;
    case 6: fixedColor(CRGB::Yellow); break;
    case 7: whitePulse();    break;
  }
}

// =================================================
// TASTER
// =================================================
void handleButton() {

  bool reading = digitalRead(BUTTON_PIN);

  if (reading != lastButtonState)
    debounceTime = millis();

  if (millis() - debounceTime > DEBOUNCE) {

    if (reading != buttonState) {

      buttonState = reading;

      // gedrückt
      if (buttonState == LOW) {
        buttonTime = millis();
        longDone = false;
      }

      // losgelassen
      else {

        // kurzer Druck
        if (!longDone &&
            millis() - buttonTime < LONG_PRESS) {

          nextProgram();
        }
      }
    }
  }

  // langer Druck
  if (buttonState == LOW &&
      !longDone &&
      millis() - buttonTime >= LONG_PRESS) {

    nextDim();
    longDone = true;
  }

  lastButtonState = reading;
}

// =================================================
// NÄCHSTES PROGRAMM
// =================================================
void nextProgram() {

  program++;

  if (program >= NUM_PROGRAMS)
    program = 0;

  saveSettings();

  animTime = millis();

  // Anzeige kurz zurücksetzen
  fill_solid(leds, NUM_LEDS, CRGB::Black);
  FastLED.show();
}

// =================================================
// NÄCHSTE DIMMSTUFE
// =================================================
void nextDim() {

  dim++;

  if (dim >= NUM_DIM_LEVELS)
    dim = 0;

  // Helligkeit SOFORT ändern
  FastLED.setBrightness(dimLevel[dim]);

  saveSettings();

  // sofort neu anzeigen
  FastLED.show();
}

// =================================================
// SPEICHERN
// =================================================
void saveSettings() {

  EEPROM.write(EE_PROGRAM, program);
  EEPROM.write(EE_DIM, dim);
  EEPROM.commit();
}

// =================================================
// PROGRAMM 1
// JEDE ZWEITE LED / ZUFALLSFARBEN
// =================================================
void randomProgram() {

  if (millis() - animTime < 1500)
    return;

  animTime = millis();

  static bool phase = false;
  phase = !phase;

  for (uint8_t i = 0; i < NUM_LEDS; i++) {

    if ((i & 1) == phase)
      leds[i] = CHSV(random8(), 255, 255);
    else
      leds[i] = CRGB::Black;
  }

  FastLED.show();
}

// =================================================
// PROGRAMM 2
// WEISS -> GRÜN -> GELB -> ROT
// 60 SEKUNDEN
// =================================================
void colorPulse() {

  uint32_t t = (millis() - animTime) % 60000UL;

  CRGB c;

  if (t < 15000) {

    // Weiß -> Grün
    uint8_t x = map(t, 0, 15000, 0, 255);

    c.r = 255 - x;
    c.g = 255;
    c.b = 255 - x;

  } else if (t < 30000) {

    // Grün -> Gelb
    uint8_t x = map(t, 15000, 30000, 0, 255);

    c.r = x;
    c.g = 255;
    c.b = 0;

  } else {

    // Gelb -> Rot
    uint8_t x = map(t, 30000, 60000, 255, 0);

    c.r = 255;
    c.g = x;
    c.b = 0;
  }

  fill_solid(leds, NUM_LEDS, c);

  FastLED.show();
}

// =================================================
// FESTE FARBE
// =================================================
void fixedColor(CRGB color) {

  static uint8_t lastProgram = 255;

  if (lastProgram != program) {

    fill_solid(leds, NUM_LEDS, color);
    FastLED.show();

    lastProgram = program;
  }
}

// =================================================
// WEISS PULSIEREND
// =================================================
void whitePulse() {

  uint16_t t = millis() % 4000;

  uint8_t b;

  if (t < 2000)
    b = map(t, 0, 2000, 5, 255);
  else
    b = map(t, 2000, 4000, 255, 5);

  // Dimmstufe berücksichtigen
  b = ((uint16_t)b * dimLevel[dim]) / 255;

  fill_solid(leds, NUM_LEDS, CRGB(b, b, b));

  FastLED.show();
}