收集整理了一份《2024年最新物联网嵌入式全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升的朋友。
img
img

如果你需要这些资料,可以戳这里获取

一个人可以走的很快,但一群人才能走的更远!不论你是正从事IT行业的老鸟或是对IT行业感兴趣的新人

都欢迎加入我们的的圈子(技术交流、学习资源、职场吐槽、大厂内推、面试辅导),让我们一起学习成长!

2. 创建IOT服务器端产品

需要先创建产品、在产品下再创建设备。产品是一个大框架,产品下的设备可以有很多,在应用层,可以通过华为云平台提供的API创建设备,删除设备,查询设备属性,在做产品时,软件端可以做一个设备注册的引导界面,完成产品下的设备注册,再将数据传递给设备端,这个过程叫“配网“,具体逻辑需要配合设备端完成。最终完成自动化设备创建,注册,上线等操作。

下面先介绍如何手动创建产品,创建设备,了解创建产品创建设备的过程中需要填充什么参数,理解之后,再使用API时才更加理解参数含义。

2.1 创建产品

直接打开物联网产品页面: https://www.huaweicloud.com/product/iothub.html

打开产品页面,选择右上角创建产品。

根据自己情况填写信息。就是填写自己产品的一些参数信息。

创建成功后打开产品详情页面,拉到最下面,点击创建自定义模型文件。

这里创建模型文件主要就是为了MQTT客户端能够正确的上传传感器数据上来,每个传感器设置一个属性,这个属性就是表示了传感器的数据值类型。

比如: 先添加一个电机,这个电机就是浇水电机,能上报开关状态,云端也能下发命令控制电机,所以需要添加属性和下发的命令。

添加属性:

添加命令: 因为电机需要云端远程控制。

接下来就创建温度、湿度、光照度传感器的属性,这些传感器只是向云端上传数据,不需要下发指令控制,选择只读就可以了,电机要先实现远程浇灌控制,属性就选择读写。

创建完毕效果,一共有4个属性,电机、温度、湿度、光强度:

2.2 创建设备

选择设备页面,注册设备。

创建后保持设备密匙等信息,接下来登录服务器时,生成MQTT账号密匙需要用到这些参数。

当前创建的设备信息如下:

 {
     "device_id": "61cd1d97078a93029b84e7b6_1126626497",
     "secret": "1126626497"
 }
2.3 生成MQTT登录账号信息

官微提供的在线小工具: https://iot-tool.obs-website.cn-north-4.myhuaweicloud.com/

按照提示填入数据,生成,非常方便。

当前生成的信息如下:

 ClientId 61cd1d97078a93029b84e7b6_1126626497_0_0_2021123003
 Username 61cd1d97078a93029b84e7b6_1126626497
 Password b219f3a0099fa0284a2671a5c699b67a7cf6d5f7355d9ee8190011f3b64f71b5

3. 使用MQTT客户端模拟测试

为了验证服务器配置是否OK,先使用MQTT客户端软件进行连接测试。

3.1 华为云IOT服务器地址与端口

 端口: 1883
 域名: a161a58a78.iot-mqtts.cn-north-4.myhuaweicloud.com
 IP地址: 121.36.42.100
3.2 订阅主题

在产品页面,可以看到主题管理页面,能看到当前设备可以订阅的主题有哪些。

一般订阅下发的数据:

 格式: $oc/devices/{device_id}/sys/messages/down
 //订阅主题: 平台下发消息给设备
 $oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/messages/down
3.3 上报主题数据

官方文档介绍: https://support.huaweicloud.com/devg-iothub/iot_01_2127.html

服务ID,属性ID在产品页面查看,2.1小节创建产品里就讲了这个属性的作用。

每次可以单个属性上报,也可以一起上报。

 格式: $oc/devices/{device_id}/sys/properties/report
 //设备上报主题请求
 $oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/properties/report
 ​
 //上报的数据格式如下
 
 //电机开状态反馈
 {"services": [{"service_id": "motor","properties":{"motor":1}}]}
 ​
 //电机关状态反馈
 {"services": [{"service_id": "motor","properties":{"motor":0}}]}
 ​
 //温度上报
 {"services": [{"service_id": "motor","properties":{"SHT30_H":14}}]}
 ​
 //湿度上报
 {"services": [{"service_id": "motor","properties":{"SHT30_L":70}}]}
 ​
 //光照强度上报
 {"services": [{"service_id": "motor","properties":{"BH1750":80}}]}
 ​
 //也可以一起上报
 {"services": [{"service_id": "motor","properties":{"motor":1}},{"service_id": "motor","properties":{"SHT30_H":15}},{"service_id": "motor","properties":{"SHT30_L":70}},{"service_id": "motor","properties":{"BH1750":80}}]}
3.4 登录服务器

按照软件提示,填入相关数据即可。

如需要也需要使用和我一样的同款软件,打开百度搜索MQTT客户端_v2.4(协议3.1.1).exe 即可找到下载地址。

发送数据后查看云端,已经登录成功,数据已经上传成功。

3.5 下发命令

电机设备支持读写,支持下发命令,在设备页面测试。

点击确定之后,参看MQTT客户端软件,已经收到了下发的数据。

 len:174,Data:l$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/commands/request_id=390ce15d-6e69-4021-b83a-5e953eea874c{"paras":{"motor":1},"service_id":"motor","command_name":"motor"}

4. 设备端上华为云IOT

4.1 安装keil软件

MCU采用的STM32芯片,设备端代码编写开发就采用的keil5。

keil5安装包下载地址: http://www.myir-tech.com/download.asp

安装keil时,软件要放在英文目录下,电脑的用户名必须是英文,否则会出现一些奇怪问题。

安装过程中,根据提示下一步下一步点击即可。

4.2 编写代码

工程代码:

工程代码较多,这里就贴出main.c全部代码:

 #include "main.h"
 #include "stm32l4xx_hal.h"
 #include "i2c.h"
 #include "usart.h"
 #include "gpio.h"
 #include "E53_IA1.h"
 #include "lcd.h"
 #include "spi.h"
 #include "mqtt.h"
 #include "esp8266.h"
 ​
 ​
 /* USER CODE BEGIN Includes */
 #include "stdio.h"
 /* USER CODE END Includes */
 ​
 void SystemClock_Config(void);
 ​
 ​
 #define ESP8266_WIFI_AP_SSID  "CMCC-Cqvn"   //将要连接的路由器名称 --不要出现中文、空格等特殊字符
 #define ESP8266_AP_PASSWORD "99pu58cb"     //将要连接的路由器密码
 ​
 ​
 //华为云IOT物联网服务器的设备信息
 #define MQTT_ClientID "61cd1d97078a93029b84e7b6_1126626497_0_0_2021123003"
 #define MQTT_UserName "61cd1d97078a93029b84e7b6_1126626497"
 #define MQTT_PassWord "b219f3a0099fa0284a2671a5c699b67a7cf6d5f7355d9ee8190011f3b64f71b5"
 
 //订阅与发布的主题
 #define SET_TOPIC  "$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/messages/down"  //订阅
 #define POST_TOPIC "$oc/devices/61cd1d97078a93029b84e7b6_1126626497/sys/properties/report"  //发布
 ​
 ​
 //保存温湿度、光照强度
 E53_IA1_Data_TypeDef E53_IA1_Data;
 ​
 //显示文本
 char lcd_text_str[50];
 ​
 ​
 UART_HandleTypeDef at_usart;
 ​
 //低功耗串口初始化
 int32_t at_usart_init(void)
 {
     at_usart.Instance = LPUART1;
     at_usart.Init.BaudRate = 115200;
 ​
     at_usart.Init.WordLength = UART_WORDLENGTH_8B;
     at_usart.Init.StopBits = UART_STOPBITS_1;
     at_usart.Init.Parity = UART_PARITY_NONE;
     at_usart.Init.HwFlowCtl = UART_HWCONTROL_NONE;
     at_usart.Init.Mode = UART_MODE_RX | UART_MODE_TX;
 
     if(HAL_UART_Init(&at_usart) != HAL_OK)
     {
         _Error_Handler(__FILE__, __LINE__);
     }
    // __HAL_UART_CLEAR_FLAG(usart, UART_FLAG_TC);
     __HAL_UART_ENABLE_IT(&at_usart, UART_IT_IDLE);
     __HAL_UART_ENABLE_IT(&at_usart, UART_IT_RXNE);
     HAL_NVIC_EnableIRQ(LPUART1_IRQn);                   //使能USART1中断通道
     HAL_NVIC_SetPriority(LPUART1_IRQn, 3, 3);               //抢占优先级3,子优先级3
     return 0;
 }
 ​
 unsigned char ESP8266_RecvBuf[MAX_RECV_CNT];
 unsigned int ESP8266_Recv_cnt=0;
 unsigned int ESP8266_Recv_flag=0;
 void LPUART1_IRQHandler()
 {
     //接收到数据
     if(__HAL_UART_GET_FLAG(&at_usart, UART_FLAG_RXNE) != RESET)
     {
         if(ESP8266_Recv_cnt<MAX_RECV_CNT-1)
         {
             ESP8266_RecvBuf[ESP8266_Recv_cnt++] = (uint8_t)(at_usart.Instance->RDR & 0x00FF);
         } 
         else
         {
              ESP8266_Recv_flag=1;
         }
     }  
     else if (__HAL_UART_GET_FLAG(&at_usart, UART_FLAG_IDLE) != RESET)
     {
         __HAL_UART_CLEAR_IDLEFLAG(&at_usart);
 
          ESP8266_Recv_flag=1;
     }
 }
 ​
 ​
 void AT_SendData(unsigned char *p,unsigned int len)
 {
     int i=0;
     for(i=0;i<len;i++)
     {
         while((LPUART1->ISR & 0X40) == 0); //循环发送,直到发送完毕
         LPUART1->TDR = p[i];
     }
 }
 ​
 ​
 char mqtt_message[200];
 int main(void)
 {
     int i=0;
     int cnt=0;
     int motor_state=0;
     HAL_Init();
     SystemClock_Config();
     MX_GPIO_Init();
     MX_I2C1_Init();
     MX_SPI2_Init();
     MX_USART1_UART_Init();
     at_usart_init();
 
     //初始化硬件
     Init_E53_IA1();
 ​
     LCD_Init();                 
     LCD_Clear(BLACK);//清屏为黑色
     LCD_ShowString(0, 00, 240, 32, 32, "Init ESP8266");//显示字符串,字体大小32*32
 ​
     if(ESP8266_Init())
    {
       printf("ESP8266硬件检测错误.\n");
       LCD_Clear(BLACK);//清屏为黑色
       LCD_ShowString(0, 00, 240, 32, 32, "ESP8266 ERROR");//显示字符串,字体大小32*32
    }
    else
    {
        LCD_Clear(BLACK);//清屏为黑色
        LCD_ShowString(0, 00, 240, 32, 32, "ESP8266 OK");//显示字符串,字体大小32*32
        printf("准备连接到指定的服务器.\n");
       //非加密端口
       printf("WIFI:%d\r\n",ESP8266_STA_TCP_Client_Mode(ESP8266_WIFI_AP_SSID,ESP8266_AP_PASSWORD,"106.55.124.154",1883,1));
    }
 
     //2. MQTT协议初始化  
     MQTT_Init(); 
 
     //3. 连接华为云IOT服务器        
     while(MQTT_Connect(MQTT_ClientID,MQTT_UserName,MQTT_PassWord))
     {
         printf("服务器连接失败,正在重试...\n");
         HAL_Delay(500);
     }
     printf("服务器连接成功.\n");
 
     //3. 订阅主题
     if(MQTT_SubscribeTopic(SET_TOPIC,0,1))
     {
         printf("主题订阅失败.\n");
     }
     else
     {
         printf("主题订阅成功.\n");
     }        
 
       while (1)
       {
             if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)//查询按键KEY1低电平
             {
                 HAL_Delay(10);//消抖
                 if(HAL_GPIO_ReadPin(KEY1_GPIO_Port,KEY1_Pin)==GPIO_PIN_RESET)//查询按键KEY1低电平
                 {
                     HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_SET);//亮
 
                     //补光灯亮
                     HAL_GPIO_WritePin(IA1_Light_GPIO_Port, IA1_Light_Pin, GPIO_PIN_SET);
 
                     //电机转
                     HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_SET);
 
                     motor_state=1;
                 }
             }
 
             if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)//查询按键KEY2低电平
             {
                 HAL_Delay(10);//消抖
                 if(HAL_GPIO_ReadPin(KEY2_GPIO_Port,KEY2_Pin)==GPIO_PIN_RESET)//查询按键KEY2低电平
                 {
                     HAL_GPIO_WritePin(LED_GPIO_Port,LED_Pin,GPIO_PIN_RESET);//灭
 
                      //补光灯灭
                     HAL_GPIO_WritePin(IA1_Light_GPIO_Port, IA1_Light_Pin, GPIO_PIN_RESET);
 
                      //电机停
                     HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_RESET);
 
                     motor_state=0;
                 }
             }
 
          cnt++;
          HAL_Delay(10);   
 
          if(cnt>=100)
          {
             cnt=0;
             E53_IA1_Read_Data();
             printf("光照强度:%d %%\r\n", (int)E53_IA1_Data.Lux);
             printf("湿度:%d %%\r\n",(int)E53_IA1_Data.Humidity);
             printf("温度:%d ℃\r\n", (int)E53_IA1_Data.Temperature);
 ​
 ​
             sprintf(lcd_text_str,"L: %d %%",(int)E53_IA1_Data.Lux);
             LCD_ShowString(40, 50+10+32*1, 240, 32, 32,lcd_text_str);
 ​
 ​
             sprintf(lcd_text_str,"H: %d %%",(int)E53_IA1_Data.Humidity);
             LCD_ShowString(40, 50+10+32*2, 240, 32, 32,lcd_text_str);
 
 ​
             sprintf(lcd_text_str,"T: %d C",(int)E53_IA1_Data.Temperature);
             LCD_ShowString(40, 50+10+32*3, 240, 32, 32,lcd_text_str);
 ​
             //切换引脚的状态
             HAL_GPIO_TogglePin(LED_GPIO_Port,LED_Pin);
 
                //上传数据
             sprintf(mqtt_message,"{\"services\": [{\"service_id\": \"motor\",\"properties\":{\"motor\":%d}},"
             "{\"service_id\": \"motor\",\"properties\":{\"SHT30_H\":%d}},{\"service_id\": \"motor\",\"properties\":"
             "{\"SHT30_L\":%d}},{\"service_id\": \"motor\",\"properties\":{\"BH1750\":%d}}]}",
             motor_state,(int)E53_IA1_Data.Humidity,(int)E53_IA1_Data.Temperature,(int)E53_IA1_Data.Lux);
 
             MQTT_PublishData(POST_TOPIC,mqtt_message,0);
 
             //根据湿度自动灌溉
             if((int)E53_IA1_Data.Humidity<50)  //小于50自动灌溉
             {
                  printf("自动灌溉....\n");
                  motor_state=1; //电机状态更新
                  //电机转
                  HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_SET);
             }  
          }
 ​
           //接收到数据
           if(ESP8266_Recv_flag)
           {
                //如果是下发了属性,判断是开锁还是关锁
                 if(ESP8266_Recv_cnt>5)
                 {
                     ESP8266_RecvBuf[ESP8266_Recv_cnt]='\0';
 
                     //使用字符串查找函数
                     if(strstr((char*)&ESP8266_RecvBuf[5],"\"machine\":1"))
                     {
                          motor_state=1; //电机状态更新
                          //电机转
                          HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_SET);  
                          printf("开启电机...\n");
                     }
                     else if(strstr((char*)&ESP8266_RecvBuf[5],"\"machine\":0"))
                     {
                         //电机停
                         HAL_GPIO_WritePin(IA1_Motor_GPIO_Port, IA1_Motor_Pin, GPIO_PIN_RESET);
 
                         motor_state=0;
                         printf("关闭电机...\n");
                     }
 
                     for(i=0;i<ESP8266_Recv_cnt;i++)printf("%c",ESP8266_RecvBuf[i]);
 
                     ESP8266_Recv_cnt=0;    
                 }
                 ESP8266_Recv_flag=0;
           }
       }
 }
 ​
 ​
 void SystemClock_Config(void)
 {
 ​
   RCC_OscInitTypeDef RCC_OscInitStruct;
   RCC_ClkInitTypeDef RCC_ClkInitStruct;
   RCC_PeriphCLKInitTypeDef PeriphClkInit;
 ​
     /**Initializes the CPU, AHB and APB busses clocks 
     */
   RCC_OscInitStruct.OscillatorType = RCC_OSCILLATORTYPE_HSI|RCC_OSCILLATORTYPE_MSI;
   RCC_OscInitStruct.HSIState = RCC_HSI_ON;
   RCC_OscInitStruct.HSICalibrationValue = 16;
   RCC_OscInitStruct.MSIState = RCC_MSI_ON;
   RCC_OscInitStruct.MSICalibrationValue = 0;
   RCC_OscInitStruct.MSIClockRange = RCC_MSIRANGE_6;
   RCC_OscInitStruct.PLL.PLLState = RCC_PLL_ON;
   RCC_OscInitStruct.PLL.PLLSource = RCC_PLLSOURCE_MSI;
   RCC_OscInitStruct.PLL.PLLM = 1;
   RCC_OscInitStruct.PLL.PLLN = 40;
   RCC_OscInitStruct.PLL.PLLP = RCC_PLLP_DIV7;
   RCC_OscInitStruct.PLL.PLLQ = RCC_PLLQ_DIV2;
   RCC_OscInitStruct.PLL.PLLR = RCC_PLLR_DIV2;
   if (HAL_RCC_OscConfig(&RCC_OscInitStruct) != HAL_OK)
   {
     _Error_Handler(__FILE__, __LINE__);
   }
 ​
     /**Initializes the CPU, AHB and APB busses clocks 
     */
   RCC_ClkInitStruct.ClockType = RCC_CLOCKTYPE_HCLK|RCC_CLOCKTYPE_SYSCLK
                               |RCC_CLOCKTYPE_PCLK1|RCC_CLOCKTYPE_PCLK2;
   RCC_ClkInitStruct.SYSCLKSource = RCC_SYSCLKSOURCE_PLLCLK;
   RCC_ClkInitStruct.AHBCLKDivider = RCC_SYSCLK_DIV1;
   RCC_ClkInitStruct.APB1CLKDivider = RCC_HCLK_DIV1;
   RCC_ClkInitStruct.APB2CLKDivider = RCC_HCLK_DIV1;
 ​
   if (HAL_RCC_ClockConfig(&RCC_ClkInitStruct, FLASH_LATENCY_4) != HAL_OK)
   {
     _Error_Handler(__FILE__, __LINE__);
   }
 ​
   PeriphClkInit.PeriphClockSelection = RCC_PERIPHCLK_USART1|RCC_PERIPHCLK_I2C1;
   PeriphClkInit.Usart1ClockSelection = RCC_USART1CLKSOURCE_PCLK2;
   PeriphClkInit.I2c1ClockSelection = RCC_I2C1CLKSOURCE_HSI;
   if (HAL_RCCEx_PeriphCLKConfig(&PeriphClkInit) != HAL_OK)
   {
     _Error_Handler(__FILE__, __LINE__);
   }
 ​
     /**Configure the main internal regulator output voltage 
     */
   if (HAL_PWREx_ControlVoltageScaling(PWR_REGULATOR_VOLTAGE_SCALE1) != HAL_OK)
   {
     _Error_Handler(__FILE__, __LINE__);
   }
 ​
     /**Configure the Systick interrupt time 
     */
   HAL_SYSTICK_Config(HAL_RCC_GetHCLKFreq()/1000);
 ​
     /**Configure the Systick 
     */
   HAL_SYSTICK_CLKSourceConfig(SYSTICK_CLKSOURCE_HCLK);
 ​
   /* SysTick_IRQn interrupt configuration */
   HAL_NVIC_SetPriority(SysTick_IRQn, 0, 0);
 }
 ​
 /* USER CODE BEGIN 4 */
 ​
 /* USER CODE END 4 */
 ​
 /**
   * @brief  This function is executed in case of error occurrence.
   * @param  file: The file name as string.
   * @param  line: The line in file as a number.
   * @retval None
   */
 void _Error_Handler(char *file, int line)
 {
   /* USER CODE BEGIN Error_Handler_Debug */
   /* User can add his own implementation to report the HAL error return state */
   while(1)
   {
   }
   /* USER CODE END Error_Handler_Debug */
 }
 ​
 #ifdef  USE_FULL_ASSERT
 /**
   * @brief  Reports the name of the source file and the source line number
   *         where the assert_param error has occurred.
   * @param  file: pointer to the source file name
   * @param  line: assert_param error line source number
   * @retval None
   */
 void assert_failed(uint8_t* file, uint32_t line)
 { 
   /* USER CODE BEGIN 6 */
   /* User can add his own implementation to report the file name and line number,
      tex: printf("Wrong parameters value: file %s on line %d\r\n", file, line) */
   /* USER CODE END 6 */
 }
 #endif /* USE_FULL_ASSERT */

5. 上位机软件开发

上位机与设备之间通信,需要通过服务器完成,服务器提供了对应的API接口,所以对于上位机而言通信主要是对HTTP请求进行处理,返回的数据进行解析等操作。

当前的软件采用是采用QT设计的,实现了产品注册、设备注册、获取在线设备,获取设备属性,远程指令发送等主要功能。

访问华为云的接口都需要填一个X-Auth-Token参数,这个参数获取需要IAM账号,下面第一步就先介绍,如何创建IAM账号,如何获取X-Auth-Token参数。

5.1 创建IAM账户

创建一个IAM账户,方便接下来使用API接口访问华为云服务时,生成token登录密匙。

地址: https://console.huaweicloud.com/iam/?region=cn-north-4#/iam/users

账户创建好之后,代码里就可以编写一个获取Token的函数。

 /*
 功能: 获取token
 */
 void Widget::GetToken()


![img](https://img-blog.csdnimg.cn/img_convert/b5d1f7621fd5a06689c71a299e194ebf.png)
![img](https://img-blog.csdnimg.cn/img_convert/7e565d9cb094ddcad0e3161e9c0fa266.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

g)


![](https://i-blog.csdnimg.cn/blog_migrate/b5efff1bb8ef519b89158f3a59d8e6db.png)



![](https://i-blog.csdnimg.cn/blog_migrate/241d46bdcccf6f022402170ee76fa5e7.png)


![](https://i-blog.csdnimg.cn/blog_migrate/c4c5460ca785ee6dd5680c48afcd272d.png)


![](https://i-blog.csdnimg.cn/blog_migrate/e4863b29c57f0cbdc3aba2750ca96257.png)


账户创建好之后,代码里就可以编写一个获取Token的函数。



/*
功能: 获取token
*/
void Widget::GetToken()

[外链图片转存中…(img-TpHjhtCI-1715662682894)]
[外链图片转存中…(img-nqd9l43e-1715662682896)]

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

如果你需要这些资料,可以戳这里获取

Logo

技术共进,成长同行——讯飞AI开发者社区

更多推荐