Arnov Sharma
Published © MIT

LIGHT BLOCK- Smart Lamp

DIY RGB LED lamp with glass brick/block and ESP32

BeginnerFull instructions provided1 hour237
LIGHT BLOCK- Smart Lamp

Things used in this project

Hardware components

PCBWay Custom PCB
PCBWay Custom PCB
×1

Software apps and online services

Fusion
Autodesk Fusion
Arduino Web Editor
Arduino Web Editor

Hand tools and fabrication machines

3D Printer (generic)
3D Printer (generic)

Story

Read more

Custom parts and enclosures

cad file

Schematics

SCH

Code

code

C/C++
#include <WiFi.h>
#include <WebServer.h>
#include <FastLED.h>

/* ================= LED CONFIG ================= */
#define LED_PIN     5
#define LED_COUNT   7
#define LED_TYPE    WS2812B
#define COLOR_ORDER GRB

CRGB leds[LED_COUNT];

/* ================= WIFI ================= */
const char* ssid     = "SSID";
const char* password = "PASS";

/* ================= WEB ================= */
WebServer server(80);

/* ================= MODES ================= */
enum Mode {
  MODE_STATIC,
  MODE_OCEAN,
  MODE_FIRE,
  MODE_OFF
};

Mode currentMode = MODE_STATIC;

/* ================= UI ================= */
const char PAGE_HTML[] PROGMEM = R"rawliteral(
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Brick of Light</title>
<style>
  html, body {
    margin: 0;
    padding: 0;
    height: 100%;
    background: #0f0f14;
    display: flex;
    justify-content: center;
    align-items: center;
    font-family: system-ui, -apple-system, BlinkMacSystemFont, sans-serif;
    color: white;
  }
  .container {
    text-align: center;
  }
  h1 {
    font-weight: 500;
    letter-spacing: 0.05em;
    margin-bottom: 20px;
    opacity: 0.9;
  }
  canvas {
    touch-action: none;
  }
  .modes {
    margin-top: 22px;
    display: flex;
    justify-content: center;
    gap: 18px;
  }
  .mode-btn {
    width: 52px;
    height: 52px;
    border-radius: 50%;
    border: none;
    cursor: pointer;
  }
  .ocean {
    background: radial-gradient(circle at 30% 30%, #4fd1c5, #1e4fd8);
  }
  .fire {
    background: radial-gradient(circle at 30% 30%, #ffd166, #ff6b00);
  }
  .off {
    background: radial-gradient(circle at 30% 30%, #444, #111);
  }
  .mode-btn:active {
    transform: scale(0.95);
  }
</style>
</head>

<body>
<div class="container">
  <h1>Brick of Light</h1>
  <canvas id="wheel" width="300" height="300"></canvas>

  <div class="modes">
    <button class="mode-btn ocean" onclick="fetch('/mode?m=ocean')"></button>
    <button class="mode-btn fire" onclick="fetch('/mode?m=fire')"></button>
    <button class="mode-btn off" onclick="fetch('/mode?m=off')"></button>
  </div>
</div>

<script>
const canvas = document.getElementById("wheel");
const ctx = canvas.getContext("2d");
const r = canvas.width / 2;

function drawWheel() {
  for (let a = 0; a < 360; a++) {
    const s = (a - 1) * Math.PI / 180;
    const e = a * Math.PI / 180;
    ctx.beginPath();
    ctx.moveTo(r, r);
    ctx.arc(r, r, r, s, e);
    ctx.closePath();
    ctx.fillStyle = `hsl(${a},100%,50%)`;
    ctx.fill();
  }
}

function hslToRgb(h, s, l) {
  let r, g, b;
  if (s === 0) r = g = b = l;
  else {
    const f = (p, q, t) => {
      if (t < 0) t += 1;
      if (t > 1) t -= 1;
      if (t < 1/6) return p + (q - p) * 6 * t;
      if (t < 1/2) return q;
      if (t < 2/3) return p + (q - p) * (2/3 - t) * 6;
      return p;
    };
    const q = l < 0.5 ? l * (1 + s) : l + s - l * s;
    const p = 2 * l - q;
    r = f(p, q, h + 1/3);
    g = f(p, q, h);
    b = f(p, q, h - 1/3);
  }
  return [Math.round(r*255), Math.round(g*255), Math.round(b*255)];
}

function sendColor(x, y) {
  const dx = x - r;
  const dy = y - r;
  if (Math.sqrt(dx*dx + dy*dy) > r) return;

  let angle = Math.atan2(dy, dx) * 180 / Math.PI;
  if (angle < 0) angle += 360;

  const rgb = hslToRgb(angle/360, 1, 0.5);
  const hex = rgb.map(v => v.toString(16).padStart(2,"0")).join("");
  fetch(`/set?color=${hex}`);
}

canvas.addEventListener("pointerdown", e => handle(e));
canvas.addEventListener("pointermove", e => { if (e.buttons) handle(e); });

function handle(e) {
  const rect = canvas.getBoundingClientRect();
  sendColor(e.clientX - rect.left, e.clientY - rect.top);
}

drawWheel();
</script>
</body>
</html>
)rawliteral";

/* ================= HANDLERS ================= */

void handleRoot() {
  server.send(200, "text/html", PAGE_HTML);
}

void handleSetColor() {
  currentMode = MODE_STATIC;

  String hex = server.arg("color");
  long v = strtol(hex.c_str(), NULL, 16);

  fill_solid(leds, LED_COUNT, CRGB(v >> 16, (v >> 8) & 0xFF, v & 0xFF));
  FastLED.show();

  server.send(200, "text/plain", "OK");
}

void handleMode() {
  String m = server.arg("m");

  if (m == "ocean") currentMode = MODE_OCEAN;
  else if (m == "fire") currentMode = MODE_FIRE;
  else if (m == "off") {
    currentMode = MODE_OFF;
    FastLED.clear();
    FastLED.show();
  }

  server.send(200, "text/plain", "OK");
}

/* ================= ANIMATIONS ================= */

void oceanAnimation() {
  static uint8_t hue = 130;
  static int8_t dir = 1;

  fill_solid(leds, LED_COUNT, CHSV(hue, 180, 220));
  FastLED.show();

  hue += dir;
  if (hue > 160 || hue < 120) dir = -dir;
  delay(30);
}

void fireAnimation() {
  for (int i = 0; i < LED_COUNT; i++) {
    uint8_t flicker = random(150, 255);
    leds[i] = CRGB(flicker, random(60, 100), 0);
  }
  FastLED.show();
  delay(50);
}

/* ================= SETUP ================= */

void setup() {
  Serial.begin(115200);
  delay(1500);

  FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, LED_COUNT);
  FastLED.setBrightness(255);
  FastLED.clear();
  FastLED.show();

  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) delay(300);

  Serial.print("Brick of Light → http://");
  Serial.println(WiFi.localIP());

  server.on("/", handleRoot);
  server.on("/set", handleSetColor);
  server.on("/mode", handleMode);
  server.begin();
}

/* ================= LOOP ================= */

void loop() {
  server.handleClient();

  if (currentMode == MODE_OCEAN) oceanAnimation();
  else if (currentMode == MODE_FIRE) fireAnimation();
}

Credits

Arnov Sharma
368 projects • 377 followers
I'm Arnov. I build, design, and experiment with tech—3D printing, PCB design, and retro consoles are my jam.

Comments