STM32HAL库++ESP8266+cJSON毗连阿里云物联网平台

打印 上一主题 下一主题

主题 831|帖子 831|积分 2493

实验利用资源:正点原子F1
  USART1:PA9P、A10(串口打印调试)
  USART3:PB10、PB11(WiFi模块)
  DHT11:PG11(采集数据、上报)
  LED0、1:PB5、PE5(先容命令,控制亮灭)
  显示屏(可有可无)
  HAL库创建工程

参考之前的博客:STM32CubeMX安装_stm32cubemx下载-CSDN博客
ESP8266固件烧录

参考之前的博客:ESP8266毗连阿里云_esp8266+阿里云-CSDN博客
阿里云创建物模型

参考之前的博客:ESP8266毗连阿里云_esp8266+阿里云-CSDN博客
模块移植

   这里重要说usart模块和WiFi相关模块,其他模块的驱动很简单,不再描述
  usart模块


  • 将【stm32f1xx_it.c】内里的void USART1_IRQHandler(void) 和void USART3_IRQHandler(void)函数注释掉


  • 将下面的代码粘贴到【usart.c】中的最下面的/* USER CODE BEGIN 1 */和/* USER CODE END 1 */之间
  1. /**
  2. * @brief       ATK-MW8266D UART printf
  3. * @param       fmt: 待打印的数据
  4. * @retval      无
  5. */
  6. void atk_mw8266d_uart_printf(char *fmt, ...)
  7. {
  8.     va_list ap;
  9.     uint16_t len;
  10.    
  11.     va_start(ap, fmt);
  12.     vsprintf((char *)g_uart_tx_buf, fmt, ap);
  13.     va_end(ap);
  14.    
  15.     len = strlen((const char *)g_uart_tx_buf);
  16.     HAL_UART_Transmit(&huart3, g_uart_tx_buf, len, HAL_MAX_DELAY);
  17. }
  18. /**
  19. * @brief       ATK-MW8266D UART重新开始接收数据
  20. * @param       无
  21. * @retval      无
  22. */
  23. void atk_mw8266d_uart_rx_restart(void)
  24. {
  25.     g_uart_rx_frame.sta.len     = 0;
  26.     g_uart_rx_frame.sta.finsh   = 0;
  27. }
  28. /**
  29. * @brief       获取ATK-MW8266D UART接收到的一帧数据
  30. * @param       无
  31. * @retval      NULL: 未接收到一帧数据
  32. *              其他: 接收到的一帧数据
  33. */
  34. uint8_t *atk_mw8266d_uart_rx_get_frame(void)
  35. {
  36.     if (g_uart_rx_frame.sta.finsh == 1)
  37.     {
  38.         g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = '\0';
  39.         return g_uart_rx_frame.buf;
  40.     }
  41.     else
  42.     {
  43.         return NULL;
  44.     }
  45. }
  46. /**
  47. * @brief       获取ATK-MW8266D UART接收到的一帧数据的长度
  48. * @param       无
  49. * @retval      0   : 未接收到一帧数据
  50. *              其他: 接收到的一帧数据的长度
  51. */
  52. uint16_t atk_mw8266d_uart_rx_get_frame_len(void)
  53. {
  54.     if (g_uart_rx_frame.sta.finsh == 1)
  55.     {
  56.         return g_uart_rx_frame.sta.len;
  57.     }
  58.     else
  59.     {
  60.         return 0;
  61.     }
  62. }
  63. void USART1_IRQHandler(void)
  64. {
  65. #if SYS_SUPPORT_OS                                                   /* 使用OS */
  66.     OSIntEnter();
  67. #endif
  68.     HAL_UART_IRQHandler(&huart1);                               /* 调用HAL库中断处理公用函数 */
  69.     while (HAL_UART_Receive_IT(&huart1, (uint8_t *)g_rx_buffer, RXBUFFERSIZE) != HAL_OK)     /* 重新开启中断并接收数据 */
  70.     {
  71.         /* 如果出错会卡死在这里 */
  72.     }
  73. #if SYS_SUPPORT_OS                                                   /* 使用OS */
  74.     OSIntExit();
  75. #endif
  76. }
  77. void USART3_IRQHandler(void)
  78. {
  79.   /* USER CODE BEGIN USART3_IRQn 0 */
  80.   /* USER CODE END USART3_IRQn 0 */
  81.   HAL_UART_IRQHandler(&huart3);
  82.   /* USER CODE BEGIN USART3_IRQn 1 */
  83.     uint8_t tmp;
  84.    
  85.     if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_ORE) != RESET)        /* UART接收过载错误中断 */
  86.     {
  87.         __HAL_UART_CLEAR_OREFLAG(&huart3);                           /* 清除接收过载错误中断标志 */
  88.         (void)huart3.Instance->SR;                                   /* 先读SR寄存器,再读DR寄存器 */
  89.         (void)huart3.Instance->DR;
  90.     }
  91.    
  92.     if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_RXNE) != RESET)       /* UART接收中断 */
  93.     {
  94.         HAL_UART_Receive(&huart3, &tmp, 1, HAL_MAX_DELAY);           /* UART接收数据 */
  95.         if (g_uart_rx_frame.sta.len < (256 - 1))   /* 判断UART接收缓冲是否溢出
  96.                                                                              * 留出一位给结束符'\0'
  97.                                                                              */
  98.         {
  99.             g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
  100.             g_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
  101.         }
  102.         else                                                                /* UART接收缓冲溢出 */
  103.         {
  104.             g_uart_rx_frame.sta.len = 0;                                    /* 覆盖之前收到的数据 */
  105.             g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp;             /* 将接收到的数据写入缓冲 */
  106.             g_uart_rx_frame.sta.len++;                                      /* 更新接收到的数据长度 */
  107.         }
  108.     }
  109.    
  110.     if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET)       /* UART总线空闲中断 */
  111.     {
  112.         g_uart_rx_frame.sta.finsh = 1;                                      /* 标记帧接收完成 */
  113.         
  114.         __HAL_UART_CLEAR_IDLEFLAG(&huart3);                          /* 清除UART总线空闲中断 */
  115.     }
  116.   /* USER CODE END USART3_IRQn 1 */
  117. }
复制代码

  • 在【usart.c】上面的的/* USER CODE BEGIN 0 */和/* USER CODE END 0 */之间参加下面的代码
  1. #include <stdarg.h>
  2. #include <stdio.h>
  3. #include <string.h>
  4. #if 1
  5. #pragma import(__use_no_semihosting)            
  6. //标准库需要的支持函数                 
  7. struct __FILE
  8. {
  9.         int handle;
  10. };
  11. FILE __stdout;      
  12. //定义_sys_exit()以避免使用半主机模式   
  13. void _sys_exit(int x)
  14. {
  15.         x = x;
  16. }
  17. //重定义fputc函数
  18. int fputc(int ch, FILE *f)
  19. {      
  20.         while((USART1->SR&0X40)==0);//循环发送,直到发送完毕   
  21.     USART1->DR = (uint8_t) ch;      
  22.         return ch;
  23. }
  24. #endif
  25. uint8_t g_rx_buffer[RXBUFFERSIZE];  /* HAL库使用的串口接收缓冲 */
复制代码

  • 在【usart.c】中的void HAL_UART_MspInit(UART_HandleTypeDef* uartHandle)函数中调整中断优先级,WiFi的usart3的高于串口的,同时添加usar使能


  • 在【usart.h】中的/* USER CODE BEGIN Private defines */和/* USER CODE END Private defines */之间参加下面的代码
  1. static struct
  2. {
  3.     uint8_t buf[256];              /* 帧接收缓冲 */
  4.     struct
  5.     {
  6.         uint16_t len    : 15;                               /* 帧接收长度,sta[14:0] */
  7.         uint16_t finsh  : 1;                                /* 帧接收完成标志,sta[15] */
  8.     } sta;                                                  /* 帧状态信息 */
  9. } g_uart_rx_frame = {0};                                    /* ATK-MW8266D UART接收帧缓冲信息结构体 */
  10. static uint8_t g_uart_tx_buf[1024]; /* ATK-MW8266D UART发送缓冲 */
  11. #define RXBUFFERSIZE   1                        /* 缓存大小 */
复制代码

  • 在【usart.h】中的/* USER CODE BEGIN Prototypes */和/* USER CODE END Prototypes */之间参加下面的代码
  1. void atk_mw8266d_uart_printf(char *fmt, ...);       /* ATK-MW8266D UART printf */
  2. void atk_mw8266d_uart_rx_restart(void);             /* ATK-MW8266D UART重新开始接收数据 */
  3. uint8_t *atk_mw8266d_uart_rx_get_frame(void);       /* 获取ATK-MW8266D UART接收到的一帧数据 */
  4. uint16_t atk_mw8266d_uart_rx_get_frame_len(void);   /* 获取ATK-MW8266D UART接收到的一帧数据的长度 */
复制代码
WiFi模块


  • 将编写好的esp8266.c/.h及wifi.c/.h文件分别参加Src和Inc文件夹,然后再在keil里将wifi.c文件参加工程
  • 在wifi.h中,修改WiFi信息和阿里云物联网平台相关参数

   这里须要留意ESP8266_ClientID内里的的,须要进行转义
  增大初始栈大小

在启动文件startup_stm32f103xe.s中,将Heap Size的大小调大,不然利用cJSON后,大概烧完步伐开发板都没反应

毗连阿里云


  • 在main.c中引用wifi.h
  • 调用wifi_init();
  • 打开串口助手,观察打印的数据,同时检察阿里云平台设备是否在线
  • 连上云平台后,须要将采集的数据进行上报,同时吸收下发的指令(在后面先容利用cJSON实现)
   连不上云平台的原因:
  

  • 参数没写对,特别是ESP8266_ClientID这个参数
  • 网络不好,特别是在实验室同时很多人开热点,严峻干扰毗连
  

cJSON模块

JSON格式

JSON是一种轻量级的数据互换格式,可读性强、编写简单。键值对组合编写规则,键名利用双引号包裹,冒号:分隔符后面紧跟着数值,有两种常用的数据范例是对象和数组。
对象:利用花括号{}包裹起来的内容,数据结构{“key1”:“value1”,“key2”:“value2”…},key为对象的属性,value为对象的值。
数值:利用中括号[]包裹起来的内容,数据结构{“key”:[“value1”,“value2”,“value3”…]}。
下载

下载地点:cJSON download | SourceForge.net

移植


  • 将压缩包解压,打开cJSON文件夹,只保存cJSON.c和cJSON.h,其他文件全部删除


  • 将修改后的cJSON文件夹参加项目地点目录


  • 将该文件夹添加进工程,点击【魔法棒】-【C/C++】-【IncludePaths】中将路径参加
   如果想省略3和4,可以直接将cJSON.c和cJSON.h分别放入Src和Inc文件夹,然后直接将cJSON.c参加工程即可
  


  • 将cJSON.c添加进工程,点击【方块】-【Project Items】,在groups中创建一个cJSON文件夹,然后再在内里放入cJSON.c

关键函数



  • cJSON *cJSON_CreateObject(void)

    • 创建JSON结构对象

  • cJSON *cJSON_CreateNumber(double num)

    • 创建一个整型的数据范例

  • cJSON *cJSON_CreateString(const char *string)

    • 创建一个字符串数据范例

  • cJSON *cJSON_CreateArray(void)

    • 创建一个数组数据范例函数:

  • cJSON *cJSON_CreateIntArray(const int *numbers, int count)

    • 将多个整型数据范例增加到数组中

  • cJSON *cJSON_Parse(const char *value)

    • 从JSON文件数据缓冲区剖析JSON的对象结构,利用完成后要必须要开释对象结构

  • void cJSON_Delete(cJSON *c)

    • 开释申请的JSON结构缓存空间

  • void cJSON_AddItemToObject(cJSON *json, cJSON *, cJSON_CreateArray())

    • 向对象中增加对象

  • cJSON *cJSON_GetObjectItem(cJSON *object, const char *string)

    • 根据键名在JSON中查找子节点

上报消息

   当在物联网平台为产物定义物模型后,设备须要按照Alink JSON格式上报属性或事件
  Alink协议是针对物联网开发领域计划的一种数据互换规范,数据格式是JSON,用于设备端和物联网平台的双向通信,更便捷地实现和规范了设备端和物联网平台之间的业务数据交互
  


  • 先看一下官方给的数据上报的格式



  • 我们必须要传的就是method、id、version、params这四个组成的JSON字符串,其中params传输的内容也是个小的JSON字符串
  • 根据这个我们利用cJSON进行JSON格式的封装


  • 创建两个JSON对象,外部和内部
  1. cJSON *json = cJSON_CreateObject(); // 创建一个空的JSON对象
  2. cJSON *params_cjson = cJSON_CreateObject(); // 创建一个空的子JSON对象
复制代码


  • 封装params键对应的值,也就是内部的JSON【这个内里的键名和阿里云上定义的要保持一致】
  1. cJSON_AddNumberToObject(params_cjson, "Humidity", humidity);
  2. cJSON_AddNumberToObject(params_cjson, "temperature", temperature);
  3. cJSON_AddNumberToObject(params_cjson, "LEDSwitch", LED_Switch);
复制代码



  • 封装外部大的JSON【消息ID号。取值范围0~4294967295,且每个消息ID在当前设备中具有唯一性】
  1. cJSON_AddItemToObject(json, "method", cJSON_CreateString("thing.service.property.post"));
  2. cJSON_AddItemToObject(json, "id", cJSON_CreateString("99119635"));
  3. cJSON_AddItemToObject(json, "params", params_cjson);
  4. cJSON_AddItemToObject(json, "version", cJSON_CreateString("1.0.0"));
复制代码

  • JSON格式封装好后,对内里的内容进行处理惩罚,重要是对,进行转义
  1. // 将JSON对象转换为无格式的字符串
  2. str = cJSON_PrintUnformatted(json);
  3. // 为MQTT发布添加转义字符
  4. for(i = 0; *str != '\0'; i++){
  5.     params_buf[i] = *str;
  6.     // 如果下一个字符是引号或逗号,添加转义字符
  7.     if(*(str + 1) == '"' || *(str + 1) == ','){
  8.         params_buf[++i] = '\\';
  9.     }
  10.     str++;
  11.     move_num++;
  12. }
  13. str = str - move_num; // 回退指针到JSON字符串的开始
复制代码

  • 上报消息,同时清除内存
  1. // 构建AT命令
  2. sprintf((char *)cmd,"AT+MQTTPUB=0,""ESP8266_Post"","%s",0,0\r\n",params_buf);
  3. // 发送AT命令并通过ESP8266模块
  4. ESP8266_Sent_AT(cmd, "OK", 500);
  5. // 清理JSON对象占用的内存
  6. cJSON_Delete(json);
  7. // 如果分配了额外的字符串空间,释放它
  8. if(str != NULL){
  9.     free(str);
  10.     str = NULL;
  11.     printf("释放str空间成功\r\n");
  12. }
复制代码
  肯定要及时开释空间,cJSON不绝利用malloc超级占内存
  吸收命令





  • 这里须要先容云端下发的指令,吸收后乐成发送乐成的相应格式,失败发送失败的相应格式【这里只相应乐成的】
  • 剖析下发的JSON字符串,找到params中,须要的数据
  • 在先容乐成,获取到数据后,向云平台发送接受乐成相应消息
  1. void rcv_json(void){
  2.         uint8_t cmd[1024]; // 用于存储构建的AT命令
  3.     uint8_t *ret = NULL; // 用于存储接收到的数据帧
  4.     cJSON *cjson = NULL; // 用于存储解析后的JSON对象
  5.         cJSON *re_json = NULL;
  6.         char *str = NULL;
  7.     char topic_buff[1024]; // 用于存储MQTT主题
  8.     int num; // 用于存储接收数据的数量
  9.     char recv_buffer[1024]; // 用于存储接收到的JSON数据
  10.         uint8_t params_buf[1024]; // 用于存储处理过的JSON字符串
  11.     uint16_t move_num = 0; // 用于记录字符串处理过程中的移动次数
  12.         int i = 0; // 循环迭代变量
  13.    
  14.     ret = atk_mw8266d_uart_rx_get_frame(); // 获取UART接收到的数据帧
  15.     atk_mw8266d_uart_rx_restart(); // 重启UART接收
  16.        
  17.     char *ptr_recv = strstr((const char *)ret,"+MQTTSUBRECV"); // 检查是否包含MQTT订阅数据标志
  18.    
  19.     if(ptr_recv!=NULL){ // 如果是MQTT订阅数据
  20.         memset(topic_buff,0,sizeof(topic_buff)); // 清空主题缓冲区
  21.         
  22.         sscanf((char *)ret,"+MQTTSUBRECV:0,%[^,],%d,%s",topic_buff,&num,recv_buffer); // 解析数据,提取主题、数据数量和JSON数据
  23.         
  24.         if(strstr(topic_buff,ESP8266_SET)) { // 如果主题包含特定标记
  25.             printf("接收数据成功,开始解析  %s\r\n",recv_buffer); // 打印接收到的数据
  26.             cjson = cJSON_Parse(recv_buffer); // 解析JSON数据
  27.                        
  28.         }
  29.         
  30.         if(cjson==NULL) // 如果JSON解析失败
  31.             printf("cjson 解析错误\r\n"); // 打印错误信息
  32.         else{
  33.             cJSON *json_data = cJSON_GetObjectItem(cjson,"params"); // 获取JSON对象中的params项
  34.             if(json_data==NULL){ // 如果params项不存在
  35.                 printf("cjson  没有数据\r\n"); // 打印错误信息
  36.                 return;
  37.             }
  38.             else{
  39.                 printf("cjson 内存大小为 = %d\r\n",sizeof(cjson)); // 打印JSON对象的内存大小
  40.                 // 解析数据
  41.                 if(cJSON_GetObjectItem(json_data,"LEDSwitch")!=NULL) // 如果存在LEDSwitch键
  42.                 {
  43.                     LED_Switch = cJSON_GetObjectItem(json_data,"LEDSwitch")->valueint; // 提取LEDSwitch的值
  44.                     printf("csjon解析成功 LED_Switch = %d\r\n",LED_Switch); // 打印LEDSwitch的值
  45.                                        
  46.                                         //接收响应
  47.                                         //------
  48.                                         sprintf((char *)cmd,"AT+MQTTPUB=0,""ESP8266_Post_re"","{\\"code\\": 200, \\"data\\": {}, \\"id\\": \\"%s\\", \\"message\\": \\"success\\", \\"version\\": \\"1.0.0\\"}",0,0\r\n",cJSON_GetObjectItem(cjson,"id")->valuestring);
  49.                                         printf("开始发送数据:%s\r\n", cmd);
  50.                                         ESP8266_Sent_AT(cmd, "OK", 500);
  51.                     //------
  52.                 }
  53.             }
  54.             cJSON_Delete(cjson); // 删除JSON对象,释放内存
  55.                        
  56.                         //接收响应
  57.             cJSON_Delete(re_json);
  58.                         if(str != NULL){
  59.                                 free(str);
  60.                                 str = NULL;
  61.                                 printf("释放str空间成功\r\n");
  62.                         }
  63.         }
  64.     }
  65. }
复制代码
源码地点:
CSDN:【免费】STM32HAL库++ESP8266+cJSON毗连阿里云物联网平台资源-CSDN文库
GitHub:CSDN_Share/STM32HAL库++ESP8266+cJSON毗连阿里云物联网平台 at master · lucasZyh/CSDN_Share (github.com)

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

欢乐狗

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表