← 返回平台主页

金橙智能硬件平台 · 开发使用手册

版本 v2.0  |  最后更新:2026-06-08  |  支持 MQTT / TCP 双协议,提供设备上云、APP 控制、实时推送全链路方案。

1. 平台概述

金橙智能硬件平台是一个全栈物联网平台,提供设备接入、数据存储、反向控制及 APP 交互能力。核心特性:

  • ✅ 多协议支持:MQTT(标准物联网协议)和 TCP 长连接(轻量嵌入式设备)
  • ✅ 灵活的产品物模型:自定义属性(温度、湿度等)和服务(开关、调节)
  • ✅ 设备生命周期管理:在线/离线状态、最后活跃时间、数据点配置
  • ✅ 双向通信:APP 可实时获取设备数据、下发控制指令,设备回复命令执行结果
  • ✅ 实时推送:设备上报数据后立即推送给绑定的 APP 客户端(无需轮询)
  • ✅ 统一账户:Web 控制台、APP 端(MQTT/TCP)共享用户体系
  • ✅ 自动离线检测:TCP 空闲超时(30秒) + MQTT 后台扫描,确保状态准确
💡 架构说明:设备可选择 MQTT 或 TCP 接入;APP 也可选择 MQTT 或 TCP 接入。平台同时运行 MQTT Broker (1883) 和 TCP Server (6666),三方(设备、平台、APP)通过标准协议通信。

2. 核心概念:产品 · 设备 · 物模型

在使用平台之前,必须理解产品、设备和物模型之间的关系。这是整个平台数据建模和通信的基础。

2.1 产品(Product)

产品是设备的“抽象类型”或“模板”。它定义了同一类设备的共同特征:通信协议(MQTT/TCP)、物模型(属性与服务)、以及产品的描述信息。例如,“智能温湿度传感器”是一个产品,所有该型号的传感器都继承这个产品的定义。

  • 一个产品可以包含多个设备。
  • 产品创建后,可以修改物模型,但已创建的设备不会自动同步变化(平台当前不会自动更新,需要手动更新或重新创建设备)。
  • 产品关联一个用户(创建者)。

2.2 设备(Device)

设备是具体物理设备的实例。每个设备属于一个产品,并继承产品的协议和物模型。设备拥有唯一的 device_idsecret(MQTT 注册验证用)。设备可以上报数据、接收命令、更新状态等。

  • 设备的上报数据格式应与产品的物模型属性相匹配,但不是强制严格校验(平台目前不校验数据类型,建议开发者自行保证)。
  • 设备可以有自己的“数据点”(datapoints)覆盖或扩展产品的物模型,用于特殊场景。
  • 设备的状态(online/offline)由平台根据心跳或上报自动维护。

2.3 物模型(Thing Model)

物模型是产品能力的标准化描述,包含两部分:属性(Properties)服务(Services)。属性是设备可上报的监测数据(如温度、湿度);服务是设备可被调用的动作(如打开开关、调整档位)。

📦 产品 (Product)
├─ 通信协议: MQTT / TCP
├─ 物模型 (Thing Model)
│ ├─ 属性 (Properties): 温度(temp), 湿度(hum), 开关状态(switch)
│ └─ 服务 (Services): 打开开关(turn_on), 设置阈值(set_threshold)
└─ 描述信息

⬇️ 包含多个设备
├─ 设备A (device_id=abc123)
├─ 设备B (device_id=def456)
└─ 设备C (device_id=ghi789)
每个设备独立上报数据,但共享同一套物模型定义

3. 快速开始(5分钟体验)

3.1 注册账号

访问 http://www.zzjczn.com/iot-platform/register,填写用户名、邮箱、密码即可注册,默认赠送3台免费设备额度。

3.2 创建产品

登录后进入“控制台” → “创建产品”,选择协议(MQTT / TCP),定义物模型属性(例如温度、湿度)。

3.3 添加设备

在产品下创建设备,获得唯一的 device_idsecret(用于 MQTT 注册验证)。TCP 设备只需 device_id。

3.4 设备接入示例

参考第8、9章中的 STM32 代码示例,编写设备固件,连接平台后即可上报数据。

4. 用户管理

用户通过 Web 端注册,同一账号可用于 Web 控制台和 APP(MQTT/TCP 方式)。

支持邮箱/手机号找回密码,设备数量限制按套餐动态调整。

5. 产品管理

产品是设备的分类模板,创建产品时需要填写:

  • 产品名称:如“智能插座”。
  • 通信协议:MQTT 或 TCP。
  • 物模型:通过可视化编辑器或 JSON 编辑,定义属性和服务(详见第7章)。
  • 描述:可选。

产品创建后,可以在产品列表中进行编辑或删除。删除产品会同时删除其下所有设备(及设备数据),请谨慎操作。

6. 设备管理

每个设备包含字段:device_idsecret、状态(online/offline)、最后上线时间。设备密钥仅用于 MQTT 注册验证,TCP 设备不需要 secret 验证(但必须已存在且属于当前用户)。

设备详情页可查看最新数据、历史数据图表、通信示例及下发指令。

7. 物模型详细定义与注意事项

物模型是平台的核心,它决定了设备上报什么数据、APP 下发什么命令。本节提供完整的定义规范、字段说明和最佳实践。

7.1 属性(Property)字段规范

属性描述设备上报的监测数据。每个属性包含以下字段:

字段名类型必填说明
namestring属性显示名称,如“温度”。
identifierstring属性标识符,设备上报时的 JSON 键名。建议使用英文小写+下划线。
dataTypestring数据类型:float, int, string, bool, enum。
unitstring单位,如“℃”、“%”。
rangearray数值范围,如 [0,100]。
stepfloat步长,用于数值调节控件。
enumValuesarray当 dataType 为 enum 时,枚举值列表,如 ["on","off"]。

示例:温度属性

{
  "name": "温度",
  "identifier": "temp",
  "dataType": "float",
  "unit": "℃",
  "range": [-40, 125]
}

7.2 服务(Service)字段规范

服务描述设备可被调用的动作。每个服务包含:

字段名类型必填说明
namestring服务显示名称,如“打开开关”。
identifierstring服务标识符,APP 下发命令时 cmd 字段的值。建议使用小写+下划线。
callTypestring调用类型,目前仅支持 "async"(异步)。
paramsarray服务参数列表,每个参数包含 name, dataType, description 等。

示例:设置温度阈值的服务

{
  "name": "设置温度阈值",
  "identifier": "set_threshold",
  "callType": "async",
  "params": [
    { "name": "value", "dataType": "float", "description": "目标温度值" }
  ]
}
⚠️ 重要注意事项:
  • 标识符不可重复:同一产品下,所有属性的 identifier 必须唯一,所有服务的 identifier 也必须唯一。
  • 命名规范:标识符只能包含小写字母、数字、下划线,且不能以数字开头。例如:temperature_1 合法,1temp 不合法。
  • 数据类型匹配:设备上报的数据类型应与物模型定义一致。例如定义 float 却上报字符串,会导致图表显示异常或解析错误。
  • 向后兼容:产品一旦发布并投入生产,不要修改已有属性的 identifier 或 dataType,否则旧设备将无法上报或解析。可以新增属性,但不要删除或修改已有字段。
  • 服务参数名称:APP 下发命令时,参数键名应与服务参数定义中的 name 一致。平台不强制校验,但建议遵循以保持统一。
  • 物模型不是数据库约束:平台不会严格校验设备上报是否符合模型,目的是保持灵活性。但 APP 开发时会依赖物模型来解析数据,因此请确保设备上报的字段与模型一致。
  • 枚举型属性:若 dataType 为 enum,建议提供 enumValues 数组,便于 APP 呈现下拉选择框。

7.3 一个完整的物模型示例

下面是一个智能插座产品的物模型,包含两个属性(开关状态、功率)和两个服务(打开开关、关闭开关):

{
  "properties": [
    {
      "name": "开关状态",
      "identifier": "switch_state",
      "dataType": "enum",
      "enumValues": ["on", "off"],
      "description": "当前插座的开/关状态"
    },
    {
      "name": "实时功率",
      "identifier": "power",
      "dataType": "float",
      "unit": "W",
      "range": [0, 2500]
    }
  ],
  "services": [
    {
      "name": "打开插座",
      "identifier": "turn_on",
      "callType": "async",
      "params": []
    },
    {
      "name": "关闭插座",
      "identifier": "turn_off",
      "callType": "async",
      "params": []
    }
  ]
}

7.4 可视化定义物模型

平台 Web 控制台提供了可视化编辑器,您可以直接在界面上添加属性、服务,无需手写 JSON。编辑器会自动校验字段合法性,并生成标准 JSON。

💡 对于复杂设备(如工业控制器带有数十个参数),建议先在本地编辑好 JSON 再粘贴到编辑器中,以提高效率。

8. MQTT 接入指南(设备端)

设备使用 MQTT 协议时,需要连接 Broker(默认 localhost:1883)。主题规范如下:

方向Topic 格式说明
设备发布数据iot/devices/{device_id}/data上报传感器数据,Payload 为 JSON,不需要包含 device_id
设备发布状态iot/devices/{device_id}/statuspayload: online / offline
设备注册验证(可选)iot/devices/{device_id}/registerpayload: {"secret":"xxxx"}
设备接收命令订阅 iot/devices/{device_id}/cmd平台下发的指令
设备回复命令ACKiot/devices/{device_id}/cmd/ack格式: {"command_id":123,"result":"success","message":"..."}

STM32 + ESP8266 (AT指令) 示例

下面展示基于 STM32F103 系列单片机 + ESP8266 模块,通过 AT 指令连接 MQTT Broker 并上报数据:

// 伪代码,实际需根据具体 AT 固件调整
#include "stdio.h"
#include "string.h"

extern void UART_SendString(char *str);
extern int UART_ReceiveLine(char *buf, int maxlen);

#define DEVICE_ID "2058748a9db2"
#define MQTT_HOST "192.168.1.100"
#define MQTT_PORT 1883

void MQTT_Connect(void) {
    char cmd[256];
    // 1. 设置 WiFi 模式并连接 AP(略)
    // 2. 建立 TCP 连接
    sprintf(cmd, "AT+CIPSTART=\"TCP\",\"%s\",%d\r\n", MQTT_HOST, MQTT_PORT);
    UART_SendString(cmd);
    // 等待连接成功...
    
    // 3. 发送 MQTT CONNECT 报文(使用 paho 等库简化,此处仅为示意)
    // 实际应使用 MQTT 协议栈,建议移植 eclipse paho mqtt 库。
}

void PublishData(float temp, float hum) {
    char payload[128];
    char topic[128];
    sprintf(topic, "iot/devices/%s/data", DEVICE_ID);
    sprintf(payload, "{\"temp\":%.1f,\"hum\":%.1f}", temp, hum);
    
    // 使用 MQTT 库发布消息
    // MQTT_Publish(topic, payload);
}

int main(void) {
    MQTT_Connect();
    while(1) {
        float temperature = 23.5; // 实际 ADC 采集
        float humidity = 68.0;
        PublishData(temperature, humidity);
        HAL_Delay(20000); // 20秒上报一次,保持在线
    }
}
📝 注意:完整 MQTT 协议栈建议使用开源库(如 paho-mqtt-embedded-c),上述代码仅体现逻辑流程。

9. TCP 接入指南(设备端)

TCP 服务器地址 your-server-ip:6666,设备建立长连接后发送一行 JSON(必须以换行符 \n 结尾)。

⚠️ 重要:每条 JSON 必须包含 "device_id" 字段,并且末尾一定要有换行符(\n)。服务器会基于大括号自动解析,但推荐携带换行。

STM32 连接 TCP 服务器示例(使用 lwIP 或 AT 指令)

#include "lwip/sockets.h"
#include 
#include 

#define SERVER_IP "192.168.1.100"
#define SERVER_PORT 6666
#define DEVICE_ID "2058748a9db2"

int sockfd;

void TCP_Connect(void) {
    struct sockaddr_in server_addr;
    sockfd = socket(AF_INET, SOCK_STREAM, 0);
    server_addr.sin_family = AF_INET;
    server_addr.sin_port = htons(SERVER_PORT);
    inet_pton(AF_INET, SERVER_IP, &server_addr.sin_addr);
    connect(sockfd, (struct sockaddr*)&server_addr, sizeof(server_addr));
}

void SendData(float temp, float hum) {
    char buf[128];
    snprintf(buf, sizeof(buf), "{\"device_id\":\"%s\",\"temp\":%.1f,\"hum\":%.1f}\n", 
             DEVICE_ID, temp, hum);
    send(sockfd, buf, strlen(buf), 0);
}

void ReceiveCommand(void) {
    char recv_buf[256];
    int len = recv(sockfd, recv_buf, sizeof(recv_buf)-1, 0);
    if(len > 0) {
        recv_buf[len] = '\0';
        // 解析 JSON 得到命令 {"cmd":"turn_on","params":{},"id":123}
        // 执行命令后发送 ACK
        char ack_buf[128];
        snprintf(ack_buf, sizeof(ack_buf), 
                 "{\"type\":\"cmd_ack\",\"command_id\":123,\"result\":\"success\"}\n");
        send(sockfd, ack_buf, strlen(ack_buf), 0);
    }
}

int main() {
    TCP_Connect();
    while(1) {
        SendData(23.5, 68.0);
        HAL_Delay(20000);
        // 非阻塞检查是否有命令
        ReceiveCommand();
    }
}

设备必须维持长连接,并定期上报数据(建议间隔 ≤ 30 秒),否则服务器会判定离线。

10. 超时与离线检测机制

为了保证设备状态准确,平台对 TCP 和 MQTT 设备分别采用以下离线判定策略:

10.1 TCP 设备

  • 空闲超时:服务器记录每个 TCP 连接的最后活动时间(收到任何数据即更新)。如果连续 30 秒 内未收到任何数据,服务器将主动断开连接,并将设备状态置为 offline
  • 主动断开:设备正常关闭连接时,服务器立即更新状态为 offline
💡 提示:设备只需保证上报间隔 ≤ 30 秒即可维持在线。若传感器上报频率较低,可发送空 JSON {"device_id":"xxx"} 作为保活。

10.2 MQTT 设备

  • 后台扫描:平台每隔 30 秒扫描一次数据库,将所有状态为 onlinelast_seen 时间超过 30 秒的设备自动标记为 offline
  • 遗嘱消息(推荐):设备连接 MQTT Broker 时设置 Will 消息,当设备异常断开时 Broker 会自动发布 offline 状态,平台收到后立即更新。

11. APP 接入 (MQTT 方式)

APP 可通过 MQTT 与平台交互,支持登录、获取产品/设备、实时数据查询、历史数据、命令下发。APP 的通信 Topic 规范:

方向TopicPayload 说明
APP 登录iot/apps/{client_id}/login{"username":"xx","password":"xx"}
APP 请求iot/apps/{client_id}/request请求类型: get_products, get_devices, get_realtime, get_history, send_command
平台响应订阅 iot/apps/{client_id}/response服务器返回结果

APP 代码示例(Python + paho-mqtt)

import paho.mqtt.client as mqtt
import json

client_id = "my_app_001"
host = "mqtt.example.com"
port = 1883

def on_message(client, userdata, msg):
    resp = json.loads(msg.payload)
    print(f"Received: {resp}")

client = mqtt.Client(client_id)
client.on_message = on_message
client.connect(host, port)
client.subscribe(f"iot/apps/{client_id}/response")

# 登录
client.publish(f"iot/apps/{client_id}/login", json.dumps({"username":"test","password":"123"}))

# 请求设备实时数据
req = {"request_type":"get_realtime","device_id":"2058748a9db2","request_id":"100"}
client.publish(f"iot/apps/{client_id}/request", json.dumps(req))

client.loop_forever()

12. APP 接入 (TCP 方式)

APP 与服务器建立 TCP 长连接(端口 6666),通过 JSON 格式交互,同样支持登录、查询、命令下发及实时推送。

登录请求

{"type":"app_login","username":"testuser","password":"123456"}

获取产品列表

{"type":"app_request","request_type":"get_products","request_id":"1"}

获取设备历史数据

{"type":"app_request","request_type":"get_history","device_id":"2058748a9db2","start":"2026-06-01T00:00:00","end":"2026-06-08T23:59:59","limit":50,"request_id":"2"}

发送命令

{"type":"app_request","request_type":"send_command","device_id":"2058748a9db2","command":"turn_on","params":{},"request_id":"3"}

所有响应格式:{"type":"app_response","response_type":"...","success":true/false,"data":...,"request_id":"..."}

实时推送格式:{"type":"realtime_data","device_id":"...","data":{...},"timestamp":"..."}

13. 命令下发与响应流程

平台支持 APP → 设备 的同步命令模式,设备执行后应回复 ACK,平台将 ACK 转发给 APP。

  • MQTT 设备:APP 请求后,平台发布到设备命令主题,设备回复 cmd/ack 主题,平台转发给 APP。
  • TCP 设备:APP 请求后,若设备在线,服务器直接通过设备已有的 TCP 连接发送命令,设备回复 {"type":"cmd_ack",...},服务器转发给 APP。

设备端命令处理(STM32 示例)

// 伪代码:在 TCP 接收回调中解析
if (strstr(recv_buf, "\"cmd\":\"turn_on\"")) {
    // 执行开灯操作
    HAL_GPIO_WritePin(LED_GPIO_Port, LED_Pin, GPIO_PIN_SET);
    // 发送 ACK
    char ack[128];
    snprintf(ack, sizeof(ack), "{\"type\":\"cmd_ack\",\"command_id\":%d,\"result\":\"success\"}\n", cmd_id);
    send(sockfd, ack, strlen(ack), 0);
}

14. 实时推送 & 历史数据查询

设备每次上报数据,平台会:

  1. 存储到数据库(iot_device_data 表)供历史查询。
  2. 立即推送给该设备所属用户的所有在线 APP(无论是 MQTT 还是 TCP 连接)。

APP 可通过 get_history 接口分页获取历史数据,支持时间范围筛选。

15. 常见问题

Q1: 设备上报数据后没有收到 ACK?
TCP 方式请确保每条 JSON 末尾带有换行符 \n;MQTT 方式请检查主题是否正确,且设备已在平台注册。
Q2: APP 收不到实时推送?
确认 APP 已经登录成功,并且与设备属于同一个用户。推送只会推送给在线 APP 连接。
Q3: TCP 设备不在线,命令会怎样?
命令会保存为 pending 状态,等待设备下次上线后自动下发(需平台扩展补发机制)。
Q4: 设备离线状态多久能更新?
最长 30 秒内更新为 offline。TCP 空闲超时立即断开,MQTT 后台扫描每 30 秒执行一次。
Q5: STM32 如何解析 JSON?
建议使用轻量级 JSON 库如 cJSON,或手动字符串匹配简单字段。
Q6: 物模型定义错误后能修改吗?
可以,但已创建的设备不会自动同步。建议在产品未投入生产前完成物模型设计。若需修改,请手动更新每个设备的 datapoints。