ESP32 智能锁双协议联动:WebSocket + MQTT 远程控制与状态同步详解

目录

一、在 ESP32 上实现 WebSocket 客户端

1.WebSocket 客户端工作原理

2.安装 WebSockets 库

3.连接 WebSocket 服务器

4.WebSocket 服务器(Node.js 示例)

(1)安装 WebSocket 服务器

(2)Node.js WebSocket 服务器

(3)启动服务器

5.通过 WebSocket 远程控制 ESP32

(1)更新 ESP32 代码

(2)在服务器发送控制命令

6.WebSocket vs. MQTT

7.总结

二、初阶示例:智能锁使用WebSocket 实现远程控制和状态监控

1.WebSocket 智能锁架构

2.ESP32 代码(WebSocket 客户端)

3.WebSocket 服务器(Node.js)

4.React 前端(Web 远程控制锁)

5.总结

三、进阶示例:扩展身份验证、存储开锁记录 和 MQTT 多设备支持

1.增加用户身份验证

2.存储开锁记录(MySQL/MongoDB)

3.集成 MQTT(适用于多个设备)

(1)MQTT 方案架构

(2)安装 Mosquitto

(3)后端(Node.js)集成 MQTT

(4)ESP32 连接 MQTT

4.总结

四、高阶示例:增加远程开锁和时间段授权功能

1.远程开锁

2.时间段授权

3.管理员设置时间段

4.总结

五、扩展功能:一次性密码、时间段密码、指纹识别

1.一次性密码(OTP 开锁)

2.时间段密码

3.指纹识

4.结合 MQTT 实现指纹远程授权

5.总结

六、扩展功能:蓝牙解锁 和 NFC 解锁

1.蓝牙解锁

2.NFC 解锁

3.NFC 手机模拟开锁

4.总结

扩展阅读

一、在 ESP32 上实现 WebSocket 客户端

ESP32 上实现 WebSocket 客户端,可以使用 Arduino WebSockets 库,让 ESP32 连接 WebSocket 服务器,并实时接收/发送数据(如传感器数据)。


1.WebSocket 客户端工作原理

ESP32 作为 WebSocket 客户端,连接到服务器:

  • 服务器可以是 本地服务器(如 Node.js)或 云端 WebSocket 服务器

  • ESP32 可以发送传感器数据(温度、湿度等)到服务器。

  • ESP32 也可以接收服务器指令(如控制继电器开关)。


2.安装 WebSockets 库

Arduino IDE 中:

  1. 进入 库管理器工具库管理器)。

  2. 搜索 "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前端实时更新,适合网页/移动端应用低延迟,适合双向通信
MQTTIoT 设备间通信(如智能家居、工厂)低功耗,适合大量设备

对于简单的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-plxFlutter 蓝牙插件

  • 连接 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 IDEPlatformIO:用于编写和上传 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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

34号树洞

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值