چراغ خواب آفتابپرست با ESP32 و سنسور رنگ
تصور کن چراغی داشته باشی که وقتی روی هر سطحی قرار میگیره، دقیقا همرنگ همون سطح بشه. چراغ آفتابپرست دقیقا همین کار رو میکنه! این لامپ هوشمند با استفاده از یه سنسور رنگ خیلی دقیق، سطح زیرین خودش رو اسکن میکنه و فورا همون رنگ رو روی ریسه الایدی خودش شبیهسازی میکنه. این پروژه ترکیبی بینظیر از نجاری ظریف، الکترونیک و برنامهنویسیه که نتیجهاش یه گجت تعاملی و بهشدت جذاب روی میز کارت میشه.
ما تو تیم آیسیمدار همیشه دنبال پروژههایی هستیم که هم ظاهر لوکسی داشته باشن و هم مهارتهای فنی تو رو به چالش بکشن. تو این آموزش قراره با مغز متفکر ESP32، چشمهای سنسور رنگ TCS34725 و درخشش ریسه الایدی آدرسپذیر WS2812B یه سیستم کاملا وایرلس بسازیم. به لطف باتریهای لیتیومی داخلی، میتونی این چراغ رو به راحتی تو کل خونه جابهجا کنی و از قابلیتهای لمسی و جادویی اون لذت ببری.
شاید ترکیب کردن شیشه، چوب و قطعات الکترونیکی تو نگاه اول یه کم پیچیده به نظر برسه، اما اصلا جای نگرانی نیست. قراره تمام مراحل، از سیمکشیهای روی بردبرد گرفته تا چسباندن شیشهها و اجرای کدهای نرمافزاری رو خیلی ساده و قدمبهقدم با هم پیش ببریم. پس ابزارهایت رو آماده کن تا با هم به دنیای نور و رنگ قدم بذاریم.
قطعات موردنیاز
- برد توسعه ESP32
- سنسور رنگ RGB مدل TCS34725 (به عنوان چشم چراغ)
- ریسه الایدی آدرسپذیر WS2812B (نسخه 5 ولت با تراکم 60 الایدی در متر)
- دو عدد باتری لیتیوم-یون 18650 (برای بیسیم شدن دستگاه)
- جاباتری دوتایی 18650 (سیمکشی شده به صورت موازی)
- ماژول شارژر باتری TP4056 (با پورت Type-C)
- ماژول افزاینده ولتاژ MT3608 (برای تبدیل 3.7 ولت باتری به 5 ولت ثابت)
- کلید قطع و وصل ساده
- شیشه شفاف 3 میلیمتری (دو عدد 200 در 100، دو عدد 200 در 96 و یک عدد 96 در 96 میلیمتر)
- برچسب ماتکننده شیشه (برای پخش کردن نور الایدیها)
- چسب B7000 (بهترین انتخاب برای چسباندن شیشهها)
- تخته چوب چندلایه 8 میلیمتری برای باکس پایینی (دو عدد 100 در 100، دو عدد 34 در 100 و دو عدد 34 در 86 میلیمتر)
- تخته سهلا 12 میلیمتری برای ساخت چهار ستون داخلی (چهار عدد 200 در 12 میلیمتر)
- هویه و وسایل لحیمکاری
- سیم افشان (ترجیحا با روکش سیلیکونی برای انعطاف بیشتر)
راهاندازی اولیه و تست مدار روی بردبرد
قبل از اینکه هویه رو داغ کنی یا سراغ چسبکاری قطعات چوبی بری، باید مطمئن بشیم که مغز مدار (ESP32) و چشمهای اون (سنسور رنگ) میتونن به خوبی با هم ارتباط برقرار کنن. بستن مدار روی بردبرد بهت کمک میکنه تا سنسور رنگ و سنسورهای لمسی دستساز رو به راحتی تست کنی. سنسور TCS34725 از پروتکل I2C استفاده میکنه و کلا به چهار تا سیم نیاز داره.
پین VIN سنسور رو به پین 3.3 ولت برد ESP32 وصل کن. (اصلا از 5 ولت استفاده نکن چون سنسور درجا میسوزه!). پین GND به GND وصل میشه. پین SDA (دیتا) رو به پین GPIO 21 و پین SCL (کلاک) رو به پین GPIO 22 متصل کن.
حالا نوبت ریسه الایدیه. سیم 5 ولت ریسه رو به پین VIN (یا VBUS) و سیم GND رو به GND برد ESP32 وصل کن. سیم دیتای الایدی (DIN) هم باید به پین GPIO 13 وصل بشه.
برای بخش تغذیه، باتریها رو به پایههای ورودی TP4056 متصل کن. بعد از خروجی TP4056 به ورودی ماژول افزاینده ولتاژ (MT3608) سیم بکش.
ساخت بدنه شیشهای و ستونهای چوبی داخلی
برای اینکه چراغ آفتابپرست ما ظاهر لوکس و جذابی داشته باشه، به جای پلاستیک از شیشه واقعی برای برج نوری استفاده میکنیم. برای اینکه هیچ براکت و پیچ اضافهای دیده نشه، یه اسکلت چوبی داخلی میسازیم و شیشهها رو با چسب B7000 روی اون سوار میکنیم.
اول چهار تا نوار چوبی کاملا یکشکل از تخته سهلا ببر تا نقش ستونهای داخلی رو بازی کنن. چون از چوب 12 میلیمتری استفاده کردیم، ابعاد نهایی هر ستون باید 12 در 12 در 200 میلیمتر باشه.
نکته خیلی مهم اینه که طول این ستونهای چوبی باید دقیقا 4 میلیمتر کوتاهتر از شیشهها باشه! وقتی ستونها رو به شیشه میچسبونی، از پایین کاملا مماسشون کن. اینطوری اون 4 میلیمتر فضای خالی میفته بالای کار و یه لبه عالی ایجاد میکنه تا شیشه بالایی دقیقا سر جای خودش بشینه.
حالا چسب B7000 رو بردار و یه خط چسب ضخیم و مارپیچ روی کل طول ستون چوبی بریز. از اونجایی که شیشه سنگینه، به یه اتصال خیلی قوی نیاز داریم. بهترین روش استفاده از چسب B7000 اینه که باهاش مثل چسب آهن رفتار کنی.
یعنی شیشه رو همون لحظه نچسبون! حدود دو سه دقیقه صبر کن تا حلال چسب کمی بپره و حالت نوچ پیدا کنه. اینطوری به محض اینکه شیشه رو روش بذاری، سریع درگیر میشه و شیشه رو محکم میگیره. شیشه اول رو با دقت روی ستون چوبی نوچ شده فشار بده.
اصلا سعی نکن این کار رو روی هوا و با دست نگه داری. از یه میز صاف و چند تکه چوب باطله کمک بگیر تا یه زاویه 90 درجه موقت بسازی و گوشههای کارت کاملا گونیا و تمیز دربیاد. این روش رو برای تمام اضلاع تکرار کن.
این چسب برای خشک شدن کامل بین 24 تا 48 ساعت زمان نیاز داره و تو این مدت باید تحت فشار باشه. گیرههای سنگین نجاری به خاطر فشار نقطهای و زیادشون شیشه رو میشکنن. راهکار چیه؟ کشپول! وقتی هر چهار طرف شیشه رو دور ستونهای چوبی ساختی، چند تا کش پول ضخیم رو دور تا دور برج شیشهای بنداز.
کشها رو از بالا تا پایین با فاصله مساوی تنظیم کن. این کشها دقیقا همون فشار ملایم و یکنواختی که برای چسبیدن محکم شیشه به چوب نیاز داری رو بدون هیچ آسیبی فراهم میکنن.
جاسازی قطعات الکترونیکی و ساخت پایه چراغ
تا زمانی که چسب برج شیشهای خشک میشه، میریم سراغ ساخت پایه و باکس الکترونیکی. چون فضا کمه و نباید نویز الکتریکی داشته باشیم، چیدمان قطعات تو این بخش خیلی مهمه. پایه باید هم محکم باشه و هم به خوبی قطعات رو تو خودش جا بده.
من برای ساخت پایه از چوب چندلایه استفاده کردم و قطعات رو تو دو بخش چیدم: یه کف صاف برای باتریهای سنگین و مدار اصلی، و یه دیواره عمودی برای ماژولهای تغذیه و کلید.
سنسور رنگ TCS34725 برای اینکه دقیق کار کنه، باید دقیقا بین 3 تا 10 میلیمتر با سطحی که روی اون قرار میگیره فاصله داشته باشه. همچنین پنل پایینی نباید هیچ نور اضافهای از محیط به سنسور برسونه، وگرنه رنگها اشتباه خونده میشن.
پنل کف باکس رو برش بزن، مرکزش رو پیدا کن و دقیقا به اندازه ابعاد سنسور یه مربع بکش. این مربع رو تا نصف ضخامت چوب خالی کن (سوراخ رو کامل نکن). سنسور باید کیپ کیپ داخل این حفره بشینه. این کار باعث میشه نور اتاق به سنسور نفوذ نکنه و اسکن رنگ مختل نشه.
چهار تا سیم (VIN, GND, SDA, SCL) رو به سنسور لحیم کن. یه نکته خیلی خیلی مهم: یه پل لحیمی کوچیک بین پین الایدی و پین 3.3 ولت روی خود برد سنسور ایجاد کن. این کار باعث میشه الایدی سفید رنگ سنسور همیشه روشن بمونه تا بتونه اشیای زیرش رو به خوبی روشن کنه. حالا سنسور رو تو حفرهای که ساختی قرار بده و سیمهاش رو از یه سوراخ ریز بیار داخل باکس اصلی.
برای بخش تغذیه، ماژول شارژر TP4056 و افزاینده ولتاژ MT3608 رو روی دیواره عمودی نصب میکنیم تا پورت تایپسی از بیرون در دسترس باشه. بهتره قبل از اینکه بردها رو به دیواره بچسبونی، اتصالات لحیمی رو انجام بدی.
ماژول شارژر دو تا الایدی SMD ریز داره که وضعیت شارژ رو نشون میدن. چون این ماژول داخل باکس مخفی میشه، باید این چراغها رو بیاریم بیرون بدنه تا وضعیت باتری رو ببینیم.
خیلی بااحتیاط دو تا سیم نازک به پدهای این الایدیهای روی برد لحیم کن و سر دیگهشون رو به الایدیهای معمولی متصل کن. دو تا سوراخ ریز روی دیواره باکس (نزدیک پورت شارژ) در بیار. این الایدیها رو از داخل هول بده بیرون تا داخل سوراخ دیواره محکم بشن.
بعدا که روی کار برچسب طرح چوب میزنیم، با یه سوزن روی این چراغها رو سوراخ میکنیم تا نورشون معلوم باشه. اینطوری هم ظاهر چراغ یه دست میمونه و هم همیشه میدونی وضعیت شارژ باتری چطوره.
ماژول شارژر و افزاینده ولتاژ رو صاف روی دیواره عمودی بچسبون. پورت Type-C باید دقیقا مماس با لبه باشه تا کابل راحت بهش وصل بشه. خروجیهای ماژول شارژر (OUT+ و OUT-) رو مستقیم به ورودیهای افزاینده ولتاژ (VIN+ و VIN-) لحیم کن. یه کلید فیزیکی قطع و وصل هم تو مسیر سیم مثبت خروجی ماژول افزاینده قرار بده.
حالا نوبت محکم کردن قطعات سنگینه. جاباتری دوتایی 18650 رو کف باکس اصلی با چسب محکم کن. این کار باعث میشه مرکز ثقل چراغ پایین بمونه و به این راحتیها نیفته.
برد ESP32 رو روی پینهدرهای مادگی قرار بده و پایههای پینهدر رو تو چوب کف فرو کن و با چسب قطرهای محکم کن. دیواره عمودی رو به لبه پشتی کف بچسبون تا شکل L پیدا کنه. سیمهای قرمز و مشکی باتری رو به پدهای B+ و B- ماژول شارژر وصل کن.
برای صرفهجویی تو فضای عمودی و جلوگیری از قطعی سیمها تو طولانیمدت، بیخیال سیمهای جامپر بردبردی شو و همهچیز رو مستقیم به پینهای ESP32 لحیم کن.
یه نکته حیاتی: باتری رو روشن کن و با مولتیمتر ولتاژ خروجی MT3608 رو چک کن. پیچ طلایی رنگ روش رو اونقدر بچرخون تا دقیقا ولتاژ 5.0 ولت رو نشون بده. (اگه این کار رو نکنی، ESP32 رو به فنا میدی!)
خروجی 5 ولت مثبت رو از طریق کلید به پین VIN و خروجی منفی رو به پین GND برد ESP32 وصل کن. سیمهای سنسور رو هم به این شکل وصل کن: VIN به 3.3 ولت، پین GND به GND، پین SDA به GPIO 21 و پین SCL به GPIO 22.
در نهایت سیمهای ریسه الایدی (دیتا، مثبت و منفی) و سیمهای سنسور لمسی رو هم آماده کن و ترجیحا اونها رو کوتاه نگه دار تا باکس شلوغ نشه. بهتره سیمها رو به کف باکس چسب بزنی تا ول و آویزون نمونن.
مونتاژ ستون نوری و اتصال نهایی به شاسی
تو این مرحله باید هسته مرکزی نور رو بسازیم و باکس پایینی رو ببندیم تا برج شیشهای بتونه خیلی تمیز روی اون سوار بشه. درپوش باکس الکترونیکی هم قراره کار درب رو بکنه و هم شیشه رو سر جای خودش قفل کنه. یه تکه چوب چندلایه مربعی رو برای درپوش برش بزن.
چهار گوشه این درپوش رو به اندازه ابعاد همون ستونهای چوبی داخلی شیشه، دقیقا به شکل مربع خالی کن. ابعاد باید کاملا مچ بشن. یه سوراخ هم نزدیک مرکز این درپوش برای عبور سیمها در بیار. حالا باید هسته الایدی رو آماده کنیم.
برای اینکه نور خیلی یکدست تو شیشههای مات پخش بشه و نقطههای الایدی معلوم نباشن، باید ریسهها رو به صورت مارپیچ و متراکم بچسبونیم. یه لوله پلیکا (PVC) بردار که طولش یه مقدار از ارتفاع برج شیشهای کمتر باشه.
چسب پشت ریسه WS2812B رو باز کن و از پایین لوله شروع کن به پیچیدن ریسه به صورت مارپیچ به سمت بالا. حواست باشه که فاصله بین حلقهها کاملا مساوی باشه. سه تا سیم خروجی از پایین ریسه (5 ولت، دیتا و منفی) رو از همون سوراخ وسط درپوش رد کن و ببر داخل باکس.
انتهای لوله پیویسی رو با چسب قطرهای دقیقا وسط درپوش محکم کن. حالا سه تا سیم الایدی رو به اتصالات داخل باکس (5 ولت به خروجی افزاینده، GND به زمین مشترک، و دیتا به پین GPIO 13 برد ESP32) لحیم کن. درپوش رو روی باکس الکترونیکی بذار.
درپوش رو با چهار تا پیچ تو گوشههاش محکم ببند. حواست باشه جای پیچها طوری باشه که روی بریدگیهای مربعشکل نیفته تا بعدا با برج شیشهای تداخل نداشته باشه.
من از 4 تا پیچ برای بستن باکس استفاده کردم. بعدش یه مقدار چسب B7000 روی شیارهای گوشه درپوش و لبههای باکس بزن. برج شیشهای مات شده رو که کاملا خشک شده، بردار و آروم از بالا روی ستون الایدی بکش پایین. چهار تا ستون داخلی چوبی دقیقا میرن داخل شیارهای درپوش.
برج شیشهای رو تا انتها فشار بده پایین. ستونهای چوبی ساختار رو قفل میکنن تا هیچ تکونی نخوره. لبههای شیشه هم خیلی تمیز روی لبههای باکس پایینی میشینن.
برنامهنویسی و کدهای لامپ هوشمند
#include <Wire.h>
#include "Adafruit_TCS34725.h"
#include <FastLED.h>
#include <math.h>
// --- PIN CONFIGURATION ---
#define LED_PIN 13
#define INDICATOR_LED 2
#define TOUCH_BOSS 4
#define TOUCH_ENT 27
#define NUM_LEDS 60
#define BRIGHTNESS 100
#define TOUCH_LIMIT 40
Adafruit_TCS34725 tcs = Adafruit_TCS34725(TCS34725_INTEGRATIONTIME_50MS, TCS34725_GAIN_4X);
CRGB leds[NUM_LEDS];
// --- STATE VARIABLES ---
CRGB activeColor = CRGB::White;
int currentMode = 0;
uint8_t gHue = 0;
bool continuousScan = false;
// --- TOUCH 1 (BOSS) VARIABLES ---
bool t1_wasActive = false;
bool t1_isHolding = false;
unsigned long t1_downTime = 0;
unsigned long t1_upTime = 0;
int t1_tapCount = 0;
// --- TOUCH 2 (ENTERTAINER) VARIABLES ---
bool t2_wasActive = false;
unsigned long t2_downTime = 0;
unsigned long t2_upTime = 0;
int t2_tapCount = 0;
void setup() {
Serial.begin(115200);
Wire.begin();
pinMode(INDICATOR_LED, OUTPUT);
digitalWrite(INDICATOR_LED, LOW);
FastLED.addLeds<WS2812B, LED_PIN, GRB>(leds, NUM_LEDS);
FastLED.setBrightness(BRIGHTNESS);
// Power Governor to keep your MT3608 booster safe
FastLED.setMaxPowerInVoltsAndMilliamps(5, 950);
if (tcs.begin()) {
Serial.println("SYSTEM BOOT: Defaulting to White");
fill_solid(leds, NUM_LEDS, activeColor);
FastLED.show();
} else {
Serial.println("ERROR: Sensor missing.");
}
}
void loop() {
bool t1_active = (touchRead(TOUCH_BOSS) < TOUCH_LIMIT);
bool t2_active = (touchRead(TOUCH_ENT) < TOUCH_LIMIT);
// ==========================================
// TOUCH 1: THE BOSS (Scan Lock & Live Mode)
// ==========================================
if (t1_active && !t1_wasActive) {
t1_downTime = millis();
t1_tapCount++;
}
if (t1_active && t1_wasActive) {
if (millis() - t1_downTime > 500) {
// --- LONG PRESS DETECTED ---
t1_isHolding = true;
t1_tapCount = 0;
continuousScan = false;
digitalWrite(INDICATOR_LED, HIGH);
runScanner();
}
}
if (!t1_active && t1_wasActive) {
t1_upTime = millis();
if (t1_isHolding) {
// --- RELEASED FROM LONG PRESS ---
t1_isHolding = false;
digitalWrite(INDICATOR_LED, LOW);
Serial.println("COLOR LOCKED");
}
}
if (!t1_active && !t1_wasActive) {
if (t1_tapCount > 0 && (millis() - t1_upTime > 400)) {
if (t1_tapCount == 2) {
// --- DOUBLE TAP CONFIRMED ---
continuousScan = !continuousScan;
currentMode = 0; // Snap to solid mode so we can clearly see the live scan
if (continuousScan) {
digitalWrite(INDICATOR_LED, HIGH);
Serial.println("LIVE SCAN: ON");
// NOTIFICATION: BLINK TWICE (Entering Live Mode)
for(int i=0; i<2; i++) {
fill_solid(leds, NUM_LEDS, CRGB::White);
FastLED.show();
delay(120);
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
delay(120);
}
} else {
digitalWrite(INDICATOR_LED, LOW);
Serial.println("LIVE SCAN: OFF (Locked)");
// NOTIFICATION: BLINK ONCE (Exiting Live Mode)
fill_solid(leds, NUM_LEDS, activeColor);
FastLED.show();
delay(200);
fill_solid(leds, NUM_LEDS, CRGB::Black);
FastLED.show();
delay(100);
}
}
t1_tapCount = 0;
}
}
t1_wasActive = t1_active;
// ==========================================
// TOUCH 2: THE ENTERTAINER (Animations)
// ==========================================
if (t2_active && !t2_wasActive) {
t2_downTime = millis();
t2_tapCount++;
}
if (!t2_active && t2_wasActive) {
t2_upTime = millis();
}
if (!t2_active && !t2_wasActive) {
if (t2_tapCount > 0 && (millis() - t2_upTime > 400)) {
if (t2_tapCount == 2) {
// --- DOUBLE TAP CONFIRMED ---
currentMode = (currentMode + 1) % 11;
fill_solid(leds, NUM_LEDS, CRGB::Black); // Flash black to confirm tap
FastLED.show();
delay(50);
Serial.print("ANIMATION MODE: "); Serial.println(currentMode);
}
t2_tapCount = 0;
}
}
t2_wasActive = t2_active;
// ==========================================
// LIVE BACKGROUND SCANNER
// ==========================================
if (continuousScan) {
// Updates every 100ms so the strip animations remain smooth
EVERY_N_MILLISECONDS(100) {
runScanner();
}
}
// ==========================================
// RENDER OUTPUT
// ==========================================
runAnimations();
FastLED.show();
delay(10); // Very brief delay to keep the touch sensing hyper-responsive
}
// ==========================================
// THE COLOR ACCURACY ENGINE
// ==========================================
void runScanner() {
uint16_t r, g, b, c;
tcs.getRawData(&r, &g, &b, &c);
if (c > 50) {
float r_ratio = (float)r / c;
float g_ratio = (float)g / c;
float b_ratio = (float)b / c;
// 1. YOUR CUSTOM WHITE-BALANCE MULTIPLIERS (Based on your raw data)
float rf = r_ratio * 1.00;
float gf = g_ratio * 1.21;
float bf = b_ratio * 1.88;
// 2. SATURATION BOOSTER (Stripping the white glare)
float min_val = min(rf, min(gf, bf));
rf = (rf - min_val * 0.85) * 255.0;
gf = (gf - min_val * 0.85) * 255.0;
bf = (bf - min_val * 0.85) * 255.0;
// 3. BRIGHTNESS NORMALIZATION
float max_val = max(rf, max(gf, bf));
if (max_val > 0) {
float scale = 255.0 / max_val;
rf *= scale;
gf *= scale;
bf *= scale;
}
// 4. GAMMA CORRECTION (Mapping to human eye curves)
int final_r = 255.0 * pow(rf / 255.0, 2.5);
int final_g = 255.0 * pow(gf / 255.0, 2.5);
int final_b = 255.0 * pow(bf / 255.0, 2.5);
// 5. SMOOTH BLENDING
activeColor.r = (activeColor.r * 0.90) + (constrain(final_r, 0, 255) * 0.10);
activeColor.g = (activeColor.g * 0.90) + (constrain(final_g, 0, 255) * 0.10);
activeColor.b = (activeColor.b * 0.90) + (constrain(final_b, 0, 255) * 0.10);
}
}
// ==========================================
// 10-MODE ANIMATION SUITE
// ==========================================
void runAnimations() {
EVERY_N_MILLISECONDS(20) { gHue++; }
switch (currentMode) {
case 0: fill_solid(leds, NUM_LEDS, activeColor); break;
case 1: fill_rainbow(leds, NUM_LEDS, gHue, 7); break;
case 2:
fadeToBlackBy(leds, NUM_LEDS, 10);
if(random8() < 40) leds[random16(NUM_LEDS)] += CHSV(gHue + random8(64), 200, 255);
break;
case 3:
fill_solid(leds, NUM_LEDS, activeColor);
FastLED.setBrightness(beatsin8(15, 30, BRIGHTNESS));
break;
case 4:
fadeToBlackBy(leds, NUM_LEDS, 30);
leds[beatsin16(25, 0, NUM_LEDS-1)] = activeColor;
break;
case 5:
fadeToBlackBy(leds, NUM_LEDS, 40);
for(int i=0; i<NUM_LEDS; i++) if((i+(millis()/100))%5==0) leds[i]=activeColor;
break;
case 6:
fadeToBlackBy(leds, NUM_LEDS, 20);
for(int i=0; i<8; i++) leds[beatsin16(i+7, 0, NUM_LEDS-1)] |= CHSV(gHue+(i*32), 200, 255);
break;
case 7:
for(int i=0; i<NUM_LEDS; i++) leds[i] = ColorFromPalette(PartyColors_p, gHue+(i*2), beatsin8(62,64,255));
break;
case 8:
fill_solid(leds, NUM_LEDS, CRGB(0,0,30));
if(random8()<20) leds[random16(NUM_LEDS)]=CRGB::White;
fadeToBlackBy(leds, NUM_LEDS, 10);
break;
case 9:
fadeToBlackBy(leds, NUM_LEDS, 20);
leds[beatsin16(25, 0, NUM_LEDS-1)] = CRGB::Red;
break;
case 10:
fadeToBlackBy(leds, NUM_LEDS, 50);
for(int i=0; i<NUM_LEDS; i++) if(i%3==(millis()/100)%3) leds[i]=activeColor;
break;
}
// Ensure the brightness restores perfectly if you cycle out of breathing mode
if (currentMode != 3) FastLED.setBrightness(BRIGHTNESS);
}
این کد چطور کار میکنه؟ کدهای این پروژه برای اجرا شدن به دو تا کتابخونه اصلی نیاز دارن: کتابخونه FastLED برای کنترل ریسه WS2812B و کتابخونه Adafruit_TCS34725 برای ارتباط با سنسور رنگ. تو بخش setup، برد ESP32 سنسور و ریسه رو راهاندازی میکنه. یکی از توابع خیلی مهمی که اینجا استفاده شده، setMaxPowerInVoltsAndMilliamps هست.
این تابع جریان مصرفی الایدیها رو محدود میکنه تا ماژول افزاینده 5 ولت موقع نمایش نور سفید با تمام 60 پیکسل، زیر بار کم نیاره و خاموش نشه.
حلقه اصلی برنامه (loop) وظیفه خوندن وضعیت دو تا سنسور لمسی خازنی رو بر عهده داره که از طریق توابع داخلی touchRead خود ESP32 انجام میشه. یه سری منطقهای زمانبندی هم تعریف شده تا بین لمس طولانی و دوبار ضربه زدن (Double-tap) تفاوت قائل بشه. سنسور لمسی اول، وظیفه کنترل اسکنر رو داره.
اگه دستت رو روش نگه داری، یه اسکن رنگ تکی انجام میشه. اگه دوبار روش ضربه بزنی، میره رو حالت "لایو" و با حرکت دادن چراغ روی سطوح مختلف، رنگ به صورت زنده آپدیت میشه. سنسور لمسی دوم هم برای تغییر انیمیشنهاست؛ با هربار دابلتپ کردن، سیستم بین 10 تا افکت نوری مختلف جابهجا میشه.
وقتی اسکنر فعال میشه، تابع runScanner مقادیر خام رنگهای قرمز، سبز، آبی و کانال شفاف رو از سنسور TCS34725 میخونه. از اونجایی که شیشهها و برچسبهای ماتکننده روی رنگ نهایی تاثیر میذارن، این دیتای خام با یه سری محاسبات ریاضی کالیبره میشه.
برنامه نسبت هر رنگ رو محاسبه میکنه، ضرایب خاصی رو اعمال میکنه تا خطای طبیعی سنسور گرفته بشه و رنگ سفید اضافی رو حذف میکنه تا غلظت رنگ بالا بره.
در نهایت با اعمال اصلاح گاما، نور تولیدی دقیقا شبیه همون چیزی میشه که چشم انسان میبینه. برای جلوگیری از پرش نور هم، تغییر رنگ از حالت قبلی به رنگ جدید خیلی نرم و اصطلاحا فید (Fade) میشه.
تابع runAnimations هم وظیفه اجرای افکتها رو به کمک ساختار Switch-Case داره. حالت 0 رنگ ثابت اسکن شده رو نشون میده و بقیه حالتها با استفاده از توابع داخلی FastLED افکتهایی مثل نفس کشیدن، حرکت نقطهای یا چشمکزدن ستارهای رو تولید میکنن. جالب اینجاست که چون انیمیشنها متغیر activeColor رو میخونن، افکتها بر اساس رنگ اسکن شده ساخته میشن و کاملا داینامیک هستن.
تو کدهای اصلی، یه الایدی نشانگر روی پین GPIO2 تعریف شده که من تو نسخه نهایی حذفش کردم، اما کدش هنوز هست و اگه خواستی میتونی یه الایدی به این پین اضافه کنی.
سنسورهای لمسی مخفی و کاور طرح چوب بدنه
قبل از اینکه روکش نهایی بدنه رو بچسبونیم، باید کلیدهای لمسی خازنی رو راهاندازی کنیم. به جای استفاده از دکمههای فیزیکی زشت و جاگیر، از تکههای فویل آلومینیومی به عنوان سنسور لمسی استفاده میکنیم. فویلها رو به ابعاد دلخواه برش بزن و روی سطح بیرونی باکس پایینی کاملا صاف بچسبون.
به هر تکه فویل یه سیم محکم لحیم کن و این سیمها رو از طریق سوراخهای ریزی که روی بدنه زدی بده داخل باکس تا به پینهای لمسی ESP32 وصل بشن.
بعد از نصب سنسورها، نوبت به برچسب طرح چوب (وینیل) میرسه تا تمام اتصالات و قطعات داخلی رو مخفی کنه. نوارهای باریکی از این برچسب ببر و روی چهار لبه بیرونی برج شیشهای بچسبون تا هم خطوط چسب B7000 مخفی بشن و هم ظاهر کار مثل یه پنجره قابدار و شیک بشه.
حالا کل بدنه باکس پایینی رو هم با همون برچسب طرح چوب کاور کن. این برچسب مستقیم روی فویلهای آلومینیومی کشیده میشه. از اونجایی که پینهای خازنی ESP32 میتونن تغییرات میدان الکترومغناطیسی رو از پشت لایههای نازک حس کنن، این برچسب کاملا فویلها رو از دید پنهان میکنه اما کلیدهای لمسی همچنان با بالاترین دقت کار میکنن.
با این ترفند، یه رابط کاربری کاملا نامرئی و جادویی روی پایه چراغ ساختی که هیچ پیچ، درز و دکمهای روش معلوم نیست و ظاهر دستگاه رو به شدت مینیمال و حرفهای میکنه.
ایدههایی برای توسعه چراغ جادویی تو
بعد از آپلود کد و نصب روکشهای نهایی، چراغ آفتابپرست تو آماده است تا جادو کنه! این پروژه ترکیب فوقالعادهای از ساختارهای فیزیکی، مدارات الکترونیک و برنامهنویسی میکروکنترلرها بود که تو رو به یه گجت تعاملی و کاربردی رسوند.
به خاطر ماژولار بودن این طراحی، خیلی راحت میتونی اون رو ارتقا بدی. مثلا میتونی ابعاد ستونها و شیشهها رو بزرگتر کنی تا یه چراغ ایستاده بسازی، یا پک باتری قویتری بندازی تا ساعتها بدون سیم کار کنه. حتی میتونی با تغییر رنگ برچسبهای ماتکننده، حس و حال متفاوتی به دکوراسیون اتاقت بدی.
از نظر نرمافزاری هم دستت کاملا بازه. چون برد ESP32 وایفای و بلوتوث داخلی داره، میتونی کدهات رو طوری توسعه بدی که چراغت به شبکه خانه هوشمند متصل بشه یا وقتی از حالت اسکنر رنگ استفاده نمیکنی، بتونی نورش رو با اپلیکیشن موبایل کنترل کنی.
یادت باشه، مهمترین نکاتی که موقع ساخت این دستگاه باید بهش توجه کنی اینه که سنسور رنگ از پایین کاملا کیپ باشه تا نور اضافه بهش نخوره، موقع خشک شدن چسب شیشهها فشار یکنواخت کشپولها رو فراموش نکنی، و حتما ولتاژ ماژول افزاینده رو قبل از اتصال به ESP32 روی 5 ولت تنظیم کنی. ما تو آیسیمدار بیصبرانه منتظریم تا نتیجه کارهای خلاقانهات رو ببینیم.
تو میتونی اولین سازنده باشی!
اگه پروژه رو ساختی به اشتراک بزار و اعتبار هدیه بگیر
این آموزش با استانداردهای اختصاصی آیسیمدار بازنویسی و بهینهسازی شده و تمامی حقوق انتشار آن متعلق به این مجموعه است و در صورت کپی یا بازنشر پیگرد قانونی خواهد داشت.








































گفتگوهای کاربران
هنوز نظری ثبت نشده؛ تو شروع کن!
هنوز نظری ثبت نشده؛ تو شروع کن!