本篇所实现的功能是我毕业设计的一部分,用于记录我的学习过程,以免忘记操作过程!
所使用的相关硬件:正点原子的esp8266模块、c8t6开发板一块、STLink v2、DHT11温湿度传感器一块、发光二级管一个、继电器两个
附上我的完整工程代码:代码
感谢博主:永栀哇
相关文章:1-ESP8266-AT指令初试化及部分基础知识2-STM32+ESP8266连接onenet并上传数据(HTTP)3-STM32+ESP8266连接onenet上传数据+远程控制(MQTT)
硬件接线:
最终呈现软硬件:
一、代码方面:(主要说明onenet.c和esp8266.c)
onenet.c中:
第一步要修改处
那么具体的三个参数在哪里找到捏?如下图,具体如何打开,在下文有表述:

第二步用于与oneNET端创建连接,此处无需修改
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//========================================================== // 函数名称: OneNet_DevLink // // 函数功能: 与onenet创建连接 // // 入口参数: 无 // // 返回参数: 1-成功 0-失败 // // 说明: 与onenet平台建立连接 //========================================================== _Bool OneNet_DevLink(void) { MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0}; //协议包 unsigned char *dataPtr; _Bool status = 1; printf("OneNet_DevLinkrnPROID: %s, AUIF: %s, DEVID:%srn", PROID, AUTH_INFO, DEVID); if(MQTT_PacketConnect(PROID, AUTH_INFO, DEVID, 256, 0, MQTT_QOS_LEVEL0, NULL, NULL, 0, &mqttPacket) == 0) { ESP8266_SendData(mqttPacket._data, mqttPacket._len); //上传平台 dataPtr = ESP8266_GetIPD(250); //等待平台响应 if(dataPtr != NULL) { if(MQTT_UnPacketRecv(dataPtr) == MQTT_PKT_CONNACK) { switch(MQTT_UnPacketConnectAck(dataPtr)) { case 0:printf("Tips: 连接成功rn");status = 0;break; case 1:printf("WARN: 连接失败:协议错误rn");break; case 2:printf("WARN: 连接失败:非法的clientidrn");break; case 3:printf("WARN: 连接失败:服务器失败rn");break; case 4:printf("WARN: 连接失败:用户名或密码错误rn");break; case 5:printf("WARN: 连接失败:非法链接(比如token非法)rn");break; default:printf("ERR: 连接失败:未知错误rn");break; } } } MQTT_DeleteBuffer(&mqttPacket); //删包 } else printf("WARN: MQTT_PacketConnect Failedrn"); return status; }
第三步用于将收集的温湿度数据,初始按钮命令等打包存于数组text[32]中,此处需要修改
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
36u8 key_LD = 0; //开关路灯 u8 key_FS = 0; //开关空调 u8 key_XYJ = 0; //远程开关洗衣机 extern u8 humidityH; //湿度整数部分 extern u8 humidityL; //湿度小数部分 extern u8 temperatureH; //温度整数部分 extern u8 temperatureL; //温度小数部分 unsigned char OneNet_FillBuf(char *buf) { char text[32]; memset(text, 0, sizeof(text)); strcpy(buf, ",;"); memset(text, 0, sizeof(text)); sprintf(text, "key_LD,%d;", key_LD); strcat(buf, text); memset(text, 0, sizeof(text)); sprintf(text, "key_FS,%d;", key_FS); strcat(buf, text); memset(text, 0, sizeof(text)); sprintf(text, "key_XYJ,%d;", key_XYJ); strcat(buf, text); memset(text, 0, sizeof(text)); sprintf(text, "Tempreture,%d.%d;",temperatureH,temperatureL); strcat(buf, text); memset(text, 0, sizeof(text)); sprintf(text, "Humidity,%d.%d;",humidityH,humidityL); strcat(buf, text); return strlen(buf); }
第四步 用于上传数据到平台,此处无需修改
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//========================================================== // 函数名称: OneNet_SendData // // 函数功能: 上传数据到平台 // // 入口参数: type:发送数据的格式 // // 返回参数: 无 // // 说明: //========================================================== void OneNet_SendData(void) { MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0}; //协议包 char buf[128]; short body_len = 0, i = 0; // printf("Tips: OneNet_SendData-MQTTrn"); memset(buf, 0, sizeof(buf)); body_len = OneNet_FillBuf(buf); //获取当前需要发送的数据流的总长度 if(body_len) { if(MQTT_PacketSaveData(DEVID, body_len, NULL, 5, &mqttPacket) == 0) //封包 { for(; i < body_len; i++) mqttPacket._data[mqttPacket._len++] = buf[i]; ESP8266_SendData(mqttPacket._data, mqttPacket._len); //上传数据到平台 // printf("Send %d Bytesrn", mqttPacket._len); MQTT_DeleteBuffer(&mqttPacket); //删包 } else printf("WARN: EDP_NewBuffer Failedrn"); } }
第五步 用于平台返回数据检测,此处需要修改,具体修改处已标记
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
127
128
129//========================================================== // 函数名称: OneNet_RevPro // // 函数功能: 平台返回数据检测 // // 入口参数: dataPtr:平台返回的数据 // // 返回参数: 无 // // 说明: //========================================================== void OneNet_RevPro(unsigned char *cmd) { MQTT_PACKET_STRUCTURE mqttPacket = {NULL, 0, 0, 0}; //协议包 char *req_payload = NULL; char *cmdid_topic = NULL; unsigned short req_len = 0; unsigned char type = 0; short result = 0; char *dataPtr = NULL; char numBuf[10]; int num = 0; type = MQTT_UnPacketRecv(cmd);//MQTT数据接收类型判断 switch(type) { case MQTT_PKT_CMD: //命令下发 //参数1收到的 result = MQTT_UnPacketCmd(cmd, &cmdid_topic, &req_payload, &req_len); //解出topic和消息体 if(result == 0) { //打印收到的信息,参数2数据,参数3数据长度 printf( "cmdid: %s, req: %s, req_len: %drn", cmdid_topic, req_payload, req_len); if(MQTT_PacketCmdResp(cmdid_topic, req_payload, &mqttPacket) == 0) //命令回复组包 { printf( "Tips: Send CmdResprn"); ESP8266_SendData(mqttPacket._data, mqttPacket._len); //回复命令 MQTT_DeleteBuffer(&mqttPacket); //删包 } } break; case MQTT_PKT_PUBACK: //发送Publish消息,平台回复的Ack if(MQTT_UnPacketPublishAck(cmd) == 0) printf( "Tips: MQTT Publish Send OKrn"); break; default: result = -1; break; } ESP8266_Clear(); //清空缓存 if(result == -1) return; dataPtr = strchr(req_payload, ':'); //搜索':' if(dataPtr != NULL && result != -1) //如果找到了 { dataPtr++; while(*dataPtr >= '0' && *dataPtr <= '9') //判断是否是下发的命令控制数据 { numBuf[num++] = *dataPtr++; } numBuf[num] = 0; num = atoi((const char *)numBuf); //转为数值形式 // /*************************主要修改此处***************************/ if(strstr((char *)req_payload, "key_LD")) //搜索"key_LD" { if(num == 1) //控制数据如果为1,代表开 { LED=1; } else if(num == 0) //控制数据如果为0,代表关 { LED=0; } key_LD = num; //更新数据到云平台 } if(strstr((char *)req_payload, "key_FS")) //搜索"key_FS" { if(num == 1) //控制数据如果为0,代表开 { JDQ_FS = 0; } else if(num == 0) //控制数据如果为1,代表关 { JDQ_FS = 1; } key_FS = num; //更新数据到云平台 } if(strstr((char *)req_payload, "key_XYJ")) //搜索"key_FS" { if(num == 1) //控制数据如果为0,代表开 { JDQ_XYJ = 0; } else if(num == 0) //控制数据如果为1,代表关 { JDQ_XYJ = 1; } key_XYJ = num; //更新数据到云平台 } } if(type == MQTT_PKT_CMD || type == MQTT_PKT_PUBLISH) { MQTT_FreeBuffer(cmdid_topic); MQTT_FreeBuffer(req_payload); } }
ESP8266.c
第一步需要修改的:连接自己家的WI-FI
第二步初始化 ESP8266
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//========================================================== // 函数名称: ESP8266_Init // // 函数功能: 初始化ESP8266 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_Init(void) { GPIO_InitTypeDef GPIO_Initure; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE); //ESP8266复位引脚 GPIO_Initure.GPIO_Mode = GPIO_Mode_Out_PP; GPIO_Initure.GPIO_Pin = GPIO_Pin_1; //GPIOB1-复位 GPIO_Initure.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOB, &GPIO_Initure); GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_RESET); delay_ms(250); GPIO_WriteBit(GPIOB, GPIO_Pin_1, Bit_SET); delay_ms(500); ESP8266_Clear(); printf("ATrn"); while(ESP8266_SendCmd("ATrnr", "OK", 200)) delay_ms(500); printf("CWMODErn"); while(ESP8266_SendCmd("AT+CWMODE=1rn", "OK", 200)) delay_ms(500); printf("AT+CWDHCPrn"); while(ESP8266_SendCmd("AT+CWDHCP=1,1rn", "OK", 200)) delay_ms(500); printf("CWJAPrn"); while(ESP8266_SendCmd(ESP8266_WIFI_INFO, "GOT IP", 200)) delay_ms(500); printf("CIPSTARTrn"); while(ESP8266_SendCmd(ESP8266_ONENET_INFO, "CONNECT", 200)) delay_ms(500); printf("ESP8266 Init OKrn"); }
第三步初始化c8t6串口二用于启动esp8266(A2、A3)
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/* ************************************************************ * 函数名称: Usart2_Init * * 函数功能: 串口2初始化 * * 入口参数: baud:设定的波特率 * * 返回参数: 无 * * 说明: TX-PA2 RX-PA3 ************************************************************ */ void Usart2_Init(unsigned int baud) { GPIO_InitTypeDef gpio_initstruct; USART_InitTypeDef usart_initstruct; NVIC_InitTypeDef nvic_initstruct; RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE); RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE); //PA2 TXD gpio_initstruct.GPIO_Mode = GPIO_Mode_AF_PP; gpio_initstruct.GPIO_Pin = GPIO_Pin_2; gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio_initstruct); //PA3 RXD gpio_initstruct.GPIO_Mode = GPIO_Mode_IN_FLOATING; gpio_initstruct.GPIO_Pin = GPIO_Pin_3; gpio_initstruct.GPIO_Speed = GPIO_Speed_50MHz; GPIO_Init(GPIOA, &gpio_initstruct); usart_initstruct.USART_BaudRate = baud; usart_initstruct.USART_HardwareFlowControl = USART_HardwareFlowControl_None; //无硬件流控 usart_initstruct.USART_Mode = USART_Mode_Rx | USART_Mode_Tx; //接收和发送 usart_initstruct.USART_Parity = USART_Parity_No; //无校验 usart_initstruct.USART_StopBits = USART_StopBits_1; //1位停止位 usart_initstruct.USART_WordLength = USART_WordLength_8b; //8位数据位 USART_Init(USART2, &usart_initstruct); USART_Cmd(USART2, ENABLE); //使能串口 USART_ITConfig(USART2, USART_IT_RXNE, ENABLE); //使能接收中断 nvic_initstruct.NVIC_IRQChannel = USART2_IRQn; nvic_initstruct.NVIC_IRQChannelCmd = ENABLE; nvic_initstruct.NVIC_IRQChannelPreemptionPriority = 0; nvic_initstruct.NVIC_IRQChannelSubPriority = 0; NVIC_Init(&nvic_initstruct); } //========================================================== // 函数名称: USART2_IRQHandler // // 函数功能: 串口2收发中断 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void USART2_IRQHandler(void) { if(USART_GetITStatus(USART2, USART_IT_RXNE) != RESET) //接收中断 { if(esp8266_cnt >= sizeof(esp8266_buf)) esp8266_cnt = 0; //防止串口被刷爆 esp8266_buf[esp8266_cnt++] = USART2->DR; USART_ClearFlag(USART2, USART_FLAG_RXNE); } } /* ************************************************************ * 函数名称: Usart_SendString * * 函数功能: 串口数据发送 * * 入口参数: USARTx:串口组 * str:要发送的数据 * len:数据长度 * * 返回参数: 无 * * 说明: ************************************************************ */ void Usart2_SendString(unsigned char *str, unsigned short len) { unsigned short count = 0; for(; count < len; count++) { USART_SendData(USART2, *str++); //发送数据 while(USART_GetFlagStatus(USART2, USART_FLAG_TC) == RESET); //等待发送完成 } }
第四步ESP8266相关函数,无需修改
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
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156//========================================================== // 函数名称: ESP8266_Clear // // 函数功能: 清空缓存 // // 入口参数: 无 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_Clear(void) { memset(esp8266_buf, 0, sizeof(esp8266_buf)); esp8266_cnt = 0; } //========================================================== // 函数名称: ESP8266_WaitRecive // // 函数功能: 等待接收完成 // // 入口参数: 无 // // 返回参数: REV_OK-接收完成 REV_WAIT-接收超时未完成 // // 说明: 循环调用检测是否接收完成 //========================================================== _Bool ESP8266_WaitRecive(void) { if(esp8266_cnt == 0) //如果接收计数为0 则说明没有处于接收数据中,所以直接跳出,结束函数 return REV_WAIT; if(esp8266_cnt == esp8266_cntPre) //如果上一次的值和这次相同,则说明接收完毕 { esp8266_cnt = 0; //清0接收计数 return REV_OK; //返回接收完成标志 } esp8266_cntPre = esp8266_cnt; //置为相同 return REV_WAIT; //返回接收未完成标志 } //========================================================== // 函数名称: ESP8266_SendCmd // // 函数功能: 发送命令 // // 入口参数: cmd:命令 // res:需要检查的返回指令 // // 返回参数: 0-成功 1-失败 // // 说明: //========================================================== _Bool ESP8266_SendCmd(char *cmd, char *res, u16 time) { Usart2_SendString((unsigned char *)cmd, strlen((const char *)cmd)); while(time--) { if(ESP8266_WaitRecive() == REV_OK) //如果收到数据 { if(strstr((const char *)esp8266_buf, res) != NULL) //如果检索到关键词 { ESP8266_Clear(); //清空缓存 return 0; } } delay_ms(10); } return 1; } //========================================================== // 函数名称: ESP8266_SendData // // 函数功能: 发送数据 // // 入口参数: data:数据 // len:长度 // // 返回参数: 无 // // 说明: //========================================================== void ESP8266_SendData(unsigned char *data, unsigned short len) { char cmdBuf[32]; ESP8266_Clear(); //清空接收缓存 sprintf(cmdBuf, "AT+CIPSEND=%drn", len); //发送命令 if(!ESP8266_SendCmd(cmdBuf, ">", 200)) //收到‘>’时可以发送数据 { Usart2_SendString(data, len); //发送设备连接请求数据 } } //========================================================== // 函数名称: ESP8266_GetIPD // // 函数功能: 获取平台返回的数据 // // 入口参数: 等待的时间(乘以10ms) // // 返回参数: 平台返回的原始数据 // // 说明: 不同网络设备返回的格式不同,需要去调试 // 如ESP8266的返回格式为 "+IPD,x:yyy" x代表数据长度,yyy是数据内容 //========================================================== unsigned char *ESP8266_GetIPD(unsigned short timeOut) { char *ptrIPD = NULL; do { if(ESP8266_WaitRecive() == REV_OK) //如果接收完成 { ptrIPD = strstr((char *)esp8266_buf, "IPD,"); //搜索“IPD”头 if(ptrIPD == NULL) //如果没找到,可能是IPD头的延迟,还是需要等待一会,但不会超过设定的时间 { //printf(""IPD" not foundrn"); } else { ptrIPD = strchr(ptrIPD, ':'); //找到':' if(ptrIPD != NULL) { ptrIPD++; return (unsigned char *)(ptrIPD); } else return NULL; } } delay_ms(5); //延时等待 } while(timeOut--); return NULL; //超时还未找到,返回空指针 }
二、关于oneNET平台最新的可视化View的具体使用方法
1、创建oneNet账号,这个自己解决啦
点击链接控制台首页,进入后注册账号
2、在菜单找到如图按键,点击多协议接入
3、进入多协议接入后,初始界面应该是MQTT(旧版)【反正我的界面是这个】点击添加产品
蓝色框里自己填,红色的可以按照我的来勾选,主要还是看自己的需求
4、点击你创建的设备
5、进入后的界面,会出现下面的图,点击添加设备
此处的鉴权信息自己填一串就好,但是切记,我们在代码onenet.c需要修改的,不要忘记!!
6、创建好设备后,出现如下界面,记住所框数据
7、点击设备列表,打开刚刚创建的设备,并进行配置,并通过MQTT协议下发命令,打开路灯
注意发送格式!!!!!!!!!!为什么会出现这个格式呢?下文会有解释
8、点开应用管理,CSDN论坛好多大佬都有写在应用管理处创建一个应用就可以,结果更新后,啥都没了,我咧个去,坑爹啊!!要用新版的可视化view,难顶,下面是操作过程:需要注意的是,想要实现按钮下发指令,需要专业版可视化,他有七天免费试用,期间建立的工程皆可保留,切记不要超过时间啦!
9、具体控件所在位置说明
按钮(我的会员过期了,所以用不了)
仪表盘(免费)
文字:
10、数据流说明:(重点!!!!)
1、 数据流处理如下,点击一个仪表盘,完成所有的的配置,如下图所示,一共有五个数据:
蓝色的按照我的填就好,红色的是你的Master-APIkey具体位置如图:
2、还有就是按钮的设置啦,需要注意的点如下图:(下面以路灯为例,其他的一致)
3、最后说明一下:下发的命令格式(key_XX:{V})和私有过滤器是个是个什么玩意!
下发的命令格式,是因为我们在源码处给了一个奇葩造型(如下图),且需要先搜索是否有 ‘ :’故而需要加上冒号,而冒号后边的{V}则是我们所需数据,这个数据由按钮控制下发。
找冒号:
而私有过滤器(个人理解,要不官方感觉很抽象):就是保证页面初始化值,同时保证该按钮的传递值属于这个按钮的本身数据
以上,再次鸣谢永栀哇
最后
以上就是欣喜石头最近收集整理的关于STM32F103C8T6+ESP8266+MQTT使用最新版的oneNet可视化View实现远程控制(详细)硬件接线:最终呈现软硬件:一、代码方面:(主要说明onenet.c和esp8266.c)ESP8266.c二、关于oneNET平台最新的可视化View的具体使用方法的全部内容,更多相关STM32F103C8T6+ESP8266+MQTT使用最新版内容请搜索靠谱客的其他文章。
发表评论 取消回复