# 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>
## 電路圖

## 程式碼說明
- 首先是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. 燈光秀
- 因此可以稍微規劃一下架構

- 主要控制頁面
```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;
}
```