ساخت نمایشگر POV با نئوپیکسل و ESP8266
در این پروژه میخواهیم یک نمایشگر پایداری دید یا نمایشگر POV با LED نئوپیکسل WS2812B بسازیم. در حین ساخت پروژه، تنها تمرکز ما بر این بود که چگونه میتوانیم آن را سادهتر و کم سختافزار تر کنیم. در نتیجه، ما چند LED ساده WS2812B RGB و یک ماژول ESP8266-01 را به عنوان مغز این پروژه انتخاب کردیم، زیرا آنها سبک وزن هستند و هزینه کمتری دارند.
در یکی از پروژه های قبلی خود، یک نمایشگر LED پنکه ای آردوینو ساخته ایم که از فناوری POV برای نمایش متن استفاده می کند. اگر میخواهید متنی را روی یک نمایشگر POV چاپ کنید، میتوانید آن پروژه را بررسی کنید.
مدار نمایشگر POV با ESP8266
شماتیک مدار نمایشگر POV مبتنی بر WS2812B و ESP8266-01 در زیر نشان داده شده است و همانطور که می بینید بسیار ساده و قابل درک است.
قطعات مورد نیاز
اجزای مورد نیاز برای ساخت نمایشگر POV ساده و ارزان هستند. لیست اجزا در زیر آورده شده است.
- ESP8266 – 01
- LED های RGB WS2812B
- باتری لیتیومی 3.7 ولت 400 میلی آمپر
- موتور 12 ولت DC
- برد بورد
- باتری 12 ولتی (برای تغذیه موتور)
مغز مدار ماژول ESP8266 است که توسط یک باتری لیتیومی 3.7 ولتی 400 میلی آمپر تغذیه می شود و LED های WS2812B به GPIO 0 ماژول ESP8266-01 متصل می شوند. در مجموع 8 LED در نئوپیکسل وجود دارد، و ما از یک ماژول برای تغذیه آنها استفاده کردیم. اگر ماژول را ندارید می توانید از نوارهای LED به عنوان جایگزین استفاده کنید. در این مدار، ما از GPIO0 به جای GPIO2 استفاده کرده ایم، زیرا پس از آزمایش تعداد زیادی از ماژول های ESP8266-01 متوجه شدیم که GPIO0 از GPIO2 ماژول پایدارتر است. لطفا توجه داشته باشید که در حین ساخت این پروژه ما هیچ مداری برای شارژ باتری قرار ندادیم، زیرا باعث سنگین شدن برد می شود و حرکت دادن آن بسیار دشوار است.
برای هر نمایشگر POV، ما به یک موتور قدرتمند برای کار با آن نیاز داریم. ما از یک موتور گیربکس 200 RPM استفاده کرده ایم.
برای نصب موتور، از یک کانال آلومینیومی بزرگ با یک نگهدارنده کوچک L شکل چاپ شده سه بعدی استفاده کرده ایم که موتور را به کانال آلومینیومی متصل نگه می دارد. تصویری از کانال چاپ سه بعدی در زیر نشان داده شده است:
برای تغذیه موتور، از یک منبع تغذیه 5 ولتی استفاده می کنیم.
کد آردوینو ساخت نمایشگر POV نئوپیکسل WS2812
اکنون که تمام سخت افزار و کار این پروژه را می دانیم، می توانیم به قسمت کد این پروژه برویم. بخش کد برای این پروژه کوچک نیست اما درک آن آسان است. ما کد خود را با گنجاندن تمام کتابخانه های مورد نیاز و تعریف تمام پین ها و پارامترهای لازم برای اجرای WS2812B LED های WI-Fi و OTA شروع می کنیم.
کد کامل در فایل دانلودی انتهای صفحه قرار گرفته است.
// کتاب خانه های مورد نیاز #include <ESP8266WiFi.h> #include <FastLED.h> #include "OTA.h" #define LED_PIN 0 // پین LED #define NUM_LEDS 8 // تعداد ال ای دی #define BRIGHTNESS 50 // روشنایی #define LED_TYPE WS2812 // نوع WS2812B #define COLOR_ORDER GRB // ترتیب رنگ #define UPDATES_PER_SECOND 100 // فریم بر ثانیه
در مرحله بعد، ما برای LED های RGB و همچنین سرور آبجکت ایجاد می کنیم.
CRGB leds[NUM_LEDS]; // آرایه ال ای دی CRGBPalette16 currentPalette; // تنظیم رنگ TBlendType currentBlending ;// تنظیم ترکیب extern CRGBPalette16 myRedWhiteBluePalette; //ایجاد آبجکت برای پالت extern const TProgmemPalette16 myRedWhiteBluePalette_p PROGMEM; // ایجاد وب سرور در پورت 80 WiFiServer server(80);
سپس، همه متغیرهای مورد نیاز را اعلام میکنیم، از جمله متغیرهایی که حالت های دکمه را با نامهای Animation_1، Animation_2، و Animation_3 ذخیره میکنند، و متغیری را برای ذخیره کل صفحه وب html که هدر header دارد، ایجاد میکنیم. در نهایت، دو مجموعه متغیر وجود دارد که برای ذخیره SSID و رمز عبور برای هات اسپات Wi-Fi استفاده می شود.
String Animation_1 = "off"; String Animation_2 = "off"; String Animation_3 = "off"; //متغیر درخواست http String header; const char* ssid = "Your SSID"; const char* password = "Your Password"; unsigned long currentTime = millis(); // زمان قبلی unsigned long previousTime = 0; // زمان تایم اوت const long timeoutTime = 2000;
در مرحله بعد، تابع setup() خود را داریم. در تابع setup، ما موارد معمول خود را داریم مانند فعال کردن سریال مانیتور برای اشکال زدایی، راه اندازی OTA، راه اندازی Neopixels و راه اندازی وب سرور.
void setup() { Serial.begin(115200); Serial.println("Booting"); setupOTA("POV_Display", ssid , password); delay( 300 ); // power-up safety delay FastLED.addLeds<LED_TYPE, LED_PIN, COLOR_ORDER>(leds, NUM_LEDS).setCorrection( TypicalLEDStrip ); FastLED.setBrightness( BRIGHTNESS ); currentPalette = RainbowColors_p; currentBlending = LINEARBLEND; server.begin(); }
در مرحله بعد، ما لیستی از الگوها را داریم که با یک اشاره گر تعریف کرده ایم و هنگام جابجایی بین الگوهای مختلف، فراخوانی می شود.
typedef void (*SimplePatternList[])(); SimplePatternList gPatterns = { rainbow, rainbowWithGlitter, confetti, sinelon, juggle, bpm }; uint8_t gCurrentPatternNumber = 0; // شماره ایندکس uint8_t gHue = 0;
بعد، بخش loop خود را داریم. در بخش loop ، تابع ArduinoOTA.handle() را فراخوانی می کنیم تا بتوانیم یک به روز رسانی OTA ESP را در حالی که ESP روی PCB نصب شده و به موتور متصل است، انجام دهیم. در مرحله بعد، آبجکت کلاینت WiFiClient را اعلام و تعریف می کنیم و بررسی می کنیم که آیا اتصالات جدیدی در دسترس است یا خیر.
void loop() { while ((unsigned long) millis() < 10000) { ArduinoOTA.handle(); delay(10); } ArduinoOTA.handle(); WiFiClient client = server.available(); // بررسی وجود کلاینت جدید
حال، اگر یک کلاینت جدید متصل شود و این عبارت درست شود، زمان فعلی را می گیریم و آن را در متغیر currentTime ذخیره می کنیم و سپس همان داده ها را برای استفاده بعدی در متغیر previousTime ذخیره می کنیم. سپس، بیانیهای را در پنجره نمایشگر سریال چاپ میکنیم تا به کاربر اطلاع دهیم که کاربر جدیدی متصل شده و صفحه وب را درخواست کرده است. ما همچنین یک متغیر نوع رشته تعریف می کنیم تا آن داده های ورودی را نگه دارد تا بتوانیم بعداً داده ها را تجزیه کنیم و بررسی کنیم که آیا درخواست دقیقاً همان چیزی است که می خواهیم.
if (client) { // اتصال یک کلاینت جدید currentTime = millis(); previousTime = currentTime; Serial.println("New Client"); // چاپ کلاینت جدید در سریال مانیتور String incomingData = ""; // ایجاد رشته برای نگهداری داده ها
در مرحله بعد، یک حلقه while تعریف می کنیم، این حلقه while فقط برای ارائه دنباله زمان پایان است. اگر تاخیر زمانی بیشتر شود، حلقه while به سادگی شکسته می شود. در داخل حلقه while، ما به سادگی بررسی میکنیم که آیا دادهای در سمت کلاینت موجود است یا خیر، اگر چنین است، یک متغیر محلی به نام read_byte ایجاد میکنیم و کل بیتاستریم را که از کلاینت میآید ذخیره میکنیم. سپس کل بیت استریم را در پنجره نمایشگر سریال برای اشکال زدایی چاپ می کنیم. در نهایت، کل بیت استریم را در متغیر نوع رشته ای به نام header ذخیره می کنیم.
while (client.connected() && currentTime - previousTime <= timeoutTime) { // کلاینت متصل است و درخواست میکند currentTime = millis(); if (client.available()) { // اگر کلاینت چیزی را ارسال کنید char read_byte = client.read(); // آن را میخوانیم Serial.write(read_byte); // در سریال مانیتور چاپ میکنیم header += read_byte;
بعد، بررسی می کنیم که آیا متغیر بایت ما دارای یک کاراکتر “\n” است یا نه. اگر چنین است، پس می توانیم بگوییم که این پایان درخواست HTTP است. بنابراین اکنون می توانیم صفحه وب خود را به عنوان پاسخ ارسال کنیم. در بخش بعدی کد، ما به سادگی کل صفحه وب را به عنوان پاسخ ارسال می کنیم. پاسخ همیشه با یک پاسخ OK شروع می شود، بنابراین در ابتدا، زمانی که متوجه شدیم درخواست کامل شده است، آن را ارسال می کنیم. پس از آن، پارامترهای لازم دیگر را ارسال می کنیم.
if (incomingData.length() == 0) { // HTTP response همیشه با ok شروع میشود client.println("HTTP/1.1 200 OK"); client.println("Content-type:text/html"); client.println("Connection: close"); client.println();
قسمت بعدی کد مهمترین قسمت است زیرا در این قسمت داده ها را از وب سرور خود دریافت کرده ایم و متغیرهای نوع رشته ای را که قبلاً اعلام کرده ایم به روز کرده ایم و در تابع حلقه این متغیر را بررسی می کنیم و LED ها را به روز می کنیم. .
//تنظیم حالت های مختلف برای ال ای دی if (header.indexOf("GET /Animation_1/on") >= 0) { // Serial.println("Animation 1 on"); Animation = "on"; } else if (header.indexOf("GET /Animation_1/off") >= 0) { // Serial.println("Animation 1 off"); Animation = "off"; } else if (header.indexOf("GET /Animation_2/on") >= 0) { // Serial.println("Animation 2 on"); Animation_2 = "on"; } else if (header.indexOf("GET /Animation_2/off") >= 0) { // Serial.println("Animation 2 off"); Animation = "off"; } else if (header.indexOf("GET /Animation_3/on") >= 0) { // Serial.println("Animation 3 on"); Animation = "on"; } else if (header.indexOf("GET /Animation_3/off") >= 0) { // Serial.println("Animation 3 off"); Animation = "off"; }
در قسمت بعدی کل صفحه وب را به عنوان پاسخ برای کلاینت ارسال می کنیم و در این قسمت عمدتا فایل های HTML و CSS را با استفاده از تابع client.println() ارسال می کنیم.
client.println("<!DOCTYPE html><html>"); client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">"); client.println("<link rel=\"icon\" href=\"data:,\">"); client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}"); client.println(".button { background-color: #3bc041; color: #ffffff; padding: 9px 30px ; text-decoration: none; font-size: 30px; margin: 2px ; border-radius: 5px ; border: 1px solid #ddffdf; box-shadow: 1px 2px 3px #0000001c; "); client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}"); client.println(".button:hover{ background-color: #31b237; }"); client.println(".button2 { background-color: #aaaaaa; border: 1px solid #bbbbbb; }</style></head>"); // This is the heading of the webpage client.println("<body><h1>ESP32 Web Server</h1>");
در قسمت بعدی کد، صفحه وب را با وضعیت فعلی دکمه به روز می کنیم و هرگونه تغییر در وضعیت دکمه را بررسی می کنیم. اگر تغییر حالت رخ داده باشد، تغییرات را با متد ()client.println به روز می کنیم. ما این کار را برای هر سه دکمه انجام می دهیم. در نهایت با ارسال یک عبارت خالی به پاسخ پایان می دهیم.
// نمایش وضعیت دکمه 1 client.println("<p>Animation 1 - State " + Animation_1 + "</p>"); if (Animation_1 == "off") client.println("<p><a href=\"/Animation_1/on\"><button class=\"button\">ON</button></a></p>"); else client.println("<p><a href=\"/Animation_1/off\"><button class=\"button button2\">OFF</button></a></p>"); // نمایش وضعیت دکمه 2 client.println("<p>Animation 2 - State " + Animation_2 + "</p>"); if (Animation_2 == "off") client.println("<p><a href=\"/Animation_2/on\"><button class=\"button\">ON</button></a></p>"); else client.println("<p><a href=\"/Animation_2/off\"><button class=\"button button2\">OFF</button></a></p>"); // نمایش وضعیت دکمه 3 client.println("<p>Animation 3 - State " + Animation_3 + "</p>"); if (Animation_3 == "off") client.println("<p><a href=\"/Animation_3/on\"><button class=\"button\">ON</button></a></p>"); else client.println("<p><a href=\"/Animation_3/off\"><button class=\"button button2\">OFF</button></a></p>"); client.println("</body></html>"); // پایان پاسخ http client.println();
در مرحله بعد، دستورات دیگری و other if را داریم که هر استثنایی را که در طول فرآیند ارتباط سرور-کلینت رخ می دهد، کنترل می کند. حالا اگر همه چیز درست باشد و با موفقیت اجرا شود، متغیر هدر اصلی خود را پاک کرده و اتصال را قطع می کنیم.
else // اگر دستور خط جدید دریافت شد incomingData = ""; } else if (read_byte != '\r') // اگر هر چچیز دیگری دریافت شد incomingData += read_byte; // بایت خوانده شده به متغیر اضافه شود } } // Clear the header variable header = ""; // Close the connection client.stop(); Serial.println("Client disconnected"); Serial.println("\n"); }
اگر فرآیند بالا با موفقیت به پایان برسد، به تابع loop اصلی خود برمی گردیم و در مرحله بعدی، در تابع loop ، هرگونه به روز رسانی در متغیرهای نوع رشته خود Animation_1، Animation_2 و Animation_3 را بررسی می کنیم. اگر تغییری رخ داده باشد، عملکرد مربوطه را فراخوانی کرده و صفحه نمایش را روشن می کنیم. اگر هر دکمه روشن باشد، نمایشگر POV را خاموش می کنیم.
if (Animation_1 == "on") { color_palette(); } else if (Animation_2 == "on") { animation(); //cyclone_colour(); } else if (Animation_3 == "on") { //first_light(); demo_reel100(); } else if (Animation_1 == "off" && Animation_2 == "off" && Animation_3 == "off" { for (int i = 0; i < 8; i++) { leds[i] = CRGB::Black; FastLED.show(); delay(2); } }
این نشاندهنده پایان بخش کد است، اما همانطور که در کد مشاهده میکنید، بخش بزرگی از کد وجود دارد که توضیح داده نشده است، زیرا کل این بخش از کد از کتابخانه LED سریع گرفته شده است و میتوانید آن را بررسی کنید. از آنجایی که ما فقط سه دکمه را نمایش دادهایم، شما فقط میتوانید بین سه الگو یکی را انتخاب کنید، اما الگوهای دیگری در کد موجود است که میتوانید آنها را فراخوانی و نمایش دهید.
هنگامی که کد آپلود شد و IP خود را در پنجره نمایشگر سریال دریافت کردید، چیزی را دریافت می کنید که در بالا نشان داده شده است. با فشار دادن دکمه فعال سازی می توانید انیمیشنی را که در صفحه نمایش نشان داده شده است تغییر دهید.
برای آزمایش نمایشگر POV از مقداری نوار چسب دو طرفه برای چسباندن موقت نوار آلومینیومی به میز استفاده کردیم و موتور را با برق 5 ولت تغذیه می کنیم. تصویر زیر تنظیمات تست ما را نشان می دهد.
هنگامی که تنظیمات سخت افزار انجام شد، برنامه وب سرور خود را که قبلا ساخته بودیم و انیمیشن ها و افکت های مختلف را آزمایش کرده بودیم، باز کردیم. تصویر برخی از آنها در زیر نشان داده شده است:
با این کار، ما پروژه خود را که صفحه نمایش POV با LED های RGB WS2812B بود به پایان می رسانیم. امیدوارم از خواندن این مطلب لذت برده باشید و چیز مفیدی یاد گرفته باشید. اکنون وقت آن است که خودتان آن را بسازید و در بخش نظرات زیر به ما اطلاع دهید که با این پروژه چه میکنید. در صورت داشتن هرگونه سوال یا مشکلی می توانید آنها را در انجمن ما مطرح کنید .
موارد موجود در فایل : سورس
برای دانلود فایل ها باید حساب کاربری داشته باشید ثبت نام / ورود