尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

STM32F4+阿里云MQTT:嵌入式设备上云全链路实战解析

STM32F4+阿里云MQTT:嵌入式设备上云全链路实战解析 简介本资源是一套面向嵌入式物联网开发者的完整实践项目聚焦STM32F4微控制器与阿里云IoT平台的MQTT双向通信实现适用于具备C语言基础与Keil开发经验的中级以上学习者可支撑智能家居、工业传感、远程监控等典型IoT场景的原型开发与教学实践。压缩包共376个文件含170个C源码如ff.c、stm32f4xx_tim.c、mib2.c等驱动与协议栈实现、161个头文件定义外设寄存器、MQTT结构体及阿里云认证参数、14张PNG图表含系统架构与流程图、8个TXT说明文档及2份README含编译配置与接入指南整体3.24MB结构清晰、模块解耦便于按功能网络层/应用层/云对接快速定位。已有111人下载学习提供从STM32裸机MQTT客户端构建、TLS安全连接、阿里云三元组认证、Topic订阅发布到设备状态同步的全链路源码含Keil工程uvprojx、启动汇编os_cpu_a.asm、中文字库cc936.c等及固件烧录脚本bat开箱即用是深入理解嵌入式端云协同机制的优质参考范例。 每年都有不少做嵌入式、物联网方向的朋友卡在同一个地方开发板上跑通了点灯、跑通了传感器采集但一旦要把数据发到云端就一头雾水。MQTT协议看了不少、阿里云控制台也注册了真到自己动手写代码的时候要么连不上、要么掉线要么数据死活不上报。这个STM32F4阿里云物联网平台的MQTT项目正是解决这个阶段最典型的困惑。这套项目源码我实际跑过基础板型是STM32F407系列网络侧用ESP8266模块做透传平台上对接阿里云物联网平台协议走的是MQTT。它不只是一个能连上的Demo而是把设备端上云全链路打通了设备接入、三元组签名、属性上报、云端指令下发、数据解析、保活机制全都包含。适合正在学嵌入式、准备做物联网课程设计或者刚进公司需要上手IoT产品的工程师参考。代码底子干净迁移到自己板子上也比想象中容易。1. 项目整体拆解为什么是STM32F4阿里云MQTT这套组合1.1 这套组合到底解决了什么问题先说说我拿到这个源码时第一时间的判断。很多入门级物联网项目喜欢用ESP8266直接跑因为便宜、简单但说句实话ESP8266的MCU资源太紧张跑复杂业务逻辑容易捉襟见肘。用STM32F4做主控就不一样了它是Cortex-M4内核主频能到168MHzFlash和RAM都够大跑RTOS、跑协议栈、做本地数据处理都有余量。更重要的是STM32F4几乎是国内嵌入式学习的主流平台资料多、例程全遇到问题随便搜都有答案。拿它当主控相当于站在一个非常成熟的生态上。阿里云物联网平台在这套方案里承担的是设备接入与消息路由的角色。它不只是一个MQTT Broker还提供了设备管理、物模型、规则引擎、数据可视化这些能力。比如你在平台上创建了一个产品定义好温度和湿度两个属性设备端只要按规定的JSON格式上报平台就能自动解析出数据不用你自己写后端服务去存数据、画图表。这点对学习项目特别友好——你不需要懂服务器开发也能看到数据上云后的完整形态。MQTT协议则是连接设备和平台的桥梁。选它而不是HTTP、TCP长连接核心原因就是它够轻、够省、适合物联网场景。HTTP是请求应答模式设备要主动去请求云端的指令难以及时推给设备MQTT是发布订阅模式设备订阅了某个主题云端往这个主题发消息设备马上就能收到真正做到双向实时通信。再加上MQTT报文头很小在2G、4G、WiFi这些网络条件下都稳定所以现在物联网行业里MQTT基本成了标配协议。1.2 方案选型时的几个关键考量说实话做这个项目之前我也犹豫过几轮要不要用CoAP要不要用HTTPS后来实测对比下来MQTT还是最适合传感器数据上报远程控制这种典型场景。我给你列个对比表方便你理解当时的取舍协议传输方式实时性报文开销适用场景MQTTTCP长连接发布订阅高云端可主动下发小固定头仅2字节设备状态上报、远程控制、消息推送HTTP/HTTPS短连接请求应答低需设备轮询大Header冗长固件包下载、文件上传、低频数据上报CoAPUDP类HTTP请求应答中小低功耗、NB-IoT等受限网络环境选MQTT还有一个现实原因就是阿里云物联网平台原生支持标准MQTT接入。你不需要自己搭服务器平台直接给出接入域名和端口还提供了Topic定义、设备证书、物模型整套机制拿来就能用。如果自建EMQX Broker虽然自由度更高但部署运维、安全防护这些都是额外成本学习项目阶段没必要给自己加这个负担。至于ESP8266做网络模块而不是用板载以太网或者4G模块主要考虑三个点成本低一个ESP8266模块几块钱功耗可控待机时可以让它睡眠最关键的是它和STM32之间的连接特别简单串口透传就行STM32只需把MQTT报文通过串口发给ESP8266ESP8266负责TCP连接和网络收发。这种分工让代码逻辑很清晰——串口驱动、网络模块、MQTT协议、业务逻辑各管各的排错方便后期换4G模块只需要改网络模块的驱动层。2. MQTT协议到底在说什么从报文到阿里云的握手细节2.1 一句话讲清发布订阅模型MQTT协议最核心的思想就是发布订阅你可以把它类比成微信群有人发消息群里所有人都能看到但发消息的人不需要认识每个人接收的人也不用时刻盯着发消息的人。MQTT里有个角色叫Broker消息代理服务器阿里云物联网平台就扮演这个角色设备既可以当Publisher发布者往某个Topic主题发消息也可以当Subscriber订阅者去监听某个Topic收到消息后立即处理。这里有个很重要的点设备之间不会直接通信所有消息都要经过Broker转发。所以设备根本不需要知道对方是谁只需要关心往哪个Topic发和订哪个Topic。阿里云就是围绕Topic来做权限管理的每个设备只能往自己权限范围内的Topic发布或订阅消息换成生活场景来说就是我说的话只会推送到关心这个主题的人别人进不来。2.2 连接阿里云时的三个关键报文我第一次调阿里云MQTT时以为只要TCP连上就完事了结果发了一堆数据上去平台什么反应都没有。后来才明白MQTT在TCP之上还有一套完整的握手机制连接阿里云时至少要处理三种报文CONNECT连接请求、CONNACK连接确认、PINGREQ/PINGRESP心跳保活。CONNECT报文是最关键的因为阿里云要求把设备身份凭证放到这个报文的三个字段里。我记得我当时拿到的三元组是ProductKey、DeviceName、DeviceSecretProductKey是产品唯一标识DeviceName是设备名DeviceSecret是设备密钥。在阿里云的接入规则里MQTT连接的clientId要拼接成deviceName|securemode3,signmethodhmacsha1,timestampxxx这种格式username是deviceNameproductKey而password则是用deviceSecret对一段字符串做HMAC-SHA1签名后Base64的结果。这套规则如果自己从头折腾光是签名算法就能卡你好几天。好在官方文档里有明确说明代码里照着实现就行。核心代码思路差不多是这样// 先从网络获取当前UTC时间戳毫秒级用于签名和clientId unsigned long timestamp get_utc_timestamp(); // 拼接clientId注意格式中的竖线和参数 char clientId[128]; sprintf(clientId, %s|securemode3,signmethodhmacsha1,timestamp%lu, deviceName, timestamp); // 拼接username char username[64]; sprintf(username, %s%s, deviceName, productKey); // 签名原文四个值按顺序直接拼接不要加字段名 char content[256]; sprintf(content, %s%s%s%lu, clientId, deviceName, productKey, timestamp); // 使用deviceSecret作为密钥做HMAC-SHA1再Base64编码 char password[64]; hmac_sha1_base64((unsigned char*)deviceSecret, strlen(deviceSecret), (unsigned char*)content, strlen(content), password);这个签名规则我踩过坑当时照着网上一份老教程写结果一直返回设备认证失败。后来逐字节对才发现签名原文的拼接顺序有讲究而且不能把clientId这种字段名拼进去。如果你也遇到类似问题先别怀疑加密库直接去阿里云物联网平台的官方文档里看最新的签名示例对照着改。2.3 QoS等级怎么选才不掉消息又不浪费流量MQTT定义了三个消息服务质量等级QoS 0最多发一次可能丢消息QoS 1保证至少一次可能重复QoS 2保证恰好一次但开销最大、握手最复杂。阿里云物联网平台支持QoS 0和QoS 1不支持QoS 2。实际项目里怎么选我的经验是上行数据传感器上报用QoS 0就够了因为精度传感器本身就有噪声偶尔丢一帧不影响整体趋势下行指令远程开关灯建议用QoS 1因为控制指令丢了就出大事了宁可重复收到也要保证送达。QoS 1的重复问题也不要太担心应用层靠消息里的id字段做去重处理就行。我们后面讲代码的时候会看到阿里云的JSON报文里都带了一个id字段这个id就能用来判断是不是重复消息。3. 阿里云物联网平台配置十分钟搞定设备侧接入3.1 从零创建一个产品和设备这个源码拿到手之后你第一件要做的事不是在STM32上编译而是先去阿里云物联网平台创建一个自己的产品。因为代码里写死的三元组肯定是原作者申请的直接连你是连不上的。登录阿里云物联网平台控制台在产品列表页面点击创建产品。这时候有几个选项要注意产品名称随便填比如STM32_MQTT_Demo所属品类选自定义品类这样属性、服务、事件都能自己定义灵活性最高节点类型选直连设备因为你的STM32就是最终端设备不需要通过网关接入。认证方式选设备密钥因为这对嵌入式开发最友好不需要申请证书。创建完之后再在产品下添加一个设备会自动生成DeviceName其实你可以自己改成dev01这种好记的名字DeviceSecret是自动生成的点开设备详情就能看到。创建完成后把ProductKey、DeviceName、DeviceSecret这三个值记下来后面代码里都要用。这里有个小技巧你在本地新建一个头文件比如aliyun_config.h把三元组用宏定义放好方便后面维护。3.2 Topic与物模型设计平台侧的另一块关键配置是物模型和Topic。物模型就是把设备的物理世界抽象成平台能理解的数据模型包含属性Property、服务Service、事件Event。我建议你在产品详情页的功能定义里先添加两个属性温度和湿度。温度属性标识符可以叫Temperature数据类型选float单位写℃湿度叫Humidityfloat类型单位写%。为什么要把物模型定义好因为阿里云对上报数据有固定的JSON格式要求属性上报的Topic格式是/sys/{productKey}/{deviceName}/thing/event/property/post上报的payload必须是这样{ id: 123, version: 1.0, method: thing.event.property.post, params: { Temperature: 25.5, Humidity: 60.2 } }当你想从云端给设备下发指令比如远程控制LED开关订阅的Topic就是/sys/{productKey}/{deviceName}/thing/service/property/set这个Topic收到设备文件的消息payload大概是{ id: 456, version: 1.0, method: thing.service.property.set, params: { LedSwitch: 1 } }所以你在平台功能定义里添加的属性名identifier必须和代码里JSON的key保持一致否则平台解析不了。这是初学者最容易忽略的地方明明报文发出去了平台日志里却显示数据格式错误或者属性不存在。3.3 上线前的平台侧检查清单如果你是用别人的源码跑自己的三元组有几个检查点必须过一遍。每次我帮人排查连不上平台的问题90%都出在这几处检查ProductKey、DeviceName、DeviceSecret有没有粘贴错尤其是DeviceSecret中间不能有空格检查产品是否开启了公版签名或一机一密的正确模式这里认证方式要和代码里的signmethod对应检查MQTT接入域名是不是你产品所在Region对应的地址上海地区是${productKey}.iot-as-mqtt.cn-shanghai.aliyuncs.com其他Region后缀不一样检查设备状态是不是未激活新设备首次连上后才会变成在线检查Topic的权限产品详情页里Topic类列表要确认你代码里用到的Topic权限是发布或者订阅别是发布和订阅的类目却连权限没开检查物模型属性和代码里JSON的字段名是否一致这些检查做完平台侧基本就稳了。再往下就是设备端的硬功夫。4. STM32F4端实现从硬件接线到云端数据4.1 硬件清单与接线说明硬件上这次我用的是STM32F407ZGT6核心板外接一个ESP8266-01S模块做WiFi透传一个DHT11温湿度传感器外加一个按键和一个LED灯做本地交互。整套接线不算复杂但有几个地方特别容易翻车我先把接线表给你STM32F407引脚外设模块引脚说明USART2_TX (PA2)ESP8266 RXD串口发送到WiFi模块USART2_RX (PA3)ESP8266 TXD串口接收WiFi模块数据GNDESP8266 GND必须共地3.3VESP8266 VCC注意ESP8266对供电要求PB0DHT11 DATA单总线数据引脚PF9LED正极串联电阻云端控制的LEDPE0按键一端另一端接GND手动控制上报注意ESP8266-01S的供电很多人用3.3V直接供电结果发现模块重启或者连接不上因为ESP8266发射瞬间电流能到几百毫安普通的LDO容易压降。我这边的做法是给ESP8266单独用AMS1117-3.3稳压供电输入5V输出端并两个104电容和100uF电解电容实测稳定很多。如果你想省事直接买带稳压的ESP8266模块也行不用在电源问题上纠结。还有一个细节STM32F4和ESP8266之间的串口波特率不要用115200的默认值我建议统一设成9600或者57600。因为115200在高频干扰下偶发误码尤其是杜邦线连接的时候57600是个比较稳的折中。源码里配置的时候两端的波特率一定要一致。4.2 关键链路时间戳获取与HMAC签名前面说了阿里云MQTT接入需要拿到一个UTC毫秒级时间戳。这个时间戳从哪来不能指望STM32上的RTC因为RTC断电就清零了。最直接的办法让ESP8266通过AT指令的ATCIPSNTPCFG和ATCIPSNTPTIME去NTP服务器获取时间再把时间转成毫秒时间戳。// 初始化SNTP使用阿里云的时间服务器 ATCIPSNTPCFG1,8,ntp.aliyun.com // 查询当前时间 ATCIPSNTPTIME? // 返回示例: CIPSNPTIME: Fri Dec 20 10:30:45 2024拿到这种字符串之后设备端需要把它转成UTC毫秒时间戳。这个转换本身不难用C标准库的mktime就可以但要注意时区问题。NTP服务器返回的是UTC时间你如果直接用本地时间去算时间戳签名就会错服务器直接拒绝连接。HMAC-SHA1部分源码里用了独立的加密库移植到自己的工程也不难。核心就是调用现成的HMAC-SHA1计算函数把结果做Base64编码。如果你不想引整个加密库只需要SHA1和Base64两个模块加起来代码量几百行Keil工程里加进去就行。我在实际移植时是把这两个文件单独放在middleware/hmac_sha1目录下方便复用。4.3 MQTT连接与数据上报网络链路打通之后STM32端的工作就是按MQTT协议去构造报文。源码的设计思路很清晰我简单说一下流程。第一步初始化串口和GPIOESP8266上电后先发AT测试指令确认模块响应正常。 第二步配置ESP8266连接WiFi// 设置STA模式 ATCWMODE1 // 连接路由器注意密码和SSID要替换 ATCWJAPyour_ssid,your_password // 等返回WIFI GOT IP第三步ESP8266以透传模式建立TCP连接到阿里云MQTT接入地址ATCIPSTARTTCP,xxx.iot-as-mqtt.cn-shanghai.aliyuncs.com,1883 // 连接成功后 ATCIPMODE1 ATCIPSEND // 进入透传模式之后发给串口的数据会直接以TCP方式发出第四步拼接MQTT CONNECT报文并发送。这里的报文构造是核心逻辑需要按MQTT协议格式填字段报文类型、剩余长度、协议名、协议级别、连接标志、保活时间、clientId、username、password。代码里用了一个通用的MQTT报文打包函数核心逻辑类似这样// MQTT CONNECT报文构造简化示意 uint8_t packet[512]; uint16_t index 0; packet[index] 0x10; // CONNECT报文固定头 // 剩余长度在后续回填建议按MQTT协议规则逐字节编码 packet[index] 0x00; // 占位 // Protocol Name: MQTT packet[index] 0x00; packet[index] 0x04; memcpy(packet[index], MQTT, 4); index 4; // Protocol Level: 4 (MQTT 3.1.1) packet[index] 0x04; // Connect Flags: Clean Session1且用户名密码标志位置1 packet[index] 0xC2; // Keep Alive: 120秒 packet[index] 0x00; packet[index] 0x78; // 依次写入clientId、username、password长度前缀用两字节 // ...我之前不小心把CONNECT报文的协议级别写成了5结果阿里云直接拒绝连接。这个字段MQTT 3.1.1协议必须写4如果以后想升级MQTT 5.0再改成5。连接成功后上报传感器数据就简单多了直接把DHT11读到的温湿度封装成JSON通过透传通道发出去即可// 上报属性温度25.5湿度60.2 // 先拼接JSON payload char payload[128]; sprintf(payload, {\id\:\1\,\version\:\1.0\,\method\:\thing.event.property.post\, \params\:{\Temperature\:%.1f,\Humidity\:%.1f}}, temp, humi); // 透传模式下直接串口发送 usart_send_string(USART2, payload);发送之后平台不会对每次上报都回一帧数据。你可以在控制台看到设备在线然后到监控运维/日志服务里查看上行消息分析。如果看到数据格式错误那多半是JSON少了个逗号或者括号不匹配。4.4 接收云端下发指令并解析设备要远程可控必须订阅属性设置Topic。代码里连接时会发送SUBSCRIBE报文订阅/sys/{productKey}/{deviceName}/thing/service/property/set订阅成功之后云端下发的消息会通过ESP8266透传回串口STM32这边需要在串口中断或轮询里做MQTT报文解析。MQTT消息的固定头首字节如果是0x30就是PUBLISH报文里面携带了Topic和Payload需要提取并判断Topic是哪个再解析JSON。JSON解析可以用cJSON库这个库很轻量代码量不大嵌入式里用得很普遍。拿到payload后判断method字段是否为thing.service.property.set然后从params里取出LedSwitch的值cJSON *root cJSON_Parse(payload); cJSON *method cJSON_GetObjectItem(root, method); cJSON *params cJSON_GetObjectItem(root, params); if (strcmp(method-valuestring, thing.service.property.set) 0) { cJSON *led cJSON_GetObjectItem(params, LedSwitch); if (led-valueint 1) { HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_SET); } else { HAL_GPIO_WritePin(GPIOF, GPIO_PIN_9, GPIO_PIN_RESET); } // 建议回复一条属性上报让平台更新设备状态 } cJSON_Delete(root);这里有个坑要注意属性设置后如果设备不回发一条Current值平台上的设备影子可能不会更新控制台页面还显示旧状态。所以实际项目里云端下发控制指令后设备执行完动作都会重新上报一次当前属性这个习惯建议保留。5. 常见问题与排查实录5.1 高频问题速查表我把这几次带人跑这个项目时最常见的故障整理成了表格对照排查比自己瞎试快得多故障现象可能原因排查与解决办法ESP8266一直回ERROR供电不足或波特率不匹配单独稳压供电检查串口波特率两端是否一致ATCWJAP返回FAIL路由器2.4G/5G兼容问题或WiFi名称密码错误ESP8266只支持2.4G关掉路由器5G优先确认SSID无特殊字符TCP连接不上阿里云域名域名错误或网络不通用手机热点测试确认接入域名里的productKey对应正确产品CONNECT发出后无CONNACK签名错误或时间戳问题检查HMAC签名原文拼接顺序确认时间戳是UTC毫秒返回RET CODE: 4用户名或密码错误仔细核对三元组确认password的Base64结果没有空格或截断连接成功后马上掉线心跳保活没启动代码里要定时发送PINGREQ报文保活时间要小于平台限制的120秒数据上报后平台无日志JSON格式损坏或Temp属性不存在用串口打印完整报文对照物模型的identifier检查JSON key设备显示在线但控制台无法下发订阅Topic写错或权限不足检查SUBSCRIBE报文里的Topic路径确认产品Topic类权限收到指令但LED不动作JSON解析失败或GPIO初始化错误串口打印原始payload用cJSON逐步解析看返回指针是否为NULL程序运行一段时间卡死堆内存被cJSON反复分配没有释放每次解析完必须调用cJSON_Delete长期运行需要关注堆栈设置5.2 几个容易忽略的隐性坑说实话表格里这些问题都还算好排查我实际带跑项目时最头疼的是一些玄学问题其实背后都有确定原因只是不够明显。第一个是大端小端问题。MQTT协议规定所有长度字段都是大端序高字节在前但STM32默认是小端模式低字节在前。如果你在构造报文时直接对uint16_t做memcpy就会把长度写反。源码里用了专门的写大端函数这个细节很容易被忽略。我见过有人移植代码后改了几处逻辑莫名其妙连不上最后查出来是某个地方把长度字段写反了。第二个是ESP8266透传模式下的退出问题。一旦用ATCIPSEND进入透传网络关闭前串口只能收发MQTT数据不能再发AT指令。这意味着你想在运行时查询WiFi信号强度、重启模块都必须先退出透传模式。源码里提供了一个退出透传的函数发送并且有时间窗口要求我建议你在量产代码里留一个远程重启的口子不然现场设备一旦WiFi掉线恢复逻辑会很麻烦。第三个是JSON数字精度。DHT11温度本身就只有一位小数但如果你上报的是float序列化时最好控制一下格式不然cJSON可能输出超长小数浪费流量还难看。源码里用%.1f格式化这是经验之谈。第四个是心跳间隔。阿里云平台规定保活时间范围一般是30秒到1200秒但我实测如果超过120秒平台的空闲连接可能会被之前的超时策略断开。稳妥起见代码里设置保活时间为60秒并且用定时器每30秒发送一次PINGREQ。这个节奏是比较安全的。5.3 调试工具怎么用才高效排查MQTT问题时手里有一个趁手的工具能省一半时间。我常备三个串口调试助手、MQTT客户端测试工具、阿里云平台日志。串口调试助手用来抓STM32发出的原始数据尤其是CONNECT报文和PUBLISH报文。建议开启Hex显示能直观看到报文头是否正确。MQTT客户端测试工具比如MQTT X可以先用电脑连同一个阿里云产品和设备验证三元组和签名对不对。如果PC端能连上STM32端连不上那问题就锁定在STM32的报文构造或ESP8266链路上。阿里云控制台的监控运维/日志服务能查看到设备和平台之间的消息流转包括上线记录、上行消息、下行消息、错误信息。每次改动代码后都要养成查日志的习惯日志里写的错误原因比你自己猜准得多。6. 这个项目还能怎么扩展6.1 从单设备到多设备管理源码目前支持一个设备产品如果你以后做多设备项目可以考虑在平台上创建多个设备代码里把DeviceName做成可配置项比如通过按键切换设备编号或者通过AT指令远程修改三元组。平台侧的规则引擎还能做设备间的联动设备A上报温度超过阈值自动触发设备B的某个动作这就可以玩出智能联动的效果了。6.2 数据可视化与告警数据上云之后可视化就不需要自己开发了。阿里云物联网平台自带数据报表和可视化大屏你只要在产品里定义好物模型就能在控制台配置图表展示温湿度曲线。如果要做实时告警可以用平台的规则引擎当属性值超过设定范围就触发钉钉或短信通知。这个扩展方向对课程设计、毕业设计非常加分评委看到的效果完全不一样。6.3 从WiFi到4G/以太网如果项目要部署到野外或工业现场WiFi的局限性就很明显。源码的网络驱动层是独立的你可以把ESP8266替换成4G Cat.1模块或者以太网模块只改驱动层和AT指令部分MQTT协议栈和业务逻辑基本不用动。这就是模块化设计的好处当初写代码时把网络层和MQTT层分开后面升级省了很多事。6.4 与小程序或App联动阿里云物联网平台有开放API你可以通过App Server调用平台的HTTP接口获取设备状态、下发指令。如果只想做个Demo甚至可以用平台自带的调试功能实现控制但如果要做成完整产品建议走官方API结合微信小程序或App做用户界面。这个扩展路线对想做产品原型的同学非常有价值毕竟手机端控制才是物联网最容易让人哇的一步。最后再分享一个我在实操中的体会这种MCU云平台协议的项目最难的不是某一个点而是整个链条的拉通。每换一个环境、每换一个设备都可能跳出新的问题。但只要养成看官方文档的习惯再加上串口日志和平台日志两边对照大多数问题都能在半小时内定位。这套源码的意义其实是把那条容易踩坑的路替你走了一遍让你能把精力放到真正想做的业务上。本文还有配套的精品资源点击获取
返回列表