目录
二、初阶示例:智能锁使用WebSocket 实现远程控制和状态监控
三、进阶示例:扩展身份验证、存储开锁记录 和 MQTT 多设备支持
一、在 ESP32 上实现 WebSocket 客户端
在 ESP32 上实现 WebSocket 客户端,可以使用 Arduino WebSockets 库,让 ESP32 连接 WebSocket 服务器,并实时接收/发送数据(如传感器数据)。
1.WebSocket 客户端工作原理
ESP32 作为 WebSocket 客户端,连接到服务器:
-
服务器可以是 本地服务器(如
Node.js
)或 云端 WebSocket 服务器。 -
ESP32 可以发送传感器数据(温度、湿度等)到服务器。
-
ESP32 也可以接收服务器指令(如控制继电器开关)。
2.安装 WebSockets 库
在 Arduino IDE 中:
-
进入 库管理器(
工具
→库管理器
)。 -
搜索 "WebSockets",并安装 "ArduinoWebsockets"。
3.连接 WebSocket 服务器
示例:ESP32 连接 WebSocket 服务器,并定期发送 模拟温湿度数据。
代码示例
#include <WiFi.h>
#include <ArduinoWebsockets.h>
const char* ssid = "你的WiFi名称"; // WiFi 名称
const char* password = "你的WiFi密码"; // WiFi 密码
const char* ws_server = "ws://192.168.1.100:8080"; // WebSocket 服务器地址
using namespace websockets;
WebsocketsClient client;
void setup() {
Serial.begin(115200);
// 连接 WiFi
WiFi.begin(ssid, password);
Serial.print("连接 WiFi 中...");
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi 已连接");
// 连接 WebSocket 服务器
if (client.connect(ws_server)) {
Serial.println("WebSocket 连接成功");
} else {
Serial.println("WebSocket 连接失败");
}
// 监听 WebSocket 消息
client.onMessage([](WebsocketsMessage message) {
Serial.print("收到服务器消息: ");
Serial.println(message.data());
});
}
void loop() {
if (client.available()) {
// 生成模拟温湿度数据
float temperature = 20 + random(500) / 100.0; // 20°C - 25°C
float humidity = 50 + random(1000) / 100.0; // 50% - 60%
// 发送 JSON 数据
String data = "{\"temperature\": " + String(temperature) + ", \"humidity\": " + String(humidity) + "}";
client.send(data);
Serial.println("发送数据: " + data);
} else {
Serial.println("WebSocket 连接断开,尝试重连...");
client.connect(ws_server);
}
delay(2000); // 每 2 秒发送一次
}
代码解析
(1)连接 WiFi 网络。
(2)连接 WebSocket 服务器。
(3)监听 WebSocket 消息,并在 loop()
中定时发送传感器数据(温度 & 湿度)。
(4)如果连接断开,自动重连。
4.WebSocket 服务器(Node.js 示例)
可以使用
ws
搭建一个 Node.js WebSocket 服务器,用来接收 ESP32 传感器数据。
(1)安装 WebSocket 服务器
npm install ws
(2)Node.js WebSocket 服务器
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', ws => {
console.log('ESP32 已连接');
ws.on('message', message => {
console.log('收到 ESP32 数据:', message);
});
ws.on('close', () => console.log('ESP32 断开连接'));
});
console.log("WebSocket 服务器已启动,监听端口 8080");
(3)启动服务器
node server.js
ESP32 发送的数据会在服务器上打印,例如:
收到 ESP32 数据: {"temperature": 22.5, "humidity": 55.2}
5.通过 WebSocket 远程控制 ESP32
如果服务器需要发送指令(如控制 LED/继电器),ESP32 可以接收 WebSocket 消息,并执行相应操作。
(1)更新 ESP32 代码
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi 已连接");
if (client.connect(ws_server)) {
Serial.println("WebSocket 连接成功");
}
// 监听 WebSocket 消息
client.onMessage([](WebsocketsMessage message) {
Serial.print("收到服务器指令: ");
Serial.println(message.data());
if (message.data() == "LED_ON") {
digitalWrite(2, HIGH);
Serial.println("LED 已打开");
} else if (message.data() == "LED_OFF") {
digitalWrite(2, LOW);
Serial.println("LED 已关闭");
}
});
}
(2)在服务器发送控制命令
服务器发送 "LED_ON"
指令:
ws.send("LED_ON");
ESP32 收到后会点亮 LED。
6.WebSocket vs. MQTT
如果ESP32 设备数量较多(如 IoT 物联网应用),可以考虑 MQTT:
协议 | 适用场景 | 优势 |
WebSocket | 前端实时更新,适合网页/移动端应用 | 低延迟,适合双向通信 |
MQTT | IoT 设备间通信(如智能家居、工厂) | 低功耗,适合大量设备 |
对于简单的Web 前端展示传感器数据,WebSocket 更合适。 如果有 大量 ESP32 设备 需要通信,MQTT 更稳定。
7.总结
(1)ESP32 通过 WebSocket 发送/接收数据 (2)适用于物联网 IoT,如远程监控传感器数据 (3)支持 WebSocket 服务器(Node.js) (4)支持远程控制(如 LED 开关) (5)如果设备多,可以考虑 MQTT 方案
二、初阶示例:智能锁使用WebSocket 实现远程控制和状态监控
在 智能锁 项目中,使用 WebSocket 可以实现 远程控制 和 状态监控,比如:
-
远程 开锁/上锁
-
实时 监控锁的状态(如是否锁定、电量、异常告警)
-
授权管理(通过 WebSocket 发送指令授权用户)
1.WebSocket 智能锁架构
(1)ESP32(嵌入式设备):控制电机/继电器开锁、检测锁状态,并与服务器保持 WebSocket 连接。
(2)WebSocket 服务器(Node.js):管理 WebSocket 连接,并转发控制指令。
(3)前端应用(Web/APP):用户可以远程开锁/上锁,查看锁状态。
2.ESP32 代码(WebSocket 客户端)
功能
-
连接 WiFi
-
连接 WebSocket 服务器
-
监听 WebSocket 指令,控制智能锁电机
-
发送 锁状态(上锁/解锁、电量)
#include <WiFi.h>
#include <ArduinoWebsockets.h>
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";
const char* ws_server = "ws://192.168.1.100:8080"; // WebSocket 服务器地址
using namespace websockets;
WebsocketsClient client;
#define LOCK_PIN 2 // 继电器/电机控制引脚
#define STATUS_PIN 4 // 读取锁状态的传感器引脚
void setup() {
Serial.begin(115200);
pinMode(LOCK_PIN, OUTPUT);
pinMode(STATUS_PIN, INPUT);
// 连接 WiFi
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWiFi 连接成功");
// 连接 WebSocket 服务器
if (client.connect(ws_server)) {
Serial.println("WebSocket 连接成功");
} else {
Serial.println("WebSocket 连接失败");
}
// 监听 WebSocket 消息
client.onMessage([](WebsocketsMessage message) {
Serial.print("收到服务器指令: ");
Serial.println(message.data());
if (message.data() == "LOCK") {
digitalWrite(LOCK_PIN, HIGH);
Serial.println("智能锁已上锁");
client.send("{\"status\": \"locked\"}");
} else if (message.data() == "UNLOCK") {
digitalWrite(LOCK_PIN, LOW);
Serial.println("智能锁已解锁");
client.send("{\"status\": \"unlocked\"}");
}
});
}
void loop() {
if (client.available()) {
// 读取锁状态
int lockStatus = digitalRead(STATUS_PIN);
String status = lockStatus == HIGH ? "locked" : "unlocked";
// 发送锁状态
String data = "{\"status\": \"" + status + "\"}";
client.send(data);
Serial.println("发送状态: " + data);
} else {
Serial.println("WebSocket 连接断开,尝试重连...");
client.connect(ws_server);
}
delay(5000); // 每 5 秒发送一次状态
}
ESP32 代码解析
(1)连接 WiFi 并连接 WebSocket 服务器。
(2)监听服务器指令:
-
LOCK → 继电器上锁
-
UNLOCK → 继电器解锁
(3)每 5 秒上报锁状态(上锁/解锁)。
3.WebSocket 服务器(Node.js)
使用 WebSocket 服务器 转发 前端 和 ESP32 之间的指令。
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
let esp32Client = null; // 存储 ESP32 连接
let webClient = null; // 存储 Web 端连接
wss.on('connection', ws => {
ws.on('message', message => {
console.log('收到消息:', message);
try {
const data = JSON.parse(message);
// 识别客户端(ESP32 或 Web)
if (data.device === "esp32") {
esp32Client = ws;
console.log("ESP32 连接成功");
} else if (data.device === "web") {
webClient = ws;
console.log("Web 端连接成功");
}
// 转发数据
if (esp32Client && webClient) {
webClient.send(JSON.stringify(data));
}
} catch (error) {
console.error("消息解析错误:", error);
}
});
ws.on('close', () => console.log("连接断开"));
});
console.log("WebSocket 服务器已启动,监听端口 8080");
服务器功能
(1)存储 ESP32 和 Web 连接 (2)前端发送指令 → 转发给 ESP32 (3)ESP32 发送状态 → 转发给 前端
4.React 前端(Web 远程控制锁)
安装 WebSocket 客户端
npm install react-use-websocket
前端代码
import React, { useState, useEffect } from "react";
import useWebSocket from "react-use-websocket";
const SmartLock = () => {
const [lockStatus, setLockStatus] = useState("未知");
const { sendMessage, lastMessage } = useWebSocket("ws://localhost:8080", {
onOpen: () => console.log("WebSocket 连接成功"),
});
useEffect(() => {
if (lastMessage) {
const data = JSON.parse(lastMessage.data);
if (data.status) setLockStatus(data.status);
}
}, [lastMessage]);
return (
<div style={{ textAlign: "center", padding: "20px" }}>
<h2>智能锁状态: {lockStatus === "locked" ? "已上锁" : "已解锁"}</h2>
<button onClick={() => sendMessage('{"device": "web", "command": "LOCK"}')} style={{ margin: "10px", padding: "10px 20px" }}>
上锁
</button>
<button onClick={() => sendMessage('{"device": "web", "command": "UNLOCK"}')} style={{ margin: "10px", padding: "10px 20px" }}>
解锁
</button>
</div>
);
};
export default SmartLock;
前端功能
(1)显示智能锁状态(ESP32 反馈的状态) (2)远程上锁/解锁(点击按钮发送 WebSocket 指令)
5.总结
(1)ESP32 作为 WebSocket 客户端,控制智能锁 (2)Node.js 服务器中转 WebSocket 消息 (3)React 前端远程控制智能锁 (4)支持锁状态实时监控 (5)适用于智能家居、远程门禁等 IoT 应用
三、进阶示例:扩展身份验证、存储开锁记录 和 MQTT 多设备支持
智能锁项目已经有了基础的 WebSocket 控制,现在我们来扩展 身份验证、存储开锁记录 和 MQTT 多设备支持。
1.增加用户身份验证
为了防止未授权用户控制智能锁,我们可以:
(1)使用 JWT 令牌 进行用户身份验证。
(2)在 WebSocket 连接时验证用户。
(3)后端管理用户权限(如管理员 vs 普通用户)。
(1)后端(Node.js + JWT)
1)安装 JWT 相关库
npm install jsonwebtoken express
2)后端登录接口
const jwt = require('jsonwebtoken');
const secretKey = "supersecret"; // 加密密钥,生产环境请存储在环境变量中
// 模拟用户数据库
const users = { "admin": "123456" };
// 生成 JWT 令牌
app.post("/login", (req, res) => {
const { username, password } = req.body;
if (users[username] && users[username] === password) {
const token = jwt.sign({ username }, secretKey, { expiresIn: "1h" });
res.json({ success: true, token });
} else {
res.status(401).json({ success: false, message: "用户名或密码错误" });
}
});
(2)WebSocket 连接时验证 JWT
const WebSocket = require('ws');
const wss = new WebSocket.Server({ port: 8080 });
wss.on('connection', (ws, req) => {
const token = req.url.split("?token=")[1]; // 从 URL 获取 JWT
try {
const decoded = jwt.verify(token, secretKey);
console.log(
用户 ${decoded.username} 已连接
);
ws.send(JSON.stringify({ message: "身份验证成功" }));
} catch (err) {
ws.send(JSON.stringify({ error: "身份验证失败" }));
ws.close();
}
});
2.存储开锁记录(MySQL/MongoDB)
每次开锁/上锁,我们需要记录:
-
时间
-
用户
-
操作类型(LOCK/UNLOCK)
-
设备 ID
(1)后端数据库存储
1)使用 MySQL(适合结构化数据)
安装 MySQL 相关库
npm install mysql2
创建数据库表
CREATE TABLE lock_records (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
action VARCHAR(10),
device_id VARCHAR(50),
timestamp TIMESTAMP DEFAULT CURRENT_TIMESTAMP
);
存储开锁记录
const mysql = require('mysql2');
const db = mysql.createConnection({
host: "localhost",
user: "root",
password: "password",
database: "smart_lock"
});
function saveLockRecord(username, action, deviceId) {
db.query(
"INSERT INTO lock_records (username, action, device_id) VALUES (?, ?, ?)",
[username, action, deviceId],
(err, result) => {
if (err) console.error("数据库插入错误:", err);
else console.log("记录已存储:", result.insertId);
}
);
}
WebSocket 触发存储
ws.on('message', message => {
const data = JSON.parse(message);
if (data.command === "LOCK" || data.command === "UNLOCK") {
saveLockRecord(data.username, data.command, data.device_id);
}
});
2)使用 MongoDB(适合非结构化数据)
安装 MongoDB
npm install mongoose
创建 Schema
const mongoose = require("mongoose");
mongoose.connect("mongodb://localhost:27017/smart_lock");
const LockRecord = mongoose.model("LockRecord", {
username: String,
action: String,
device_id: String,
timestamp: { type: Date, default: Date.now }
});
function saveLockRecord(username, action, deviceId) {
new LockRecord({ username, action, device_id: deviceId }).save()
.then(() => console.log("记录已存储"))
.catch(err => console.error("存储错误:", err));
}
3.集成 MQTT(适用于多个设备)
当多个智能锁设备需要通信时,MQTT 更稳定、低功耗。
(1)MQTT 方案架构
[ESP32] <=====> [MQTT Broker (EMQX/Mosquitto)] <=====> [Node.js 服务器] <=====> [前端]
(2)安装 Mosquitto
sudo apt update
sudo apt install mosquitto mosquitto-clients
启动服务:
sudo systemctl enable mosquitto
sudo systemctl start mosquitto
(3)后端(Node.js)集成 MQTT
const mqtt = require('mqtt');
const client = mqtt.connect("mqtt://localhost:1883");
// 监听 MQTT 主题
client.on("connect", () => {
console.log("MQTT 连接成功");
client.subscribe("smartlock/status");
});
client.on("message", (topic, message) => {
console.log(
收到 MQTT 消息: ${message.toString()}
);
});
(4)ESP32 连接 MQTT
安装 MQTT 库 在 Arduino IDE 安装 PubSubClient。
ESP32 MQTT 代码
#include <WiFi.h>
#include <PubSubClient.h>
const char* ssid = "你的WiFi名称";
const char* password = "你的WiFi密码";
const char* mqtt_server = "192.168.1.100"; // MQTT 服务器地址
WiFiClient espClient;
PubSubClient client(espClient);
void callback(char* topic, byte* payload, unsigned int length) {
String message = "";
for (int i = 0; i < length; i++) message += (char)payload[i];
Serial.print("收到 MQTT 消息: ");
Serial.println(message);
if (message == "LOCK") digitalWrite(2, HIGH);
else if (message == "UNLOCK") digitalWrite(2, LOW);
}
void setup() {
Serial.begin(115200);
WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) delay(500);
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
client.connect("esp32-client");
client.subscribe("smartlock/control");
}
void loop() {
if (!client.connected()) client.connect("esp32-client");
client.loop();
// 发送锁状态
String status = digitalRead(2) ? "locked" : "unlocked";
client.publish("smartlock/status", status.c_str());
delay(5000);
}
4.总结
(1)用户身份验证
-
通过 JWT 令牌 确保 WebSocket 连接安全。
-
服务器验证用户身份后才允许控制智能锁。
(2)存储开锁记录
-
使用 MySQL(结构化存储)或 MongoDB(非结构化存储)。
-
每次锁定/解锁,都记录 用户、时间、设备ID。
(3)集成 MQTT
-
适用于 多个智能锁设备,比 WebSocket 更适合大规模物联网设备。
-
ESP32 订阅 MQTT 主题,远程控制更加稳定。
四、高阶示例:增加远程开锁和时间段授权功能
智能锁项目要增加 远程开锁 和 时间段授权,我们可以这样实现:
1.远程开锁
目标:用户可以在手机 App 或 Web 界面 远程控制 智能锁。
(1)方案
-
后端通过 WebSocket/MQTT 发送开锁指令。
-
ESP32 监听指令,执行开锁操作。
-
用户需要身份验证(JWT),防止未经授权的开锁。
(2)后端(Node.js)
WebSocket 远程开锁
wss.on('connection', (ws, req) => {
const token = req.url.split("?token=")[1];
try {
const decoded = jwt.verify(token, secretKey);
console.log(
用户 ${decoded.username} 已连接
);
ws.on('message', message => {
const data = JSON.parse(message);
if (data.command === "UNLOCK") {
console.log(
远程开锁请求: ${decoded.username}
);
// 发送开锁命令到设备(MQTT/WebSocket)
client.publish("smartlock/control", "UNLOCK");
}
});
} catch (err) {
ws.send(JSON.stringify({ error: "身份验证失败" }));
ws.close();
}
});
(3)ESP32 监听 MQTT 远程开锁
void callback(char* topic, byte* payload, unsigned int length) {
String message = "";
for (int i = 0; i < length; i++) message += (char)payload[i];
Serial.print("收到 MQTT 指令: ");
Serial.println(message);
if (message == "UNLOCK") {
digitalWrite(2, LOW); // 触发开锁
Serial.println("远程开锁成功");
}
}
void setup() {
client.setServer(mqtt_server, 1883);
client.setCallback(callback);
client.connect("esp32-client");
client.subscribe("smartlock/control");
}
2.时间段授权
目标:管理员可以设置特定时间段,用户只能在指定时间内开锁。
方案
-
后端存储授权时间段(MySQL/MongoDB)。
-
开锁前检查当前时间,是否在授权时间内。
-
未在授权时间,拒绝开锁。
后端数据库存储授权时间
MySQL 方案
创建授权时间表
CREATE TABLE lock_access (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
device_id VARCHAR(50),
start_time DATETIME,
end_time DATETIME
);
查询当前时间是否允许开锁
function isAccessAllowed(username, deviceId, callback) {
db.query(
"SELECT * FROM lock_access WHERE username = ? AND device_id = ? AND NOW() BETWEEN start_time AND end_time",
[username, deviceId],
(err, results) => {
if (err) {
console.error("数据库查询错误:", err);
callback(false);
} else {
callback(results.length > 0);
}
}
);
}
WebSocket 处理远程开锁请求
ws.on('message', message => {
const data = JSON.parse(message);
if (data.command === "UNLOCK") {
isAccessAllowed(data.username, data.device_id, (allowed) => {
if (allowed) {
console.log(
用户 ${data.username} 远程开锁
);
client.publish("smartlock/control", "UNLOCK");
} else {
console.log(
用户 ${data.username} 无权在此时间段开锁
);
ws.send(JSON.stringify({ error: "未在授权时间段" }));
}
});
}
});
3.管理员设置时间段
管理员可以在 Web 端设置用户的 开锁时间段。
前端示例(React)
function setAccessTime(username, deviceId, startTime, endTime) {
fetch("/setAccess", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ username, deviceId, startTime, endTime })
}).then(res => res.json()).then(data => {
alert(data.success ? "授权成功" : "授权失败");
});
}
后端处理设置授权时间
app.post("/setAccess", (req, res) => {
const { username, deviceId, startTime, endTime } = req.body;
db.query(
"INSERT INTO lock_access (username, device_id, start_time, end_time) VALUES (?, ?, ?, ?)",
[username, deviceId, startTime, endTime],
(err, result) => {
res.json({ success: !err });
}
);
});
4.总结
(1)远程开锁:WebSocket/MQTT 发送指令,ESP32 执行开锁。
(2) 时间段授权:存储用户的 授权时间,开锁前校验。
(3)管理员管理授权:Web 端可以设置 用户的开锁权限。
五、扩展功能:一次性密码、时间段密码、指纹识别
智能锁项目需要 一次性密码、时间段密码、指纹识别,下面是实现方案:
1.一次性密码(OTP 开锁)
目标:用户可以生成一个 短期有效的开锁密码,使用一次后失效。
方案
-
服务器 生成 OTP(一次性密码),存入数据库,设置 有效时间。
-
用户输入密码后,服务器 验证 OTP 并 删除已使用的密码。
-
可使用 6 位数字 OTP,类似短信验证码。
后端生成 OTP
const crypto = require("crypto");
// 生成 6 位随机 OTP
function generateOTP() {
return crypto.randomInt(100000, 999999).toString();
}
// 存储 OTP 到数据库(10 分钟有效)
app.post("/generateOTP", (req, res) => {
const { username, deviceId } = req.body;
const otp = generateOTP();
const expiresAt = new Date(Date.now() + 10 * 60000); // 10 分钟后失效
db.query(
"INSERT INTO otp_codes (username, device_id, otp, expires_at) VALUES (?, ?, ?, ?)",
[username, deviceId, otp, expiresAt],
(err) => {
if (err) res.status(500).json({ success: false, error: err });
else res.json({ success: true, otp });
}
);
});
后端验证 OTP
app.post("/verifyOTP", (req, res) => {
const { username, deviceId, otp } = req.body;
db.query(
"SELECT * FROM otp_codes WHERE username = ? AND device_id = ? AND otp = ? AND expires_at > NOW()",
[username, deviceId, otp],
(err, results) => {
if (err || results.length === 0) {
return res.status(400).json({ success: false, message: "无效或过期的 OTP" });
}
// OTP 验证成功,删除 OTP 并发送开锁指令
db.query("DELETE FROM otp_codes WHERE otp = ?", [otp]);
client.publish(
smartlock/control/${deviceId}
, "UNLOCK");
res.json({ success: true });
}
);
});
2.时间段密码
目标:用户可以设置一个 在特定时间段内有效的密码。
方案
-
服务器存储 用户设定的密码 和 有效时间段。
-
用户输入密码时,服务器 检查当前时间是否在有效时间段内。
-
时间到期后,密码自动失效。
数据库结构
CREATE TABLE timed_passwords (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
device_id VARCHAR(50),
password VARCHAR(10),
start_time DATETIME,
end_time DATETIME
);
后端验证时间段密码
app.post("/verifyTimedPassword", (req, res) => {
const { username, deviceId, password } = req.body;
db.query(
"SELECT * FROM timed_passwords WHERE username = ? AND device_id = ? AND password = ? AND NOW() BETWEEN start_time AND end_time",
[username, deviceId, password],
(err, results) => {
if (err || results.length === 0) {
return res.status(400).json({ success: false, message: "无效或过期的密码" });
}
client.publish(
smartlock/control/${deviceId}
, "UNLOCK");
res.json({ success: true });
}
);
});
3.指纹识别
目标:用户可以使用指纹 直接解锁,不需要输入密码。
方案
-
使用 指纹传感器(如 R503、FPM10A) 连接 ESP32。
-
ESP32 读取指纹数据并 与存储的指纹进行匹配。
-
匹配成功,ESP32 发送开锁指令。
ESP32 连接指纹模块
安装 Adafruit Fingerprint Sensor Library:
#include <Adafruit_Fingerprint.h>
SoftwareSerial mySerial(2, 3); // RX, TX
Adafruit_Fingerprint finger = Adafruit_Fingerprint(&mySerial);
void setup() {
Serial.begin(115200);
finger.begin(57600);
if (finger.verifyPassword()) {
Serial.println("指纹传感器已连接");
} else {
Serial.println("无法找到指纹传感器");
}
}
void loop() {
Serial.println("请按压指纹...");
int id = getFingerprintID();
if (id > 0) {
Serial.print("指纹匹配成功,ID: ");
Serial.println(id);
digitalWrite(2, LOW); // 开锁
}
delay(1000);
}
int getFingerprintID() {
uint8_t p = finger.getImage();
if (p != FINGERPRINT_OK) return -1;
p = finger.image2Tz();
if (p != FINGERPRINT_OK) return -1;
p = finger.fingerFastSearch();
if (p == FINGERPRINT_OK) {
return finger.fingerID;
}
return -1;
}
4.结合 MQTT 实现指纹远程授权
目标:管理员可以远程添加或删除指纹。
ESP32 监听 MQTT 远程指纹管理
void callback(char* topic, byte* payload, unsigned int length) {
String message = "";
for (int i = 0; i < length; i++) message += (char)payload[i];
if (message.startsWith("ADD_FINGER")) {
int id = message.substring(10).toInt();
enrollFingerprint(id);
} else if (message.startsWith("DELETE_FINGER")) {
int id = message.substring(13).toInt();
finger.deleteModel(id);
}
}
管理员远程添加指纹
app.post("/addFingerprint", (req, res) => {
const { deviceId, userId } = req.body;
client.publish(
smartlock/fingerprint/${deviceId}
,
ADD_FINGER ${userId}
);
res.json({ success: true });
});
5.总结
一次性密码(OTP)
-
服务器生成 6 位短期密码,用户输入后自动失效。
-
适用于临时访客、外卖配送等场景。
时间段密码
-
管理员设置特定时间段的密码,在有效时间内可用。
-
适用于定期家政人员、租客等。
指纹识别
-
ESP32 读取指纹传感器,本地匹配后开锁。
-
管理员可以远程添加/删除指纹(MQTT 远程管理)。
六、扩展功能:蓝牙解锁 和 NFC 解锁
智能锁项目需要 蓝牙解锁 和 NFC 解锁,以下是详细的实现方案:
1.蓝牙解锁
目标:用户可以使用手机 通过蓝牙 连接智能锁并开锁。
方案
-
ESP32 开启蓝牙(BLE),等待手机连接。
-
手机 App 发送 开锁指令,ESP32 验证身份后执行开锁。
-
可使用 AES 加密 确保通信安全。
ESP32 作为 BLE 服务器
安装 ESP32 BLE 库:
#include <BLEDevice.h>
#include <BLEUtils.h>
#include <BLEServer.h>
BLEServer *pServer = NULL;
BLECharacteristic *pCharacteristic = NULL;
bool deviceConnected = false;
// 自定义 UUID
#define SERVICE_UUID "12345678-1234-5678-1234-56789abcdef0"
#define CHARACTERISTIC_UUID "abcd1234-5678-1234-5678-abcdef012345"
void setup() {
Serial.begin(115200);
BLEDevice::init("SmartLock");
pServer = BLEDevice::createServer();
pServer->setCallbacks(new MyServerCallbacks());
BLEService *pService = pServer->createService(SERVICE_UUID);
pCharacteristic = pService->createCharacteristic(
CHARACTERISTIC_UUID,
BLECharacteristic::PROPERTY_WRITE);
pCharacteristic->setCallbacks(new MyCallbacks());
pService->start();
BLEAdvertising *pAdvertising = BLEDevice::getAdvertising();
pAdvertising->addServiceUUID(SERVICE_UUID);
BLEDevice::startAdvertising();
Serial.println("等待手机连接...");
}
void loop() {
delay(1000);
}
// 处理蓝牙消息
class MyCallbacks: public BLECharacteristicCallbacks {
void onWrite(BLECharacteristic *pCharacteristic) {
std::string value = pCharacteristic->getValue();
if (value == "UNLOCK") {
Serial.println("蓝牙解锁成功!");
digitalWrite(2, LOW); // 触发开锁
}
}
};
手机 App 发送开锁指令
-
使用 React Native + react-native-ble-plx 或 Flutter 蓝牙插件。
-
连接 ESP32 后 发送 "UNLOCK" 指令。
import { BleManager } from 'react-native-ble-plx';
const bleManager = new BleManager();
const DEVICE_ID = "SmartLock";
async function unlockWithBluetooth() {
const devices = await bleManager.startDeviceScan(null, null, (error, device) => {
if (device.name === DEVICE_ID) {
device.connect().then(() => {
return device.writeCharacteristicWithResponseForService(
"12345678-1234-5678-1234-56789abcdef0",
"abcd1234-5678-1234-5678-abcdef012345",
"UNLOCK"
);
}).then(() => {
alert("解锁成功!");
}).catch(console.error);
}
});
}
2.NFC 解锁
目标:用户可以 刷 NFC 卡或手机 NFC 进行开锁。
方案
-
ESP32 连接 RC522 NFC 读卡器,读取 NFC 卡 UID。
-
后端数据库存储授权 UID,比对后开锁。
-
也可以 使用手机 NFC,模拟 NFC 卡解锁。
ESP32 连接 NFC 读卡器
安装 MFRC522 库:
#include <SPI.h>
#include <MFRC522.h>
#define SS_PIN 5
#define RST_PIN 0
MFRC522 mfrc522(SS_PIN, RST_PIN);
void setup() {
Serial.begin(115200);
SPI.begin();
mfrc522.PCD_Init();
pinMode(2, OUTPUT);
}
void loop() {
if (!mfrc522.PICC_IsNewCardPresent() || !mfrc522.PICC_ReadCardSerial()) {
delay(500);
return;
}
String uid = "";
for (byte i = 0; i < mfrc522.uid.size; i++) {
uid += String(mfrc522.uid.uidByte[i], HEX);
}
Serial.println("检测到 NFC 设备,UID: " + uid);
// 校验 UID 是否授权
if (uid == "aabbccdd") {
Serial.println("NFC 认证成功,开锁!");
digitalWrite(2, LOW); // 开锁
} else {
Serial.println("NFC 认证失败!");
}
delay(1000);
}
后端存储授权 NFC 卡 UID
CREATE TABLE nfc_keys (
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(50),
device_id VARCHAR(50),
uid VARCHAR(20) UNIQUE
);
app.post("/registerNFC", (req, res) => {
const { username, deviceId, uid } = req.body;
db.query(
"INSERT INTO nfc_keys (username, device_id, uid) VALUES (?, ?, ?)",
[username, deviceId, uid],
(err) => res.json({ success: !err })
);
});
app.post("/verifyNFC", (req, res) => {
const { deviceId, uid } = req.body;
db.query(
"SELECT * FROM nfc_keys WHERE device_id = ? AND uid = ?",
[deviceId, uid],
(err, results) => {
if (results.length > 0) {
client.publish(
smartlock/control/${deviceId}
, "UNLOCK");
res.json({ success: true });
} else {
res.status(403).json({ success: false, message: "未授权 NFC" });
}
}
);
});
3.NFC 手机模拟开锁
目标:用户可以使用 手机 NFC 代替 NFC 卡 进行解锁。
方案
-
手机 App 模拟 NFC 卡 UID,发送给 ESP32。
-
ESP32 读取 UID 后,交由后端验证。
手机 App 发送 NFC UID
import NfcManager, { NfcTech } from 'react-native-nfc-manager';
async function sendNFCUnlock() {
await NfcManager.start();
const tag = await NfcManager.requestTechnology(NfcTech.Ndef);
const ndef = await NfcManager.getTag();
const uid = ndef.id;
fetch("/verifyNFC", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ deviceId: "lock123", uid })
}).then(res => res.json()).then(data => {
if (data.success) alert("解锁成功!");
});
NfcManager.cancelTechnologyRequest();
}
4.总结
蓝牙解锁
-
手机连接 ESP32 蓝牙,发送 UNLOCK 指令 开锁。
-
适用于无网络环境的近距离解锁(如停车场、办公室)。
NFC 解锁
-
通过 NFC 卡或手机 NFC,比对 UID 进行开锁。
-
适用于门禁、酒店房卡等场景。
NFC 手机模拟卡
-
用户可以使用手机 模拟 NFC 卡,解锁智能锁。
扩展阅读:
1.了解 WebSocket 和 ESP32
WebSocket 是一种基于 TCP 的协议,可以让客户端和服务器之间进行全双工通信。在智能锁的场景下,ESP32 可以作为 WebSocket 客户端与服务器进行通信,执行如开锁、查询状态等操作。
2.硬件需求
-
ESP32 开发板
-
智能锁硬件(带有电机或伺服控制开锁功能,带有指纹传感器及支持NFC开锁)
3.软件需求
-
Arduino IDE 或 PlatformIO:用于编写和上传 ESP32 的代码
-
WebSocket 库:使用
ArduinoWebsockets
库或WebSocketClient
库来实现 WebSocket 客户端
扩展阅读:
MQTT 实战手册:从初学者到高级开发者的进阶之路 | MQTT 实战手册:从初学者到高级开发者的进阶之路-优快云博客 |
MQTT开发者指南:15个实战问题全解析 | MQTT开发者指南:15个实战问题全解析-优快云博客 |
WebSocket:实时双向通信技术详解与实战示例优化指南 | https://blog.youkuaiyun.com/moton2017/article/details/146475710 |
ESP32 智能锁双协议联动:WebSocket + MQTT 远程控制与状态同步详解 | https://blog.youkuaiyun.com/moton2017/article/details/146478480 |