# LAB3 遠端控制 RGB LED 燈光秀 ## demo <iframe width="560" height="315" src="https://www.youtube.com/embed/kZ1sO3Nf2ws?si=lOWUC2_PqTIMbk3c" title="YouTube video player" frameborder="0" allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share" referrerpolicy="strict-origin-when-cross-origin" allowfullscreen></iframe> ## 電路圖 ![image](https://hackmd.io/_uploads/BJUjEDbJyl.png) ## 程式碼說明 - 首先是wifi的設定 ```c= void setwifi(){ WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi..."); } Serial.println(" Successfully Connected to WiFi"); Serial.print(" Your IP Address: "); Serial.println(WiFi.localIP()); } ``` - 需求上有幾點 1. Slidebar 調整顏色 2. 燈光秀 - 因此可以稍微規劃一下架構 ![image](https://hackmd.io/_uploads/S19guvWykx.png) - 主要控制頁面 ```c++= server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(200,"text/html",HTMLpage()); }); ``` - 頁面放在函數中了,不然全部放在一起很可怕 - 話說Python是好工具,字串處理有夠好用,感謝王老闆orz,教我把HTML轉成cpp - API介面 - 更換顏色 ```c++= server.on("/change_color", HTTP_GET, [](AsyncWebServerRequest *request){ if (request->hasArg("r") && request->hasArg("g") && request->hasArg("b")){ red_val = request->arg("r").toInt(); green_val = request->arg("g").toInt(); blue_val = request->arg("b").toInt(); color(red_val,green_val,blue_val); } request->redirect("/"); }); ``` - 開啟燈光秀 ```c++= server.on("/show/on", HTTP_GET, [](AsyncWebServerRequest *request){ show = true; request->redirect("/"); }); ``` - 關閉燈光秀 ```c++= server.on("/show/off", HTTP_GET, [](AsyncWebServerRequest *request){ show = false; red_val = 128; green_val = 128; blue_val = 128; delay(4500); color(red_val,green_val,blue_val); request->redirect("/"); }); ``` - 燈光秀 - 燈光秀由global varible控制,如果是真,就會顯示燈光秀 ```c++= if (show){ // 隨機變化顏色並產生閃爍效果 for (int i = 0; i < 10; i++) { int red = random(0, 256); int green = random(0, 256); int blue = random(0, 256); // 快速閃爍效果 color(red, green, blue); delay(random(50, 150)); // 延遲時間隨機,模擬不同頻率閃爍效果 // 暫時關閉LED,模擬閃爍 color(0, 0, 0); delay(random(50, 100)); // 閃爍的間隔時間 } // 漸變效果 for (int brightness = 0; brightness <= 255; brightness += 5) { color(brightness, 0, 255 - brightness); // 由紅漸變到藍 delay(20); } for (int brightness = 255; brightness >= 0; brightness -= 5) { color(brightness, 0, 255 - brightness); // 由藍漸變到紅 delay(20); } } ``` - 燈光調節請求 ```js= function updateRGB() { var red = document.getElementById("redSlider").value; var green = document.getElementById("greenSlider").value; var blue = document.getElementById("blueSlider").value; // Update the text display document.getElementById("redValue").innerHTML = red; document.getElementById("greenValue").innerHTML = green; document.getElementById("blueValue").innerHTML = blue; // 改變顯示 RGB 顏色的區塊 document.getElementById("colorDisplay").style.backgroundColor = 'rgb(' + red + ',' + green + ',' + blue + ')'; document.getElementById("colorDisplay2").style.backgroundColor = 'rgb(' + (255-red) + ',' + (255-green) + ',' + (255-blue) + ')'; // Send the RGB values to the server var xhr = new XMLHttpRequest(); xhr.open("GET", "/change_color?r=" + red + "&g=" + green + "&b=" + blue, true); xhr.send(); } ``` ## 程式碼 ```c++= /* * 使用WiFi.h函數庫,預設 * 使用analogWrite.h函數庫,預設 * 使用ESPAsyncWebServer.h,需要安裝 */ #include <WiFi.h> #include <ESPAsyncWebServer.h> #include <analogWrite.h> #define R1_PIN 13 #define G1_PIN 27 #define B1_PIN 26 #define R2_PIN 25 #define G2_PIN 33 #define B2_PIN 32 const char* ssid = "xxxxxxxxxxxxxx"; const char* password = "xxxxxxxxxxxxxx"; bool show = false; unsigned char red_val = 0; unsigned char green_val = 0; unsigned char blue_val = 0; AsyncWebServer server(80); void setup() { Serial.begin(9600); // put your setup code here, to run once: setwifi(); color(128,128,128); server.on("/", HTTP_GET, [](AsyncWebServerRequest *request){ request->send(200,"text/html",HTMLpage()); }); server.on("/change_color", HTTP_GET, [](AsyncWebServerRequest *request){ if (request->hasArg("r") && request->hasArg("g") && request->hasArg("b")){ red_val = request->arg("r").toInt(); green_val = request->arg("g").toInt(); blue_val = request->arg("b").toInt(); color(red_val,green_val,blue_val); } request->redirect("/"); }); server.on("/show/on", HTTP_GET, [](AsyncWebServerRequest *request){ show = true; request->redirect("/"); }); server.on("/show/off", HTTP_GET, [](AsyncWebServerRequest *request){ show = false; red_val = 128; green_val = 128; blue_val = 128; delay(4500); color(red_val,green_val,blue_val); request->redirect("/"); }); server.begin(); } void loop() { if (show){ // 隨機變化顏色並產生閃爍效果 for (int i = 0; i < 10; i++) { int red = random(0, 256); int green = random(0, 256); int blue = random(0, 256); // 快速閃爍效果 color(red, green, blue); delay(random(50, 150)); // 延遲時間隨機,模擬不同頻率閃爍效果 // 暫時關閉LED,模擬閃爍 color(0, 0, 0); delay(random(50, 100)); // 閃爍的間隔時間 } // 漸變效果 for (int brightness = 0; brightness <= 255; brightness += 5) { color(brightness, 0, 255 - brightness); // 由紅漸變到藍 delay(20); } for (int brightness = 255; brightness >= 0; brightness -= 5) { color(brightness, 0, 255 - brightness); // 由藍漸變到紅 delay(20); } } delay(50); } void setwifi(){ WiFi.begin(ssid, password); while (WiFi.status() != WL_CONNECTED) { delay(1000); Serial.println("Connecting to WiFi..."); } Serial.println(" Successfully Connected to WiFi"); Serial.print(" Your IP Address: "); Serial.println(WiFi.localIP()); } void color (unsigned char red, unsigned char green, unsigned char blue) { analogWrite(R1_PIN, red); analogWrite(G1_PIN, green); analogWrite(B1_PIN, blue); analogWrite(R2_PIN, 255-red); analogWrite(G2_PIN, 255-green); analogWrite(B2_PIN, 255-blue); } String HTMLpage(){ String html = ""; String disabled = ""; if (show){ disabled = "disabled"; } html += "<!DOCTYPE html>\n"; html += "<html lang=\"zh-tw\">\n"; html += "<head>\n"; html += " <meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">\n"; html += " <meta charset=\"utf-8\">\n"; html += " <link rel=\"icon\" href=\"data:,\">\n"; html += " <style>\n"; html += " html { \n"; html += " font-family: Helvetica; \n"; html += " display: inline-block; \n"; html += " margin: 0px auto; \n"; html += " text-align: center;\n"; html += " }\n"; html += " body {\n"; html += " background-color: #f2f2f2; /* 保持淡色背景 */\n"; html += " }\n"; html += " .button-container {\n"; html += " text-align: center;\n"; html += " }\n"; html += " .button-container p {\n"; html += " display: inline-block;\n"; html += " margin: 10px;\n"; html += " }\n"; html += " .button {\n"; html += " background-color: #000000;\n"; html += " border: none;\n"; html += " color: white;\n"; html += " padding: 16px 40px;\n"; html += " text-decoration: none;\n"; html += " border-radius: 5px;\n"; html += " font-size: 20px;\n"; html += " cursor: pointer;\n"; html += " }\n"; html += " .button2 {\n"; html += " background-color: #555555;\n"; html += " }\n"; html += " /* 自定義滑桿樣式 */\n"; html += " input[type=range] {\n"; html += " -webkit-appearance: none;\n"; html += " width: 30%;\n"; html += " height: 15px;\n"; html += " border-radius: 5px;\n"; html += " background: #ddd;\n"; html += " outline: none;\n"; html += " opacity: 0.7;\n"; html += " -webkit-transition: .2s;\n"; html += " transition: opacity .2s;\n"; html += " }\n"; html += " input[type=range]:hover {\n"; html += " opacity: 1;\n"; html += " }\n"; html += " /* 滑塊樣式 */\n"; html += " input[type=range]::-webkit-slider-thumb {\n"; html += " -webkit-appearance: none;\n"; html += " appearance: none;\n"; html += " width: 25px;\n"; html += " height: 25px;\n"; html += " border-radius: 50%;\n"; html += " background: #4CAF50;\n"; html += " cursor: pointer;\n"; html += " }\n"; html += " input[type=range]::-moz-range-thumb {\n"; html += " width: 25px;\n"; html += " height: 25px;\n"; html += " border-radius: 50%;\n"; html += " background: #4CAF50;\n"; html += " cursor: pointer;\n"; html += " }\n"; html += " /* 根據 RGB 通道設置不同背景顏色 */\n"; html += " #redSlider {\n"; html += " background: linear-gradient(to right, red, #fff);\n"; html += " }\n"; html += " #greenSlider {\n"; html += " background: linear-gradient(to right, green, #fff);\n"; html += " }\n"; html += " #blueSlider {\n"; html += " background: linear-gradient(to right, blue, #fff);\n"; html += " }\n"; html += " .color-display-container {\n"; html += " display: flex;\n"; html += " justify-content: center; /* 將兩個顏色區塊置中對齊 */\n"; html += " gap: 20px; /* 減少兩個區塊之間的間距 */\n"; html += " }\n"; html += " \n"; html += " /* 新增顯示 RGB 顏色的區塊 */\n"; html += " .color-display {\n"; html += " width: 100px;\n"; html += " height: 100px;\n"; html += " margin: 0px ;\n"; html += " border-radius: 10px;\n"; html += " border: 2px solid #000;\n"; html += " background-color: rgb(128, 128, 128); /* 初始顏色 */\n"; html += " transition: background-color 0.5s ease;\n"; html += " }\n"; html += " /* 七彩燈光秀按鈕動畫 */\n"; html += " .rainbow-button {\n"; html += " padding: 16px 40px;\n"; html += " font-size: 20px;\n"; html += " border: none;\n"; html += " border-radius: 5px;\n"; html += " color: white;\n"; html += " cursor: pointer;\n"; html += " text-decoration: none;\n"; html += " background-size: 400% 400%;\n"; html += " background: linear-gradient(45deg, red, orange, yellow, green, blue, indigo, violet);\n"; html += " animation: rainbow 3s ease infinite;\n"; html += " }\n"; html += " </style>\n"; html += "</head>\n"; html += "<body>\n"; html += " <h1>ESP32 Web Server</h1>\n"; html += " <!-- RGB LED Control -->\n"; html += " <h2>RGB LED Control</h2>\n"; html += " <!-- 顯示 RGB 顏色的區塊 -->\n"; html += " <div class=\"color-display-container\">\n"; html += " <div class=\"color-display\" id=\"colorDisplay\"></div>\n"; html += " <div class=\"color-display\" id=\"colorDisplay2\"></div>\n"; html += " </div>\n"; html += " <!-- Red Slider -->\n"; html += " <p>Red: <span id=\"redValue\">128</span></p>\n"; html += " <input type=\"range\" min=\"0\" max=\"255\" value=\"128\" class=\"slider\" id=\"redSlider\" oninput=\"updateRGB()\"" + disabled + ">\n"; html += " <!-- Green Slider -->\n"; html += " <p>Green: <span id=\"greenValue\">128</span></p>\n"; html += " <input type=\"range\" min=\"0\" max=\"255\" value=\"128\" class=\"slider\" id=\"greenSlider\" oninput=\"updateRGB()\"" + disabled + ">\n"; html += " <!-- Blue Slider -->\n"; html += " <p>Blue: <span id=\"blueValue\">128</span></p>\n"; html += " <input type=\"range\" min=\"0\" max=\"255\" value=\"128\" class=\"slider\" id=\"blueSlider\" oninput=\"updateRGB()\"" + disabled + ">\n"; html += " <!-- Display current state and ON/OFF buttons for GPIO 26 & GPIO 27 -->\n"; html += " <div class=\"button-container\">\n"; html += " <p><a href=\"/show/off\"><button class=\"button\">停止🛑</button></a></p>\n"; html += " <p><a href=\"/show/on\"><button class=\"rainbow-button\">燈光秀</button></a></p>\n"; html += " </div>\n"; html += " <!-- JavaScript to handle slider value and update display -->\n"; html += " <script>\n"; html += " function updateRGB() {\n"; html += " var red = document.getElementById(\"redSlider\").value;\n"; html += " var green = document.getElementById(\"greenSlider\").value;\n"; html += " var blue = document.getElementById(\"blueSlider\").value;\n"; html += " document.getElementById(\"redValue\").innerHTML = red;\n"; html += " document.getElementById(\"greenValue\").innerHTML = green;\n"; html += " document.getElementById(\"blueValue\").innerHTML = blue;\n"; html += " document.getElementById(\"colorDisplay\").style.backgroundColor = 'rgb(' + red + ',' + green + ',' + blue + ')';\n"; html += " document.getElementById(\"colorDisplay2\").style.backgroundColor = 'rgb(' + (255-red) + ',' + (255-green) + ',' + (255-blue) + ')';\n"; html += " var xhr = new XMLHttpRequest();\n"; html += " xhr.open(\"GET\", \"/change_color?r=\" + red + \"&g=\" + green + \"&b=\" + blue, true);\n"; html += " xhr.send();\n"; html += " }\n"; html += " </script>\n"; html += "</body>\n"; html += "</html>\n"; return html; } ```