用网页图案解锁保护你的 Arduino - 简单设置与远程控制
组件与材料
| Arduino UNO | ×1 |
| PHPoC WiFi Shield for Arduino | ×1 |
| Seeed 舵机 SG92R | ×1 |
| 杜邦线 | ×1 |
项目简介
如果你是初学者,建议先阅读以下教程:
- Arduino - 电机控制
- Arduino - 舵机控制
- Arduino - WiFi 连接
你可能熟悉手机的图案解锁功能,现在这个特性也可以用在 Arduino 上。它能防止未经授权的人员控制或监控 Arduino 设备。
用户可以自由复用本项目的代码用于其他应用。为演示方便,本项目以舵机控制作为示例。
本项目灵感来源于两个基于 PHPoC 平台的项目:
我将它们改造为适用于 Arduino 平台。
选择 PHPoC WiFi Shield 的原因
- PHPoC Shield 支持 WebSocket,便于实现实时通信。
- PHPoC Shield 内置嵌入式 Web 服务器,可直接在 Shield 上存储 Web 应用程序。
PHPoC Shield 自带一些内置 Web 应用,让用户无需 Web 编程知识即可控制/监控 Arduino。同时,它也允许会 Web 编程的用户开发自己的 Web 应用并存储在 Shield 上。
演示效果
用户在网页上绘制解锁图案,系统验证通过后即可远程控制舵机转动。
数据流向
网页浏览器 ←→ PHPoC WiFi Shield ←→ Arduino

工作原理
图案映射与验证
当用户在网页上绘制图案时,图案会被映射为一个字符串(如 "1,4,8,6,3"),通过 WebSocket 发送给 Arduino(经由 PHPoC Shield)。

Arduino 接收到图案字符串后,将其与预设的字符串比对:
- 匹配成功:返回 ACCEPTED 代码 (202),将 authenticated 变量 设为 true。
- 匹配失败:返回 DENIED 代码 (401),将 authenticated 变量 设为 false。
当 Arduino 接收到控制指令时,会先检查 authenticated 变量:
- 为 true:执行相应任务(如控制舵机)。
- 为 false:返回 DENIED 代码。
系统设有超时机制:一段时间内无操作,认证失效,用户需重新输入图案解锁。
源代码文件
- ArduinoUnlockExample.ino:通过 Arduino IDE 编译上传到 Arduino。
- unlock.php:Web 应用代码,通过 PHPoC Debugger 上传到 PHPoC Shield。
实施步骤
1. 设置 PHPoC Shield 的 WiFi 信息
参考 官方说明 配置 SSID 和密码。
2. 上传 Web UI 到 PHPoC Shield
- 下载
unlock.php源代码(见代码区)。 - 下载舵机控制所需的两张图片:
servo_body.png和servo_bracket.png。 - 使用 PHPoC Debugger 上传到 PHPoC Shield(注意:不要删除 Shield 上现有文件)。
3. 编写 Arduino 代码
- 在 Arduino IDE 中安装 PHPoC 库(参考 安装指南)。
- 参考代码区的源代码编写。
- 通过 Arduino IDE 编译上传到 Arduino。
测试运行
- 打开 Arduino IDE 串口监视器,查看 IP 地址。
- 在浏览器输入
https://<你的IP地址>/unlock.php。 - 点击连接按钮进行测试。
适合初学者的 Arduino 套件推荐
如果你在寻找 Arduino 套件,可参考《最适合初学者的 Arduino 套件》。
函数参考
- Arduino - Servo 库
- Servo.attach() / write() / writeMicroseconds() / read() / attached() / detach()
- Serial.begin() / println()
- delay() / millis()
- for / while 循环
- if else 条件判断
- loop() / setup()
- String 类方法:toInt(), substring(), indexOf(), remove(), equals()
核心代码
unlock.php (Web 界面)
<!DOCTYPE html>
<html><head><title>Arduino - PHPoC Shield</title>
<meta name="viewport" content="width=device-width, initial-scale=0.7, maximum-scale=0.7">
<meta charset="utf-8">
<style>body{text-align:center;font-size:width/2pt}h1,h2,button{font-weight:bold;font-size:width/2pt}</style>
<script>
// 核心逻辑:WebSocket通信、图案绘制与识别、舵机控制界面
// 详见原文完整代码
</script></head><body>
<h1>Arduino - Web Pattern Unlock</h1>
<div id="unlock_area"><canvas id="unlock"></canvas></div>
<div id="control_area" style="display:none"><canvas id="servo"></canvas><p>Angle: <span id="debug">0</span></p></div>
<h2>WebSocket: <span id="ws_state">null</span><button id="bt_connect" onclick="connect_onclick()">Connect</button></h2>
</body></html>
ArduinoUnlockExample.ino (Arduino 端)
/* Arduino Web Server - Pattern Unlock */
#include "SPI.h"
#include "Phpoc.h"
#include
#define CMD_AUTH 0
#define CMD_CTRL 1
#define ACCEPTED "202"
#define UNAUTHORIZED "401"
PhpocServer server(80);
Servo servo;
String pattern;
bool authenticated;
unsigned long timeout;
unsigned long lastActiveTime;
void setup() {
Serial.begin(9600);
while(!Serial);
Phpoc.begin(PF_LOG_SPI | PF_LOG_NET);
server.beginWebSocket("web_pattern");
Serial.print("WebSocket server address: ");
Serial.println(Phpoc.localIP());
servo.attach(8);
servo.write(90);
pattern = String("1,4,8,6,3");
authenticated = false;
timeout = 10000; // 10秒超时
lastActiveTime = 0;
}
void loop() {
PhpocClient client = server.available();
if (client) {
String data = client.readLine();
if(data) {
int pos = data.indexOf(':');
int cmd = data.substring(0, pos).toInt();
if(cmd == CMD_AUTH) {
String reqPattern = data.substring(pos+1);
reqPattern.remove(reqPattern.indexOf(13));
reqPattern.remove(reqPattern.indexOf(10));
if(pattern.equals(reqPattern)) {
authenticated = true;
sendResponse(ACCEPTED, 3);
lastActiveTime = millis();
} else {
authenticated = false;
sendResponse(UNAUTHORIZED, 3);
}
} else if(cmd == CMD_CTRL) {
if(authenticated) {
int angle = data.substring(pos+1).toInt();
angle = map(angle, 90, -90, 0, 180);
servo.write(angle);
lastActiveTime = millis();
Serial.println(angle);
} else {
sendResponse(UNAUTHORIZED, 3);
}
}
}
}
// 超时检测
if (authenticated && ((millis() - lastActiveTime) > timeout)) {
authenticated = false;
sendResponse(UNAUTHORIZED, 3);
}
}
void sendResponse(char *data, int len) {
server.write(data, len);
}
原理图

制造工艺
- 服装纸样量产演变史:从手工定制到工业化标准
- 基于 Web 控制的 DMX 灯光系统 – Arduino 主控制器
- DIY 蓝牙控制 Arduino 间谍小车
- FlickMote:基于手势的智能家居红外遥控器
- DIY Arduino TV-B-Gone:万能关机遥控器
- 自制大尺寸LED主时钟带闹钟功能 — 12米外清晰可见
- Find Me:基于 Arduino 与蓝牙的智能寻物器制作指南
- Arduino 项目优化供电方案:从按键开关到电池监测与数据记录
- Arduino 井字棋游戏:MAX7219 点阵屏 + 纸板外壳 DIY
- 从零构建智能 Arduino 四足机器人:完整指南
- Arduino UNO + WiFi 盾板:串口实时推送温度到网页绘图
- DIY Arduino 手柄方向盘 - 游戏竞速模拟器自制指南