原理是:
- 打开浏览器时,将晴天颜色变量中的JSON串发送给浏览器。
- 浏览器接收到JSON串,解析之,并以三原色滑块的形式展现出来
- 用户滑动滑块,预览当前颜色,点击“保存”按钮后,发送请求给后台,改变晴天颜色变量,以此实现对晴天颜色的控制。
- 以此类推,其他天气的颜色同样处理,预期实现的效果是这样的
但在实际运行时出现错误,因为把从后端返回来的JSON串赋值给前端的时候,因为双引号的问题导致出错。
var result="{"r":255,"g":255,"b":150}"
类似这样的结果,会引发js错误,导致网页无法正常显示和运行。
无奈才疏学浅,尝试了各种办法都不行,只好把这个JSON串编码为base64发送给网页,然后js再解码这个base64字符串,然后再进行JSON解析,最终成功。
目前已经可以实现用网页来设定各种天气下的颜色效果,下一步,将设定后的效果保存进EEPROM就基本上完成这个小制作了。
大致的代码原理:
#include <base64.h>//多引用了一个库,用来进行base64编码
const char* PARAM_INPUT_QINGTIANVALUE = "qingtianValue";//定义晴天web请求名
const char* PARAM_INPUT_DUOYUNVALUE = "duoyunValue";//定义多云web请求名,其他天气以此类推
String rgbJsonQing = "{\"r\":255,\"g\":255,\"b\":150}";//定义晴天颜色json
String rgbJsonDuoyun = "{\"r\":255,\"g\":255,\"b\":225}";//定义多云颜色json,其他天气以此类推
//设置这个异步网页服务器的端口为80
AsyncWebServer server(80);
//要打印的首页
//标记在%之间的LIGHTVALUE,是要向网页处理器发出名为LIGHTVALUE的请求,然后获得值[对于本例,就是灯光亮度]
const char index_html[] PROGMEM = R"rawliteral(
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1">
<style>
table {
border: 1px solid #000000;
border-collapse: collapse
}
td {
border: 1px solid #000000;
border-collapse: collapse
}
</style>
<title>彩灯控制</title>
</head>
<body onload="firstLoad()">
<h2>亮度控制</h2>
<p>当前灯光亮度:<span id="textLightValue">%LIGHTVALUE%</span>/255</p>
<p>设置灯光亮度:<input type="range" min="0" max="255" value="%LIGHTVALUE%" step="1" onchange="changeLightValue(this.value)"></p>
<hr />
<h2>颜色控制</h2>
<h3>未来12小时天气:%TIANQINAME%</h3>
<p id="table"></p>
<script>
var arr = new Array("qingtian", "duoyun");
function base64_decode (input) { // 解码,配合decodeURIComponent使用
var base64EncodeChars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
var output = "";
var chr1, chr2, chr3;
var enc1, enc2, enc3, enc4;
var i = 0;
input = input.replace(/[^A-Za-z0-9\+\/\=]/g, "");
while (i < input.length) {
enc1 = base64EncodeChars.indexOf(input.charAt(i++));
enc2 = base64EncodeChars.indexOf(input.charAt(i++));
enc3 = base64EncodeChars.indexOf(input.charAt(i++));
enc4 = base64EncodeChars.indexOf(input.charAt(i++));
chr1 = (enc1 << 2) | (enc2 >> 4);
chr2 = ((enc2 & 15) << 4) | (enc3 >> 2);
chr3 = ((enc3 & 3) << 6) | enc4;
output = output + String.fromCharCode(chr1);
if (enc3 != 64) {
output = output + String.fromCharCode(chr2);
}
if (enc4 != 64) {
output = output + String.fromCharCode(chr3);
}
}
return utf8_decode(output);
}
function utf8_decode (utftext) { // utf-8解码
var string = '';
let i = 0;
let c = 0;
let c1 = 0;
let c2 = 0;
while (i < utftext.length) {
c = utftext.charCodeAt(i);
if (c < 128) {
string += String.fromCharCode(c);
i++;
} else if ((c > 191) && (c < 224)) {
c1 = utftext.charCodeAt(i + 1);
string += String.fromCharCode(((c & 31) << 6) | (c1 & 63));
i += 2;
} else {
c1 = utftext.charCodeAt(i + 1);
c2 = utftext.charCodeAt(i + 2);
string += String.fromCharCode(((c & 15) << 12) | ((c1 & 63) << 6) | (c2 & 63));
i += 3;
}
}
return string;
}
function firstLoad() {
var chName = "";
var table = "<table>";
table += "<tr>";
table += "<td></td>";
table += "<td>颜色值</td>";
table += "<td>原始颜色</td>";
table += "<td>新颜色</td>";
table += "<td></td>";
table += "</tr>";
for (var i in arr) {
switch (arr[i]) {
case "qingtian":
chName = "晴天"
break;
case "duoyun":
chName = "多云"
break;
default:
chName = "未知天气"
break;
}
table += "<tr>";
table += "<td>" + chName+"</td>";
table += "<td>";
table += "红色<input type=\"range\" id=\"" + arr[i] + "_r\" value=\"100\" min=\"0\" max=\"255\" onchange=\"changeColor('" + arr[i] +"')\" /><br />";
table += "绿色<input type=\"range\" id=\"" + arr[i] + "_g\" value=\"100\" min=\"0\" max=\"255\" onchange=\"changeColor('" + arr[i] +"')\" /><br />";
table += "蓝色<input type=\"range\" id=\"" + arr[i] + "_b\" value=\"100\" min=\"0\" max=\"255\" onchange=\"changeColor('" + arr[i] +"')\" />";
table += "</td>";
table += " <td id=\"td_yuanshi_" + arr[i] +"\"></td>";
table += "<td id=\"td_" + arr[i] +"\"></td>";
table += "<td><input type=\"button\" value=\"保存并生效\" onclick=\"saveColor('" + arr[i] +"')\" /></td>";
table += "</tr>";
}
table += "</table>";
document.getElementById("table").innerHTML = table;
firstSetColor();
}
//改变灯光亮度
function changeLightValue(value) {
var sliderValue = value;
document.getElementById("textLightValue").innerHTML = sliderValue;
console.log(sliderValue);
var xhr = new XMLHttpRequest();
xhr.open("GET", "/sliderLight?lightValue=" + sliderValue, true);
xhr.send();
}
//打开页面时,初始化各种天气下的框值和颜色预览
function firstSetColor() {
firstSetColorDo("qingtian", "%QINGTIANVALUE%");
firstSetColorDo("duoyun", "%DUOYUNVALUE%");
}
//为配合上述方法的共用方法
function firstSetColorDo(str, rgbstr) {
var RGB = JSON.parse(base64_decode(rgbstr));
document.getElementById(str + "_r").value = RGB["r"];
document.getElementById(str + "_g").value = RGB["g"];
document.getElementById(str + "_b").value = RGB["b"];
document.getElementById("td_yuanshi_" + str).innerHTML = "<div style='background-color:rgb(" + RGB["r"] + "," + RGB["g"] + "," + RGB["b"] + ");'> </div>";
document.getElementById("td_" + str).innerHTML = "<div style='background-color:rgb(" + RGB["r"] + "," + RGB["g"] + "," + RGB["b"] + ");'> </div>";
}
//输入颜色时,改变颜色预览
function changeColor(str) {
var r = document.getElementById(str + "_r").value;
var g = document.getElementById(str + "_g").value;
var b = document.getElementById(str + "_b").value;
document.getElementById("td_" + str).innerHTML = "<div style='background-color:rgb(" + r + "," + g + "," + b + ");'> </div>";
}
//确认,发送命令到webserver,
function saveColor(str) {
var r = document.getElementById(str + "_r").value;
var g = document.getElementById(str + "_g").value;
var b = document.getElementById(str + "_b").value;
var sliderValue = "{\"r\":" + r + ",\"g\":" + g + ",\"b\":" + b + "}";
var xhr = new XMLHttpRequest();
xhr.open("GET", "/sliderRGB?" + str + "Value=" + sliderValue, true);
xhr.send();
document.getElementById("td_yuanshi_" + str).innerHTML = "<div style='background-color:rgb(" + r + "," + g + "," + b + ");'> </div>";
}
</script>
</body>
</html>
)rawliteral";
//网页处理器,根据从网页中发送的不同调用请求返回值
String processor(const String& var){
if (var == "QINGTIANVALUE"){
return base64::encode(rgbJsonQing);
}
if (var == "DUOYUNVALUE"){
return base64::encode(rgbJsonDuoyun);
}
return String();
}
//解析颜色json串的方法
void formatRGBFromJson(String json)
{
Serial.println(json);
DynamicJsonDocument doc(100);//rgb字符串长度不会大于100,所以这里设定100即可
DeserializationError error = deserializeJson(doc, json);
if (error) {
Serial.print(F("deserializeJson() failed: "));
Serial.println(error.f_str());
return;
}
JsonObject obj = doc.as<JsonObject>();
color = strip.Color(obj["r"], obj["g"], obj["b"]);
}
//不同天气,赋值不同颜色。这里可以自己任意修改。
void setLed()
{
if (tianqi == 0 || tianqi == 1 || tianqi == 2 || tianqi == 3) //晴
{
formatRGBFromJson(rgbJsonQing);
}
else if (tianqi == 4 || tianqi == 5 || tianqi == 6 || tianqi == 7 || tianqi == 8) //多云
{
formatRGBFromJson(rgbJsonDuoyun);
}
else
{
formatRGBFromJson(rgbJsonQing);
}
for (i = 0; i < 9; i++)
{
strip.setPixelColor(i, color);
}
}
void setup() {
// 开始设置web服务器——彩灯颜色控制设置
server.on("/sliderRGB", HTTP_GET, [] (AsyncWebServerRequest *request) {
String inputMessage;
if (request->hasParam(PARAM_INPUT_QINGTIANVALUE)) {
inputMessage = request->getParam(PARAM_INPUT_QINGTIANVALUE)->value();//get传递过来的value参数
rgbJsonQing = inputMessage;//设置晴天的颜色
set_String(indexQing,indexQing+1,rgbJsonQing);//保存进EEPROM
}
if (request->hasParam(PARAM_INPUT_DUOYUNVALUE)) {
inputMessage = request->getParam(PARAM_INPUT_DUOYUNVALUE)->value();//get传递过来的value参数
rgbJsonDuoyun= inputMessage;//设置多云的颜色
set_String(indexDuoyun,indexDuoyun+1,rgbJsonDuoyun);//保存进EEPROM
}
Serial.println(inputMessage);
request->send(200, "text/plain", "OK");
});
// 启动网页服务
server.begin();
}