金橙智能硬件平台 · 开发使用手册
版本 v2.0 | 最后更新:2026-06-08 | 支持 MQTT / TCP 双协议,提供设备上云、APP 控制、实时推送全链路方案。
1. 平台概述
金橙智能硬件平台是一个全栈物联网平台,提供设备接入、数据存储、反向控制及 APP 交互能力。核心特性:
- ✅ 多协议支持:MQTT(标准物联网协议)和 TCP 长连接(轻量嵌入式设备)
- ✅ 灵活的产品物模型:自定义属性(温度、湿度等)和服务(开关、调节)
- ✅ 设备生命周期管理:在线/离线状态、最后活跃时间、数据点配置
- ✅ 双向通信:APP 可实时获取设备数据、下发控制指令,设备回复命令执行结果
- ✅ 实时推送:设备上报数据后立即推送给绑定的 APP 客户端(无需轮询)
- ✅ 统一账户:Web 控制台、APP 端(MQTT/TCP)共享用户体系
- ✅ 自动离线检测:TCP 空闲超时(30秒) + MQTT 后台扫描,确保状态准确
2. 核心概念:产品 · 设备 · 物模型
在使用平台之前,必须理解产品、设备和物模型之间的关系。这是整个平台数据建模和通信的基础。
2.1 产品(Product)
产品是设备的“抽象类型”或“模板”。它定义了同一类设备的共同特征:通信协议(MQTT/TCP)、物模型(属性与服务)、以及产品的描述信息。例如,“智能温湿度传感器”是一个产品,所有该型号的传感器都继承这个产品的定义。
- 一个产品可以包含多个设备。
- 产品创建后,可以修改物模型,但已创建的设备不会自动同步变化(平台当前不会自动更新,需要手动更新或重新创建设备)。
- 产品关联一个用户(创建者)。
2.2 设备(Device)
设备是具体物理设备的实例。每个设备属于一个产品,并继承产品的协议和物模型。设备拥有唯一的 device_id 和 secret(MQTT 注册验证用)。设备可以上报数据、接收命令、更新状态等。
- 设备的上报数据格式应与产品的物模型属性相匹配,但不是强制严格校验(平台目前不校验数据类型,建议开发者自行保证)。
- 设备可以有自己的“数据点”(datapoints)覆盖或扩展产品的物模型,用于特殊场景。
- 设备的状态(online/offline)由平台根据心跳或上报自动维护。
2.3 物模型(Thing Model)
物模型是产品能力的标准化描述,包含两部分:属性(Properties) 和 服务(Services)。属性是设备可上报的监测数据(如温度、湿度);服务是设备可被调用的动作(如打开开关、调整档位)。
├─ 通信协议: 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_id 和 secret(用于 MQTT 注册验证)。TCP 设备只需 device_id。
3.4 设备接入示例
参考第8、9章中的 STM32 代码示例,编写设备固件,连接平台后即可上报数据。
4. 用户管理
用户通过 Web 端注册,同一账号可用于 Web 控制台和 APP(MQTT/TCP 方式)。
支持邮箱/手机号找回密码,设备数量限制按套餐动态调整。
5. 产品管理
产品是设备的分类模板,创建产品时需要填写:
- 产品名称:如“智能插座”。
- 通信协议:MQTT 或 TCP。
- 物模型:通过可视化编辑器或 JSON 编辑,定义属性和服务(详见第7章)。
- 描述:可选。
产品创建后,可以在产品列表中进行编辑或删除。删除产品会同时删除其下所有设备(及设备数据),请谨慎操作。
6. 设备管理
每个设备包含字段:device_id、secret、状态(online/offline)、最后上线时间。设备密钥仅用于 MQTT 注册验证,TCP 设备不需要 secret 验证(但必须已存在且属于当前用户)。
设备详情页可查看最新数据、历史数据图表、通信示例及下发指令。
7. 物模型详细定义与注意事项
物模型是平台的核心,它决定了设备上报什么数据、APP 下发什么命令。本节提供完整的定义规范、字段说明和最佳实践。
7.1 属性(Property)字段规范
属性描述设备上报的监测数据。每个属性包含以下字段:
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 属性显示名称,如“温度”。 |
| identifier | string | 是 | 属性标识符,设备上报时的 JSON 键名。建议使用英文小写+下划线。 |
| dataType | string | 是 | 数据类型:float, int, string, bool, enum。 |
| unit | string | 否 | 单位,如“℃”、“%”。 |
| range | array | 否 | 数值范围,如 [0,100]。 |
| step | float | 否 | 步长,用于数值调节控件。 |
| enumValues | array | 否 | 当 dataType 为 enum 时,枚举值列表,如 ["on","off"]。 |
示例:温度属性
{
"name": "温度",
"identifier": "temp",
"dataType": "float",
"unit": "℃",
"range": [-40, 125]
}
7.2 服务(Service)字段规范
服务描述设备可被调用的动作。每个服务包含:
| 字段名 | 类型 | 必填 | 说明 |
|---|---|---|---|
| name | string | 是 | 服务显示名称,如“打开开关”。 |
| identifier | string | 是 | 服务标识符,APP 下发命令时 cmd 字段的值。建议使用小写+下划线。 |
| callType | string | 是 | 调用类型,目前仅支持 "async"(异步)。 |
| params | array | 否 | 服务参数列表,每个参数包含 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。
8. MQTT 接入指南(设备端)
设备使用 MQTT 协议时,需要连接 Broker(默认 localhost:1883)。主题规范如下:
| 方向 | Topic 格式 | 说明 |
|---|---|---|
| 设备发布数据 | iot/devices/{device_id}/data | 上报传感器数据,Payload 为 JSON,不需要包含 device_id |
| 设备发布状态 | iot/devices/{device_id}/status | payload: online / offline |
| 设备注册验证(可选) | iot/devices/{device_id}/register | payload: {"secret":"xxxx"} |
| 设备接收命令 | 订阅 iot/devices/{device_id}/cmd | 平台下发的指令 |
| 设备回复命令ACK | iot/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秒上报一次,保持在线
}
}
9. TCP 接入指南(设备端)
TCP 服务器地址 your-server-ip:6666,设备建立长连接后发送一行 JSON(必须以换行符 \n 结尾)。
"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。
{"device_id":"xxx"} 作为保活。10.2 MQTT 设备
- 后台扫描:平台每隔 30 秒扫描一次数据库,将所有状态为
online且last_seen时间超过 30 秒的设备自动标记为offline。 - 遗嘱消息(推荐):设备连接 MQTT Broker 时设置 Will 消息,当设备异常断开时 Broker 会自动发布
offline状态,平台收到后立即更新。
11. APP 接入 (MQTT 方式)
APP 可通过 MQTT 与平台交互,支持登录、获取产品/设备、实时数据查询、历史数据、命令下发。APP 的通信 Topic 规范:
| 方向 | Topic | Payload 说明 |
|---|---|---|
| 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. 实时推送 & 历史数据查询
设备每次上报数据,平台会:
- 存储到数据库(
iot_device_data表)供历史查询。 - 立即推送给该设备所属用户的所有在线 APP(无论是 MQTT 还是 TCP 连接)。
APP 可通过 get_history 接口分页获取历史数据,支持时间范围筛选。
15. 常见问题
TCP 方式请确保每条 JSON 末尾带有换行符
\n;MQTT 方式请检查主题是否正确,且设备已在平台注册。
确认 APP 已经登录成功,并且与设备属于同一个用户。推送只会推送给在线 APP 连接。
命令会保存为 pending 状态,等待设备下次上线后自动下发(需平台扩展补发机制)。
最长 30 秒内更新为 offline。TCP 空闲超时立即断开,MQTT 后台扫描每 30 秒执行一次。
建议使用轻量级 JSON 库如
cJSON,或手动字符串匹配简单字段。
可以,但已创建的设备不会自动同步。建议在产品未投入生产前完成物模型设计。若需修改,请手动更新每个设备的 datapoints。