用 ESP-01S 和 RC522 打造 WiFi 门禁:UDP 通信 + 舵机控制实战


Lceberg.冰山
原创
发布时间: 2026-01-28 14:07:45 | 阅读数 0收藏数 0评论数 0
封面
本文介绍如何基于 ESP-01S 与 ESP8266 实现局域网内的 UDP 通信,并结合 RC522 射频识别模块构建一个门禁系统。当 RC522 读取到白名单中的卡片 UID 时,系统将自动通过 UDP 协议向 ESP-01S 发送指令,驱动舵机完成开门操作。

准备工作:

材料:

材料名称
数量
备注
ESP8266
1
ESP-01s
1
SG90
1
RC522
1
锂电池块
2
电池盒
1
电池
4节
电工胶布
1卷

工具:

工具名称
数量
备注
烙铁
1
剥线钳
1
1

设置ESP8266下载地址

在Arduino IDE的开发板管理器中,添加ESP8266的下载源地址:http://arduino.esp8266.com/stable/package_esp8266com_index.json。

2

下载开发板

安装ESP8266开发板:

  1. 工具 → 开发板 → 开发板管理器 → 搜索 esp8266 → 安装

安装 MFRC522 库

  1. 工具 → 管理库 → 搜索 MFRC522 → 安装 “MFRC522 by GithubCommunity”
3

esp01s烧录代码

这是ESP01s和烧录器的连接对应:


usb转ttl

esp01

3.3v

3.3v

GND

GND

TX

RX

RX

TX

GND

GPIO0


// === ESP-01S: 舵机控制端 ===
#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <Servo.h>

const char* ssid = "vivoX200";
const char* password = "12345678";

WiFiUDP udp;
const int localPort = 8888;

Servo myservo;
const int SERVO_PIN = 2; // GPIO2
const int LOCK_ANGLE = 0;
const int UNLOCK_ANGLE = 180;

void setup() {
Serial.begin(115200);
myservo.attach(SERVO_PIN);
myservo.write(LOCK_ANGLE); // 默认上锁

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\n ESP-01S 已联网");
Serial.print("IP: "); Serial.println(WiFi.localIP());

udp.begin(localPort);
Serial.println("监听 UDP 端口 8888...");
}

void loop() {
int packetSize = udp.parsePacket();
if (packetSize) {
char cmd[10];
int len = udp.read(cmd, 9);
cmd[len] = '\0';

Serial.print(" 收到指令: ");
Serial.println(cmd);

if (strcmp(cmd, "OPEN") == 0) {
myservo.write(UNLOCK_ANGLE);
delay(5000); // 保持 5 秒
myservo.write(LOCK_ANGLE);
Serial.println("门已重新上锁");
}
}
}

烧录步骤说明:

  1. 按照上表连接好线路,其中将烧录器的 GND 同时连接到 ESP-01S 的 GND 和 GPIO0,使模块进入下载模式。
  2. 完成代码烧录后,断开 GPIO0 与 GND 的连接
  3. 拔掉烧录器并重新插入(或重新上电),此时 ESP-01S 将正常启动,并可通过串口查看打印信息。
  4. 请记录串口输出中的 IP 地址,后续 ESP8266 的通信将使用该 IP。
INO
ESP-01s.ino
1.10KB
4

ESP-01s、舵机接线图

制作共地连接线:

取一根公对公杜邦线和一根母对母杜邦线,分别从中间剪断,使用剥线钳剥去两端的绝缘外皮,然后将对应导线拧在一起,形成一根可靠的接地(GND)连接线。

准备两个独立电源:

  1. 电源 A:输出 5V,用于驱动舵机。
  2. 电源 B:输出 3.3V ± 0.1V,用于为 ESP-01S 供电。

连接舵机:

  1. 舵机 红色线(VCC) → 接 5V 电源正极(+)
  2. 舵机 棕色或黑色线(GND) → 接 5V 电源负极(–)

连接 ESP-01S:

  1. ESP-01S VCC 引脚 → 接 3.3V 电源正极(+)
  2. ESP-01S GND 引脚 → 接 3.3V 电源负极(–)

关键步骤:共地(Common Ground)

使用一根杜邦线,将 5V 电源的负极(–)3.3V 电源的负极(–) 连接起来,确保两个电源共用同一个参考地,这是信号正常通信的前提。

信号线连接:

  1. 舵机的 黄色或橙色线(信号线) → 接 ESP-01S 的 GPIO2 引脚
5

esp8266和RC522接线图

8266和RC522的引脚对应:


RC522引脚

NodeMCU

3.3v

3.3v

GND

GND

RST

D3

SDA

D8

SCK

D5

MOSI

D7

MISO

D6


RC522 模块工作电压为 3.3V,需稳定供电。建议使用 3.3V 锂电池 为整体供电:

  1. 将锂电池的 正极 接至 8266的 3.3V 引脚
  2. 将锂电池的 负极 接至 8266的 GND 引脚
// NFC 门禁系统 - NodeMCU + RC522

#include <ESP8266WiFi.h>
#include <WiFiUdp.h>
#include <SPI.h>
#include <MFRC522.h>
#include <EEPROM.h>

// ====== Wi-Fi 配置 ======
const char* ssid = "名称";
const char* password = "密码";

// ====== UDP 目标 ======
const char* remoteIP = "192.168.190.236"; // 修改为自己的IP
const int remotePort = 8888;

WiFiUDP udp;

// ====== RC522 引脚 ======
#define SS_PIN 15 // D8
#define RST_PIN 2 // D4 (避免 GPIO0)

MFRC522 mfrc522(SS_PIN, RST_PIN);

// ====== 配置 ======
#define MAX_CARDS 20
#define UID_SIZE 4
const int EEPROM_RESERVED_SIZE = 512; // 必须 >= 实际使用

bool learningMode = false;
unsigned long learningStartTime = 0;

// =============================================================================
void setup() {
Serial.begin(115200);
delay(500);
Serial.println("\nNFC 门禁系统启动...");

WiFi.begin(ssid, password);
while (WiFi.status() != WL_CONNECTED) {
delay(500);
Serial.print(".");
}
Serial.println("\nWi-Fi 已连接");
Serial.print("本机 IP: ");
Serial.println(WiFi.localIP());

SPI.begin();
mfrc522.PCD_Init();
mfrc522.PCD_SetAntennaGain(mfrc522.RxGain_max);

EEPROM.begin(EEPROM_RESERVED_SIZE);

// 正确统计已授权卡数(基于 0xFF 判断空)
int count = 0;
for (int i = 0; i < MAX_CARDS; i++) {
bool isEmpty = true;
for (int j = 0; j < UID_SIZE; j++) {
if (EEPROM.read(i * UID_SIZE + j) != 0xFF) {
isEmpty = false;
break;
}
}
if (!isEmpty) count++;
}
Serial.printf("已授权卡片: %d / %d 张\n", count, MAX_CARDS);

learningMode = true;
learningStartTime = millis();
Serial.println("5 秒内刷卡可录入新卡!");
}

// =============================================================================
void loop() {
unsigned long now = millis();

if (learningMode && (now - learningStartTime > 5000)) {
learningMode = false;
int count = 0;
for (int i = 0; i < MAX_CARDS; i++) {
bool isEmpty = true;
for (int j = 0; j < UID_SIZE; j++) {
if (EEPROM.read(i * UID_SIZE + j) != 0xFF) {
isEmpty = false;
break;
}
}
if (!isEmpty) count++;
}
Serial.printf("当前授权卡: %d 张\n", count);
}

if (!mfrc522.PICC_IsNewCardPresent()) return;
if (!mfrc522.PICC_ReadCardSerial()) return;

if (mfrc522.uid.size != UID_SIZE) {
Serial.println("不支持的卡类型");
goto halt;
}

byte uid[UID_SIZE];
for (int i = 0; i < UID_SIZE; i++) uid[i] = mfrc522.uid.uidByte[i];

Serial.print("UID: ");
for (int i = 0; i < UID_SIZE; i++) {
Serial.printf("%02X", uid[i]);
if (i < UID_SIZE - 1) Serial.print(":");
}

if (learningMode) {
if (addCardToEEPROM(uid)) {
Serial.println(" 录入成功!");
} else {
Serial.println(" 已存在或存储已满");
}
} else {
if (isAuthorized(uid)) {
Serial.println(" 合法卡,开门!");
sendUDP("OPEN");
} else {
Serial.println(" 未授权卡");
}
}

halt:
mfrc522.PICC_HaltA();
mfrc522.PCD_StopCrypto1();
delay(500);
}

// =============================================================================
// 正确判断槽位是否为空(基于 0xFF)
bool isSlotEmpty(int slot) {
for (int j = 0; j < UID_SIZE; j++) {
if (EEPROM.read(slot * UID_SIZE + j) != 0xFF) {
return false;
}
}
return true;
}

// =============================================================================
bool isAuthorized(byte* uid) {
for (int i = 0; i < MAX_CARDS; i++) {
if (isSlotEmpty(i)) continue; // 跳过空槽

bool match = true;
for (int j = 0; j < UID_SIZE; j++) {
if (EEPROM.read(i * UID_SIZE + j) != uid[j]) {
match = false;
break;
}
}
if (match) return true;
}
return false;
}

bool addCardToEEPROM(byte* uid) {
if (isAuthorized(uid)) return false;

for (int i = 0; i < MAX_CARDS; i++) {
if (isSlotEmpty(i)) {
for (int j = 0; j < UID_SIZE; j++) {
EEPROM.write(i * UID_SIZE + j, uid[j]);
}
EEPROM.commit();
return true;
}
}
return false;
}

void sendUDP(const char* msg) {
udp.beginPacket(remoteIP, remotePort);
udp.write(msg);
udp.endPacket();
}
INO
ESP8266.ino
5.28KB
6

最终

我用纸壳搭了个原型,实际应用时完全可以装到真正的门上——把控制模块(比如 ESP-01S 和电源)藏在门后,RC522 留在外面方便刷卡。

布线完成后,整个系统就准备好了:刷一张白名单里的卡,信号通过 UDP 发出去,另一边收到指令驱动舵机开门。

阅读记录0
点赞0
收藏0
禁止 本文未经作者允许授权,禁止转载
猜你喜欢
评论/提问(已发布 0 条)
评论 评论
收藏 收藏
分享 分享
pdf下载 下载
pdf下载 举报