MENU
ESP32(服务端)
C++
cpp
#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
const char* ssid = "jifu";
const char* pass = "2022xinchan!@#";
const int ledPin = 5;
bool isTwinkle = false;
bool isFlowing = false;
int pin_list[5] = { 13, 23, 14, 27, 4 };
int size = sizeof(pin_list) / sizeof(pin_list[0]);
bool isBreathing = false;
WebServer server(8068);
// 通用响应函数
void sendResponse(int code, const char* status, const char* message, const String& response) {
StaticJsonDocument<200> doc;
doc["code"] = code;
doc["status"] = status;
doc["message"] = message;
doc["response"] = response;
String res;
serializeJson(doc, res);
server.send(code, "application/json", res);
}
// LED开关
void ledSwitch() {
String isSwitch = server.arg("isSwitch");
if (isSwitch == "true") {
digitalWrite(ledPin, HIGH);
sendResponse(200, "success", "LED已打开", isSwitch);
} else if (isSwitch == "false") {
digitalWrite(ledPin, LOW);
sendResponse(200, "success", "LED已关闭", isSwitch);
} else {
sendResponse(400, "error", "无效的开关状态", isSwitch);
}
}
// LED闪烁
void ledTwinkle() {
String twinkle = server.arg("isTwinkle");
if (twinkle == "true") {
isTwinkle = true;
sendResponse(200, "success", "LED开始闪烁", twinkle);
} else if (twinkle == "false") {
isTwinkle = false;
sendResponse(200, "success", "LED停止闪烁", twinkle);
} else {
sendResponse(400, "error", "无效的闪烁状态", twinkle);
}
}
// LED呼吸
void ledBreathing() {
String breathing = server.arg("isBreathing");
if (breathing == "true") {
isBreathing = true;
sendResponse(200, "success", "LED开始呼吸", breathing);
} else if (breathing == "false") {
isBreathing = false;
// 因为使用同一个引脚
// 当启动过呼吸灯后
// 同一引脚的其他功能无法使用
// 所以在关闭呼吸灯时初始化一下引脚
pinMode(ledPin, OUTPUT);
sendResponse(200, "success", "LED停止呼吸", breathing);
} else {
sendResponse(400, "error", "无效的呼吸状态", breathing);
}
}
// LED流水
void ledFlowing() {
String flowing = server.arg("isFlowing");
if (flowing == "true") {
isFlowing = true;
sendResponse(200, "success", "LED开始流水", flowing);
} else if (flowing == "false") {
isFlowing = false;
for (int i = 0; i < size; i++) digitalWrite(pin_list[i], LOW);
sendResponse(200, "success", "LED停止流水", flowing);
} else {
isFlowing = false;
for (int i = 0; i < size; i++) digitalWrite(pin_list[i], LOW);
sendResponse(400, "error", "无效的流水状态", flowing);
}
}
// 主函数
void setup() {
Serial.begin(9600);
pinMode(ledPin, OUTPUT);
WiFi.mode(WIFI_STA);
WiFi.begin(ssid, pass);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print("Loading in progress...");
}
Serial.print("\nWiFiIP: ");
Serial.println(WiFi.localIP());
server.on("/api/led/switch", HTTP_GET, ledSwitch);
server.on("/api/led/twinkle", HTTP_GET, ledTwinkle);
server.on("/api/led/breathing", HTTP_GET, ledBreathing);
server.on("/api/led/flowing", HTTP_GET, ledFlowing);
for (int i = 0; i < size; i++) pinMode(pin_list[i], OUTPUT);
server.begin();
}
// 循环函数
void loop() {
server.handleClient();
if (isTwinkle) {
digitalWrite(ledPin, HIGH);
delay(1000);
digitalWrite(ledPin, LOW);
delay(1000);
}
if (isBreathing) {
for (int i = 0; i < 256; i++) {
analogWrite(ledPin, i);
delay(10);
}
for (int i = 255; i >= 0; i--) {
analogWrite(ledPin, i);
delay(10);
}
}
if (isFlowing) {
for (int i = 0; i < size; i++) {
digitalWrite(pin_list[i], HIGH);
digitalWrite(pin_list[(i > 0) ? (i - 1) : (size - 1)], LOW);
delay(250);
}
}
}
解析
前言
程序是一个通过WiFi使用Web接口控制ESP32上LED灯的应用。它使用WiFi连接网络,并通过WebServer库处理HTTP请求,从而根据请求执行各种LED操作(开关灯、闪烁灯、呼吸灯和流水灯)。
包含的库
cpp#include <WiFi.h> #include <WebServer.h> #include <ArduinoJson.h>
WiFi.h
用于连接ESP32到WiFi网络。
WebServer.h
用于创建一个Web服务器来处理HTTP请求。
ArduinoJson.h
用于构建和解析JSON数据,方便API的响应格式化。
全局变量
cppconst char* ssid = "jifu"; const char* pass = "2022xinchan!@#"; const int ledPin = 5; bool isTwinkle = false; bool isFlowing = false; int pin_list[5] = { 13, 23, 14, 27, 4 }; int size = sizeof(pin_list) / sizeof(pin_list[0]); bool isBreathing = false; WebServer server(8068);
ssid和pass
WiFi的SSID和密码,用于连接WiFi网络。
ledPin
控制LED灯的GPIO引脚,这里设置为5号引脚。
isTwinkle
、isFlowing
和isBreathing
分别标识LED是否正在执行闪烁、流水、呼吸灯效果。
pin_list
存储多个GPIO引脚,用于控制流水灯效果。
server(8068)
创建一个Web服务器,监听8086端口。
通用响应函数
cppvoid sendResponse(int code, const char* status, const char* message, const String& response) { StaticJsonDocument<200> doc; doc["code"] = code; doc["status"] = status; doc["message"] = message; doc["response"] = response; String res; serializeJson(doc, res); server.send(code, "application/json", res); }
sendResponse
用于发送HTTP响应,将请求结果打包成JSON格式,包含code、status、message和response字段,返回给客户端。
LED开关控制
cppvoid ledSwitch() { String isSwitch = server.arg("isSwitch"); if (isSwitch == "true") { digitalWrite(ledPin, HIGH); sendResponse(200, "success", "LED已打开", isSwitch); } else if (isSwitch == "false") { digitalWrite(ledPin, LOW); sendResponse(200, "success", "LED已关闭", isSwitch); } else { sendResponse(400, "error", "无效的开关状态", isSwitch); } }
根据isSwitch参数来控制LED的开关状态,true表示打开,false表示关闭。
LED闪烁控制
cppvoid ledTwinkle() { String twinkle = server.arg("isTwinkle"); if (twinkle == "true") { isTwinkle = true; sendResponse(200, "success", "LED开始闪烁", twinkle); } else if (twinkle == "false") { isTwinkle = false; sendResponse(200, "success", "LED停止闪烁", twinkle); } else { sendResponse(400, "error", "无效的闪烁状态", twinkle); } }
通过控制isTwinkle变量,来启动或停止LED的闪烁效果。
LED呼吸灯控制
cppvoid ledBreathing() { String breathing = server.arg("isBreathing"); if (breathing == "true") { isBreathing = true; sendResponse(200, "success", "LED开始呼吸", breathing); } else if (breathing == "false") { isBreathing = false; pinMode(ledPin, OUTPUT); sendResponse(200, "success", "LED停止呼吸", breathing); } else { sendResponse(400, "error", "无效的呼吸状态", breathing); } }
根据isBreathing参数控制LED的呼吸效果,呼吸灯效果通过循环改变LED亮度实现。
LED流水灯控制
cppvoid ledFlowing() { String flowing = server.arg("isFlowing"); if (flowing == "true") { isFlowing = true; sendResponse(200, "success", "LED开始流水", flowing); } else if (flowing == "false") { isFlowing = false; for (int i = 0; i < size; i++) digitalWrite(pin_list[i], LOW); sendResponse(200, "success", "LED停止流水", flowing); } else { sendResponse(400, "error", "无效的流水状态", flowing); } }
控制多个引脚上的LED依次亮灭,模拟流水灯效果。
主函数setup()
cppvoid setup() { Serial.begin(9600); pinMode(ledPin, OUTPUT); WiFi.mode(WIFI_STA); WiFi.begin(ssid, pass); while (WiFi.status() != WL_CONNECTED) { delay(500); Serial.print("Loading in progress..."); } Serial.print("\nWiFiIP: "); Serial.println(WiFi.localIP()); server.on("/api/led/switch", HTTP_GET, ledSwitch); server.on("/api/led/twinkle", HTTP_GET, ledTwinkle); server.on("/api/led/breathing", HTTP_GET, ledBreathing); server.on("/api/led/flowing", HTTP_GET, ledFlowing); for (int i = 0; i < size; i++) pinMode(pin_list[i], OUTPUT); server.begin(); }
设置WiFi连接、初始化LED引脚和Web服务器,监听不同的API路径并关联对应的处理函数。
主循环loop()
cppvoid loop() { server.handleClient(); if (isTwinkle) { digitalWrite(ledPin, HIGH); delay(1000); digitalWrite(ledPin, LOW); delay(1000); } if (isBreathing) { for (int i = 0; i < 256; i++) { analogWrite(ledPin, i); delay(10); } for (int i = 255; i >= 0; i--) { analogWrite(ledPin, i); delay(10); } } if (isFlowing) { for (int i = 0; i < size; i++) { digitalWrite(pin_list[i], HIGH); digitalWrite(pin_list[(i > 0) ? (i - 1) : (size - 1)], LOW); delay(250); } } }
循环执行LED的控制逻辑,处理客户端的请求,并根据当前的状态变量决定是否执行闪烁、呼吸或流水效果。
总结
程序通过简单的HTTP接口,通过Web页面实现控制ESP32的LED开关灯、闪烁灯、呼吸灯和流水灯功能。
微信小程序(移动端)
Html
代码
html<view> <view class="p_18 bs_bb bs_0_8_8_efefef radius_6 fs_28"> <view class="fw_b">名称: {{info.title}}</view> <view class="color_909399">别名: {{info.aliasListStr}}</view> </view> <view class="mt_58 d_g gtc_2fr gg_38"> <view class="w_100_ pt_28 pb_28 bs_bb ta_c bc_efefef radius_8 {{activa===item.id?'color_409eff':''}}" wx:for="{{apiList}}" wx:key="id" data-id="{{item.id}}" catchtap="handleLedAction"> LED{{item.title}}灯已{{ledSwitch?'开启':'关闭'}} </view> </view> </view>
解析
前言
代码段用于微信小程序的WXML模板,构建一个视图,用于显示LED灯的控制面板。包含一些动态数据绑定、条件渲染和事件处理。
外层
<view>
标签
最外层的<view>
标签包含整个视图的结构布局,是微信小程序中用于显示布局的基础容器。它内嵌多个<view>
子元素,每个子元素分别对应不同的功能展示。
第一个
<view>
块显示信息
html<view class="p_18 bs_bb bs_0_8_8_efefef radius_6 fs_28"> <view class="fw_b">名称: {{info.title}}</view> <view class="color_909399">别名: {{info.aliasListStr}}</view> </view>
1、
class="p_18 bs_bb bs_0_8_8_efefef radius_6 fs_28"
用于<view>
多个CSS样式类,控制外边距、边框样式、圆角半径和字体大小。
1.1、p_18
表示内边距为18单位。
1.2、bs_bb
表示边框样式为双线框。
1.3、radius_6
表示圆角半径为6单位。
1.4、fs_28
表示字体大小为28单位。
2、{``{info.title}}
通过数据绑定显示info.title,这是一个动态数据,用于显示某个对象的名称。
3、{``{info.aliasListStr}}
通过数据绑定显示info.aliasListStr,显示对象的别名。
第二个
<view>
块控制LED面板
html<view class="mt_58 d_g gtc_2fr gg_38"> <view class="w_100_ pt_28 pb_28 bs_bb ta_c bc_efefef radius_8 {{activa===item.id?'color_409eff':''}}" wx:for="{{apiList}}" wx:key="id" data-id="{{item.id}}" catchtap="handleLedAction"> LED{{item.title}}灯已{{ledSwitch?'开启':'关闭'}} </view> </view>
1、外层
<view>
样式,
1.1、mt_58
设置顶部外边距为58单位。
1.2、d_g
设置为网格布局(display: grid)。
1.3、gtc_2fr
设置网格布局的列定义为2个fr单位。
1.4、gg_38
网格单元格之间的间距为38单位。
2、内层<view>
,每个内层<view>
是一个LED控制按钮。
2.1、w_100_
宽度设置为100%。
2.2、pt_28 pb_28
上下内边距为28单位。
2.3、bs_bb
边框样式为双线框。
2.4、ta_c
文本居中。
2.5、bc_efefef
背景颜色设置为浅灰色(#efefef)。
2.6、radius_8
圆角半径为8单位。
2.7、{``{activa===item.id?'color_409eff':''}}
是一个条件样式绑定,检查activa是否等于当前项的id,如果是,则应用color_409eff(蓝色)样式;否则,不应用任何颜色样式。
3、wx:for="{``{apiList}}" wx:key="id"
使用wx:for指令进行循环渲染,apiList是一个列表,表示多个LED控制选项,每个选项的id作为唯一键。
4、data-id="{``{item.id}}"
为每个<view>
元素设置一个自定义数据属性data-id,保存当前LED的id,以便在事件中使用。
5、catchtap="handleLedAction"
绑定点击事件catchtap,当用户点击该LED控制时,会调用handleLedAction函数。
6、LED{``{item.title}}灯已{``{ledSwitch?'开启':'关闭'}}
动态显示LED状态,{{item.title}}显示当前LED的名称,{{ledSwitch ? '开启' : '关闭'}}根据ledSwitch变量的布尔值来显示"开启"或"关闭"状态。
总结
代码段是微信小程序中的WXML模板,显示一个LED灯控制面板。每个LED通过一个点击区域显示其当前状态(开启或关闭),并且点击该区域时触发相应的事件来切换LED的状态。
JavaScript
前言
代码段是一个微信小程序的页面逻辑,主要功能是通过API接口控制LED灯的不同操作(开关灯、闪烁灯、呼吸灯和流水灯)。
代码
javascript
import {
apiLedSwitch,
apiLedTwinkle,
apiBreathing,
apiLedFlowing
} from '../../api/led.js';
const {
showToast
} = getApp();
Page({
/**
* 页面的初始数据
*/
data: {
info: {},
apiObj: {},
apiList: [],
activa: ''
},
async handleLedAction(event) {
const that = this;
const thatData = that.data;
const apiObj = thatData.apiObj;
const id = event.target.dataset.id;
let activa = thatData.activa;
let api = undefined;
let text = undefined;
if (activa === '') {
activa = id;
} else if (id === activa) {
activa = '';
} else if (activa !== '' && id !== activa) {
return showToast('请先关闭其他LED');
}
api = apiObj[id];
text = api.text;
const {
message
} = await api.fn({
[text]: activa ? true : false
});
that.setData({
activa
},
() => showToast(message)
);
},
runInit() {
let that = this,
thatData = thatData,
apiObj = {
ledSwitch: {
title: '开关',
text: 'isSwitch',
fn: apiLedSwitch
},
ledFlicker: {
title: '闪烁',
text: 'isTwinkle',
fn: apiLedTwinkle
},
ledBreathing: {
title: '呼吸',
text: 'isBreathing',
fn: apiBreathing
},
ledFlowing: {
title: '流水',
text: 'isFlowing',
fn: apiLedFlowing
}
},
apiList = [];
for (const key in apiObj) {
if (Object.hasOwnProperty.call(apiObj, key)) {
let title = apiObj[key].title;
apiList.push({
id: key,
title
});
}
}
that.setData({
apiObj,
apiList
});
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
this.runInit();
},
/**
* 生命周期函数--监听页面显示
*/
onShow() {
const row = wx.getStorageSync('electronicComponent');
row.aliasListStr = row.aliasList.slice(1);
row.aliasListStr = row.aliasListStr.toString();
row.aliasListStr = row.aliasListStr.replace(/,/g, "、");
this.setData({
info: row
});
}
});
导入依赖模块
javascriptimport { apiLedSwitch, apiLedTwinkle, apiBreathing, apiLedFlowing } from '../../api/led.js'; const { showToast } = getApp();
apiLedSwitch
、apiLedTwinkle
、apiBreathing
和apiLedFlowing
从led.js文件中导入控制LED操作的API函数。
showToast
通过getApp()获取全局的showToast方法,用于展示提示信息。
页面data数据
javascriptdata: { info: {}, apiObj: {}, apiList: [], activa: '' }
info
存储当前电子组件的信息。
apiObj
存储每个LED操作的API函数、文本标识以及对应的标题信息。
apiList
生成的LED操作列表,用于显示在页面上。
activa
标识当前激活的LED操作的ID,用于控制每次只能激活一个LED功能。
handleLedAction方法
javascriptasync handleLedAction(event) { const that = this; const thatData = that.data; const apiObj = thatData.apiObj; const id = event.target.dataset.id; let activa = thatData.activa; let api = undefined; let text = undefined; if (activa === '') { activa = id; } else if (id === activa) { activa = ''; } else if (activa !== '' && id !== activa) { return showToast('请先关闭其他LED'); } api = apiObj[id]; text = api.text; const { message } = await api.fn({ [text]: activa ? true : false }); that.setData({ activa }, () => showToast(message) ); }
event
通过事件对象event获取当前点击的LED按钮的id。
activa
当前激活的LED操作。如果没有激活,则将activa设置为点击的id;如果已经激活,再次点击同一个id,则取消激活;如果点击其他LED,则提示先关闭当前的LED。
apiObj
从apiObj中获取当前id对应的API函数和参数text,text用于动态生成请求的参数字段名。
api.fn
调用对应的API函数,传入动态生成的参数,如{ isSwitch: true }
或{ isSwitch: false }
,控制LED状态。
message
API响应后,显示API返回的提示信息。
setData
更新activa状态,并在回调中使用showToast显示操作结果的提示。
runInit方法
javascriptrunInit() { let that = this, thatData = thatData, apiObj = { ledSwitch: { title: '开关', text: 'isSwitch', fn: apiLedSwitch }, ledFlicker: { title: '闪烁', text: 'isTwinkle', fn: apiLedTwinkle }, ledBreathing: { title: '呼吸', text: 'isBreathing', fn: apiBreathing }, ledFlowing: { title: '流水', text: 'isFlowing', fn: apiLedFlowing } }, apiList = []; for (const key in apiObj) { if (Object.hasOwnProperty.call(apiObj, key)) { let title = apiObj[key].title; apiList.push({ id: key, title }); } } that.setData({ apiObj, apiList }); }
apiObj
是一个配置对象,包含每种LED操作的标题(如"开关"、"闪烁"等)、对应的API参数字段(如isSwitch、isTwinkle等)和API函数(如apiLedSwitch、apiLedTwinkle等)。
apiList
通过遍历apiObj生成LED操作列表,每个操作项包含id和title,用于显示在界面上。
setData
将生成的apiObj和apiList更新到页面的数据中。
onLoad生命周期函数
javascriptonLoad(options) { this.runInit(); }
页面加载时,调用runInit方法初始化API操作列表。
onShow生命周期函数
javascriptonShow() { const row = wx.getStorageSync('electronicComponent'); row.aliasListStr = row.aliasList.slice(1); row.aliasListStr = row.aliasListStr.toString(); row.aliasListStr = row.aliasListStr.replace(/,/g, "、"); this.setData({ info: row }); }
1、从本地存储中读取电子组件的信息(electronicComponent),并处理其aliasList字段。
1.1、slice(1)
移除第一个别名(可能是主名称)。
1.2、toString()
将别名列表转换为字符串。
1.3、replace(/,/g, "、")
将逗号替换为顿号。
2、将处理后的信息设置到info数据字段,用于页面展示。