资讯动态

STM32+华为云IoT+Qt:从传感器到界面的智能家居全链路实战

发布时间:2026/10/20 1:58:23 来源:尧图企业网站定制
做智能家居一年多踩过的坑加起来能写满一本笔记本。如果你手里正好有一块STM32开发板又想把传感器数据真正变成电脑或手机上一个能看、能点的界面那么“STM32华为云IoTQt”这套组合值得你花一个周末完整搭一遍。它不是一个“点灯”式的Demo教程而是一条从硬件采集、云端流转到跨平台客户端的全链路闭环做完之后你就是同时摸过嵌入式、物联网平台和桌面应用三块技术栈的人了。这套方案解决的是大多数DIY智能家居的痛点设备本地采集很容易但一旦涉及“人在外面想远程看一眼家里温度”“躺在床上用手机开关客厅灯”就需要一个稳定、安全、可扩展的中间层。华为云IoT在这里承担的就是这个中间层STM32负责执行和控制Qt负责把数据变成像样的图形界面。适合的读者是有一定STM32基础、想接触真实物联网平台的开发者也适合刚学完C想做点实际项目的Qt初学者。1. 先用整体视角拆一遍这套系统的三层架构与信号流1.1 每一层具体负责什么这套系统按职责可以很干净地切成三层每层只解决一个问题这既是嵌入式项目的基本素养也是后续调试时能快速定位问题的关键。设备层由STM32最小系统板加传感器组成。STM32负责读取温度、湿度、光线、继电器状态等物理量同时根据云端下发的控制指令去操作灯、风扇、插座这类执行设备。选STM32而不是某个Wi-Fi单片机原因很直接它外设丰富ADC、I2C、SPI、PWM全都是现成的资料和例程也最多遇到问题几乎不会被硬件资料卡住。平台层是华为云IoT设备接入服务。设备把数据上报到这个平台平台负责存储、状态管理、命令转发。选择华为云IoT的原因不是“大厂迷信”而是它提供了设备接入、设备影子、规则引擎、消息跟踪一套完整的服务尤其重要的是它原生支持MQTT协议设备端不需要折腾私有SDK一个标准MQTT客户端就能连上来。应用层是Qt开发的桌面客户端负责展示设备列表、实时温度曲线、开关控制界面。Qt在跨平台方面的优势不用多说同一份代码能跑Windows、Linux和macOS这正好对上了“跨平台智能家居控制系统”里“跨平台”三个字。三层之间通过MQTT和HTTPS两种协议串起来。MQTT走设备与平台之间的上下行数据HTTPS走客户端与平台之间的状态查询和命令下发。1.2 为什么是这套组合而不是其他替代方案拆解技术选型时我发现很多人会纠结“能不能换个方案”这里把替代路径也梳理一下。一个常见替代是自建MQTT服务器比如在云服务器上跑一个Mosquitto或者EMQX。自建方案表面上看可控性更强但实际操作起来要处理的琐事非常多服务器安全组配置、Broker高可用、数据持久化、设备证书管理、多租户隔离。对于一个两三个人的业余项目来说这些成本远远超过云平台提供的免费额度。另一个替代是干脆不做云走局域网。设备端跑一个本地MQTT客户端在同一个Wi-Fi下连接。这个方案的优点是快、零费用缺点是离开家就彻底失联而且设备发现、NAT穿透这些问题也会在下一次项目中反复出现。华为云IoT在这两者之间找到了最舒服的位置平台侧把设备接入、鉴权、影子、消息跟踪都做成了现成服务设备端只需要按标准MQTT协议连上来即可客户端则通过完善的REST API和平台交互。对于个人项目来说整个链路里只有你自己写的代码是变量其他都是稳定服务。1.3 一帧数据从传感器到界面按钮的完整旅程为了后面好讲先明确一条完整的数据流。传感器数据采集流程STM32通过ADC或单总线读取传感器数值比如DHT22出来的温湿度原始信号在MCU内部换算成标准单位后拼装成一段JSON。属性上报流程STM32把JSON作为MQTT消息发布到华为云IoT的固定Topic比如$oc/devices/{设备ID}/sys/properties/report。平台收到后校验消息格式匹配产品模型把属性值更新到设备影子中同时触发规则引擎规则。客户端展示流程Qt客户端每隔几秒调用一次平台的查询接口或者订阅影子推送拿到最新的温湿度、开关状态刷新到界面上。命令下发流程用户在Qt界面点击“打开客厅灯”客户端调平台的命令下发接口平台把这个命令打包成MQTT消息推送到设备在线订阅的Topic上。STM32在回调里解析命令拉高继电器对应的GPIO再把执行结果上报回平台。整条链路在正常网络环境下从点击到灯亮大约在一秒以内。这四步串起来就是一套完整的“端-云-端”闭环。下文的所有细节都围绕着怎么让这条链路跑得更加稳定、可维护。2. STM32设备端接云实战MQTT链路接入与上下行数据处理2.1 设备端任务划分别一上来就写裸奔主循环先说说STM32侧怎么组织代码。很多初学者拿到这块板子第一反应是写一个巨大的while循环串行地做“采集、上报、等待”。这么做在只有一个传感器、一个开关的时候没问题但一旦设备数量增加串行轮询会让每个任务的响应时间变得不可控。更合理的做法是采用状态机加定时器的架构。假设一个典型的房间节点STM32的任务可以分成四块采集任务定时500毫秒读一次温湿度传感器和光照ADC数据做简单滤波后存在全局结构体里。状态维护任务维护设备当前的电量、连接状态、固件版本这些元数据这些在平台端也需要展示。MQTT收发任务检查Wi-Fi模块串口缓冲有下行数据就解析是要发送的属性上报就排入发送队列。命令执行任务把收到的开关、占空比、定时指令映射到具体GPIO或PWM输出。设备端的程序设计原则只有一条阻塞操作要少而短。串口发送、Wi-Fi握手这类操作要放进队列异步处理否则一次网络超时就会卡住整个采集流程。如果项目规模再大一些建议直接上FreeRTOS让每个任务有自己的独立栈调度交给系统去处理。2.2 用ESP8266模块接入华为云IoT的AT指令实操硬件连接方面我用的是ESP8266-01S这个经典Wi-Fi模块通过串口和STM32通信。为什么用ESP8266而不是直接上一块带Wi-Fi的MCU因为ESP8266经过多年生态沉淀AT指令集非常成熟STM32这边只需要把MQTT报文封装成串口AT指令发出不用在MCU里搬一整套TCP/IP协议栈开发量小得多。设备端接入华为云IoT的完整步骤核心就是让ESP8266建立起一个MQTT连接。不同固件版本AT指令略有差异我以安信可的MQTT AT固件为例给出一个典型的操作序列。首先让模块进入Station模式并连接家庭路由器ATCWMODE1 ATCWJAP你的Wi-Fi名称,你的Wi-Fi密码等待返回WIFI GOT IP之后配置MQTT连接参数。这些参数由华为云IoT平台的设备接入信息生成核心是ClientId、Username和Password三个值生成规则我会在下一章详细展开这里先在AT指令里调用它们ATMQTTUSERCFG0,1,你的设备ID,你的密码,0,0, ATMQTTCONN0,你的设备接入域名,1883,1ATMQTTCONN的最后一个参数是1代表开启干净会话标志置反也就是说要用持久会话这样设备在意外掉线后重连云端还没确认的QoS1消息可以继续接收后面断线重连部分会提到这个细节。连接建立后发布属性上报消息ATMQTTSUB0,$oc/devices/你的设备ID/sys/commands/#,1 ATMQTTPUB0,$oc/devices/你的设备ID/sys/properties/report,1,0 {services:[{service_id:SmartHome,properties:{temperature:26.5,humidity:60}}]}这里ATMQTTPUB的第三个参数1表示QoS级别设备上报建议至少用QoS1让平台确认收到后再清掉本地重发队列保证数据不丢。实测下来必须提醒一点不同ESP8266固件版本的AT指令集并不完全统一有的版本把MQTT配置指令叫ATMQTTUSERCFG有的是ATMQTTUSER还有的需要先ATMQTTCLEAN清一下旧会话。拿到模块先对照固件版本查指令集别照搬网上的旧命令浪费时间。2.3 属性上报与命令下发JSON格式是双方约定的“翻译词典”华为云IoT平台本身不强绑定二进制协议只要设备接入时选择JSON格式设备端直接用标准JSON上报即可。这省掉了开发编解码插件的环节。但JSON格式不是随手写的它必须和你在平台侧定义的产品模型严格对齐。平台产品模型的组织结构是“产品-服务-属性”三层。比如我定义了一个服务service_id叫SmartHome这个服务下面有temperature、humidity、power_switch三个属性。那么设备上报时要这样写{ services: [ { service_id: SmartHome, properties: { temperature: 26.5, humidity: 60, power_switch: 1 } } ] }属性名、服务ID、数据类型任何一项不匹配平台都会把消息标记为“数据解析失败”。这个问题非常隐蔽因为平台不会返回一个明确的报错给设备它只是安静地把消息丢掉你只能到控制台的消息跟踪里看失败原因。命令下发的方向反过来。用户从Qt界面下发一个开灯指令平台往$oc/devices/{设备ID}/sys/commands/#这个Topic上推送消息格式大概是这样{ object_device_id: 你的设备ID, command_name: SetSwitch, service_id: SmartHome, paras: { power_switch: 1 } }STM32在MQTT回调里拿到这段JSON后用cJSON库解析出paras.power_switch然后操作GPIOcJSON *root cJSON_Parse(payload); cJSON *paras cJSON_GetObjectItem(root, paras); cJSON *sw cJSON_GetObjectItem(paras, power_switch); if (sw sw-valueint 1) { HAL_GPIO_WritePin(LIGHT_GPIO_Port, LIGHT_Pin, GPIO_PIN_SET); } cJSON_Delete(root);执行完命令之后设备还需要向$oc/devices/{设备ID}/sys/commands/response/request_id{请求ID}这个Topic发送一条响应消息平台看到响应后才会把这次命令标记为“已执行”。如果不回响应控制台上永远显示“超时”这是新手排查时很容易忽略的环节。2.4 断线重连与消息补偿智能家居最容易被忽略的可靠性问题家庭Wi-Fi并不稳定路由器重启、微波炉干扰、运营商波动都会让ESP8266掉线。设备端必须有一套完整的断线重连机制否则你回家后发现灯控失效体验会非常糟糕。我的做法分三层。第一层是心跳保活MQTT协议自带心跳机制ESP8266固件会按设定周期发送PINGREQ华为云平台默认的心跳周期上限大约是90秒左右设备端设置30到60秒比较稳妥。第二层是断线重连。ESP8266掉线后STM32的串口会收到WIFI DISCONNECT提示此时主控要进入一个重连状态机先重启Wi-Fi连接再重建MQTT会话。重连时使用持久会话cleanSession0这样平台会缓存离线期间下发的指令设备恢复上线后能立刻收到累积的命令。第三层是消息补偿。QoS1的确认机制是平台收到消息后回复PUBACK。如果在串口层没收到PUBACK确认要把这条消息继续留在重发队列里下次上线重新发布。STM32内存有限重发队列配合环形缓冲区容量大小按消息数量和业务重要性来定比如温度上报最多缓存最近20条就够了开关状态这种“最终一致”的数据直接以最新值为准不需要全部重放。3. 华为云IoT平台的建模规则与数据流转设计3.1 产品模型定义表单里的一小步系统联调的一大步在华为云IoT控制台创建一个产品时看起来只是填几个表单但实际上这步决定了后面所有代码的字段名和数据类型。我把这步称为“系统级接口约定”因为它本质上是设备端和云端共享的一份数据结构字典。产品模型包含三个层级。产品是最顶层对应一类设备比如“智能家庭网关”。产品下面可以建多个服务每个服务是业务功能的集合我习惯用一个服务管环境感知温湿度、光照用另一个服务管执行控制开关、亮度。每个服务下面定义属性和命令属性是设备上报的状态值比如温度、湿度、开关状态需要指定数据类型和读写权限。命令是平台下发给设备的操作指令需要定义输入参数。比如SetSwitch命令带一个power_switch整型参数取值范围0和1。有一个细节值得特别留意平台属性定义的数据类型有int、decimal、string、datetime几种如果你的温湿度采集精度到小数点后一位就要用decimal而不是int。设备上报JSON里的数值格式也必须和类型严格匹配decimal对应数字字面量不能用字符串类型的26.5。另一个我在实作中反复踩的坑属性名称大小写敏感。产品模型里定义了temperature设备上报写成了Temperature平台数据解析那一栏就亮红灯但设备端不加日志根本察觉不到。建议产品模型定义完成后把每个属性名复制出来作为常量放到STM32代码的头文件里杜绝手输拼写错误。3.2 设备注册与MQTT连接参数生成一个容易栽跟头的细节设备接入华为云IoT最关键的是生成三个MQTT连接参数ClientId、Username、Password。很多人在这上面卡了两三天其实规则很简单问题往往出在一个毫秒级时间戳。先去控制台注册设备会拿到设备ID和密钥。设备ID形如设备所在产品ID_设备节点ID密钥是平台生成的一串字符串这两个值要妥善保管泄露等于别人可以冒充你的设备。连接参数生成规则ClientId格式为设备ID_0_0_时间戳其中时间戳是当前时间的毫秒值。Username就是设备ID本身。Password是用设备密钥对时间戳做HMAC-SHA256计算后得到的十六进制字符串而不是密钥本身。给你一个Python脚本方便在联调时快速生成这三个值import hmac import hashlib import time device_id 你的设备ID secret 你的设备密钥 timestamp str(int(time.time() * 1000)) client_id f{device_id}_0_0_{timestamp} username device_id password hmac.new(secret.encode(), timestamp.encode(), hashlib.sha256).hexdigest() print(ClientId:, client_id) print(Username:, username) print(Password:, password)生成Password时用的时间戳必须和ClientId里拼接的时间戳是同一个值。如果你先用time.time()拼了ClientId过了几秒又用新时间戳算密码服务端校验时发现两边时间戳不一致直接拒绝连接。3.3 设备影子与控制台消息跟踪让数据流“看得见”设备影子是华为云IoT里非常有价值的一个功能理解它对客户端开发帮助很大。影子是平台为每个设备维护的一个JSON状态文档包含reported设备最近一次上报的属性和desired平台期望设备达到的状态两部分。设备每次上报属性平台自动更新影子里的reported字段。客户端查询设备状态时直接查影子就能拿到最新的温湿度、开关状态不用等设备在线。命令下发时如果设备离线命令会进入desired字段缓存等设备上线后影子同步机制会把期望值推送给设备这是解决“离线控制”问题的标准手段。开发调试阶段控制台里的“消息跟踪”功能是最重要的观测窗口。它能看到设备上行消息是否被平台收到、数据结构是否正确、命令是否下发成功、失败原因是什么。我调试时始终开着两个屏幕一边是消息跟踪页面一边是串口日志两边对着看问题的定位速度能快出好几倍。3.4 数据流转设备数据除了进影子还能往哪里走如果项目需要历史数据展示或者告警通知可以配置平台的规则引擎。规则引擎的作用是监听设备上报的特定Topic然后把消息转发到其他服务比如注入Kafka这类消息队列或者转发给函数计算做处理。对这个项目来说基础版本用影子加轮询就够了。但如果后面做“温度过高自动推送告警”这类功能就需要让规则引擎把设备数据转到一个能触发主动通知的地方。规则引擎的配置界面是可视化表单核心就两步选择数据来源Topic选择转发目标。需要留意转发目标的数据格式兼容性比如转发到函数计算时函数的入参需要按平台的JSON消息格式来解析。4. Qt客户端开发桌面侧对接云端的方案选择与代码实现4.1 项目创建与模块取舍Widgets还是QuickQt客户端这块我先说结论如果你主要目标是快速做一个功能完整的管理桌面端就选Qt Widgets如果你更关注触摸交互、动画流畅度再考虑Qt Quick/QML。Widgets在桌面应用上依然非常能打成熟的控件、稳定的布局系统、丰富的第三方示例大部分智能家居的管理界面用Widgets完全够用。项目创建时建议选择Qt 5.15 LTS或Qt 6.5以上的版本模块勾选Network、Charts、Core、Gui、Widgets这几个就够。Charts模块在绘制温湿度曲线时很实用如果你不想引入额外依赖自绘折线图也是可以接受的方案。有一点容易忽略编译器套件选择。Windows上用MinGW 64-bit还是MSVC会影响后续二进制兼容性建议一次性选好。如果以后要打包给其他人用MSVC配合Windeployqt在Windows上会省不少事。4.2 客户端与云端的对接方式两种方案都要知道Qt客户端怎么拿设备数据业内有两种主流做法我建议根据项目阶段选择。第一种方案是客户端直接调用华为云IoT的REST API。这个方案看起来最直接但实际上有一个很大的坎调用平台API需要IAM认证而IAM认证需要访问密钥AK/SK或者用户名密码换取Token。直接把AK/SK写进客户端程序里意味着任何拿到你程序的人都能拿到你的云资源权限这是非常危险的做法。第二种方案是自建一个轻量后端做凭证中转。让Qt客户端只跟自建后端交互由后端持有AK/SK帮助客户端完成云平台API的调用、Token缓存等逻辑。这个方案多了一个后端进程但对DIY项目来说用Python Flask或Node.js写一个几十行的代理服务成本非常低安全性却提升了一个量级。我的实际选择是第二种。自建后端开放几个简单接口比如/api/devices返回设备列表/api/devices/{id}/shadow返回设备影子/api/devices/{id}/command下发命令。Qt客户端只处理界面展示和定时轮询。如果你的需求只是“远端看一眼家里状态”还有一个更轻量的方案直接用MQTT让Qt客户端扮演一个合法设备。用设备级凭证连上平台订阅设备属性Topic这样甚至不需要自建后端。但这样做的缺点是客户端和设备是平级角色没法管理系统里的多个设备。权衡之后做多设备管理还是走后端转发更合理。4.3 核心接口与代码骨架Qt侧怎么拉数据和发指令下面给出一段Qt客户端通过自建后端拉取设备列表的核心代码。使用QNetworkAccessManager发起GET请求拿到JSON后解析成设备对象列表void DeviceManager::fetchDevices() { QNetworkAccessManager* manager new QNetworkAccessManager(this); QNetworkRequest request; request.setUrl(QUrl(http://127.0.0.1:8000/api/devices)); request.setHeader(QNetworkRequest::ContentTypeHeader, application/json); QNetworkReply* reply manager-get(request); connect(reply, QNetworkReply::finished, this, []() { if (reply-error() QNetworkReply::NoError) { QByteArray data reply-readAll(); QJsonDocument doc QJsonDocument::fromJson(data); QJsonArray arr doc.array(); for (const auto item : arr) { QJsonObject obj item.toObject(); Device dev; dev.id obj[device_id].toString(); dev.name obj[device_name].toString(); dev.online obj[online].toBool(); m_devices.append(dev); } emit devicesFetched(); } else { qWarning() Failed to fetch devices: reply-errorString(); } reply-deleteLater(); }); }下发命令的代码类似换成POST请求把命令参数放进JSON的body里QJsonObject cmdObj; cmdObj[command_name] SetSwitch; cmdObj[service_id] SmartHome; QJsonObject paras; paras[power_switch] 1; cmdObj[paras] paras; QJsonDocument doc(cmdObj); QNetworkRequest request; request.setUrl(QUrl(http://127.0.0.1:8000/api/devices/ deviceId /command)); request.setHeader(QNetworkRequest::ContentTypeHeader, application/json); QNetworkReply* reply manager-post(request, doc.toJson());实测下来用Qt的QNetworkAccessManager做这套HTTP交互非常顺手信号槽机制天然适合异步网络请求不需要额外处理线程问题。唯一要注意的是如果请求频繁注意及时deleteLater释放reply对象否则长时间运行的内存占用会慢慢涨上去。4.4 界面刷新策略与交互细节轮询、曲线图和状态同步Qt界面侧的刷新策略我用的是一个QTimer定时器每3秒调用一次影子查询接口。3秒这个间隔是个不错的中庸值太快会频繁打后端接口太慢会感觉开关响应迟钝。如果你需要更实时的体验可以改为长轮询或者WebSocket推送但DIY项目用定时轮询已经足够。状态同步有一个很容易踩的坑Qt界面上的开关状态和设备实际状态可能不一致。用户在界面点了“开”后端命令下发成功了但设备离线执行不了界面上不能想当然地立刻把开关置为开。我的做法是界面上的每个控件维护两个状态一个是“用户意图状态”一个是“设备确认状态”。命令下发后先显示一个“指令已发送”的挂起状态等下一次影子查询发现power_switch真的变成1了再切到“已开启”。这个细节决定了你的界面是“花架子”还是“真正能用的控制系统”。温湿度曲线的绘制用Qt Charts自带的折线图就能搞定。把每次轮询到的温度和湿度值追加进时间序列横轴用时间戳纵轴用数值范围。要注意Qt Charts的QDateTimeAxis对时间戳格式有要求直接用QDateTime::currentMSecsSinceEpoch()作为x轴数值再用setFormat(hh:mm:ss)格式化显示是最常见也最稳的写法。5. 全链路调试实录五类高频故障的定位思路与解决方法5.1 设备始终连不上平台从连接日志反推MQTT参数问题第一个高频故障是ESP8266串口日志里反复出现CONNECTION REFUSED或MQTT CONN FAIL。正常情况下先从三个参数排查。第一看ClientId是否包含了_0_0_这个固定分段。很多人把这个分段漏掉或者把下划线写成了中划线服务端直接拒绝。第二看时间戳是否与密码生成时使用的时间戳一致。我有一个习惯生成连接参数后用MQTT.fx桌面工具先手动测试一次连上再移到设备端。第三步看域名和端口1883是明文端口如果网络环境特殊需要用8883加密端口ESP8266固件还要额外配置证书复杂度会明显上升。这里分享一个适用于任何场景的排查顺序先从PC上用通用MQTT客户端验证“参数本身是否正确”再从设备端打印串口日志看“AT指令过程是否报错”。两步就能把问题从“参数问题”和“设备执行问题”里快速分开。5.2 设备上报成功但平台影子不更新数据解析失败这类故障最迷惑人的地方在于MQTT层看起来一切正常消息跟踪里也能看到消息到达平台但影子里的数据就是没变。排查突破口是打开平台控制台的消息跟踪查看这一条消息的详细信息。大多数情况下原因出在消息内容无法匹配产品模型定义比如上报JSON里的服务ID写错、属性名大小写不一致、数据类型不是平台定义的类型。我在做温度采集时遇到过一种很隐蔽的情况平台属性定义为decimal而设备上报了26整型平台同样会判定解析失败。C语言里float转int再拼JSON时尤其容易触发这种问题拼JSON前先强制转成浮点格式再输出字符串。5.3 命令下发总是超时订阅Topic和响应机制命令下发超时是客户端开发阶段最打击信心的问题界面点了一下开关控制台显示“超时”但设备端明明收到了数据。先确认一件事情设备端是否订阅了正确的Topic。华为云平台的命令下发消息推送到$oc/devices/{设备ID}/sys/commands/#注意设备ID必须和ClientId里的一致。很多人在代码里把Topic写死成测试时的设备ID换了新设备忘了改结果所有命令都发到了旧设备的Topic上。再确认设备收到命令后有没有回响应。不回响应的设备平台会一直等到超时时间结束。有经验的开发者拿到命令后第一件事就是解析request_id然后往命令响应Topic发一条空响应这样控制台立刻就能看到“成功”。5.4 我的调试三件套MQTT.fx、消息跟踪、串口日志最后分享一下我的调试方法和工具搭配这套“三件套”组合帮我节省了至少几十个小时的排查时间。第一件是MQTT.fx或者MQTTX这类桌面MQTT调试工具。它能用同一个设备ID连接平台模拟设备上报、订阅命令Topic用来验证参数正确性和Topic格式非常高效。任何“设备端连不上”的问题我第一反应是用它复现一遍。第二件是华为云IoT控制台自带的消息跟踪。这是信息量最大的地方每次联调都打开能看到每一条消息的流转状态、失败原因、耗时是整个系统里唯一能同时观察上行和下行的视窗。第三件是串口日志。STM32端所有AT指令回显、MQTT回调内容都要打印出来建议增加一个简易的日志模块支持开关分级。联调时开最高级别跑稳定后把它关掉避免日志拖慢主循环。这三件工具分别覆盖“外部模拟”“云端观测”“设备观察”三个层面组合在一起几乎任何问题都能在两轮排查内定位到具体是哪一个环节。回看这个项目从STM32固件编写到华为云IoT平台配置再到Qt客户端的落地每一个环节都算不上难但串起来之后它会逼你把嵌入式、网络协议、云端服务、桌面GUI的知识全部揉在一起。我个人的建议是按“先模拟后实物”的顺序推进先用MQTT.fx模拟设备把云端和Qt端调通再跑真机最后再处理界面美化这样每一层的验证成本都最低。等这套链路稳定了后面你想加语音控制、人脸识别、数据大屏都只是在这个框架上添砖加瓦的事。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑