随着物联网设备的普及,越来越多的智能场景开始从本地局域网扩展到广域网。对于一些需要远程触发的物理设备,例如按键机器人、舵机驱动机构等,完全依赖商业云平台并非唯一选择。通过自建基础设施,可以获得更高的隐私性、稳定性与可控性。

本文提供一套完整的、可在任意 VPS 上部署的广域网远程控制方案,实现:

  • ESP8266/ESP32 持续连接 VPS
  • 使用 MQTT(TLS 加密)进行指令下发与状态回传
  • 舵机执行按压动作
  • 远程客户端可通过命令行或 HTTP API 触发指令
  • 全程无需使用任何商业云服务

该方案适用于智能按键器、远程机械执行器、低成本自动化场景等。


方案整体架构

系统由三个核心部分组成:

控制客户端(电脑/脚本/小程序)
        ↓ MQTT 指令
VPS(Mosquitto Broker)
        ↓ 加密连接
ESP8266/ESP32(驱动舵机)

远程控制流程如下:

  1. ESP 自动连接家庭 Wi-Fi
  2. ESP 主动连接 VPS 上的 MQTT Broker
  3. 控制端发布指令至 MQTT
  4. ESP 收到指令 → 驱动舵机执行按键动作
  5. ESP 上报“状态/心跳”等信息

整个过程安全、轻量且易扩展。


一、VPS 部署 MQTT Broker(Mosquitto)

以下示例基于 Debian / Ubuntu 服务器,可根据实际环境调整。

1. 安装 Mosquitto

apt update
apt install mosquitto mosquitto-clients
systemctl enable mosquitto

2. 使用 TLS 证书

如果服务器已有 ACME/Certbot 证书,可直接复用:

  • /etc/letsencrypt/live/<domain>/fullchain.pem
  • /etc/letsencrypt/live/<domain>/privkey.pem

3. 创建 MQTT 用户

mosquitto_passwd -c /etc/mosquitto/passwd iotuser

4. 编写配置文件

文件:/etc/mosquitto/conf.d/iot.conf

listener 8883
protocol mqtt

cafile /etc/letsencrypt/live/<domain>/fullchain.pem
certfile /etc/letsencrypt/live/<domain>/fullchain.pem
keyfile /etc/letsencrypt/live/<domain>/privkey.pem

allow_anonymous false
password_file /etc/mosquitto/passwd

log_dest syslog
log_type error
log_type warning
log_type notice

5. 重启服务

systemctl restart mosquitto
systemctl status mosquitto

6. 防火墙放行端口

ufw allow 8883/tcp

二、客户端验证 MQTT 服务

订阅监控全部消息

mosquitto_sub \
  -h <domain> \
  -p 8883 \
  --cafile /etc/ssl/certs/ca-certificates.crt \
  -u iotuser \
  -P 'password' \
  -t 'iot/#' -v

发布测试消息

mosquitto_pub \
  -h <domain> \
  -p 8883 \
  --cafile /etc/ssl/certs/ca-certificates.crt \
  -u iotuser \
  -P 'password' \
  -t 'iot/button1/cmd' \
  -m 'TEST'

若能在订阅端收到消息,则服务器配置成功。


三、硬件接线(ESP + 舵机)

以 SG90 舵机为例:

  • 舵机红线 → 5V 电源
  • 舵机黑/棕线 → GND
  • 舵机橙线(信号)→ ESP GPIO14(D5)

建议使用单独稳压电源为舵机供电,ESP 与舵机 共地


四、ESP 端 MQTT 控制代码

适用于 Arduino / PlatformIO(ESP8266 示例)。

#include <ESP8266WiFi.h>
#include <PubSubClient.h>
#include <Servo.h>

const char* ssid = "WiFi";
const char* password = "password";

const char* mqtt_server = "<domain>";
const int mqtt_port = 8883;
const char* mqtt_user = "iotuser";
const char* mqtt_pass = "password";

WiFiClientSecure espClient;
PubSubClient client(espClient);
Servo servo;

const int SERVO_PIN = 14;

void pressButton() {
  servo.write(0);
  delay(200);
  servo.write(40);
  delay(300);
  servo.write(0);
  delay(200);
}

void callback(char* topic, byte* payload, unsigned int length) {
  String msg;
  for (unsigned int i = 0; i < length; i++) msg += (char)payload[i];

  if (String(topic) == "iot/button1/cmd") {
    if (msg == "PRESS") {
      client.publish("iot/button1/status", "RUN");
      pressButton();
      client.publish("iot/button1/status", "DONE");
    }
  }
}

void setup_wifi() {
  WiFi.mode(WIFI_STA);
  WiFi.begin(ssid, password);
  while (WiFi.status() != WL_CONNECTED) delay(500);
}

void reconnect() {
  while (!client.connected()) {
    if (client.connect("button1", mqtt_user, mqtt_pass)) {
      client.subscribe("iot/button1/cmd");
      client.publish("iot/button1/status", "ONLINE");
    } else {
      delay(5000);
    }
  }
}

unsigned long lastHeartbeat = 0;

void setup() {
  servo.attach(SERVO_PIN);
  servo.write(0);

  setup_wifi();

  espClient.setInsecure(); // 使用 TLS 通道但不验证证书

  client.setServer(mqtt_server, mqtt_port);
  client.setCallback(callback);
}

void loop() {
  if (!client.connected()) reconnect();
  client.loop();

  if (millis() - lastHeartbeat > 30000) {
    lastHeartbeat = millis();
    client.publish("iot/button1/heartbeat", "alive");
  }
}

ESP 启动后即会注册:

  • 指令主题:iot/button1/cmd
  • 状态主题:iot/button1/status
  • 心跳主题:iot/button1/heartbeat

五、远程控制方式

1. 命令行触发动作

mosquitto_pub \
  -h <domain> \
  -p 8883 \
  --cafile /etc/ssl/certs/ca-certificates.crt \
  -u iotuser \
  -P 'password' \
  -t 'iot/button1/cmd' \
  -m 'PRESS'

2. 监控状态

mosquitto_sub \
  -h <domain> \
  -p 8883 \
  --cafile /etc/ssl/certs/ca-certificates.crt \
  -u iotuser \
  -P 'password' \
  -t 'iot/button1/#' \
  -v

六、可选:HTTP → MQTT 网关

如需通过浏览器或外部程序直接触发控制,可在 VPS 上部署轻量 Web 网关。示例(Flask + paho-mqtt):

from flask import Flask, request, jsonify
import paho.mqtt.publish as publish

app = Flask(__name__)

@app.route("/press")
def press():
    publish.single(
        "iot/button1/cmd",
        payload="PRESS",
        hostname="<domain>",
        port=8883,
        auth={"username": "iotuser", "password": "password"},
        tls={"ca_certs": "/etc/ssl/certs/ca-certificates.crt"}
    )
    return jsonify({"status": "ok"})

if __name__ == "__main__":
    app.run(host="0.0.0.0", port=5001)

结合反向代理或认证后,即可安全开放给外界使用。


七、安全设计建议

为了在广域网上安全运行,建议:

  • 使用强密码与独立账号
  • 强制启用 TLS 加密
  • 限制 MQTT 端口的访问来源(可选)
  • 监控心跳与系统日志
  • 完善指令权限与可恢复机制

可进一步使用 WireGuard/Tailscale 将所有节点拉入虚拟内网,仅在内网中暴露 MQTT 服务。


总结

该方案通过 VPS + MQTT 构建稳定的广域网远程控制体系,适合智能按键器、机械执行器、家庭自动化、远程实验装置等场景。具备以下特点:

  • 完全自托管
  • 不依赖商业云
  • 具备良好的安全性与可扩展性
  • 支持全球范围远程控制
  • 极低成本即可部署

通过适当扩展,还可以接入多设备、脚本化自动化、定时任务、Web 控制面板等更多功能,构建完整的 DIY 物联网环境。

Leave a Reply

Your email address will not be published. Required fields are marked *