ArcGIS Add-In批量截图工具开发实战
2026/10/14 12:08:49
摘要:高校毕设里,物联网项目最容易卡在“真机跑不通”——设备一离线、数据一丢包,老师一句“现场演示怎么办”就把人问住。本文用一套刚答辩通过的实战方案,带你把 ESP32 + Raspberry Pi 的边缘架构真正跑起来:从协议选型、代码落地到生产级避坑,一条线捋清,让毕设不再只能“PPT 演示”。
结论:毕设场景必须“断电可续、弱网可活、十分钟内可复现”,否则再炫的界面都救不了场。
| 维度 | MQTT | CoAP | HTTP/2 |
|-------------|-------------|-------------|-------------|
| 包头大小 | 2 Byte | 4 Byte | 200+ Byte |
| 连接保持 | 长连接 | UDP 无连接 | TCP 短连接 |
| QoS 级别 | 0/1/2 | 可重传 | 应用层重传 |
| 资源占用 | <30 kB RAM | <20 kB RAM | >80 kB RAM |
| TLS 支持 | 成熟 | 实验性 | 标配 |
// ESP32 低功耗 MQTT 客户端,Clean Code 原则:单一职责 + 超时防御 #include <WiFiClientSecure.h> #include <PubSubClient.h> #include <ArduinoJson.h> const char* ssid = "lab_wifi"; const char* pass = "12345678"; const char* mqtt_srv = "192.168.31.99"; // 边缘 Pi 地址 const uint16_t mqtt_port = 1883; const char* user = "esp32_001"; const char* pwd = "p@ssw0rd"; const char* topic = "sensor/data"; WiFiClient espClient; PubSubClient mqtt(espClient); unsigned long lastReconnect = 0; void mqttReconnect() { // 指数退避,避免狂扫 Broker static int retry = 0; if (millis() - lastReconnect < (1 << retry) * 1000) return; lastReconnect = millis(); if (mqtt.connect("esp32_001", user, pwd)) { retry = 0; } else { retry = min(retry + 1, 6); } } void setup() { WiFi.begin(ssid, pass); mqtt.setServer(mqtt_srv, mqtt_port); } void loop() { if (!mqtt.connected()) mqttReconnect(); mqtt.loop(); static unsigned long lastPub = 0; if (millis() - lastPub > 30000) { // 30 s 周期 lastPub = millis(); char buf[128]; StaticJsonDocument<96> doc; doc["t"] = random(20, 30); // 伪传感器 doc["h"] = random(50, 70); serializeJson(doc, buf); mqtt.publish(topic, buf, true); // retain 标记,冷启动可恢复 } }要点:
retain=true让边缘网关在设备重启瞬间就能拿到最新一条数据,解决“冷启动空窗”。- 指数退避重连,避免毕设现场 50 台设备同时重扫 Broker 把 Pi 打挂。
#!/usr/bin/env python3 # file: edge_bridge.py import paho.mqtt.client as mqtt import sqlite3, json, time, ssl LOCAL_BROKER = "localhost" CLOUD_BROKER = "xxx.iot.aliyuncs.com" CA = "/etc/ssl/certs/aliroot.crt" conn = sqlite3.connect("cache.db", check_same_thread=False) conn.execute """CREATE TABLE IF NOT EXISTS msg (id INTEGER PRIMARY KEY AUTOINCREMENT, topic TEXT, payload TEXT, ts REAL);""" def on_message(client, userdata, msg): # 本地落盘,保证断网可续传 conn.execute("INSERT INTO msg(topic,payload,ts) VALUES(?,?,?)", (msg.topic, msg.payload.decode(), time.time())) conn.commit() # 立即尝试转发云端 try: cloud_client.publish(msg.topic, msg.payload, qos=1) except Exception as e: print("cloud down:", e) local_client = mqtt.Client() local_client.on_message = on_message local_client.connect(LOCAL_BROKER, 1883) local_client.subscribe("#") cloud_client = mqtt.Client() cloud_client.tls_set(ca_certs=CA, tls_version=ssl.PROTOCOL_TLSv1_2) cloud_client.username_pw_set("edge_gateway", "token") def drainer(): # 幂等性:按主键逐条重发,成功即删除 c = conn.cursor() for row in c.execute("SELECT id,topic,payload FROM msg ORDER BY ts"): mid, topic, payload = row inf = cloud_client.publish(topic, payload, qos=1) inf.wait_for_publish() conn.execute("DELETE FROM msg WHERE id=?", (mid,)) conn.commit() # 定时 30 s 批量同步 local_client.loop_start() while True: if cloud_client.is_connected(): drainer() else: try: cloud_client.connect(CLOUD_BROKER, 8883) except: pass time.sleep(30)要点:
- SQLite 当本地队列,比 Redis 省内存,Pi Zero 够用。
- 幂等性靠“主键删除”实现,云端收到重复 msg 只需覆盖即可。
certificate verify failed。解决:Pi 定期广播 MQTT 消息带 NTP 时间,设备收到后configTime()校准,误差 < 1 s。payload=""的 retain 到本主题,清空历史。如果毕设场景换成“温室大棚”,现场根本没宽带,只有 4G 路由器偶尔在线,怎么办?
留给读者思考:你的边缘算力够跑多复杂的模型?模型结果如何回灌到设备端 OTA 升级?这或许是下一个毕设立项的亮点。
整套方案把“通信可靠、部署简单、成本可毕业”三个硬指标压到位:
答辩那天,评委老师只问了一句“数据延迟多少”,我答“三秒内”,他就点头过了。技术细节写在这里,希望你也能把毕设从“PPT 物联网”带进“真机物联网”。