在STM32上使用ESP8266模块实现网络通信

源代码

ESP8266.h

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
#ifndef __ESP8266_H_
#define __ESP8266_H_

#define __ESP8266_DEBUG // 定义这个宏会将ESP8266回传到单片机的数据传到电脑的串口助手里
#include "stm32f10x.h"
#include "Delay.h"
#include "string.h"
#include "stdio.h"
#define ESP_EN GPIO_Pin_1 // ESP8266使能引脚的选择
#define ESP_EN_Periph GPIOA // ESP8266使能引脚所在的外设
#define RCC_ESP_EN_Periph RCC_APB2Periph_GPIOA
#define RCC_ESP_EN_PeriphCmd RCC_APB2PeriphClockCmd

/* ESP8266选择了哪一个USART,这里使用条件编译。仅列出C8T6所支持的3个外设,以及USART1的重定义
该宏用于初始化函数。
*/
#define ESP8266_SELECT_USART1
// #define ESP8266_SELECT_USART2
// #define ESP8266_SELECT_USART3
// #define ESP8266_SELECT_USART1_AFIO

// DEBUG选择哪一个USART
#define DEBUG_SELECT_USART2
// #define DEBUG_SELECT_USART3

// ESP8266以及DEBUG选择的USART外设
#define ESP_USART USART1
#define ESP_USART_IRQn USART1_IRQn
#define ESP_USART_Interrupt USART1_IRQHandler
#define DEBUG_USART USART2
#define DEBUG_USART_IRQn USART2_IRQn
#define DEBUG_USART_Interrupt USART2_IRQHandler

#define ESP8266_BaudRate 115200 // ESP8266的波特率
#define ESP8266_PreemptionPriority 0 // ESP8266的中断优先级
#define ESP8266_SubPriority 0
#define WIFI_TIMELIMITED 20 // 连接wifi时的超时时间,单位秒
#define MQTT_TIMELIMITED 120 // 连接MQTT时的超时时间,单位秒

typedef unsigned char byte;
typedef enum { false, true } bool;

typedef enum {
NO_ERROR, // 没有错误
AT_ERROR, // AT指令始终不返回"OK",可能是模块异常
WIFI_CONNECT_ERROR, // WIFI未正常连接
WIFI_CONNECT_TIMEOUT, // WIFI连接超时
MQTT_INIT_ERROR, // MQTT初始化错误
MQTT_CONNECT_ERROR, // MQTT服务器连接失败
MQTT_CONNECT_TIMEOUT, // MQTT连接超时
MQTT_PUBLISH_FAIL, // MQTT数据上传失败
MQTT_SUBSCRIBE_FAIL, // MQTT的topic订阅失败
} ERROR_LIST;

typedef struct
{
const char* ClientID;
const char* UserName;
const char* Password;
const char* SeverAdress;
unsigned int port;
}MQTT_InitStructTypeDef;

ERROR_LIST ESP8266_Init();
ERROR_LIST ESP8266_InitConfig();
void ESP8266_Cmd(byte state);
void ESP8266_SendString(char* str);
void ESP8266_SendByte(byte ux);
void ESP8266_SendHexArray(byte* data, int len);
char* ESP8266_GetString();
void ESP8266_Reset();
bool ESP8266_IsConnectWifi();
ERROR_LIST ESP8266_ConnectWifi(const char* name, const char* pwd);
void ESP8266_DisconnectWifi();

ERROR_LIST ESP8266_ConnectMQTTSever(MQTT_InitStructTypeDef* MQTT_InitStructure);
void ESP8266_DisconnectMQTTSever();
bool ESP8266_IsConnectMQTT();
ERROR_LIST ESP8266_MQTTPublishString(const char* topic, const char* data);
ERROR_LIST ESP8266_MQTTPublishHex(const char* topic, byte* data, int len);
ERROR_LIST ESP8266_MQTTPublishVariable(const char* topic, const char* name, const char* val);
ERROR_LIST ESP8266_MQTTSubscribeTopic(const char* topic);
ERROR_LIST ESP8266_MQTTUnsubscribeTopic(const char* topic);
bool ESP8266_IsGetSubMessage();

bool strfind(const char* src, const char* inc);
void __esp8266_status(byte ux);

#ifdef __ESP8266_DEBUG
void DEBUG_SendString(char* s);
void DEBUG_SendByte(byte ux);
#endif

#endif

ESP8266.c

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
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
#include "esp8266.h"
#include "stm32f10x.h"

byte ESP8266_ReceiveBuff[2048]; // ESP8266的接受缓冲区
unsigned int ESP8266_BuffPoint = 0; // 缓冲区当前指针
bool ESP8266_BuffFlag = false; // 现在是否从ESP8266接受到了数据
bool WIFI_CONNECT_FLAG = false; // Wifi状态标志位,连接wifi则置true,断开连接则置false。由ESP8266的中断函数修改
bool MQTT_CONNECT_FLAG = false; // MQTT状态标志位,连接MQTT则置true,断开连接则置false。由ESP8266的中断函数修改
bool MQTT_SUBRECV_FLAG = false; // MQTTSUBRECV标志位,接收到订阅的消息则置true
byte FormatBuffer[1024]; // 格式化字符串所用的缓冲区
byte BackupBuffer[1024]; // 格式化字符串的备份缓冲区

ERROR_LIST ESP8266_Init()
{
/*
该函数用于ESP8266的正常运行的STM32外设。
默认使用APB2外设的USART1与ESP8266通信,即PA2为单片机的Tx,PA3为单片机的Rx。
用宏ESP_EN和ESP_EN_Periph共同决定ESP8266的使能引脚,默认为低电平,需要调用ESP8266_CMD()函数开启。
返回值为0则说明正常,非0则为错误,详情看枚举。
*/

// 初始化使能引脚
RCC_ESP_EN_PeriphCmd(RCC_ESP_EN_Periph, ENABLE);
GPIO_InitTypeDef g;
g.GPIO_Mode = GPIO_Mode_IPD;
g.GPIO_Speed = GPIO_Speed_50MHz;
g.GPIO_Pin = ESP_EN;
GPIO_Init(ESP_EN_Periph, &g);

#ifdef ESP8266_SELECT_USART1
// 初始化UART引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
g.GPIO_Pin = GPIO_Pin_9;
g.GPIO_Mode = GPIO_Mode_AF_PP;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &g);
g.GPIO_Pin = GPIO_Pin_10;
g.GPIO_Mode = GPIO_Mode_IPU;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &g);
// 初始化USART
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
#endif

#ifdef ESP8266_SELECT_USART1_AFIO
// 初始化UART引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
g.GPIO_Pin = GPIO_Pin_6;
g.GPIO_Mode = GPIO_Mode_AF_PP;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &g);
g.GPIO_Pin = GPIO_Pin_7;
g.GPIO_Mode = GPIO_Mode_IPU;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &g);
// 使用AFIO重映射引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_AFIO, ENABLE);
GPIO_PinRemapConfig(GPIO_Remap_USART1, ENABLE);
// 初始化USART
RCC_APB2PeriphClockCmd(RCC_APB2Periph_USART1, ENABLE);
#endif

#ifdef ESP8266_SELECT_USART2
// 初始化UART引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
g.GPIO_Pin = GPIO_Pin_2;
g.GPIO_Mode = GPIO_Mode_AF_PP;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &g);
g.GPIO_Pin = GPIO_Pin_3;
g.GPIO_Mode = GPIO_Mode_IPU;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &g);
// 初始化USART
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
#endif

#ifdef ESP8266_SELECT_USART3
// 初始化UART引脚
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
g.GPIO_Pin = GPIO_Pin_10;
g.GPIO_Mode = GPIO_Mode_AF_PP;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &g);
g.GPIO_Pin = GPIO_Pin_11;
g.GPIO_Mode = GPIO_Mode_IPU;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &g);
// 初始化USART
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
#endif

USART_InitTypeDef u;
u.USART_BaudRate = ESP8266_BaudRate;
u.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
u.USART_Mode = USART_Mode_Rx | USART_Mode_Tx;
u.USART_Parity = USART_Parity_No;
u.USART_StopBits = USART_StopBits_1;
u.USART_WordLength = USART_WordLength_8b;
USART_Init(ESP_USART, &u);
// 配置接受中断
USART_ITConfig(ESP_USART, USART_IT_RXNE, ENABLE);
NVIC_InitTypeDef n;
n.NVIC_IRQChannel = ESP_USART_IRQn;
n.NVIC_IRQChannelCmd = ENABLE;
n.NVIC_IRQChannelPreemptionPriority = ESP8266_PreemptionPriority;
n.NVIC_IRQChannelSubPriority = ESP8266_SubPriority;
NVIC_Init(&n);
USART_Cmd(ESP_USART, ENABLE);

#ifdef __ESP8266_DEBUG

#ifdef DEBUG_SELECT_USART2
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART2, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOA, ENABLE);
g.GPIO_Mode = GPIO_Mode_AF_PP;
g.GPIO_Pin = GPIO_Pin_2;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &g);
g.GPIO_Mode = GPIO_Mode_IPU;
g.GPIO_Pin = GPIO_Pin_3;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOA, &g);
#endif

#ifdef DEBUG_SELECT_USART3
RCC_APB1PeriphClockCmd(RCC_APB1Periph_USART3, ENABLE);
RCC_APB2PeriphClockCmd(RCC_APB2Periph_GPIOB, ENABLE);
g.GPIO_Mode = GPIO_Mode_AF_PP;
g.GPIO_Pin = GPIO_Pin_10;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &g);
g.GPIO_Mode = GPIO_Mode_IPU;
g.GPIO_Pin = GPIO_Pin_11;
g.GPIO_Speed = GPIO_Speed_50MHz;
GPIO_Init(GPIOB, &g);
#endif

u.USART_BaudRate = ESP8266_BaudRate;
u.USART_HardwareFlowControl = USART_HardwareFlowControl_None;
u.USART_Mode = USART_Mode_Tx | USART_Mode_Rx;
u.USART_Parity = USART_Parity_No;
u.USART_StopBits = USART_StopBits_1;
u.USART_WordLength = USART_WordLength_8b;
USART_Init(DEBUG_USART, &u);
USART_ITConfig(DEBUG_USART, USART_IT_RXNE, ENABLE);
n.NVIC_IRQChannel = DEBUG_USART_IRQn;
n.NVIC_IRQChannelCmd = ENABLE;
n.NVIC_IRQChannelPreemptionPriority = 1;
n.NVIC_IRQChannelSubPriority = 1;
NVIC_Init(&n);
USART_Cmd(DEBUG_USART, ENABLE);
#endif

return NO_ERROR;
}

ERROR_LIST ESP8266_InitConfig()
{
// 该函数用于初始化ESP8266模块,并用AT指令对ATE置0,CWMODE置1,便于连接wifi。
bool st = false;
for (byte i = 0; i < 10 && !st; i++)
{
// 连续传10次AT指令,若回复中不存在“OK”则模块存在问题
ESP8266_SendString("AT\r\n");
Delay_ms(100);
st = strfind(ESP8266_GetString(), "OK");
}
if (!st)
{
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: AT_ERROR\r\n");
#endif
return AT_ERROR;
}
ESP8266_SendString("ATE0\r\n");
Delay_ms(10);
ESP8266_SendString("AT+CWMODE=1\r\n"); // 用ESP8266作为设备终端
Delay_ms(10);
ESP8266_GetString();
}

void ESP8266_Cmd(byte state)
{
// 使能或失能ESP8266的EN引脚
if (state) GPIO_SetBits(ESP_EN_Periph, ESP_EN);
else GPIO_ResetBits(ESP_EN_Periph, ESP_EN);
}

void ESP8266_Reset()
{
// 复位ESP8266
ESP8266_SendString("AT+RST\r\n");
Delay_ms(500);
ESP8266_GetString();
ESP8266_SendString("AT+RST\r\n");
Delay_ms(500);
ESP8266_GetString();
}

char* ESP8266_GetString()
{
// 获取ESP8266回传的字符串,若没有数据则返回(char*)0
// 同时本函数还具有清除缓冲区的作用
if (ESP8266_BuffFlag)
{
ESP8266_BuffFlag = false;
MQTT_SUBRECV_FLAG = false;
ESP8266_ReceiveBuff[ESP8266_BuffPoint] = '\0';
ESP8266_BuffPoint = 0;
return ESP8266_ReceiveBuff;
}
else return (char*)0;
}

void ESP8266_SendString(char* str)
{
/*
给ESP8266传递字符串(用于AT指令)
本函数只负责传递以'\0'结尾的字符串,\r\n回车符需要手动写在str里。
*/
while (*str)
{
ESP8266_SendByte(*str);
str++;
}
}

void ESP8266_SendByte(byte ux)
{
// 给ESP8266发送一字节数据
USART_SendData(ESP_USART, ux);
while (USART_GetFlagStatus(ESP_USART, USART_FLAG_TXE) == RESET);
}

void ESP8266_SendHexArray(byte* data, int len)
{
// 给ESP8266发生十六进制数组
for (int i = 0; i < len; i++)
ESP8266_SendByte(data[i]);
}

bool ESP8266_IsConnectWifi()
{
// 若ESP8266现在正连着Wifi则返回true,否则返回false
return WIFI_CONNECT_FLAG;
}

ERROR_LIST ESP8266_ConnectWifi(const char* name, const char* pwd)
{
// 连接WIFI。注意name和pwd中出现的特殊符号如逗号和双引号,需要用转义字符
// 若之前工程已经配置过了ESP8266的Wifi连接情况,可以不用本函数,使用ESP8266_IsConnectWifi()检测连接状态即可。
ESP8266_SendString("ATE0\r\n");
Delay_ms(10);
ESP8266_DisconnectWifi();
sprintf(FormatBuffer, "AT+CWJAP=\"%s\",\"%s\"\r\n", name, pwd);
ESP8266_GetString(); // 清空缓冲区
ESP8266_SendString(FormatBuffer);
for (int i = 0; i < WIFI_TIMELIMITED * 100; i++)
{
if (!ESP8266_BuffFlag) Delay_ms(10);
else
{
Delay_s(1);
char* t = ESP8266_GetString();
if (strfind(t, "OK")) return NO_ERROR;
else
{
Delay_ms(10);
if (WIFI_CONNECT_FLAG) return NO_ERROR;
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: WIFI_CONNECTION_ERROR\r\n");
#endif
return WIFI_CONNECT_ERROR;
}
}
}
if (WIFI_CONNECT_FLAG) return NO_ERROR;
else
{
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: WIFI_CONNECTION_TIMEOUT\r\n");
#endif
return WIFI_CONNECT_TIMEOUT;
}
}

void ESP8266_DisconnectWifi()
{
// 断开WIFI的连接
ESP8266_SendString("AT+CWQAP\r\n");
Delay_ms(10);
ESP8266_GetString();
}

ERROR_LIST ESP8266_ConnectMQTTSever(MQTT_InitStructTypeDef* MQTT_InitStructure)
{
// 根据传入结构体的参数连接MQTT服务器,注意特殊的符号如逗号、双引号、反斜杠要写成转义的字符串,例如引号要写成 \\"
ESP8266_SendString("ATE0\r\n");
Delay_ms(10);
sprintf(FormatBuffer,
"AT+MQTTUSERCFG=0,1,\"%s\",\"%s\",\"%s\",0,0,\"\"\r\n",
MQTT_InitStructure->ClientID, MQTT_InitStructure->UserName, MQTT_InitStructure->Password);
ESP8266_GetString();
ESP8266_SendString(FormatBuffer);
Delay_ms(10);
if (!strfind(ESP8266_GetString(), "OK"))
{
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: MQTT_INIT_ERROR\r\n");
#endif
return MQTT_INIT_ERROR;
}
sprintf(FormatBuffer, "AT+MQTTCONN=0,\"%s\",%d,0\r\n",
MQTT_InitStructure->SeverAdress, MQTT_InitStructure->port);
ESP8266_SendString(FormatBuffer);
for (int i = 0; i < MQTT_TIMELIMITED * 100; i++)
{
if (!ESP8266_BuffFlag) Delay_ms(10);
else
{
Delay_s(1);
char* t = ESP8266_GetString();
if (strfind(t, "OK")) return NO_ERROR;
else
{
Delay_ms(10);
if (MQTT_CONNECT_FLAG) return NO_ERROR;
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: MQTT_CONNECTION_ERROR\r\n");
#endif
return MQTT_CONNECT_ERROR;
}
}
}
if (MQTT_CONNECT_FLAG) return NO_ERROR;
else
{
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: MQTT_CONNECTION_TIMEOUT\r\n");
#endif
return MQTT_CONNECT_TIMEOUT;
}
}

void ESP8266_DisconnectMQTTSever()
{
// 断开与MQTT服务器的连接
ESP8266_SendString("AT+MQTTCLEAN=0\r\n");
Delay_ms(10);
}

bool ESP8266_IsConnectMQTT()
{
// 判断现在是否连接着MQTT服务器
return MQTT_CONNECT_FLAG;
}

ERROR_LIST ESP8266_MQTTPublishString(const char* topic, const char* data)
{
// 给MQTT服务器指定的topic传送指定的字符串
return ESP8266_MQTTPublishHex(topic, (byte*)data, strlen(data));
}

ERROR_LIST ESP8266_MQTTPublishHex(const char* topic, byte* data, int len)
{
// 给MQTT服务器传送Hex数组
ESP8266_SendString("ATE0\r\n");
Delay_ms(10);
sprintf(FormatBuffer, "AT+MQTTPUBRAW=0,\"%s\",%d,0,0\r\n", topic, len);
ESP8266_GetString();
ESP8266_SendString(FormatBuffer);
while (!ESP8266_BuffFlag); // 等待消息回传
Delay_ms(20);
if (!strfind(ESP8266_GetString(), "OK"))
{
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: MQTTPublishError1\r\n");
#endif
return MQTT_PUBLISH_FAIL;
}
for (int i = 0; i < len; i++)
ESP8266_SendByte(data[i]);
while (!ESP8266_BuffFlag); // 等待消息回传
Delay_ms(20);
if (!strfind(ESP8266_GetString(), "OK"))
{
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: MQTTPublishError2\r\n");
#endif
return MQTT_PUBLISH_FAIL;
}
return NO_ERROR;
}

ERROR_LIST ESP8266_MQTTPublishVariable(const char* topic, const char* name, const char* val)
{
// 该函数可以上传阿里云服务器物模型的数据。name为物模型的标识符,val为值,val需要以字符串的形式给出。
sprintf(BackupBuffer,
"{\r\n \"method\":\"thing.event.property.post\",\r\n \"params\":{\r\n \"%s\":%s\r\n }\r\n}\r\n"
, name, val);
return ESP8266_MQTTPublishString(topic, BackupBuffer);
}

ERROR_LIST ESP8266_MQTTSubscribeTopic(const char* topic)
{
// 订阅topic
ESP8266_SendString("ATE0\r\n");
Delay_ms(10);
sprintf(FormatBuffer, "AT+MQTTSUB=0,\"%s\",0\r\n");
ESP8266_GetString();
ESP8266_SendString(FormatBuffer);
while (!ESP8266_BuffFlag); // 等待数据回传
Delay_ms(20);
char* t = ESP8266_GetString();
if (strfind(t, "OK") || strfind(t, "ALREADY SUBSCRIBE")) return NO_ERROR;
else
{
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: MQTT_SUBSCRIBE_FAIL\r\n");
#endif
return MQTT_SUBSCRIBE_FAIL;
}
}

ERROR_LIST ESP8266_MQTTUnsubscribeTopic(const char* topic)
{
// 取消订阅topic
ESP8266_SendString("ATE0\r\n");
Delay_ms(10);
sprintf(FormatBuffer, "AT+MQTTUNSUB=0,\"%s\"\r\n");
ESP8266_GetString();
ESP8266_SendString(FormatBuffer);
while (!ESP8266_BuffFlag); // 等待数据回传
Delay_ms(20);
char* t = ESP8266_GetString();
if (strfind(t, "OK")) return NO_ERROR;
else
{
#ifdef __ESP8266_DEBUG
DEBUG_SendString("ESP8266_DEBUG: MQTT_UNSUBSCRIBE_FAIL\r\n");
#endif
return MQTT_SUBSCRIBE_FAIL;
}
}

bool ESP8266_IsGetSubMessage()
{
// ESP8266现在缓冲区里的数据是不是云端传回来的topic
return MQTT_SUBRECV_FLAG;
}

bool strfind(const char* src, const char* inc)
{
// 判断src中是否包含inc。目前使用BF算法,若数据量较大时可以考虑换成KMP算法
int n = strlen(src);
int m = strlen(inc);
for (int i = 0; i < n; i++)
{
bool st = true;
for (int j = 0; j < m && st; j++) st = src[i + j] == inc[j];
if (st) return true;
}
return false;
}

void ESP_USART_Interrupt(void)
{
if (USART_GetITStatus(ESP_USART, USART_IT_RXNE) == SET)
{
byte ux = USART_ReceiveData(ESP_USART);
ESP8266_BuffFlag = true;
ESP8266_ReceiveBuff[ESP8266_BuffPoint] = ux;
ESP8266_BuffPoint++;
USART_ClearITPendingBit(ESP_USART, USART_IT_RXNE);
__esp8266_status(ux);
#ifdef __ESP8266_DEBUG
// 原封不动传给电脑串口助手
DEBUG_SendByte(ux);
#endif
}
}

#ifdef __ESP8266_DEBUG
void DEBUG_SendString(char* s)
{
// 给电脑串口助手传字符串
while (*s)
{
DEBUG_SendByte(*s);
s++;
}
}
void DEBUG_SendByte(byte ux)
{
USART_SendData(DEBUG_USART, ux);
while (USART_GetFlagStatus(DEBUG_USART, USART_FLAG_TXE) == RESET);
}

void DEBUG_USART_Interrupt(void)
{
if (USART_GetITStatus(DEBUG_USART, USART_IT_RXNE) == SET)
{
// 原封不动传给ESP8266
byte ux = USART_ReceiveData(DEBUG_USART);
ESP8266_SendByte(ux);
USART_ClearITPendingBit(DEBUG_USART, USART_IT_RXNE);
}
}
#endif


void __esp8266_status(byte ux)
{
static int wifi_flagd = 0, wifi_flagc = 0;
/* 判断是否出现过"WIFI DISCONNECT"或"WIFI CONNECTED"字符串。
由于这两句话出现时机不确定,只能在中断函数中判断,用状态机算法每个字节进行比对。*/
// 同时用于mqtt状态检测
static int mqtt_flagd = 0, mqtt_flagc = 0;
static int mqtt_subrecv = 0;
switch (wifi_flagd)
{
case 0:
if (ux == 'W') wifi_flagd++;
else wifi_flagd = 0;
break;
case 1:
if (ux == 'I') wifi_flagd++;
else wifi_flagd = 0;
break;
case 2:
if (ux == 'F') wifi_flagd++;
else wifi_flagd = 0;
break;
case 3:
if (ux == 'I') wifi_flagd++;
else wifi_flagd = 0;
break;
case 4:
if (ux == ' ') wifi_flagd++;
else wifi_flagd = 0;
break;
case 5:
if (ux == 'D') wifi_flagd++;
else wifi_flagd = 0;
break;
case 6:
if (ux == 'I') wifi_flagd++;
else wifi_flagd = 0;
break;
case 7:
if (ux == 'S') wifi_flagd++;
else wifi_flagd = 0;
break;
case 8:
if (ux == 'C') wifi_flagd++;
else wifi_flagd = 0;
break;
case 9:
if (ux == 'O') wifi_flagd++;
else wifi_flagd = 0;
break;
case 10:
if (ux == 'N') wifi_flagd++;
else wifi_flagd = 0;
break;
case 11:
if (ux == 'N') wifi_flagd++;
else wifi_flagd = 0;
break;
case 12:
if (ux == 'E') wifi_flagd++;
else wifi_flagd = 0;
break;
case 13:
if (ux == 'C') wifi_flagd++;
else wifi_flagd = 0;
break;
case 14:
if (ux == 'T') WIFI_CONNECT_FLAG = false;
wifi_flagd = 0;
break;
}
switch (wifi_flagc)
{
case 0:
if (ux == 'W') wifi_flagc++;
else wifi_flagc = 0;
break;
case 1:
if (ux == 'I') wifi_flagc++;
else wifi_flagc = 0;
break;
case 2:
if (ux == 'F') wifi_flagc++;
else wifi_flagc = 0;
break;
case 3:
if (ux == 'I') wifi_flagc++;
else wifi_flagc = 0;
break;
case 4:
if (ux == ' ') wifi_flagc++;
else wifi_flagc = 0;
break;
case 5:
if (ux == 'C') wifi_flagc++;
else wifi_flagc = 0;
break;
case 6:
if (ux == 'O') wifi_flagc++;
else wifi_flagc = 0;
break;
case 7:
if (ux == 'N') wifi_flagc++;
else wifi_flagc = 0;
break;
case 8:
if (ux == 'N') wifi_flagc++;
else wifi_flagc = 0;
break;
case 9:
if (ux == 'E') wifi_flagc++;
else wifi_flagc = 0;
break;
case 10:
if (ux == 'C') wifi_flagc++;
else wifi_flagc = 0;
break;
case 11:
if (ux == 'T') wifi_flagc++;
else wifi_flagc = 0;
break;
case 12:
if (ux == 'E') wifi_flagc++;
else wifi_flagc = 0;
break;
case 13:
if (ux == 'D') WIFI_CONNECT_FLAG = true;
wifi_flagc = 0;
break;
}

switch (mqtt_flagd)
{
case 0:
if (ux == 'M') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 1:
if (ux == 'Q') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 2:
if (ux == 'T') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 3:
if (ux == 'T') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 4:
if (ux == 'D') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 5:
if (ux == 'I') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 6:
if (ux == 'S') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 7:
if (ux == 'C') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 8:
if (ux == 'O') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 9:
if (ux == 'N') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 10:
if (ux == 'N') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 11:
if (ux == 'E') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 12:
if (ux == 'C') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 13:
if (ux == 'T') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 14:
if (ux == 'E') mqtt_flagd++;
else mqtt_flagd = 0;
break;
case 15:
if (ux == 'D') MQTT_CONNECT_FLAG = false;
mqtt_flagd = 0;
break;
}
switch (mqtt_flagc)
{
case 0:
if (ux == 'M') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 1:
if (ux == 'Q') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 2:
if (ux == 'T') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 3:
if (ux == 'T') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 4:
if (ux == 'C') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 5:
if (ux == 'O') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 6:
if (ux == 'N') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 7:
if (ux == 'N') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 8:
if (ux == 'E') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 9:
if (ux == 'C') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 10:
if (ux == 'T') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 11:
if (ux == 'E') mqtt_flagc++;
else mqtt_flagc = 0;
break;
case 12:
if (ux == 'D') MQTT_CONNECT_FLAG = true;
mqtt_flagc = 0;
break;
}
switch (mqtt_subrecv)
{
case 0:
if (ux == 'M') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 1:
if (ux == 'Q') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 2:
if (ux == 'T') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 3:
if (ux == 'T') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 4:
if (ux == 'S') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 5:
if (ux == 'U') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 6:
if (ux == 'B') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 7:
if (ux == 'R') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 8:
if (ux == 'E') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 9:
if (ux == 'C') mqtt_subrecv++;
else mqtt_subrecv = 0;
break;
case 10:
if (ux == 'V') MQTT_SUBRECV_FLAG = true;
mqtt_subrecv = 0;
break;
}
}

代码说明

ESP8266模块配置说明:

模块与stm32接5个引脚,分别为VCC、GND、RxD、TxD、EN

在使用ESP8266模块之前,需要烧录官方提供的固件(1471)ESP8266-AT_MQTT-1M.bin

下面是ESP8266.c涉及到的函数、变量和宏的简要说明,该代码用stm32标准库所写。

1
#define __ESP8266_DEBUG

定义这个宏会占用stm32的一个USART将ESP8266传回stm32的字符串传到电脑的串口助手中

1
2
3
4
#define ESP_EN GPIO_Pin_1             // ESP8266使能引脚的选择
#define ESP_EN_Periph GPIOA // ESP8266使能引脚所在的外设
#define RCC_ESP_EN_Periph RCC_APB2Periph_GPIOA
#define RCC_ESP_EN_PeriphCmd RCC_APB2PeriphClockCmd

这4个宏是定义ESP8266的EN引脚所接的IO口。这4个宏一定要配套。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
/* ESP8266选择了哪一个USART,这里使用条件编译。仅列出C8T6所支持的3个外设,以及USART1的重定义
该宏用于初始化函数。
*/
#define ESP8266_SELECT_USART1
// #define ESP8266_SELECT_USART2
// #define ESP8266_SELECT_USART3
// #define ESP8266_SELECT_USART1_AFIO

// DEBUG选择哪一个USART
#define DEBUG_SELECT_USART2
// #define DEBUG_SELECT_USART3

// ESP8266以及DEBUG选择的USART外设
#define ESP_USART USART1
#define ESP_USART_IRQn USART1_IRQn
#define ESP_USART_Interrupt USART1_IRQHandler
#define DEBUG_USART USART2
#define DEBUG_USART_IRQn USART2_IRQn
#define DEBUG_USART_Interrupt USART2_IRQHandler

这些宏共同决定ESP8266和电脑的串口助手具体与stm32哪个USART外设相连接。其中带SELECT标识符的用于初始化函数中。我只定义了C8T6所支持的3个USART外设以及USART1的重映射。一定要配置成正确的参数。默认情况为ESP8266接USART1,电脑接USART2.

1
2
3
4
5
#define ESP8266_BaudRate 115200         // ESP8266的波特率
#define ESP8266_PreemptionPriority 0 // ESP8266的中断优先级
#define ESP8266_SubPriority 0
#define WIFI_TIMELIMITED 20 // 连接wifi时的超时时间,单位秒
#define MQTT_TIMELIMITED 120 // 连接MQTT时的超时时间,单位秒

这部分宏定义了一些基本参数,ESP8266的波特率,中断函数的优先级以及连接wifi和mqtt的最长等待时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
typedef unsigned char byte;
typedef enum { false, true } bool;
typedef enum {
NO_ERROR, // 没有错误
AT_ERROR, // AT指令始终不返回"OK",可能是模块异常
WIFI_CONNECT_ERROR, // WIFI未正常连接
WIFI_CONNECT_TIMEOUT, // WIFI连接超时
MQTT_INIT_ERROR, // MQTT初始化错误
MQTT_CONNECT_ERROR, // MQTT服务器连接失败
MQTT_CONNECT_TIMEOUT, // MQTT连接超时
MQTT_PUBLISH_FAIL, // MQTT数据上传失败
MQTT_SUBSCRIBE_FAIL, // MQTT的topic订阅失败
} ERROR_LIST;

这部分定义了一些基本变量。bool是为了向C++那样方便表示逻辑类型。ERROR_LIST是多数主要函数的返回值。(写完才发现多数函数的返回值只有0和非0……还不如直接用bool类型了……)

1
2
3
4
5
6
7
8
typedef struct
{
const char* ClientID;
const char* UserName;
const char* Password;
const char* SeverAdress;
unsigned int port;
}MQTT_InitStructTypeDef;

初始化MQTT信息这部分仿照了stm32标准库函数的定义方法写了个结构体,记录MQTT服务器的ID、用户名、密码、服务器地址、端口。测试发现,服务器地址可以直接用域名,也可以用ip地址。

1
2
3
4
ERROR_LIST ESP8266_Init();
ERROR_LIST ESP8266_InitConfig();
void ESP8266_Cmd(byte state);
void ESP8266_Reset();

ESP8266的初始化函数。第一个是初始化stm32上对应的USART外设(由头文件的宏定义),第二个是用AT指令初始化ESP8266模块,以便wifi连接使用。第三个函数为EN引脚使能ENABLE或者使能DISABLE。第四个将重启ESP8266。

1
2
3
4
void ESP8266_SendString(char* str);
void ESP8266_SendByte(byte ux);
void ESP8266_SendHexArray(byte* data, int len);
char* ESP8266_GetString();

这部分是stm32与ESP8266进行串口通信的基本函数。意义如函数名所示。其中第四个ESP8266_GetString()函数使用后会把缓冲区指针复位,因此调用该函数之后立即再调用一次无法获得一样的数据。这个特性也可以用于复位缓冲区。

1
2
3
bool ESP8266_IsConnectWifi();
ERROR_LIST ESP8266_ConnectWifi(const char* name, const char* pwd);
void ESP8266_DisconnectWifi();

这部分函数将配置ESP8266的WIFI连接。测试得出,如果ESP8266之前连接过wifi,可以不使用第二个函数重新配置wifi,ESP8266会直接尝试之前连过的wifi。任何时候都可以用第一个函数查询当前wifi连接状态。

1
2
3
ERROR_LIST ESP8266_ConnectMQTTSever(MQTT_InitStructTypeDef* MQTT_InitStructure);
void ESP8266_DisconnectMQTTSever();
bool ESP8266_IsConnectMQTT();

这部分将连接MQTT服务器。使用这部分函数时请检查wifi是否连接。如果网络速度慢可能会返回连接失败。

1
2
3
ERROR_LIST ESP8266_MQTTPublishString(const char* topic, const char* data);
ERROR_LIST ESP8266_MQTTPublishHex(const char* topic, byte* data, int len);
ERROR_LIST ESP8266_MQTTPublishVariable(const char* topic, const char* name, const char* val);

向MQTT服务器指定的topic传送字符串或者Hex数组。其中第三个函数是可以直接提供name和val更新阿里云服务器的物模型的数据,topic要指定成物模型的传输topic。(不知道其他云服务器是否存在物模型这个概念,以及模式字符串是否和阿里云服务器一样。但是这个函数用阿里云服务器测试是可以的)前两个函数可以用在任意的MQTT服务器,直接传入数据即可。

1
2
3
ERROR_LIST ESP8266_MQTTSubscribeTopic(const char* topic);
ERROR_LIST ESP8266_MQTTUnsubscribeTopic(const char* topic);
bool ESP8266_IsGetSubMessage();

订阅或取消订阅MQTT服务器的topic。因为服务器回传数据时间未知,最好配合定时器外设隔一段时间调用ESP8266_IsGetSubMessage()函数判断是否回传了数据。然后用ESP8266_GetString()读出数据。

1
2
3
4
bool strfind(const char* src, const char* inc);
void __esp8266_status(byte ux);
void DEBUG_SendString(char* s);
void DEBUG_SendByte(byte ux);

这几个函数是代码内部所用的函数。第二个函数虽然代码量比较大,但这是我目前想到的效率最高的办法。因为wifi和mqtt信息是不定时出现的,只能在中断函数内进行判断;用ESP8266_GetString()会复位缓冲区,可能破坏正常要接受的数据。因此直接在中断函数内用类似于状态机的思路直接判断是最方便的。而且只有5次switch,效率也不低。就是代码看起来多……

1
2
3
4
5
6
7
8
byte ESP8266_ReceiveBuff[2048];                 // ESP8266的接受缓冲区
unsigned int ESP8266_BuffPoint = 0; // 缓冲区当前指针
bool ESP8266_BuffFlag = false; // 现在是否从ESP8266接受到了数据
bool WIFI_CONNECT_FLAG = false; // Wifi状态标志位,连接wifi则置true,断开连接则置false。由ESP8266的中断函数修改
bool MQTT_CONNECT_FLAG = false; // MQTT状态标志位,连接MQTT则置true,断开连接则置false。由ESP8266的中断函数修改
bool MQTT_SUBRECV_FLAG = false; // MQTTSUBRECV标志位,接收到订阅的消息则置true
byte FormatBuffer[1024]; // 格式化字符串所用的缓冲区
byte BackupBuffer[1024]; // 格式化字符串的备份缓冲区

ESP8266.c所用到的全局变量