实验使用资源:正点原子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 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126
|
void atk_mw8266d_uart_printf(char *fmt, ...) { va_list ap; uint16_t len; va_start(ap, fmt); vsprintf((char *)g_uart_tx_buf, fmt, ap); va_end(ap); len = strlen((const char *)g_uart_tx_buf); HAL_UART_Transmit(&huart3, g_uart_tx_buf, len, HAL_MAX_DELAY); }
void atk_mw8266d_uart_rx_restart(void) { g_uart_rx_frame.sta.len = 0; g_uart_rx_frame.sta.finsh = 0; }
uint8_t *atk_mw8266d_uart_rx_get_frame(void) { if (g_uart_rx_frame.sta.finsh == 1) { g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = '\0'; return g_uart_rx_frame.buf; } else { return NULL; } }
uint16_t atk_mw8266d_uart_rx_get_frame_len(void) { if (g_uart_rx_frame.sta.finsh == 1) { return g_uart_rx_frame.sta.len; } else { return 0; } }
void USART1_IRQHandler(void) { #if SYS_SUPPORT_OS OSIntEnter(); #endif HAL_UART_IRQHandler(&huart1);
while (HAL_UART_Receive_IT(&huart1, (uint8_t *)g_rx_buffer, RXBUFFERSIZE) != HAL_OK) { }
#if SYS_SUPPORT_OS OSIntExit(); #endif }
void USART3_IRQHandler(void) {
HAL_UART_IRQHandler(&huart3); uint8_t tmp; if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_ORE) != RESET) { __HAL_UART_CLEAR_OREFLAG(&huart3); (void)huart3.Instance->SR; (void)huart3.Instance->DR; } if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_RXNE) != RESET) { HAL_UART_Receive(&huart3, &tmp, 1, HAL_MAX_DELAY); if (g_uart_rx_frame.sta.len < (256 - 1))
{ g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp; g_uart_rx_frame.sta.len++; } else { g_uart_rx_frame.sta.len = 0; g_uart_rx_frame.buf[g_uart_rx_frame.sta.len] = tmp; g_uart_rx_frame.sta.len++; } } if (__HAL_UART_GET_FLAG(&huart3, UART_FLAG_IDLE) != RESET) { g_uart_rx_frame.sta.finsh = 1; __HAL_UART_CLEAR_IDLEFLAG(&huart3); } }
|
- 在【usart.c】上面的的
/* USER CODE BEGIN 0 */
和/* USER CODE END 0 */
之间加入下面的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30
| #include <stdarg.h> #include <stdio.h> #include <string.h>
#if 1 #pragma import(__use_no_semihosting)
struct __FILE { int handle;
};
FILE __stdout;
void _sys_exit(int x) { x = x; }
int fputc(int ch, FILE *f) { while((USART1->SR&0X40)==0); USART1->DR = (uint8_t) ch; return ch; } #endif
uint8_t g_rx_buffer[RXBUFFERSIZE];
|
- 在【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 2 3 4 5 6 7 8 9 10 11
| static struct { uint8_t buf[256]; struct { uint16_t len : 15; uint16_t finsh : 1; } sta; } g_uart_rx_frame = {0}; static uint8_t g_uart_tx_buf[1024]; #define RXBUFFERSIZE 1
|
- 在【usart.h】中的
/* USER CODE BEGIN Prototypes */
和/* USER CODE END Prototypes */
之间加入下面的代码
1 2 3 4
| void atk_mw8266d_uart_printf(char *fmt, ...); void atk_mw8266d_uart_rx_restart(void); uint8_t *atk_mw8266d_uart_rx_get_frame(void); uint16_t atk_mw8266d_uart_rx_get_frame_len(void);
|
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)
-
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)
-
void cJSON_AddItemToObject(cJSON *json, cJSON *, cJSON_CreateArray())
-
cJSON *cJSON_GetObjectItem(cJSON *object, const char *string)
上报消息
当在物联网平台为产品定义物模型后,设备需要按照Alink JSON格式上报属性或事件
Alink协议是针对物联网开发领域设计的一种数据交换规范,数据格式是JSON,用于设备端和物联网平台的双向通信,更便捷地实现和规范了设备端和物联网平台之间的业务数据交互
- 先看一下官方给的数据上报的格式
- 我们必须要传的就是method、id、version、params这四个组成的JSON字符串,其中params传输的内容也是个小的JSON字符串
- 根据这个我们使用cJSON进行JSON格式的封装
1 2
| cJSON *json = cJSON_CreateObject(); cJSON *params_cjson = cJSON_CreateObject();
|
- 封装params键对应的值,也就是内部的JSON【这个里面的键名和阿里云上定义的要保持一致】
1 2 3
| cJSON_AddNumberToObject(params_cjson, "Humidity", humidity); cJSON_AddNumberToObject(params_cjson, "temperature", temperature); cJSON_AddNumberToObject(params_cjson, "LEDSwitch", LED_Switch);
|
- 封装外部大的JSON【消息ID号。取值范围0~4294967295,且每个消息ID在当前设备中具有唯一性】
1 2 3 4
| cJSON_AddItemToObject(json, "method", cJSON_CreateString("thing.service.property.post")); cJSON_AddItemToObject(json, "id", cJSON_CreateString("99119635")); cJSON_AddItemToObject(json, "params", params_cjson); cJSON_AddItemToObject(json, "version", cJSON_CreateString("1.0.0"));
|
- JSON格式封装好后,对里面的内容进行处理,主要是对
,
进行转义
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| str = cJSON_PrintUnformatted(json);
for(i = 0; *str != '\0'; i++){ params_buf[i] = *str; if(*(str + 1) == '"' || *(str + 1) == ','){ params_buf[++i] = '\\'; } str++; move_num++; } str = str - move_num;
|
- 上报消息,同时清除内存
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| sprintf((char *)cmd,"AT+MQTTPUB=0,\""ESP8266_Post"\",\"%s\",0,0\r\n",params_buf);
ESP8266_Sent_AT(cmd, "OK", 500);
cJSON_Delete(json);
if(str != NULL){ free(str); str = NULL; printf("释放str空间成功\r\n"); }
|
一定要及时释放空间,cJSON不断使用malloc超级占内存
接收命令
-
这里需要介绍云端下发的指令,接收后成功发送成功的响应格式,失败发送失败的响应格式【这里只响应成功的】
-
解析下发的JSON字符串,找到params中,需要的数据
-
在介绍成功,获取到数据后,向云平台发送接受成功响应消息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65
| void rcv_json(void){ uint8_t cmd[1024]; uint8_t *ret = NULL; cJSON *cjson = NULL; cJSON *re_json = NULL; char *str = NULL; char topic_buff[1024]; int num; char recv_buffer[1024]; uint8_t params_buf[1024]; uint16_t move_num = 0; int i = 0; ret = atk_mw8266d_uart_rx_get_frame(); atk_mw8266d_uart_rx_restart(); char *ptr_recv = strstr((const char *)ret,"+MQTTSUBRECV"); if(ptr_recv!=NULL){ memset(topic_buff,0,sizeof(topic_buff)); sscanf((char *)ret,"+MQTTSUBRECV:0,%[^,],%d,%s",topic_buff,&num,recv_buffer); if(strstr(topic_buff,ESP8266_SET)) { printf("接收数据成功,开始解析 %s\r\n",recv_buffer); cjson = cJSON_Parse(recv_buffer); } if(cjson==NULL) printf("cjson 解析错误\r\n"); else{ cJSON *json_data = cJSON_GetObjectItem(cjson,"params"); if(json_data==NULL){ printf("cjson 没有数据\r\n"); return; } else{ printf("cjson 内存大小为 = %d\r\n",sizeof(cjson)); if(cJSON_GetObjectItem(json_data,"LEDSwitch")!=NULL) { LED_Switch = cJSON_GetObjectItem(json_data,"LEDSwitch")->valueint; printf("csjon解析成功 LED_Switch = %d\r\n",LED_Switch); 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); printf("开始发送数据:%s\r\n", cmd); ESP8266_Sent_AT(cmd, "OK", 500); } } cJSON_Delete(cjson); cJSON_Delete(re_json); if(str != NULL){ free(str); str = NULL; printf("释放str空间成功\r\n"); } } } }
|
源码地址:
CSDN:【免费】STM32HAL库++ESP8266+cJSON连接阿里云物联网平台资源-CSDN文库
GitHub:CSDN_Share/STM32HAL库++ESP8266+cJSON连接阿里云物联网平台 at master · lucasZyh/CSDN_Share (github.com)