ZigBee+ESP32+MQTT工业物联网全栈实践
2026/9/10 9:38:40 网站建设 项目流程

简介:这是一套面向物联网开发初学者与进阶学习者的全栈实战项目资源,覆盖硬件采集、协议通信、服务端处理及移动端交互全流程,特别适合高校课程设计、毕业设计及嵌入式+Java+Android综合能力训练。资源包含917个文件,主体为ESP32固件源码(.c/.h)、ZigBee协议相关编译产物(.r51/.lst/.s51)、Java Web后端模块(60个.java + 60个.class + 4个.war + 4个.jsp)、安卓客户端工程(含Kotlin/Gradle配置)、EMQX与Tomcat部署配置(.xml/.properties),以及UI资源(.png)和数据库脚本,压缩包共33.77MB。已有1100人学习下载,项目结构完整、模块解耦清晰,预览可见coordinator.c、DHT11Device.c、SimpleAPIExp.c等典型设备驱动与接口代码,配合Servlet+MySQL数据流设计,可直接部署调试,帮助读者深入理解MQTT消息路由、Web服务桥接、跨平台数据同步等核心IoT架构实践。

1. 这不是“拼凑技术名词”的玩具项目,而是一套可落地的工业级物联网数据链路

当你看到“ZigBee+ESP32+MQTT+EMQX+Tomcat+Servlet+MySQL+安卓App”这个标题时,第一反应可能是:又一个堆砌关键词的课程设计?但实际它描述了一条从物理层传感器到移动端可视化闭环可控的数据通路——ZigBee负责低功耗多节点组网(如温湿度、门磁、烟感等终端),ESP32作为边缘网关桥接ZigBee与IP网络,MQTT承载轻量异步消息,EMQX提供高并发、可水平扩展的消息路由中枢,Tomcat运行Java Web服务暴露标准HTTP接口,Servlet处理设备注册、指令下发、历史查询等业务逻辑,MySQL持久化结构化数据,安卓App完成用户交互与实时状态呈现。这套组合不依赖云厂商封闭生态,全部组件开源可控,适合中小制造车间、智能楼宇改造、农业监测等需自主运维、数据本地化、响应延迟敏感的真实场景。它面向的是有嵌入式开发基础、能写Java后端、会配Linux服务、懂Android基础开发的全栈型工程师或技术负责人,而非仅调用SDK的调包侠。

2. ZigBee与ESP32协同:构建稳定可靠的边缘感知层

ZigBee协议栈在ESP32上的实现并非简单“插上模块就能用”,而是需要明确角色划分、通信拓扑与数据格式。常见误区是将ESP32直接当作ZigBee协调器(Coordinator)运行Z-Stack,但ESP32原生不支持ZigBee PHY层,必须外接专用ZigBee芯片(如CC2530、CC2652R、JN5169)或模块(如Silicon Labs EFR32MG21、TI CC2652RB)。因此,ESP32在此架构中定位为ZigBee网关(Gateway),通过串口(UART)与ZigBee协调器模块通信,解析ZDO、APS、ZCL层报文,再转换为MQTT Topic和Payload。

2.1 硬件选型与串口连接规范

ZigBee模块与ESP32的物理连接必须满足电平匹配与电气隔离。以主流CC2530协调器模块为例:

ESP32引脚ZigBee模块引脚说明
GPIO16 (TX)RXESP32发送AT指令或ZNP命令
GPIO17 (RX)TX接收ZigBee模块上报的ZCL Attribute Report、Join Indication等
GPIO12RESET控制模块复位(需加10kΩ上拉)
GNDGND共地,不可省略

注意:CC2530默认波特率常为115200,但部分固件可能设为38400或9600,务必查阅模块Datasheet并用逻辑分析仪抓取初始握手帧确认。若ESP32串口初始化失败,优先检查Serial.begin(115200)参数是否与模块一致。

2.2 ESP32端ZigBee协议解析核心逻辑(Arduino框架)

使用Arduino-ESP32平台,配合ZigBee AT指令集或ZNP(Zigbee Network Processor)模式,关键在于状态机管理与报文解包。以下为解析ZCL温度测量Cluster上报数据的最小可行代码:

#include <HardwareSerial.h> HardwareSerial ZigbeeSerial(2); // 使用UART2 void setup() { Serial.begin(115200); ZigbeeSerial.begin(115200, SERIAL_8N1, 16, 17); // TX=16, RX=17 delay(1000); // 发送ZNP复位命令,确保模块进入已知状态 ZigbeeSerial.write((uint8_t*)"\x01\x21\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00\x00", 16); } void loop() { if (ZigbeeSerial.available()) { static uint8_t buffer[128]; static int len = 0; while (ZigbeeSerial.available() && len < sizeof(buffer)-1) { buffer[len++] = ZigbeeSerial.read(); } if (len > 0 && buffer[0] == 0xFE) { // ZNP帧起始标志 parseZnpFrame(buffer, len); len = 0; } } } void parseZnpFrame(uint8_t* data, int len) { // ZNP帧结构:SOH(0xFE) + LEN(2B) + CMD(2B) + DATA(NB) + CRC(1B) if (len < 6) return; uint16_t frameLen = (data[2] << 8) | data[1]; // LEN字段为小端 if (frameLen + 5 != len) return; // 总长 = SOH + LEN + CMD + DATA + CRC uint16_t cmdId = (data[4] << 8) | data[3]; if (cmdId == 0x4740) { // ZDO_MSG_CB_IND - 设备加入/离开通知 Serial.printf("Device joined: 0x%04X\n", (data[10] << 8) | data[9]); } else if (cmdId == 0x4E40) { // ZCL_INCOMING_MSG - ZCL入站消息 uint16_t clusterId = (data[14] << 8) | data[13]; if (clusterId == 0x0002) { // Temperature Measurement Cluster int16_t tempRaw = (data[22] << 8) | data[21]; float temperature = tempRaw / 100.0; // ZCL温度单位为0.01°C char topic[64]; sprintf(topic, "zigbee/sensor/%04X/temperature", (data[10] << 8) | data[9]); publishToMQTT(topic, String(temperature).c_str()); // 后续MQTT发布 Serial.printf("Temp from %04X: %.2f°C\n", (data[10] << 8) | data[9], temperature); } } }

这段代码的关键点在于:

  • 帧同步:以0xFE为SOH(Start of Header),避免因串口误码导致解析错位;
  • 长度校验:ZNP协议要求LEN字段精确指示DATA长度,必须验证frameLen + 5 == total_len
  • Cluster ID过滤:只处理0x0002(Temperature Measurement)Cluster,忽略其他如0x0006(On/Off)或0x0008(Level Control);
  • 设备地址提取data[9]data[10]构成16-bit短地址(Short Address),用于生成唯一Topic路径,这是后续EMQX路由与权限控制的基础。

2.3 ZigBee网络稳定性强化策略

ZigBee网络易受Wi-Fi信道干扰(同属2.4GHz ISM频段)、金属遮挡、节点休眠唤醒不同步影响。实测中,以下三类配置可显著提升鲁棒性:

配置项推荐值作用说明
信道选择Channel 25(2.4835 GHz)避开Wi-Fi常用信道1/6/11,减少同频干扰;需在ZigBee协调器固件中预设
广播重传次数MAX_RREQ_RETRIES = 3增加路由请求(RREQ)重试,应对临时信号衰减;修改Z-Stack源码zstack_config.h
终端设备休眠周期POLL_RATE = 30000ms设置终端每30秒主动轮询协调器,平衡功耗与响应延迟;需在终端节点固件中配置

提示:ZigBee网络拓扑应避免纯星型(所有终端直连协调器),推荐树状或网状(Mesh)。当节点数>20时,必须启用路由器(Router)角色节点(如CC2530 Router固件),否则协调器负载过载会导致Join失败或Attribute Report丢失。

3. MQTT+EMQX:构建高吞吐、低延迟的消息中枢

MQTT协议本身轻量,但生产环境中的消息可靠性、QoS分级、主题权限、规则引擎等能力,完全依赖Broker实现。EMQX作为全球下载量超千万的开源MQTT Broker,其v5.0+版本对ZigBee这类海量小包场景做了深度优化,远超Mosquitto或VerneMQ。

3.1 EMQX Docker部署与基础安全加固

使用Docker Compose一键部署EMQX,并禁用默认匿名登录、强制TLS加密:

# docker-compose.yml version: '3.8' services: emqx: image: emqx/emqx:5.7.2 restart: unless-stopped ports: - "1883:1883" # MQTT TCP - "8083:8083" # MQTT WebSocket - "8084:8084" # HTTPS Dashboard - "18831:18831" # MQTT/TCP with TLS environment: EMQX_LOADED_PLUGINS: "emqx_management,emqx_recon,emqx_retainer,emqx_dashboard" EMQX_ALLOW_ANONYMOUS: "false" # 关键!禁用匿名访问 EMQX_ZONE__EXTERNAL__MAX_PACKET_SIZE: "1MB" # 支持大Payload(如固件OTA) volumes: - ./emqx/etc/emqx.conf:/opt/emqx/etc/emqx.conf - ./emqx/data:/opt/emqx/data - ./certs:/opt/emqx/etc/certs # TLS证书挂载

启动后,必须立即执行以下安全操作:

  1. 访问https://your-server-ip:8084,用默认账号admin/admin登录Dashboard;
  2. 进入用户管理 → 新建用户,创建gateway_user(密码强复杂度),赋予mqtt_pubsub权限;
  3. 进入ACL(访问控制列表)→ 新建规则,设置:
    • publish权限:zigbee/gateway/+/commandzigbee/sensor/+/+
    • subscribe权限:zigbee/sensor/+/+zigbee/gateway/+/status
    • 拒绝所有其他Topic(#)的publish/subscribe;

注意:ACL规则按顺序匹配,拒绝规则必须放在最后。若先写deny #,则后续所有规则失效。

3.2 EMQX规则引擎实现设备状态自动落库

EMQX内置SQL-like规则引擎,可将MQTT消息直接写入MySQL,避免后端Servlet重复消费。例如,将温湿度上报自动存入sensor_data表:

-- 在EMQX Dashboard的"规则引擎 → 创建规则"中填写 SELECT payload.temp as temperature, payload.humidity as humidity, clientid as device_id, timestamp as ts FROM "zigbee/sensor/+/+" WHERE payload.temp IS NOT NULL AND payload.humidity IS NOT NULL

对应动作选择**"Data Bridge → MySQL"**,配置连接池:

  • MySQL Host:host.docker.internal(Docker内访问宿主机MySQL)
  • Port:3306
  • Database:iot_db
  • Username/Password:iot_app/StrongPass!2024
  • SQL模板:INSERT INTO sensor_data(device_id, temperature, humidity, ts) VALUES ('${device_id}', ${temperature}, ${humidity}, FROM_UNIXTIME(${ts}))

此方案将消息处理延迟从Servlet的100~300ms降至EMQX内部毫秒级,且规避了Java应用单点故障导致数据积压的风险。

3.3 MQTT客户端连接参数调优(ESP32端)

ESP32使用PubSubClient库连接EMQX时,需针对ZigBee网关场景调整超时与重连:

#include <PubSubClient.h> WiFiClient espClient; PubSubClient client(espClient); void mqttReconnect() { if (!client.connected()) { long now = millis(); if (now - lastReconnectAttempt > 5000) { // 5秒后重试 lastReconnectAttempt = now; if (client.connect("gateway_esp32", "gateway_user", "StrongPass!2024")) { client.subscribe("zigbee/gateway/+/command", 1); // QoS1确保指令不丢 client.setCallback(callback); } } } } void callback(char* topic, byte* payload, unsigned int length) { // 解析JSON指令,如{"cmd":"led_on","target":"0x1234"} StaticJsonDocument<256> doc; DeserializationError error = deserializeJson(doc, payload, length); if (!error) { const char* cmd = doc["cmd"] | ""; const char* target = doc["target"] | ""; if (strcmp(cmd, "led_on") == 0) { sendZigBeeCommand(target, 0x0006, 0x0001); // ZCL On Command } } }

关键参数说明:

  • client.connect(...)第二、三参数为认证凭据,严禁硬编码明文密码,应从ESP32 NVS存储读取;
  • subscribe(..., 1)指定QoS1,确保指令至少送达一次(ZigBee设备无ACK机制,需Broker重发);
  • lastReconnectAttempt时间戳防抖,避免网络闪断时高频重连耗尽ESP32资源。

4. Tomcat+Servlet+MySQL:构建业务逻辑与数据持久化层

Tomcat在此架构中并非仅作静态文件服务器,而是承载设备管理、指令调度、历史查询等核心业务逻辑的Java Web容器。Servlet直接对接EMQX的WebSocket或HTTP API,形成“MQTT事件驱动 + HTTP同步响应”的混合模式。

4.1 Servlet接收MQTT事件并触发业务流程

EMQX提供HTTP钩子(Web Hook),当设备上线、消息发布、客户端连接时,自动向指定URL发送POST请求。在web.xml中配置监听器:

<servlet> <servlet-name>EmqxHookServlet</servlet-name> <servlet-class>com.iot.servlet.EmqxHookServlet</servlet-class> </servlet> <servlet-mapping> <servlet-name>EmqxHookServlet</servlet-name> <url-pattern>/emqx/hook</url-pattern> </servlet-mapping>

EmqxHookServlet.java处理设备上线事件,自动注册到MySQL:

@WebServlet("/emqx/hook") public class EmqxHookServlet extends HttpServlet { private static final long serialVersionUID = 1L; private ConnectionPool pool = ConnectionPool.getInstance(); protected void doPost(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { StringBuilder json = new StringBuilder(); String line; try (BufferedReader reader = request.getReader()) { while ((line = reader.readLine()) != null) { json.append(line); } } JsonObject obj = JsonParser.parseString(json.toString()).getAsJsonObject(); String action = obj.get("action").getAsString(); // "client_connected", "message_publish" if ("client_connected".equals(action)) { String clientId = obj.get("clientid").getAsString(); String ipaddr = obj.get("ipaddress").getAsString(); // 插入设备注册表,标记为在线 String sql = "INSERT INTO device_registry (client_id, ip_address, status, last_seen) VALUES (?, ?, 'online', NOW()) ON DUPLICATE KEY UPDATE status='online', last_seen=NOW()"; try (Connection conn = pool.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, clientId); ps.setString(2, ipaddr); ps.executeUpdate(); } } else if ("message_publish".equals(action)) { String topic = obj.get("topic").getAsString(); String payload = obj.get("payload").getAsString(); // 解析payload,存入sensor_data表(若未启用EMQX规则引擎) if (topic.startsWith("zigbee/sensor/")) { saveSensorData(topic, payload); } } } private void saveSensorData(String topic, String payload) { // 提取device_id:topic格式为 zigbee/sensor/0x1234/temperature String[] parts = topic.split("/"); String deviceId = parts.length > 3 ? parts[2] : "unknown"; JsonObject data = JsonParser.parseString(payload).getAsJsonObject(); double temp = data.has("temperature") ? data.get("temperature").getAsDouble() : 0.0; double humi = data.has("humidity") ? data.get("humidity").getAsDouble() : 0.0; String sql = "INSERT INTO sensor_data (device_id, temperature, humidity, created_at) VALUES (?, ?, ?, NOW())"; try (Connection conn = pool.getConnection(); PreparedStatement ps = conn.prepareStatement(sql)) { ps.setString(1, deviceId); ps.setDouble(2, temp); ps.setDouble(3, humi); ps.executeUpdate(); } catch (SQLException e) { e.printStackTrace(); } } }

此设计优势在于:

  • 解耦:Servlet不主动轮询MQTT,由EMQX事件驱动,降低CPU占用;
  • 幂等ON DUPLICATE KEY UPDATE确保设备重复上线不产生脏数据;
  • 可扩展:新增事件类型(如client_disconnected)只需追加if-else分支。

4.2 MySQL表结构与索引优化

针对ZigBee设备高频上报(单设备每分钟1~10次),sensor_data表需针对性优化:

CREATE TABLE `sensor_data` ( `id` BIGINT UNSIGNED NOT NULL AUTO_INCREMENT, `device_id` VARCHAR(16) NOT NULL COMMENT 'ZigBee短地址,如0x1234', `temperature` DECIMAL(5,2) DEFAULT NULL, `humidity` DECIMAL(5,2) DEFAULT NULL, `created_at` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`), INDEX `idx_device_time` (`device_id`, `created_at`) USING BTREE, -- 覆盖查询:某设备最近N条 INDEX `idx_time` (`created_at`) USING BTREE -- 覆盖查询:某时段所有设备 ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;

提示device_id使用VARCHAR(16)而非INT,因ZigBee地址为16进制字符串(如0xABCD),转为整数易溢出且丧失可读性。idx_device_time为复合索引,查询SELECT * FROM sensor_data WHERE device_id='0x1234' ORDER BY created_at DESC LIMIT 50时,可避免filesort。

4.3 Tomcat JVM参数与线程池调优

默认Tomcat配置无法支撑千级设备并发连接。在bin/setenv.sh中添加:

export JAVA_OPTS="-server \ -Xms2g -Xmx2g \ -XX:+UseG1GC \ -XX:MaxGCPauseMillis=200 \ -XX:+UseStringDeduplication \ -Djava.awt.headless=true \ -Dfile.encoding=UTF-8"

并在conf/server.xml中调整连接器:

<Connector port="8080" protocol="HTTP/1.1" maxThreads="500" minSpareThreads="100" maxSpareThreads="200" acceptCount="500" connectionTimeout="20000" redirectPort="8443" />
  • maxThreads=500:应对大量HTTP钩子请求与安卓App API调用;
  • acceptCount=500:当所有线程繁忙时,允许500个请求排队,避免连接拒绝;
  • connectionTimeout=20000:20秒超时,防止慢客户端占满线程。

5. 安卓App:实现设备控制与数据可视化闭环

安卓端不采用WebView加载HTML页面,而是原生Java/Kotlin调用MQTT与HTTP API,确保离线缓存、后台保活、低功耗推送能力。

5.1 MQTT连接与主题订阅(Kotlin)

使用Eclipse Paho Android Client,连接EMQX并订阅设备Topic:

class MqttManager { private lateinit var mqttClient: MqttAndroidClient private val context = App.instance.applicationContext fun connect() { val serverUri = "tcp://your-emqx-server:1883" mqttClient = MqttAndroidClient(context, serverUri, "android_${System.currentTimeMillis()}") // 设置连接选项 val options = MqttConnectOptions().apply { userName = "android_user" password = "StrongPass!2024".toCharArray() isCleanSession = false // 保持会话,接收离线消息 connectionTimeout = 30 keepAliveInterval = 60 } mqttClient.setCallback(object : MqttCallbackExtended { override fun connectComplete(reconnect: Boolean, serverURI: String) { // 连接成功后订阅所有传感器Topic mqttClient.subscribe("zigbee/sensor/#", 1) { _, topic, message -> handleSensorMessage(topic, message.payloadStr) } } override fun connectionLost(cause: Throwable) { /* 重连逻辑 */ } override fun messageArrived(topic: String?, message: MqttMessage?) { } override fun deliveryComplete(token: IMqttDeliveryToken?) { } }) mqttClient.connect(options, null, object : IMqttActionListener { override fun onSuccess(asyncActionToken: IMqttActionListener?) { Log.d("MQTT", "Connected to EMQX") } override fun onFailure(asyncActionToken: IMqttActionListener?, exception: Throwable) { Log.e("MQTT", "Connect failed", exception) } }) } private fun handleSensorMessage(topic: String, payload: String) { // 解析topic:zigbee/sensor/0x1234/temperature val parts = topic.split("/") if (parts.size >= 4) { val deviceId = parts[2] val sensorType = parts[3] val value = payload.toDoubleOrNull() // 更新RecyclerView中对应设备Item的UI DeviceListActivity.updateSensorValue(deviceId, sensorType, value) } } }

5.2 HTTP API调用实现设备控制(Retrofit)

通过Tomcat暴露的Servlet接口下发指令,如开关灯:

interface ApiService { @POST("api/device/control") @Headers("Content-Type: application/json") suspend fun sendControlCommand(@Body command: ControlCommand): Response<ApiResponse> } data class ControlCommand( val deviceId: String, // ZigBee短地址 val command: String, // "led_on", "led_off" val duration: Int? = null // 可选参数 ) // 调用示例 fun controlDevice(deviceId: String, cmd: String) { lifecycleScope.launch { try { val response = apiService.sendControlCommand(ControlCommand(deviceId, cmd)) if (response.isSuccessful) { Toast.makeText(this@DeviceListActivity, "指令已发送", Toast.LENGTH_SHORT).show() } } catch (e: Exception) { Toast.makeText(this@DeviceListActivity, "发送失败: ${e.message}", Toast.LENGTH_LONG).show() } } }

对应的Servlet端/api/device/control需验证用户Session,并将指令发布至zigbee/gateway/${deviceId}/commandTopic,由ESP32网关消费。

5.3 后台保活与省电策略

安卓8.0+对后台Service限制严格,需采用前台Service+Notification:

private fun startForegroundService() { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val channel = NotificationChannel( "mqtt_channel", "MQTT Service", NotificationManager.IMPORTANCE_LOW ) notificationManager.createNotificationChannel(channel) val notification = NotificationCompat.Builder(this, "mqtt_channel") .setContentTitle("IoT Gateway Running") .setContentText("Receiving sensor data...") .setSmallIcon(R.drawable.ic_stat_iot) .build() startForeground(1, notification) } }

同时,在AndroidManifest.xml中声明:

<service android:name=".service.MqttService" android:enabled="true" android:exported="false" android:foregroundServiceType="specialized" />

注意foregroundServiceType="specialized"需在<uses-permission>中声明FOREGROUND_SERVICE_SPECIALIZED权限(API 34+),否则前台Service会被系统终止。

6. 全链路验证与典型故障排查技巧

项目交付前,必须进行端到端压力测试与边界场景验证。以下三个技巧可快速定位90%的集成问题:

6.1 分层验证法:逐级注入与观测

不要一上来就跑通全流程,而是按数据流向分四层验证:

层级验证方法预期结果常见失败点
ZigBee层用ZigBee Sniffer(如Ubiqua)抓包看到终端周期性发送ZCL Read Attributes Response终端未入网、协调器信道不匹配、ZCL Cluster ID错误
MQTT层mosquitto_sub -h emqx-ip -t "zigbee/sensor/#" -u user -P pass实时打印温湿度JSONEMQX ACL拒绝、ESP32未正确publish、Topic拼写错误(如senor
HTTP层curl -X POST http://tomcat-ip:8080/emqx/hook -d '{"action":"message_publish","topic":"zigbee/sensor/0x1234/temperature","payload":"{\"temperature\":25.5}"}'MySQLsensor_data表新增记录Servlet未部署、MySQL连接失败、JSON解析异常
App层在Android Studio Logcat过滤MQTT看到Connected to EMQXReceived: zigbee/sensor/0x1234/temperatureApp网络权限缺失、EMQX TLS证书未信任、Topic订阅QoS不匹配

6.2 EMQX Dashboard实时诊断面板

EMQX Dashboard的监控 → 实时统计页提供关键指标:

  • Messages Received:应与ZigBee终端上报频率一致(如100终端×1次/分钟 = 100/min);
  • Messages Dropped:非零值表明ACL拒绝或Topic长度超限(EMQX默认最大Topic长度64字符);
  • Clients Connected:应等于ESP32网关数 + 安卓App在线数;
  • Retained Messages:若长期增长,说明客户端未正确清除Retain Flag,需检查ESP32client.publish(topic, payload, false, 0)中第三个参数(retain)是否误设为true

6.3 MySQL慢查询日志定位性能瓶颈

开启MySQL慢查询日志,捕获执行时间>1秒的SQL:

SET GLOBAL slow_query_log = 'ON'; SET GLOBAL long_query_time = 1; SET GLOBAL log_output = 'TABLE'; -- 日志写入mysql.slow_log表

然后执行典型查询:

SELECT * FROM sensor_data WHERE device_id = '0x1234' AND created_at BETWEEN '2024-06-01 00:00:00' AND '2024-06-01 23:59:59' ORDER BY created_at DESC LIMIT 100;

若该查询执行缓慢,EXPLAIN显示type: ALL(全表扫描),证明idx_device_time索引未生效,原因可能是:

  • device_id字段在WHERE中使用了函数(如UPPER(device_id));
  • 查询条件未覆盖索引最左列(如只查created_at,未带device_id);
  • 表数据量过大(>1000万行),需考虑按月分表或归档冷数据。

最终,当ZigBee传感器数据经ESP32网关、MQTT、EMQX、Tomcat Servlet、MySQL,最终在安卓App上实时刷新,且任意环节故障时能准确定位到具体组件与日志行,这个“ZigBee+ESP32+MQTT+EMQX+Tomcat+Servlet+MySQL+安卓App”的完整链路才算真正落地。

本文还有配套的精品资源,点击获取

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询