Đèn Treo Tường Mặt Trăng Phát Sáng (Phiên bản nhỏ ~240mm)
Đèn treo tường mặt trăng 2D với đèn LED RGB có thể điều khiển qua WiFi. Hiển thị cấu trúc địa hình chi tiết của mặt trăng và có thể tùy chỉnh màu sắc, độ sáng, hiệu ứng qua giao diện web trực quan trên điện thoại.
Mô tả
ĐÈN TREO TƯỜNG MẶT TRĂNG CÓ ĐÈN (Phiên bản nhỏ) Điều khiển WiFi Đèn địa hình 2D

Một mô hình 2D tuyệt đẹp về mặt trăng, được chiếu sáng từ phía sau bằng đèn LED có thể định địa chỉ và có thể điều khiển bằng điện thoại thông minh. Sự kết hợp hoàn hảo giữa nghệ thuật, khoa học và công nghệ nhà thông minh!
Đèn treo tường mặt trăng phát sáng này hiển thị cấu trúc bề mặt chi tiết của mặt trăng dưới dạng địa hình. Nhờ đèn nền LED RGB, bạn có thể điều khiển hoàn toàn màu sắc, độ sáng và hiệu ứng ánh sáng từ điện thoại thông minh mà không cần cài đặt ứng dụng! Giao diện web thanh lịch giúp việc điều khiển trở nên trực quan và tiện lợi.
Địa hình in 3D tạo ra các bóng khác nhau tùy thuộc vào góc chiếu sáng và màu sắc, làm nổi bật rõ ràng các miệng núi lửa, biển Mặt Trăng (đồng bằng tối) và cao nguyên của mặt trăng. Một điểm nhấn thực sự cho mọi người đam mê không gian vũ trụ!
Tính năng
Cấu trúc bề mặt chi tiết của mặt trăng
💡 Đèn nền LED RGB
21 đèn LED WS2812B có thể định địa chỉ
📱 Điều khiển WiFi Kiểm soát hoàn toàn qua điện thoại thông minh/máy tính bảng
🎨 Hàng triệu màu sắc Lựa chọn màu thời gian thực với Color Picker
🌈 Hiệu ứng động Hoạt ảnh Cầu vồng và Mờ dần
💫 Độ sáng có thể điều chỉnh Điều chỉnh liên tục 0-100%
⚡ Bộ điều chỉnh tốc độ mờ dần Tốc độ hoạt ảnh có thể tùy chỉnh
🌙 Giao diện Chế độ Tối tinh tế Thiết kế màu than antraxit với các điểm nhấn màu xanh lam
🔌 Hỗ trợ mDNS Truy cập qua "ledlampe.local" mà không cần địa chỉ IP
🖼 Treo tường Móc treo tích hợp ở mặt sau
Vật liệu sử dụng
In 3D
- Sợi: PLA hoặc PETG (nên dùng màu trắng, xám hoặc mờ)
- Số lượng: Khoảng 150g tùy thuộc vào kích thước và độ đặc ruột
- Thời gian in: khoảng 6-7 giờ (tùy thuộc vào kích thước, cài đặt và máy in)
Điện tử
- 1x Bo mạch phát triển ESP32 (ESP32 DevKit V1 hoặc tương tự)
- 1x Dải đèn LED WS2812B (5V, 21 đèn LED - có thể điều chỉnh độ dài tùy kích thước)
- 1x Cáp USB
Tùy chọn:
- Tấm khuếch tán/màng khuếch tán cho ánh sáng đều hơn
- Đèn LED thông thường (KHÔNG có ESP32 và mã)


Phần mềm
- Arduino IDE (Phiên bản 1.8.x hoặc 2.x)
- Gói hỗ trợ bo mạch ESP32
- Thư viện Adafruit NeoPixel
- Thư viện ESPmDNS (có trong lõi ESP32)
Công cụ & Phụ kiện
- Mỏ hàn (tùy chọn, để kết nối chắc chắn hơn)
- Cáp / Dây
- Keo nến, keo siêu dính hoặc băng dính hai mặt
- Ống luồn dây (tùy chọn, để đi dây gọn gàng)
- Vít/Tắc kê/Đinh để treo tường
Cài đặt in
Cài đặt đề xuất
- Chiều cao lớp: 0.2mm (0.12mm để có thêm chi tiết)
- Độ đặc ruột (Infill): 15-20% (Gyroid hoặc Grid)
- Hỗ trợ (Supports): Không (hiển thị trên một số máy in nhưng không cần thiết)
- Brim/Raft: Tùy chọn để bám dính tốt hơn
- Tốc độ in: Tùy chọn
- Độ dày tường: 3-4 lớp
- Lớp trên/dưới: 4-5
Mẹo chọn vật liệu
- PLA trắng/xám: Giao diện mặt trăng cổ điển, hiển thị địa hình tốt nhất
- Sợi mờ: Cho ánh sáng xuyên qua nhiều hơn, hiệu ứng dịu hơn
- PLA Silk/Pearlescent: Hiệu ứng lung linh, huyền ảo
- PETG: Bền hơn và chịu nhiệt tốt hơn (tốt nhất cho vòng, vì đèn LED có thể khá nóng)
Đấu dây
- Dải LED DIN → ESP32 GPIO 16
- Dải LED 5V → Nguồn 5V+ (dương)
- Dải LED GND → GND (âm)
(Đối với hơn 25 đèn LED, tôi khuyên dùng thêm nguồn điện.)
CHỈ DÀNH CHO TRÊN 25 ĐÈN LED
- (hoặc qua USB) ESP32 GND → Nguồn GND (chung mass!)
Quan trọng:
- Luôn kết nối chung mass (GND) giữa ESP32, Dải LED và Nguồn điện!
- Đối với 21 đèn LED: Nguồn ít nhất 2A (21 × 60mA = 1.26A + dự phòng)
- Cố định Dải LED vào mặt trong của vòng.
Hướng dẫn lắp ráp
Bước 1: In 3D
- Tải tệp địa hình mặt trăng vào Slicer
- Điều chỉnh cài đặt (xem ở trên)
- In.
- Tùy chọn: Chà nhám và sơn lót bề mặt
Bước 2: Lắp đặt LED
- Cắt Dải LED theo chiều dài phù hợp (21 đèn LED cho kích thước mặc định)
- Cố định Dải LED vào mặt trong của vòng.
- Tùy chọn: Gắn vật liệu khuếch tán giữa đèn LED và mặt trăng
Bước 3: Đấu dây ESP32
- Hàn dây vào Dải LED hoặc gắn bằng đầu nối
- Đấu dây ESP32 theo sơ đồ trên
- Giấu ESP32 trong một hộp nhỏ.
- Hoặc gắn trực tiếp lên tường, ở mặt sau của mặt trăng.
Sử dụng
Lần khởi động đầu tiên
- Cấp nguồn cho ESP32
- Chờ cho đến khi kết nối WiFi được thiết lập (khoảng 10 giây)
- Mở trình duyệt trên điện thoại thông minh/máy tính bảng/PC
- Nhập địa chỉ: http://ledlampe.local
- Giao diện sẽ xuất hiện!
Điều khiển
- 🎨 Chọn màu: Nhấn vào ô màu và chọn màu mong muốn
- Trắng Mặt Trăng cổ điển:
#FFFFFF - Cam ấm cho "Siêu Trăng":
#FF8C00 - Xanh lam huyền bí:
#4169E1 - Đỏ rùng rợn cho "Trăng máu":
#8B0000
- Trắng Mặt Trăng cổ điển:
- 💡 Độ sáng: Kéo thanh trượt (hoàn hảo làm đèn ngủ ở mức 20-30%)
- ✨ Hiệu ứng:
- Một màu: Màu cố định bạn chọn
- 🌈 Cầu vồng: Dải màu chảy qua tất cả đèn LED
- 💫 Mờ dần: Nhấp nháy nhẹ nhàng (có thể điều chỉnh tốc độ)
- ⭕ Tắt: Nút màu đỏ
Ý tưởng tình huống
- Đèn ngủ: Trắng ấm ở độ sáng 25%
- Đèn đọc sách: Trắng trung tính ở độ sáng 60-80%
- Đèn tạo không khí: Xanh lam/Tím dịu với hiệu ứng mờ dần
- Chế độ tiệc tùng: Hiệu ứng cầu vồng ở độ sáng tối đa
Điều chỉnh & sửa đổi
Điều chỉnh mã
- Thay đổi số lượng LED: Sửa
#define NUM_LEDS 21 - Thay đổi chân GPIO: Đặt
#define LED_PIN 16sang chân khác - Thay đổi tên mDNS:
MDNS.begin("mondlampe")thay vì "ledlampe" để thay đổi "địa chỉ tên miền phụ" - Màu mờ dần: Điều chỉnh giá trị RGB trong mã dưới
case 2: - Tốc độ cầu vồng: Tăng/giảm
hue += 256
Nâng cấp phần cứng
- Thêm đèn LED: Cho đèn lớn hơn hoặc chiếu sáng mạnh hơn
- Cảm biến chuyển động: Cảm biến PIR trên ESP32 để tự động bật
- Cảm biến ánh sáng: Tự động điều chỉnh độ sáng theo thời gian trong ngày
- Cảm biến cảm ứng: Điều khiển vật lý trực tiếp trên đèn
- Phiên bản pin: Với pin LiPo và mô-đun sạc cho phiên bản không dây
Biến thể thiết kế
- Thiên thể khác: Sao Hỏa, Trái Đất, Sao Thổ (có vòng!)
- Cung hoàng đạo: Chòm sao dưới dạng hình nổi có đèn chiếu sáng
- Bộ hành tinh: Nhiều hành tinh nhỏ hơn làm bộ sưu tập treo tường
- Sợi đổi màu: In đa màu sắc để tăng thêm hiệu ứng
Khắc phục sự cố
"ledlampe.local" không hoạt động
- Windows: Cài đặt Bonjour Print Services
- Android: Sử dụng trình duyệt Chrome hoặc Firefox
- iOS/Mac: Nên hoạt động ngay lập tức
- Tùy chọn: Sử dụng địa chỉ IP từ Serial Monitor (ví dụ: 192.168.1.28)
Đèn LED nhấp nháy hoặc không sáng
- Kiểm tra nguồn điện: Sử dụng nguồn điện ngoài 5V với tối thiểu 2A
- Chung mass: Kết nối GND của ESP32, Dải LED và Nguồn điện!
- Đấu dây: DIN ở đúng chân? Cực tính có đúng không?
- Dải LED bị lỗi: Kiểm tra bằng một bản phác thảo nhấp nháy đơn giản
WiFi không kết nối
- SSID/Mật khẩu: Kiểm tra lỗi chính tả, không sử dụng ký tự đặc biệt
- Tần số: ESP32 chỉ hỗ trợ WLAN 2.4 GHz (không phải 5 GHz!)
- Cài đặt bộ định tuyến: Broadcasting SSID đã được bật chưa? Lọc MAC?
Chiếu sáng không đều
- Thêm bộ khuếch tán: Acrylic mờ hoặc giấy nến giữa đèn LED và mặt trăng
- Sử dụng nhiều đèn LED hơn: Khoảng cách gần hơn để phân bố đều hơn
- Vị trí đèn LED: Gắn đèn LED xa mặt trăng hơn
ESP32 quá nóng
- Thông gió: Hộp có khe thông gió
- Nguồn điện: Nguồn điện riêng cho đèn LED và ESP32
- Hoạt động liên tục: Bình thường, ESP32 sẽ nóng - chỉ nguy hiểm ở nhiệt độ >80°C
Lưu ý an toàn
⚠ Điện áp thấp 5V: Vô hại, nhưng vẫn cẩn thận với các đoản mạch
⚠ Nguồn điện: Chỉ sử dụng bộ nguồn được chứng nhận với công suất đủ
⚠ Quá nhiệt: Cẩn thận về nhiệt độ khi hoạt động liên tục
⚠ Treo tường: Đảm bảo cố định chắc chắn, đặc biệt với đèn nặng
Đây là mã:
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <Adafruit_NeoPixel.h>
// Dữ liệu WiFi - NHẬP VÀO ĐÂY
const char* ssid = "Tên mạng";
const char* password = "Khóa mạng";
// Cài đặt LED
#define LED_PIN 16 // Chân GPIO cho DIN
#define NUM_LEDS 21 // Số lượng đèn LED của bạn
Adafruit_NeoPixel strip(NUM_LEDS, LED_PIN, NEO_GRB + NEO_KHZ800);
WebServer server(80);
// Cài đặt hiện tại
int brightness = 128;
int r = 255, g = 0, b = 0;
int mode = 0; // 0=một màu, 1=cầu vồng, 2=mờ dần
int fadeSpeed = 30; // Tốc độ mờ dần theo ms
bool isOn = true;
bool needsUpdate = true; // Cờ cập nhật
// Giao diện Web HTML
const char* html = R"(
<!DOCTYPE html>
<html>
<head>
<meta charset='UTF-8'>
<meta name='viewport' content='width=device-width, initial-scale=1'>
<title>Bộ điều khiển LED</title>
<style>
* { box-sizing: border-box; margin: 0; padding: 0; }
body {
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
background: linear-gradient(135deg, #2c3e50 0%, #34495e 100%);
min-height: 100vh;
display: flex;
align-items: center;
justify-content: center;
padding: 20px;
}
.container {
max-width: 420px;
width: 100%;
background: #1a252f;
padding: 40px 30px;
border-radius: 25px;
box-shadow: 0 20px 60px rgba(0,0,0,0.5);
}
h1 {
color: #3498db;
margin-bottom: 35px;
font-size: 28px;
text-align: center;
font-weight: 600;
}
.section {
margin-bottom: 30px;
padding: 20px;
background: #243447;
border-radius: 15px;
}
label {
display: block;
margin-bottom: 12px;
font-weight: 600;
color: #ecf0f1;
font-size: 14px;
text-transform: uppercase;
letter-spacing: 0.5px;
}
.color-picker-wrapper {
display: flex;
align-items: center;
gap: 15px;
}
input[type='color'] {
width: 80px;
height: 80px;
border: 4px solid #34495e;
border-radius: 12px;
cursor: pointer;
box-shadow: 0 4px 15px rgba(0,0,0,0.3);
transition: transform 0.2s;
}
input[type='color']:hover {
transform: scale(1.05);
}
.color-info {
flex: 1;
font-size: 16px;
color: #95a5a6;
font-weight: 500;
}
input[type='range'] {
width: 100%;
height: 8px;
border-radius: 5px;
background: linear-gradient(to right, #34495e, #3498db);
outline: none;
-webkit-appearance: none;
}
input[type='range']::-webkit-slider-thumb {
-webkit-appearance: none;
width: 24px;
height: 24px;
border-radius: 50%;
background: #3498db;
cursor: pointer;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
transition: all 0.2s;
}
input[type='range']::-webkit-slider-thumb:hover {
background: #2980b9;
transform: scale(1.1);
}
input[type='range']::-moz-range-thumb {
width: 24px;
height: 24px;
border-radius: 50%;
background: #3498db;
cursor: pointer;
border: none;
box-shadow: 0 2px 8px rgba(0,0,0,0.3);
}
.value-display {
color: #3498db;
font-size: 18px;
font-weight: 700;
margin-top: 8px;
text-align: center;
}
.button-group {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 10px;
margin-top: 15px;
}
button {
background: linear-gradient(135deg, #3498db 0%, #2980b9 100%);
border: none;
color: white;
padding: 15px 20px;
font-size: 15px;
font-weight: 600;
border-radius: 12px;
cursor: pointer;
transition: all 0.3s;
box-shadow: 0 4px 15px rgba(52, 152, 219, 0.3);
}
button:hover {
transform: translateY(-2px);
box-shadow: 0 6px 20px rgba(52, 152, 219, 0.5);
}
button:active {
transform: translateY(0);
}
.btn-off {
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
box-shadow: 0 4px 15px rgba(231, 76, 60, 0.3);
width: 100%;
margin-top: 10px;
}
.btn-off:hover {
box-shadow: 0 6px 20px rgba(231, 76, 60, 0.5);
}
</style>
</head>
<body>
<div class='container'>
<h1>💡 LED Lampe</h1>
<div class='power-buttons'>
<button class='btn-on' onclick='turnOn()'>⚡ Einschalten</button>
<button class='btn-off' onclick='turnOff()'>⭕ Ausschalten</button>
</div>
<div class='section'>
<label>🎨 Farbe wählen</label>
<div class='color-picker-wrapper'>
<input type='color' id='color' value='#ff0000' oninput='setColorLive()'>
<div class='color-info' id='colorInfo'>Rot</div>
</div>
</div>
<div class='section'>
<label>💡 Helligkeit</label>
<input type='range' id='brightness' min='0' max='255' value='128' oninput='updateBrightDisplay()' onchange='setBrightness()'>
<div class='value-display' id='brightVal'>50%</div>
</div>
<div class='section'>
<label>✨ Effekte</label>
<div class='button-group'>
<button onclick='setMode(0)'>🎨 Einfarbig</button>
<button onclick='setMode(1)'>🌈 Regenbogen</button>
<button onclick='setMode(2)'>💫 Fade</button>
</div>
</div>
<div class='section' id='fadeSection' style='display:none;'>
<label>⚡ Fade Geschwindigkeit</label>
<input type='range' id='fadeSpeed' min='10' max='100' value='30' oninput='updateFadeDisplay()' onchange='setFadeSpeed()'>
<div class='value-display' id='fadeVal'>Normal</div>
</div>
</div>
<script>
let brightTimeout;
let fadeTimeout;
function updateBrightDisplay() {
let b = document.getElementById('brightness').value;
let pct = Math.round((b/255)*100);
document.getElementById('brightVal').innerText = pct + '%';
}
function updateFadeDisplay() {
let speed = document.getElementById('fadeSpeed').value;
let text = speed < 30 ? 'Schnell' : speed < 60 ? 'Normal' : 'Langsam';
document.getElementById('fadeVal').innerText = text;
}
function hexToRgb(hex) {
let r = parseInt(hex.substr(1,2), 16);
let g = parseInt(hex.substr(3,2), 16);
let b = parseInt(hex.substr(5,2), 16);
return {r, g, b};
}
function getColorName(hex) {
let {r, g, b} = hexToRgb(hex);
if (r > 200 && g < 100 && b < 100) return 'Rot';
if (r < 100 && g > 200 && b < 100) return 'Grün';
if (r < 100 && g < 100 && b > 200) return 'Blau';
if (r > 200 && g > 200 && b < 100) return 'Gelb';
if (r > 200 && g < 100 && b > 200) return 'Magenta';
if (r < 100 && g > 200 && b > 200) return 'Cyan';
if (r > 200 && g > 150 && b < 100) return 'Orange';
if (r > 150 && g < 100 && b > 150) return 'Lila';
if (r > 200 && g > 200 && b > 200) return 'Weiß';
if (r < 50 && g < 50 && b < 50) return 'Schwarz';
return 'RGB(' + r + ',' + g + ',' + b + ')';
}
function setColorLive() {
let c = document.getElementById('color').value;
document.getElementById('colorInfo').innerText = getColorName(c);
let {r, g, b} = hexToRgb(c);
fetch('/setColor?r=' + r + '&g=' + g + '&b=' + b);
}
function setBrightness() {
clearTimeout(brightTimeout);
brightTimeout = setTimeout(function() {
let b = document.getElementById('brightness').value;
fetch('/setBrightness?value=' + b);
}, 100);
}
function setFadeSpeed() {
clearTimeout(fadeTimeout);
fadeTimeout = setTimeout(function() {
let s = document.getElementById('fadeSpeed').value;
fetch('/setFadeSpeed?value=' + s);
}, 100);
}
function setMode(m) {
fetch('/setMode?mode=' + m);
document.getElementById('fadeSection').style.display = (m === 2) ? 'block' : 'none';
}
function turnOn() {
fetch('/on');
}
function turnOff() {
fetch('/off');
}
</script>
</body>
</html>
)";
void setup() {
Serial.begin(115200);
// LED Strip initialisieren
strip.begin();
strip.setBrightness(brightness);
strip.show();
// WiFi verbinden
WiFi.begin(ssid, password);
Serial.print("Verbinde mit WiFi");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi verbunden!");
Serial.print("IP Adresse: ");
Serial.println(WiFi.localIP());
// mDNS starten
if (MDNS.begin("ledlampe")) {
Serial.println("mDNS gestartet! Erreichbar unter: http://ledlampe.local");
} else {
Serial.println("mDNS Fehler!");
}
// Web Server Routen
server.on("/", []() {
server.send(200, "text/html", html);
});
server.on("/setColor", []() {
r = server.arg("r").toInt();
g = server.arg("g").toInt();
b = server.arg("b").toInt();
mode = 0;
needsUpdate = true;
server.send(200, "text/plain", "OK");
});
server.on("/setBrightness", []() {
brightness = server.arg("value").toInt();
strip.setBrightness(brightness);
needsUpdate = true;
server.send(200, "text/plain", "OK");
});
server.on("/setMode", []() {
mode = server.arg("mode").toInt();
needsUpdate = true;
server.send(200, "text/plain", "OK");
});
server.on("/setFadeSpeed", []() {
fadeSpeed = server.arg("value").toInt();
server.send(200, "text/plain", "OK");
});
server.on("/on", []() {
isOn = true;
needsUpdate = true;
server.send(200, "text/plain", "OK");
});
server.on("/off", []() {
isOn = false;
strip.clear();
strip.show();
server.send(200, "text/plain", "OK");
});
server.begin();
Serial.println("Web Server gestartet!");
}
uint16_t hue = 0;
uint8_t fadeVal = 0;
bool fadeDir = true;
void loop() {
server.handleClient();
if (!isOn) {
delay(100);
return;
}
switch(mode) {
case 0: // Einfarbig
if(needsUpdate) {
for(int i=0; i<NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(r, g, b));
}
strip.show();
needsUpdate = false;
}
delay(10);
break;
case 1: // Regenbogen
for(int i=0; i<NUM_LEDS; i++) {
strip.setPixelColor(i, strip.gamma32(strip.ColorHSV(hue + (i * 65536L / NUM_LEDS))));
}
strip.show();
hue += 256;
delay(20);
break;
case 2: // Fade
if(fadeDir) {
fadeVal += 5;
if(fadeVal >= 250) fadeDir = false;
} else {
fadeVal -= 5;
if(fadeVal <= 5) fadeDir = true;
}
for(int i=0; i<NUM_LEDS; i++) {
strip.setPixelColor(i, strip.Color(fadeVal, fadeVal/2, fadeVal/4));
}
strip.show();
delay(fadeSpeed);
break;
}
}
Giấy phép
Tác phẩm này được cấp phép theo
Creative Commons — Attribution — Noncommercial — Share AlikeCC-BY-NC-SA
File mô hình
Chưa có bản in nào được khoe. Hãy là người đầu tiên!
Chưa có bình luận nào. Hãy là người đầu tiên!