物联网(IoT)系统的本质是"感知-传输-存储-应用"的闭环:通过传感器感知物理世界,经过网络把数据传到云端,存储后再被业务应用消费。看似简单的链路,在工程落地时却涉及设备选型、协议选择、网关配置、平台搭建、安全认证等诸多环节。本文结合赢式科技团队的物联网项目实践,从架构设计到代码实现,系统讲解物联网系统开发的全流程要点。
一、物联网系统整体架构
物联网系统通常采用四层架构:感知层、网络层、平台层、应用层。每一层职责清晰,层间通过标准接口解耦。
┌──────────────────────────────────────────────────┐
│ 应用层 (Application) │
│ Web看板 / 移动APP / 微信小程序 / 大屏可视化 │
│ - 设备管理、报警推送、报表分析、规则引擎 │
├──────────────────────────────────────────────────┤
│ 平台层 (Platform) │
│ MQTT Broker(EMQX) / 时序数据库(TDengine/InfluxDB) │
│ / 应用服务(Java/Go) / 设备影子 / 规则引擎 │
├──────────────────────────────────────────────────┤
│ 网络层 (Network) │
│ - 边缘网关 / 4G路由器 / NB-IoT基站 │
│ - 协议转换:Modbus/OPC UA → MQTT/HTTP │
├──────────────────────────────────────────────────┤
│ 感知层 (Perception) │
│ - 传感器、PLC、仪表、RFID、摄像头 │
│ - RS485/RS232/CAN/Digital IO 接入 │
└──────────────────────────────────────────────────┘
感知层负责"看得见"物理世界,包括温度、湿度、压力、流量、位置等传感器及PLC、仪表等设备;网络层负责"传得动",把数据从设备送到云端,常用的有有线(以太网、光纤)和无线(4G/5G、LoRa、NB-IoT、WiFi)两种方式;平台层负责"管得住",是整个系统的中枢,承担设备管理、数据存储、规则引擎等核心功能;应用层负责"用得好",把数据变成业务价值。
二、设备接入方案
设备接入是物联网系统的入口,需要根据设备类型、部署环境、传输距离、功耗要求等综合选择。接入方式主要分为有线和无线两大类。
| 接入方式 | 传输距离 | 带宽 | 功耗 | 部署成本 | 典型场景 |
|---|---|---|---|---|---|
| RS-485(有线) | 1.2km | 低(115kbps) | 低 | 低 | 车间仪表、变频器 |
| Ethernet(有线) | 100m | 高(1Gbps) | 中 | 中 | PLC、工控设备 |
| WiFi(无线) | 100m | 高 | 高 | 低 | 智能家电、办公环境 |
| LoRa(无线) | 2-15km | 低(50kbps) | 极低 | 中(需自建网关) | 农业监测、智慧园区 |
| NB-IoT(无线) | 10km+ | 低(20kbps) | 极低 | 低(运营商基站) | 智能水表、燃气表 |
| 4G/5G(无线) | 基站覆盖 | 高 | 高 | 低(流量费) | 移动设备、视频回传 |
选型经验:固定部署、有电源供电、对带宽要求高的设备(如PLC、视频监控)优先有线或4G/5G;分布广、电池供电、低频次上报的设备(如水表、环境监测节点)优先NB-IoT或LoRa;办公或家庭场景的智能设备可用WiFi。在赢式科技参与的智慧园区项目中,环境监测节点采用LoRa(自建网关),能耗监测仪表采用RS-485+4G网关(数据量较大)。
三、通信协议选型
物联网通信协议众多,常见包括MQTT、CoAP、HTTP等。协议选择直接影响系统的实时性、可靠性和扩展性。
| 协议 | 传输层 | 报文大小 | 通信模式 | 实时性 | 适用场景 |
|---|---|---|---|---|---|
| MQTT | TCP | 极小(2字节固定头) | 发布订阅 | 高(毫秒级) | 实时数据上报、命令下发 |
| CoAP | UDP | 小 | 请求响应 | 中 | 低功耗受限设备 |
| HTTP | TCP | 较大(文本头) | 请求响应 | 低 | 历史数据批量上传 |
| WebSocket | TCP | 中 | 双向通信 | 高 | Web端实时推送 |
3.1 为什么是MQTT
MQTT已成为物联网领域事实上的标准协议,原因在于其设计精巧:基于发布订阅模式,设备与平台解耦;报文极小(最小仅2字节),适合低带宽环境;提供三种QoS等级(0至多一次、1至少一次、2恰好一次),可根据业务场景权衡可靠性与性能;支持遗嘱消息(LWT),设备异常掉线时平台可自动感知。
典型Topic设计示例:/{productId}/{deviceName}/things/property/post(属性上报)、/{productId}/{deviceName}/things/property/set(属性下发)、/{productId}/{deviceName}/things/event/{eventId}(事件上报)。Topic设计要兼顾可读性、扩展性与权限控制。
四、边缘网关选型与配置
边缘网关是连接感知层与平台层的桥梁,承担协议转换、数据缓存、本地计算等职责。选型时需要关注:支持的下行协议(Modbus、OPC UA、CAN等)、上行协议(MQTT、HTTP)、计算资源(CPU、内存、存储)、工作温度范围、电源冗余等。
4.1 硬件选型
常见的边缘网关硬件方案有三类:工业级网关成品(如映翰通InGateway、有人物联网网关)、嵌入式工控机+网关软件(如树莓派+Node-RED)、自研网关(基于Linux SBC)。对于稳定性和售后要求高的项目,建议选择工业级网关成品;对于成本敏感、定制化需求强的项目,可考虑嵌入式方案。
4.2 协议转换配置
以映翰通InGateway为例,配置Modbus RTU转MQTT的流程:在Web管理界面添加Modbus串口子设备→配置采集点位表(地址、数据类型、采集周期)→配置MQTT云端连接(Broker地址、端口、用户名密码、TLS证书)→配置数据上报模板(JSON格式、Topic映射)→启用规则引擎做边缘计算(如滤波、聚合、阈值判断)。配置完成后,网关会按周期采集Modbus数据,封装成JSON上报到MQTT Broker。
4.3 数据缓存策略
当网络中断或云端不可用时,网关需要在本地缓存数据,待恢复后补传。常见策略:循环队列缓存(FIFO,限制条数)+ SQLite持久化(断电不丢)+ 指数退避重传(避免恢复瞬间冲击平台)。缓存上限要根据网关存储容量设置(如最近3天数据),超过上限时旧数据可被丢弃或仅保留聚合后的统计值。
五、云端平台搭建
云端平台是物联网系统的核心,承担设备接入、数据存储、业务处理、应用接口等职责。一个典型的物联网平台技术栈如下:
| 组件 | 选型 | 职责 |
|---|---|---|
| MQTT Broker | EMQX / Mosquitto | 设备接入、消息路由、ACL权限 |
| 时序数据库 | TDengine / InfluxDB | 海量时序数据高效存储与查询 |
| 关系数据库 | MySQL / PostgreSQL | 设备元数据、用户、配置信息 |
| 缓存 | Redis | 设备影子、实时状态、限流 |
| 消息队列 | Kafka / RabbitMQ | 解耦采集与处理、削峰填谷 |
| 应用服务 | Spring Boot / Go | 业务逻辑、对外API、规则引擎 |
| 可视化 | Vue + ECharts / Grafana | 数据看板、报表、告警展示 |
5.1 MQTT Broker:EMQX
EMQX是开源的分布式MQTT Broker,单节点支持百万级连接,提供丰富的认证方式(用户名密码、JWT、HTTP认证)、ACL规则(基于Topic的发布订阅权限)、规则引擎(SQL语法过滤与转换消息)、桥接与集群功能。生产环境建议至少3节点集群部署,开启TLS加密,配合负载均衡(如Nginx、HAProxy)实现高可用。
5.2 时序数据库:TDengine
物联网数据具有"写多读少、按时间查询、降采样统计"的特点,传统关系数据库难以应对海量时序写入。TDengine是国产开源时序数据库,按"超级表-子表"组织数据,每个设备对应一张子表,写入性能远高于MySQL,原生支持降采样查询(如5分钟聚合、小时聚合)。InfluxDB是另一选择,语法简洁(Flux/InfluxQL),社区生态成熟。
六、数据上云全流程代码示例
下面以一个温度传感器数据上报为例,演示从设备注册到可视化的完整流程。
6.1 设备注册(平台侧)
POST /api/v1/devices
Content-Type: application/json
{
"productId": "temp-sensor-001",
"deviceName": "sensor-workshop-A-01",
"deviceSecret": "a3f8b2c9d1e7...",
"tags": { "location": "车间A", "owner": "张工" }
}
# 响应
{
"code": 0,
"data": {
"deviceId": "dev-2026-0814-0001",
"mqttEndpoint": "mqtt://broker.it-ys.com:1883",
"username": "sensor-workshop-A-01|temp-sensor-001",
"password": "token-generated-by-platform"
}
}
6.2 设备数据上报(设备/网关侧,Python示例)
import paho.mqtt.client as mqtt
import json
import time
import random
BROKER = "broker.it-ys.com"
PORT = 1883
USERNAME = "sensor-workshop-A-01|temp-sensor-001"
PASSWORD = "token-generated-by-platform"
DEVICE_NAME = "sensor-workshop-A-01"
PRODUCT_ID = "temp-sensor-001"
client = mqtt.Client(client_id=f"{PRODUCT_ID}_{DEVICE_NAME}")
client.username_pw_set(USERNAME, PASSWORD)
# 遗嘱消息:设备异常掉线时平台自动感知
client.will_set(
f"/{PRODUCT_ID}/{DEVICE_NAME}/status/offline",
json.dumps({"timestamp": int(time.time()), "reason": "unexpected"}),
qos=1
)
def on_connect(client, userdata, flags, rc):
if rc == 0:
print("连接成功")
# 上线通知
client.publish(
f"/{PRODUCT_ID}/{DEVICE_NAME}/status/online",
json.dumps({"timestamp": int(time.time())}),
qos=1
)
else:
print(f"连接失败,错误码: {rc}")
client.on_connect = on_connect
client.connect(BROKER, PORT, 60)
client.loop_start()
# 周期上报温度数据
while True:
payload = {
"id": int(time.time()),
"version": "1.0",
"params": {
"temperature": round(random.uniform(20.0, 35.0), 2),
"humidity": round(random.uniform(40.0, 70.0), 2)
},
"time": int(time.time() * 1000)
}
topic = f"/{PRODUCT_ID}/{DEVICE_NAME}/things/property/post"
client.publish(topic, json.dumps(payload), qos=1)
print(f"已上报: {payload}")
time.sleep(10)
6.3 平台接收与存储(Java示例)
@Service
public class DeviceMessageHandler {
@Autowired
private TdengineJdbcTemplate tdengine;
@Autowired
private RedisTemplate redis;
@KafkaListener(topics = "device-property-post")
public void onPropertyPost(String message) {
PropertyPostDTO dto = JSON.parseObject(message, PropertyPostDTO.class);
String deviceId = dto.getDeviceId();
Map params = dto.getParams();
long ts = dto.getTime();
// 1. 写入时序数据库 TDengine
String sql = "INSERT INTO dev_" + deviceId +
" USING property_data " +
"TAGS ('" + deviceId + "') " +
"VALUES (" + ts +
", " + params.get("temperature") +
", " + params.get("humidity") + ")";
tdengine.update(sql);
// 2. 更新设备影子(Redis缓存最新状态)
String shadowKey = "shadow:" + deviceId;
redis.opsForHash().putAll(shadowKey, params);
redis.opsForHash().put(shadowKey, "lastTime", String.valueOf(ts));
// 3. 规则引擎判断报警
double temp = ((Number) params.get("temperature")).doubleValue();
if (temp > 32.0) {
alarmService.publish(deviceId, "TEMP_HIGH",
"温度超过阈值: " + temp + "℃");
}
}
}
6.4 可视化查询接口
@GetMapping("/api/devices/{deviceId}/history")
public Result queryHistory(
@PathVariable String deviceId,
@RequestParam long startTime,
@RequestParam long endTime,
@RequestParam(defaultValue = "1m") String interval) {
// 按时间窗口降采样查询
String sql = String.format(
"SELECT _wstart as ts, AVG(temperature) as temp, AVG(humidity) as hum " +
"FROM dev_%s " +
"WHERE ts >= %d AND ts <= %d " +
"INTERVAL(%s)",
deviceId, startTime, endTime, interval
);
List
前端通过HTTP接口拉取历史数据,结合WebSocket订阅实时推送,即可实现实时看板。一个典型的物联看板包含:设备在线状态、实时数据曲线、报警列表、历史数据报表等模块。
七、物联网系统开发注意事项
7.1 安全认证
物联网设备一旦被恶意接管,可能造成严重后果(如智能门锁被远程打开、工业设备被恶意停机)。安全措施至少包括:设备身份认证(一机一密,避免硬编码密钥)、传输加密(TLS/SSL)、消息签名(防篡改)、平台ACL权限(按Topic限制发布订阅)、密钥定期轮换。生产环境务必关闭明文MQTT端口(1883),只开放TLS端口(8883)。
7.2 断网重连与数据补传
设备或网关侧必须实现断线自动重连,采用指数退避策略(如1s→2s→4s→8s,最大60s),避免在云端短暂不可用时疯狂重连造成"惊群效应"。本地缓存数据时按时间分片,恢复后按顺序补传,补传数据要带原始时间戳,平台侧据此区分实时数据与历史数据。
7.3 设备影子
设备影子(Device Shadow)是平台维护的一份设备状态缓存,解决"设备离线时应用层如何获取/修改状态"的问题。应用层读写影子,平台在设备在线时同步到设备。例如用户在APP上设置空调温度26℃,设备此时离线,平台将期望状态写入影子;设备下次上线时拉取影子,把状态同步为26℃。
7.4 规则引擎
规则引擎让业务人员无需改代码即可配置数据处理规则(如"温度>80℃时推送给维修工")。EMQX自带SQL规则引擎,复杂场景可引入独立规则引擎(如Drools、LiteFlow)。规则引擎要支持热更新、版本回滚,避免规则变更导致系统不可用。
八、赢式科技物联网开发服务介绍
上海赢式信息科技有限公司自2010年成立以来,长期深耕工业物联网与上位机开发领域,累计交付3000+定制化项目,服务覆盖智慧园区、智慧能源、智慧农业、智慧环保、智慧冷链等行业。公司可提供从感知层设备选型、边缘网关配置、云端平台搭建、移动端开发到运维部署的物联网全栈服务,技术栈涵盖EMQX、TDengine、Spring Boot、Vue等主流组件。如果您有物联网系统开发需求,欢迎联系赢式科技获取需求评估与方案报价。
- 电话咨询:15001875806(工作日9:00-18:00)
- 在线咨询:点击免费咨询赢式科技工程师
- 相关服务:工业物联网系统开发 | 上位机系统定制开发