🌐🔧 Alin Li · ESP32 Web Server

Build a Web Server
on a Tiny Chip!

Turn a $5 ESP32 microcontroller into a web server that you control from your phone's browser. Toggle LEDs, read sensors, and build a smart home dashboard — all running on a chip smaller than your thumb. Step by step, line by line, fully commented code.

What You Need 🛒Start Building ⚡
🤔 ماذا سنبني؟

خادم ويب على شريحة بسعر $5

🌐 Your ESP32 Becomes a Website!

A web server is a program that listens for requests from web browsers and sends back web pages. When you visit google.com, a Google server sends you a web page. In this project, YOUR ESP32 will do the same thing — but on your local Wi-Fi network.

You'll open your phone's browser, type the ESP32's IP address (like 192.168.1.42)، وشاهد صفحة ويب يقدّمها خادم على شريحتك الصغيرة. ستحتوي الصفحة على أزرار للتحكم في مصابيح LED وعرض قراءات المستشعرات والمزيد — وكل ذلك لحظيًا!

🧠 مفهوم مهم: كيف يعمل خادم الويب

1. Your browser sends a request → "Hey server, give me the page at /"

2. The server processes the request → "They want the homepage, let me build it"

3. The server sends a response → an HTML page with buttons and data

4. Your browser renders the HTML → you see a beautiful page with controls!

This is the same process that powers EVERY website on Earth — and your ESP32 will do it.

🛒 ما تحتاج إليه

قائمة المشتريات — الإجمالي ~$15–$25

🔧

ESP32 Dev Board

ESP32-WROOM-32 or ESP32-S3

~$5–$10
🔌

USB Cable

Micro-USB أو USB-C (بحسب اللوحة)

~$2
🔴

LEDs (3–5 pcs)

Any color — red, green, blue

~$1
⚡

Resistors (220Ω)

واحدة لكل LED لحمايتها

~$1
🌡️

DHT11 Sensor (optional)

Temperature + humidity sensor

~$2
🧵

Breadboard + Jumper Wires

For connecting everything

~$3
💡 Minimum Setup

For Project 1, you ONLY need the ESP32 board + USB cable — the ESP32 has a built-in LED! You can start coding immediately without buying any extra components. Add LEDs and sensors for Projects 2 and 3.

💻 الإعداد

الخطوة 0: إعداد Arduino IDE لـ ESP32

0

Install Arduino IDE + ESP32 Board Support

⏱️ 10 minutes (one-time setup)

1. Download Arduino IDE from arduino.cc/en/software (مجاني ومفتوح المصدر)

2. افتح Arduino IDE ← انتقل إلى File → Preferences

3. In "Additional Board Manager URLs", paste this URL:

Board Manager URL
https://raw.githubusercontent.com/espressif/arduino-esp32/gh-pages/package_esp32_index.json

4. Go to Tools → Board → Boards Manager → Search "ESP32" → Install "esp32 by Espressif Systems"

5. Connect your ESP32 via USB → Go to Tools → Board → Select "ESP32 Dev Module"

6. Go to Tools → Port → Select the COM port (e.g., COM3 on Windows, /dev/ttyUSB0 on Linux)

🐧 Linux Users

You may need to add yourself to the dialout group: sudo usermod -a -G dialout $USER then log out and back in. This gives permission to access the USB serial port.

🟢 المشروع 1

تحكّم في مصباح LED المدمج من متصفحك

Your first web server — no extra hardware needed!

📐 ما يفعله هذا المشروع

The ESP32 connects to your Wi-Fi, starts a web server, and serves a page with أزرار التشغيل/الإيقاف. عند نقر زر، يرسل المتصفح طلبًا إلى ESP32 لتشغيل مصباح LED المدمج أو إطفائه. وترى النتيجة فورًا من هاتفك أو أي جهاز على شبكة Wi-Fi نفسها!

project1_led_webserver.ino — Full Code with Every Line Explained
// ============================================ // ALIN LI — خادم ويب ESP32: التحكم في LED // المشروع 1: LED مدمج دون أجهزة إضافية! // ============================================ // الخطوة 1: أدرج مكتبة Wi-Fi // تتيح هذه المكتبة لـ ESP32 الاتصال بشبكات Wi-Fi // تأتي مثبّتة مسبقًا مع حزمة لوحة ESP32 #include <WiFi.h> // الخطوة 2: اضبط بيانات اعتماد Wi-Fi // استبدلها باسم شبكة Wi-Fi المنزلية وكلمة مرورها! const char* ssid = "YOUR_WIFI_NAME"; // اسم شبكة Wi-Fi الخاصة بك const char* password = "YOUR_WIFI_PASSWORD"; // كلمة مرور Wi-Fi الخاصة بك // الخطوة 3: أنشئ خادم ويب على المنفذ 80 // المنفذ 80 هو منفذ HTTP الافتراضي، تستخدمه المتصفحات تلقائيًا // عندما تكتب عنوان IP، يتصل المتصفح بالمنفذ 80 WiFiServer server(80); // الخطوة 4: عرّف طرف LED // معظم لوحات ESP32 تضم LED مدمجًا على الطرف 2 // (تحقّق من لوحتك، فبعضها يستخدم الطرف 5 أو 13) const int LED_PIN = 2; // الخطوة 5: متغير لتتبع حالة LED // نحتاج إلى تذكّر ما إذا كان LED مضاءً أو مطفأً // حتى نعرض الحالة الصحيحة في صفحة الويب bool ledState = false; // ============================================ // SETUP — يعمل مرة واحدة عند بدء ESP32 // ============================================ void setup() { // ابدأ الاتصال التسلسلي بسرعة 115200 بود // يتيح لنا هذا رؤية الرسائل في مراقب Arduino التسلسلي // (الأدوات → المراقب التسلسلي → اضبط السرعة على 115200 بود) Serial.begin(115200); // اضبط طرف LED على OUTPUT // تعني OUTPUT أننا نرسل إشارة (تشغيل/إطفاء LED) // (تعني INPUT أننا نقرأ إشارة، مثل مستشعر) pinMode(LED_PIN, OUTPUT); // تأكّد أن LED يبدأ مطفأً digitalWrite(LED_PIN, LOW); // الاتصال بـ Wi-Fi Serial.print("Connecting to Wi-Fi: "); Serial.println(ssid); // تبدأ WiFi.begin() عملية الاتصال // يرسل بيانات اعتمادك إلى الموجّه WiFi.begin(ssid, password); // انتظر حتى الاتصال، تستمر حلقة while في التحقق // تُرجع WiFi.status() القيمة WL_CONNECTED عند النجاح while (WiFi.status() != WL_CONNECTED) { delay(500); // انتظر نصف ثانية Serial.print("."); // اطبع نقطة لنعرف أنه يحاول الاتصال } // تم الاتصال! اطبع عنوان IP // هذا هو العنوان الذي ستكتبه في متصفحك Serial.println(""); Serial.println("✅ Connected to Wi-Fi!"); Serial.print("📡 IP Address: "); Serial.println(WiFi.localIP()); // ^^^ دوّن عنوان IP هذا! ستحتاج إليه! // ابدأ خادم الويب // يستمع ESP32 الآن لاتصالات المتصفح server.begin(); Serial.println("🌐 Web server started!"); } // ============================================ // LOOP — يعمل باستمرار إلى الأبد // ============================================ void loop() { // تحقّق من اتصال عميل (متصفح) // تُرجع server.available() كائن عميل إذا اتصل أحد WiFiClient client = server.available(); // إذا لم يتصل عميل، تجاوز وتحقق مجددًا if (!client) { return; } // اتصل عميل! انتظر حتى يرسل بيانات Serial.println("👤 New client connected!"); while (!client.available()) { delay(1); } // اقرأ السطر الأول من طلب HTTP // يبدو هكذا: "GET /on HTTP/1.1" أو "GET /off HTTP/1.1" String request = client.readStringUntil('\r'); Serial.println(request); // ============================================ // معالجة الطلب — على ماذا نقر المستخدم؟ // ============================================ // إذا تضمن URL المسار "/on" → شغّل LED if (request.indexOf("/on") != -1) { digitalWrite(LED_PIN, HIGH); // HIGH = 3.3V = LED مضاء ledState = true; Serial.println("💡 LED turned ON"); } // إذا تضمن URL المسار "/off" → أطفئ LED if (request.indexOf("/off") != -1) { digitalWrite(LED_PIN, LOW); // LOW = 0V = LED مطفأ ledState = false; Serial.println("🌑 LED turned OFF"); } // ============================================ // إنشاء صفحة الويب (HTML) // هذا ما سيعرضه المتصفح! // ============================================ // أرسل ترويسات استجابة HTTP // تعني "HTTP/1.1 200 OK" أن الطلب نجح // تعني "Content-Type: text/html" أنني أرسل صفحة ويب client.println("HTTP/1.1 200 OK"); client.println("Content-Type: text/html"); client.println(""); // سطر فارغ = نهاية الترويسات // أرسل صفحة HTML الآن client.println("<!DOCTYPE html><html>"); client.println("<head>"); client.println("<title>Alin Li ESP32 Controller</title>"); client.println("<meta name='viewport' content='width=device-width'>"); // تنسيق CSS — يجعلها جميلة على الهواتف! client.println("<style>"); client.println("body{font-family:Arial;text-align:center;background:#1a1a2e;color:white;padding:40px 20px;}"); client.println("h1{font-size:2rem;margin-bottom:10px;}"); client.println(".status{font-size:3rem;margin:20px 0;}"); client.println(".btn{display:inline-block;padding:20px 50px;margin:10px;"); client.println("border-radius:50px;font-size:1.2rem;font-weight:bold;"); client.println("text-decoration:none;color:white;}"); client.println(".on{background:#16a34a;}.off{background:#dc2626;}"); client.println("</style></head><body>"); // محتوى الصفحة client.println("<h1>🔧 Alin Li ESP32 Controller</h1>"); client.println("<p>Control the LED from your browser!</p>"); // اعرض حالة LED الحالية برمز تعبيري if (ledState) { client.println("<div class='status'>💡 LED is ON</div>"); } else { client.println("<div class='status'>🌑 LED is OFF</div>"); } // أزرار تربط إلى /on و/off // عند النقر، ينتقل المتصفح إلى ذلك URL // ما يفعّل شروط if أعلاه! client.println("<a href='/on' class='btn on'>Turn ON</a>"); client.println("<a href='/off' class='btn off'>Turn OFF</a>"); client.println("</body></html>"); // انتهينا! يرسل Flush كل البيانات ويغلق الاتصال client.flush(); Serial.println("📤 Page sent to client!"); }
▶️

Upload & Test!

⏱️ 2 minutes

1. Change "YOUR_WIFI_NAME" و "YOUR_WIFI_PASSWORD" to your real Wi-Fi

2. Click the Upload button (السهم →) في Arduino IDE

3. افتح Tools → Serial Monitor (اضبطه على 115200 باود)

4. Wait for the message: "📡 IP Address: 192.168.1.XX"

5. افتح متصفحًا على هاتفك واكتب عنوان IP هذا!

6. 🎉 You see your web page! Click the buttons — the LED responds!

🎉 Congratulations!

You just built a web server on a $5 chip! Your phone is talking to a microcontroller through Wi-Fi and HTTP — the same protocol that powers the entire World Wide Web. This is real IoT (Internet of Things) engineering!

🎯 Activity 1: Customize Your Page!

بعد أن نجح، جرّب هذه التغييرات:

  1. Change the page title from "Alin Li ESP32 Controller" to YOUR name
  2. Change the background color — try #0f172a (أزرق داكن) أو #064e3b (أخضر داكن)
  3. أضف زرًا ثالثًا يجعل LED يومض 5 مرات (تلميح: استخدم for loop with delay())
  4. أضف اسمك وتاريخ اليوم أسفل الصفحة
🔴🟢🔵 المشروع 2

تحكّم في 3 مصابيح LED خارجية

Control red, green, and blue LEDs independently!

🔌 مخطّط التوصيل
    ESP32              LEDs
    ┌──────────┐
    │          │
    │  GPIO 26 ├─────[220Ω]────🔴 Red LED──┐
    │          │                              │
    │  GPIO 27 ├─────[220Ω]────🟢 Green LED─┤
    │          │                              │
    │  GPIO 14 ├─────[220Ω]────🔵 Blue LED──┤
    │          │                              │
    │  GND    ├──────────────────────────────┘
    │          │          (all LED negatives
    └──────────┘           share ground)
    
⚡ Why the 220Ω Resistor?

LEDs are hungry — they'll try to draw too much current and burn out. The resistor limits current to a safe level (~15mA). استخدم دائمًا مقاومة مع كل LED! Without it, you'll destroy the LED and possibly damage the ESP32 pin.

project2_three_leds.ino — Control 3 LEDs from browser
#include <WiFi.h> const char* ssid = "YOUR_WIFI_NAME"; const char* password = "YOUR_WIFI_PASSWORD"; WiFiServer server(80); // عرّف أطراف كل LED const int RED_PIN = 26; const int GREEN_PIN = 27; const int BLUE_PIN = 14; // تتبّع حالة كل LED bool redOn = false, greenOn = false, blueOn = false; void setup() { Serial.begin(115200); pinMode(RED_PIN, OUTPUT); pinMode(GREEN_PIN, OUTPUT); pinMode(BLUE_PIN, OUTPUT); WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\n✅ IP: " + WiFi.localIP().toString()); server.begin(); } void loop() { WiFiClient client = server.available(); if (!client) return; while (!client.available()) { delay(1); } String req = client.readStringUntil('\r'); // بدّل حالة كل LED حسب URL if (req.indexOf("/red") != -1) { redOn = !redOn; digitalWrite(RED_PIN, redOn); } if (req.indexOf("/green") != -1) { greenOn = !greenOn; digitalWrite(GREEN_PIN, greenOn); } if (req.indexOf("/blue") != -1) { blueOn = !blueOn; digitalWrite(BLUE_PIN, blueOn); } // أنشئ صفحة ويب جميلة تضم 3 أزرار تبديل client.println("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n"); client.println("<!DOCTYPE html><html><head>"); client.println("<meta name='viewport' content='width=device-width'>"); client.println("<style>"); client.println("body{font-family:Arial;text-align:center;background:#0f172a;color:#e2e8f0;padding:40px 20px;}"); client.println("h1{font-size:1.8rem;} .btns{display:flex;flex-direction:column;gap:14px;max-width:300px;margin:20px auto;}"); client.println(".b{padding:18px;border-radius:14px;font-size:1.1rem;font-weight:bold;text-decoration:none;color:white;display:block;}"); client.println("</style></head><body>"); client.println("<h1>🔧 Alin Li LED Controller</h1>"); client.println("<div class='btns'>"); // يعرض كل زر الحالة الحالية ويربط بعنوان URL للتبديل client.print("<a class='b' style='background:"); client.print(redOn ? "#dc2626" : "#333"); client.println("' href='/red'>🔴 Red: " + String(redOn ? "ON" : "OFF") + "</a>"); client.print("<a class='b' style='background:"); client.print(greenOn ? "#16a34a" : "#333"); client.println("' href='/green'>🟢 Green: " + String(greenOn ? "ON" : "OFF") + "</a>"); client.print("<a class='b' style='background:"); client.print(blueOn ? "#2563eb" : "#333"); client.println("' href='/blue'>🔵 Blue: " + String(blueOn ? "ON" : "OFF") + "</a>"); client.println("</div></body></html>"); client.flush(); }

🎯 Activity 2: Expand It!

  1. أضف مصباح LED رابعًا على GPIO 25 — وأضف الزر والمنطق بنفسك!
  2. أضف زر «تشغيل الكل» الذي يشغّل مصابيح LED الثلاثة معًا (تلميح: عنوان URL جديد /all)
  3. أضف زر «إطفاء الكل»
  4. أضف زر «DISCO» يجعل جميع مصابيح LED تومض بنمط عشوائي 10 مرات
  5. Challenge: أضف شريط تمرير للسطوع باستخدام PWM ‏(analogWrite() أو ledcWrite() on ESP32)
🌡️ PROJECT 3

لوحة متابعة الحرارة والرطوبة

لوحة قراءات مستشعرات مباشرة مع تحديث تلقائي!

🔌 توصيل مستشعر DHT11
    ESP32           DHT11
    ┌──────────┐    ┌─────┐
    │          │    │     │
    │  3.3V   ├────┤ VCC │  (Power)
    │          │    │     │
    │  GPIO 4 ├────┤ DATA│  (Signal)
    │          │    │     │
    │  GND    ├────┤ GND │  (Ground)
    │          │    │     │
    └──────────┘    └─────┘

    Note: Add a 10kΩ pull-up resistor between DATA and VCC
    (some DHT11 modules have it built-in)
    
📚 ثبّت مكتبة DHT أولًا!

In Arduino IDE: Sketch → Include Library → Manage Libraries → Search "DHT sensor library" by Adafruit → Install. Also install "Adafruit Unified Sensor" when prompted.

project3_sensor_dashboard.ino — Live Temperature Dashboard
#include <WiFi.h> #include <DHT.h> // مكتبة مستشعر DHT const char* ssid = "YOUR_WIFI_NAME"; const char* password = "YOUR_WIFI_PASSWORD"; WiFiServer server(80); // مستشعر DHT11 على GPIO 4 #define DHT_PIN 4 #define DHT_TYPE DHT11 DHT dht(DHT_PIN, DHT_TYPE); const int LED_PIN = 2; bool ledOn = false; void setup() { Serial.begin(115200); pinMode(LED_PIN, OUTPUT); dht.begin(); // هيّئ مستشعر DHT WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("."); } Serial.println("\n📡 IP: " + WiFi.localIP().toString()); server.begin(); } void loop() { WiFiClient client = server.available(); if (!client) return; while (!client.available()) { delay(1); } String req = client.readStringUntil('\r'); if (req.indexOf("/toggle") != -1) { ledOn = !ledOn; digitalWrite(LED_PIN, ledOn); } // اقرأ قيم المستشعرات float temp = dht.readTemperature(); // درجة مئوية float hum = dht.readHumidity(); // النسبة المئوية // تحقّق من فشل القراءات String tempStr = isnan(temp) ? "Error" : String(temp, 1) + "°C"; String humStr = isnan(hum) ? "Error" : String(hum, 1) + "%"; // أنشئ صفحة لوحة متابعة جميلة client.println("HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n"); client.println("<!DOCTYPE html><html><head>"); client.println("<meta name='viewport' content='width=device-width'>"); // تحديث تلقائي كل 5 ثوانٍ! // يعيد هذا تحميل الصفحة لعرض بيانات المستشعرات المحدّثة client.println("<meta http-equiv='refresh' content='5'>"); client.println("<style>"); client.println("body{font-family:Arial;text-align:center;background:#0f172a;color:#e2e8f0;padding:30px 20px;}"); client.println("h1{font-size:1.5rem;color:#60a5fa;}"); client.println(".cards{display:flex;gap:14px;justify-content:center;flex-wrap:wrap;margin:20px 0;}"); client.println(".card{background:#1e293b;border-radius:16px;padding:24px;min-width:140px;}"); client.println(".card .icon{font-size:2.5rem;}"); client.println(".card .val{font-size:2rem;font-weight:bold;margin:8px 0;}"); client.println(".card .label{font-size:.8rem;color:#94a3b8;}"); client.println(".btn{display:inline-block;padding:16px 40px;border-radius:50px;margin-top:16px;"); client.println("font-size:1rem;font-weight:bold;text-decoration:none;color:white;}"); client.println("</style></head><body>"); client.println("<h1>🌐 Alin Li Sensor Dashboard</h1>"); client.println("<p style='color:#94a3b8;font-size:calc(.85rem * var(--alinli-text-scale,1));'>Auto-refreshes every 5 seconds</p>"); client.println("<div class='cards'>"); // بطاقة الحرارة client.println("<div class='card'><div class='icon'>🌡️</div>"); client.println("<div class='val' style='color:#f59e0b'>" + tempStr + "</div>"); client.println("<div class='label'>Temperature</div></div>"); // بطاقة الرطوبة client.println("<div class='card'><div class='icon'>💧</div>"); client.println("<div class='val' style='color:#60a5fa'>" + humStr + "</div>"); client.println("<div class='label'>Humidity</div></div>"); // بطاقة حالة LED client.println("<div class='card'><div class='icon'>" + String(ledOn ? "💡" : "🌑") + "</div>"); client.println("<div class='val'>" + String(ledOn ? "ON" : "OFF") + "</div>"); client.println("<div class='label'>LED</div></div>"); client.println("</div>"); // زر تبديل LED client.print("<a class='btn' style='background:"); client.print(ledOn ? "#dc2626" : "#16a34a"); client.println("' href='/toggle'>" + String(ledOn ? «إيقاف التشغيل» : «تشغيل») + "</a>"); client.println("</body></html>"); client.flush(); }

🎯 Activity 3: Make It Your Own!

  1. أضف تحذيرًا: إذا كانت الحرارة > 30°C، اعرض البطاقة بالأحمر مع «🔥 حار!»
  2. أضف عدادًا يعرض عدد زيارات الصفحة (تلميح: متغير عام int visits = 0; و visits++ in loop)
  3. سجّل درجة الحرارة كل دقيقة في مراقب المنفذ التسلسلي واحسب المتوسط
  4. أضف مستشعر ضوء (LDR على طرف تماثلي)، واعرض مستوى الضوء كنسبة مئوية
  5. Boss Challenge: أضف مخططًا يعرض آخر 10 قراءات للحرارة باستخدام HTML <div> bars
🧠 المفاهيم الرئيسية

What You Learned

🌐 HTTP Protocol

Browsers speak HTTP (HyperText Transfer Protocol). A request like GET /on HTTP/1.1 means "give me the page at /on." The server responds with 200 OK + محتوى HTML. هذا أساس شبكة الويب كلها — وأصبح ESP32 الخاص بك يفهمه الآن!

📡 Wi-Fi + TCP/IP

The ESP32 connects to your router, gets an IP address (like 192.168.1.42), and listens on port 80. When your phone sends a request to that IP, the router forwards it to the ESP32. This is networking in action.

🔌 GPIO (إدخال/إخراج للأغراض العامة)

GPIO pins can be set as OUTPUT (send voltage to control LEDs, motors, relays) or INPUT (read sensors, buttons). digitalWrite(pin, HIGH) sends 3.3V. digitalRead(pin) reads the voltage. This is how software controls the physical world.

📄 HTML + CSS

The web page your ESP32 sends is real HTML + CSS — the same technology behind every website. You're building full-stack IoT: hardware (ESP32 + sensors) + backend (C++ web server) + frontend (HTML/CSS page). That's 3 skill domains in one project!

🔄 بنية العميل والخادم

Your phone is the client (يطلب الصفحات). يكون ESP32 هو server (يرسل الصفحات وينفّذ الأوامر). هذه هي البنية نفسها التي تشغّل Netflix وGoogle والتطبيقات المصرفية وكل ما يعمل عبر الإنترنت.

🚀 ما الخطوة التالية؟

ارتقِ بمشاريع ESP32

⚡ Use ESPAsyncWebServer (Professional)

The WiFiServer we used is basic. For real projects, use the ESPAsyncWebServer library — it handles multiple clients simultaneously, supports WebSockets (real-time updates without page refresh), file serving from SPIFFS/LittleFS, and template processing. It's what professional IoT products use.

📱 أضف AJAX للتحديث دون إعادة تحميل الصفحة

Instead of reloading the page to update sensor data, use JavaScript fetch() to request data from the ESP32 in the background and update the page dynamically. The dashboard updates live without flickering!

🏠 Connect to Home Assistant

اجعل ESP32 يعمل مع Home Assistant (أتمتة منزلية مفتوحة المصدر) باستخدام ESPHome — a YAML-based firmware that turns ESP32 into smart home sensors and controllers without writing C++ code.

🔒 أضف Tailscale للوصول عن بُعد

Want to access your dashboard from outside your home? Don't use port forwarding! Install Tailscale on a Raspberry Pi on the same network — now you can access your ESP32 dashboard from anywhere through an encrypted tunnel.

📊 خزّن البيانات باستخدام InfluxDB + Grafana

Send sensor readings to InfluxDB (قاعدة بيانات سلاسل زمنية) واعرضها بصريًا باستخدام Grafana dashboards — see weeks of temperature history, humidity trends, and alerts. All open source.

🌐🔧✨

شريحة بسعر $5. خادم ويب. Your Creation.

You built a real web server on a chip smaller than your thumb. You learned HTTP, Wi-Fi networking, GPIO control, HTML, and client-server architecture — all in one project. These are the same skills used by IoT engineers at companies like Tesla, Philips Hue, and Ring. The difference? They had expensive equipment. You did it for $5.

«وَأَلَنَّا لَهُ الْحَدِيدَ»

شريحة سيليكون صغيرة — الحديد المطوّع في عصرنا. بالمعرفة والشيفرة، منحتها القدرة على تقديم صفحات الويب والتحكم بالأضواء وقراءة الحرارة. التقنية ليست سحرًا — إنها معرفة تُطبّق بقصد. ✨