作品总介绍:

​ 基于STM32单片机以STM32F103C8T6为核心控制配备ESP32运行FreeRtos,搭载LD3322,以实现通过WiFi协议技术实现HTTP协议制作网页界面和语音控制来实现桌面摆件的互动性和开关对应摆件功能,例如风扇、灯、雾化器以及MP3播放功能,同时通过SSD1603驱动IC和DHT11测量当前温度并显示在OLED上。

硬件方面:

​ 主控:STM32F103C8T6

​ 语音模块:LD3322

​ WIFI模块:ESP32

​ MP3模块:MY2490

​ 以及一些其他的模块,硬件方面的连接这里先不做讲解,后续慢慢补充。

软件方面:

​ 工具:Keil、Arduino IDE、智能公元(LD3322编写平台)

整体方案:

image-20230122134030153

软件实现步骤:

智能公元

​ 在这个平台可以进行对LD3322进行配置,详细的配置效果如下

智能公元

​ 通过配置识别语音之后,发送串口信息给主控芯片,从而进行后续的操作,对于如何操作、配置可以去入手LD3322,官方会给予详细的操作指南。

STM32代码部分截取

​ 实现串口通信代码:

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
if(USART2_RX_STA&(1<<15))	
{
USART2_RX_STA&=~(1<<15);
switch(USART2_RX_BUF[0])
{
case 0x11:
OLED_Clear();
delay_ms(100);
OLED_DrawBMP(45,1,85,6,Atomizer);
WU=1;
break;
case 0x12:
OLED_Clear();
WU=0;
break;
case 0x13:
OLED_Clear();
delay_ms(100);
OLED_ShowString(0,2,"Open the fan",32);
FAN=1;
break;
case 0x14:
OLED_Clear();
FAN=0;
break;
case 0x15:
LED=1;
OLED_Clear();
delay_ms(100);
ledon =1;
ledonsecond =1;
OLED_DrawBMP(45,1,93,7,LED);
break;
case 0x16:
LED=0;
ledonsecond =0;
OLED_Clear();
break;
case 0x17:
UART3SendByte5(tt[0],tt[1],tt[2],tt[3],tt[4]);
OLED_DrawBMP(55,1,80,6,col);
break;
case 0x18:
UART3SendByte5(tt2[0],tt2[1],tt2[2],tt2[3],tt2[4]);
OLED_Clear();
break;
case 0x19:
UART3SendByte5(tt1[0],tt1[1],tt1[2],tt1[3],tt1[4]);
OLED_DrawBMP(55,1,80,6,col);
break;
case 0x21:
ESP32RESET = 1;
esp32status =1;
break;
default:
break;
}
USART2_RX_STA=0;
}

​ 代码中详细区分了不同的串口信息,STM32将执行不同的操作。可以进行LED、雾化器、风扇的开关以及MP3的音乐控制。

​ 其中挑选几个封装函数进行讲解:

​ 1.UART3SendByte5(tt1[0],tt1[1],tt1[2],tt1[3],tt1[4]):串口发送5个字节的数据,详细代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
unsigned char tt[]={0x7E,0X03,0X11,0x12,0xEF};//播放
unsigned char tt1[]={0x7E,0X03,0X13,0x10,0xEF};//下一首
unsigned char tt2[]={0x7E,0X03,0X1C,0x1F,0xEF};//暂停

void UART3SendByte(unsigned char SendData)
{
USART_SendData(USART3,SendData);
while(USART_GetFlagStatus(USART3, USART_FLAG_TXE) == RESET);
}

void UART3SendByte5(unsigned char SendData1,unsigned char SendData2,unsigned char SendData3,unsigned char SendData4,unsigned char SendData5)
{
UART3SendByte(SendData1);
UART3SendByte(SendData2);
UART3SendByte(SendData3);
UART3SendByte(SendData4);
UART3SendByte(SendData5);
}

​ 简单的串口发送,封装发送五个字节的串口函数,使得MP3发生不同的操作。

​ 2.OLED_Clear()、void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])、void OLED_ShowString(u8 x,u8 y,u8 chr[],u8 Char_Size)这类函数就是OLED显示屏的相关函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
void OLED_DrawBMP(unsigned char x0, unsigned char y0,unsigned char x1, unsigned char y1,unsigned char BMP[])
{
unsigned int j=0;
unsigned char x,y;

if(y1%8==0) y=y1/8;
else y=y1/8+1;
for(y=y0;y<y1;y++)
{
OLED_Set_Pos(x0,y);
for(x=x0;x<x1;x++)
{
OLED_WR_Byte(BMP[j++],OLED_DATA);
}
}
}

​ OLED_DrawBMP():通过不同的位置信息摆放图像的位置,最后一个参数即为图片,代码中有Atomizer、LED这类的图片,由于没有找到满意的风扇图片,这里就显示字符串,字符串的相关代码如下:

1
2
3
4
5
6
7
8
9
10
void OLED_ShowString(u8 x,u8 y,u8 chr[],u8 Char_Size)
{
unsigned char j=0;
while (chr[j]!='\0')
{ OLED_ShowChar(x,y,chr[j],Char_Size);
x+=8;
if(x>120){x=0;y+=2;}
j++;
}
}

​ 其他的一些初始化部分代码如下:

1
2
3
4
5
6
7
8
9
10
11
int esp32status = 0;
int esp32count = 0;
Gpio();
delay_init(); //延时函数初始化
NVIC_Configuration(); //中断分组
USART2_Init(9600); //串口2初始化与LD3322连接
USART3_Init(9600); //串口3初始化与MY240连接
delay_ms(1000);
OLED_Init();
OLED_Clear();
OLED_Display_On();

ESP32相关代码

​ 这一部分中可以收获到ESP32连接WiFi、ESP32制作网页界面、ESP32运行freeRTOS。

ESP32连接WIFI

​ 整体步骤为:判断是否连接过网络,连接过则获取之前保存的信息,如果没有则进行搜索附近WIFI,选定自己熟悉的WiFi进行连接,相关代码如下:

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
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <esp_wifi.h>
const char* AP_SSID = "ESP32_Config"; //热点名称
String wifi_ssid = "";
String wifi_pass = "";
String scanNetworksID = "";//用于储存扫描到的WiFi

#define ROOT_HTML "<!DOCTYPE html><html><head><title>WIFI Config by lwang</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"></head><style type=\"text/css\">.input{display: block; margin-top: 10px;}.input span{width: 100px; float: left; float: left; height: 36px; line-height: 36px;}.input input{height: 30px;width: 200px;}.btn{width: 120px; height: 35px; background-color: #000000; border:0px; color:#ffffff; margin-top:15px; margin-left:100px;}</style><body><form method=\"GET\" action=\"connect\"><label class=\"input\"><span>WiFi SSID</span><input type=\"text\" name=\"ssid\"></label><label class=\"input\"><span>WiFi PASS</span><input type=\"text\" name=\"pass\"></label><input class=\"btn\" type=\"submit\" name=\"submit\" value=\"Submie\"> <p><span> Nearby wifi:</P></form>"
WebServer server(80);

#define RESET_PIN 13 //用于删除WiFi信息
bool AutoConfig();
void wifi_Config();
void setup() {
Serial.begin(115200);
pinMode(RESET_PIN, INPUT_PULLUP);
// 连接WiFi
if (!AutoConfig())
{
wifi_Config();
}
//用于删除已存WiFi
if (digitalRead(RESET_PIN) == LOW) {
delay(1000);
esp_wifi_restore();
delay(10);
ESP.restart(); //复位esp32
}
}
void loop() {
server.handleClient();
while (WiFi.status() == WL_CONNECTED) {//WIFI已连接
}
}
//用于配置WiFi
void wifi_Config()
{
Serial.println("scan start");
// 扫描附近WiFi
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n == 0) {
Serial.println("no networks found");
scanNetworksID = "no networks found";
} else {
Serial.print(n);
Serial.println(" networks found");
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i));
Serial.print(" (");
Serial.print(WiFi.RSSI(i));
Serial.print(")");
Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
scanNetworksID += "<P>" + WiFi.SSID(i) + "</P>";
delay(10);
}
}
Serial.println("");

WiFi.mode(WIFI_AP);//配置为AP模式
boolean result = WiFi.softAP(AP_SSID, ""); //开启WIFI热点
if (result)
{
IPAddress myIP = WiFi.softAPIP();
//打印相关信息
Serial.println("");
Serial.print("Soft-AP IP address = ");
Serial.println(myIP);
Serial.println(String("MAC address = ") + WiFi.softAPmacAddress().c_str());
Serial.println("waiting ...");
} else { //开启热点失败
Serial.println("WiFiAP Failed");
delay(3000);
ESP.restart(); //复位esp32
}

if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}

//首页
server.on("/", []() {
server.send(200, "text/html", ROOT_HTML + scanNetworksID + "</body></html>");
});

//连接
server.on("/connect", []() {

server.send(200, "text/html", "<html><body><font size=\"10\">successd,wifi connecting...<br />Please close this page manually.</font></body></html>");

WiFi.softAPdisconnect(true);
//获取输入的WIFI账户和密码
wifi_ssid = server.arg("ssid");
wifi_pass = server.arg("pass");
server.close();
WiFi.softAPdisconnect();
Serial.println("WiFi Connect SSID:" + wifi_ssid + " PASS:" + wifi_pass);

//设置为STA模式并连接WIFI
WiFi.mode(WIFI_STA);
WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
uint8_t Connect_time = 0; //用于连接计时,如果长时间连接不成功,复位设备
while (WiFi.status() != WL_CONNECTED) { //等待WIFI连接成功
delay(500);
Serial.print(".");
Connect_time ++;
if (Connect_time > 80) { //长时间连接不上,复位设备
Serial.println("Connection timeout, check input is correct or try again later!");
delay(3000);
ESP.restart();
}
}
Serial.println("");
Serial.println("WIFI Config Success");
Serial.printf("SSID:%s", WiFi.SSID().c_str());
Serial.print(" LocalIP:");
Serial.print(WiFi.localIP());
Serial.println("");

});
server.begin();
}
//用于上电自动连接WiFi
bool AutoConfig()
{
WiFi.begin();
for (int i = 0; i < 20; i++)
{
int wstatus = WiFi.status();
if (wstatus == WL_CONNECTED)
{
Serial.println("WIFI SmartConfig Success");
Serial.printf("SSID:%s", WiFi.SSID().c_str());
Serial.printf(", PSW:%s\r\n", WiFi.psk().c_str());
Serial.print("LocalIP:");
Serial.print(WiFi.localIP());
Serial.print(" ,GateIP:");
Serial.println(WiFi.gatewayIP());
return true;
}
else
{
Serial.print("WIFI AutoConfig Waiting......");
Serial.println(wstatus);
delay(1000);
}
}
Serial.println("WIFI AutoConfig Faild!" );
return false;
}


​ 这一段代码,可以实现WiFi的连接,连接后,我们进行下一步、制作网页界面,制作网页界面需要HTML相关知识用来制作控制界面,以及温湿度显示界面。

ESP32控制界面部分

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
 client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();

turns the GPIOs on and off
if (header.indexOf("GET /LED/on") >= 0) {
Serial.println("GPIO 26 on");
LED = "on";
digitalWrite(22, HIGH);
} else if (header.indexOf("GET /LED/off") >= 0) {
Serial.println("GPIO 26 off");
LED = "off";
digitalWrite(25, LOW);
} else if (header.indexOf("GET /at/on") >= 0) {
ATOMIZER = "on";
digitalWrite(5, HIGH);
} else if (header.indexOf("GET /at/off") >= 0) {
ATOMIZER = "off";
digitalWrite(33, LOW);
}
/*********************************fan***********************************/
else if(header.indexOf("GET /fan/on")>= 0){
FAN = "on";
digitalWrite(23, HIGH);

}else if(header.indexOf("GET /fan/off")>= 0){
FAN= "off";
digitalWrite(26, LOW);
}
/******************************刷新按钮***********************************/
else if(header.indexOf("GET /new") >= 0)
{
Serial.println("new");

}
/******************************END**************************************/
// Display the HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the on/off buttons
// Feel free to change the background-color and font-size attributes to fit your preferences
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #555555;}</style></head>");

// Web Page Heading
client.println("<body><h1>桌面精灵</h1>");

// Display current state, and ON/OFF buttons for GPIO 26
client.println("<p>LED - State " + LED + "</p>");
// If the output26State is off, it displays the ON button
if (LED=="off") {
client.println("<p><a href=\"/LED/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/LED/off\"><button class=\"button button2\">OFF</button></a></p>");
}

// Display current state, and ON/OFF buttons for GPIO 27
client.println("<p>atomizer - State " + ATOMIZER + "</p>");
// If the output27State is off, it displays the ON button
if (ATOMIZER=="off") {
client.println("<p><a href=\"/at/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/at/off\"><button class=\"button button2\">OFF</button></a></p>");
}
/*****************************Fan*************************************/
client.println("<p>fan - State " + FAN + "</p>");
if (FAN=="off") {
client.println("<p><a href=\"/fan/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/fan/off\"><button class=\"button button2\">OFF</button></a></p>");
}

ESP32温湿度显示界面

1
2
3
4
5
6
7
8
9
10
11
12
/*******************************温湿度显示**********************************/
if(FAN=="off" || DHT11_STATUS == 0)
{
client.println("<a ><button class=\"button top1\">&#x6E29;&#x6E7F;&#x5EA6;&#x663E;&#x793A;</button>");
client.println("</a><center style=\"position: relative;\"></br><a><button type=\"button\" class=\"button\" value=\"temp\">温度<span style=\"color: red;font-size: 25px;\">");
client.println((int)DHT11.temperature);
client.println("°C</span></button></a><a><button type=\"button\" class=\"button\" value=\"humi\">湿度<span style=\"color: green;font-size: 25px;\">");
client.println((int)DHT11.humidity);
client.println("%</span></button></a>");
client.println("</span></button></a></br>");
}
/*******************************END*************************************/

​ 由于助于大家理解,下面附上HTML界面代码:

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
<!DOCTYPE html>
<html>

<head>
<meta name="viewport" content="width=device-width, initial-scale=1" />
<link rel="icon" href="data:,">

<style>
html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}

.button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;
text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}
.button2 {background-color: #555555;}
</style>
</head>

<body>
<h1>ESP32 Web Server</h1>
<p>LED - State " + LED + "</p>
<p><a href=\"/LED/on\"><button class=\"button\">ON</button></a></p>
<a href="./pin?light1=1"><button class="button top1">&#x6E29;&#x6E7F;&#x5EA6;&#x663E;&#x793A;</button></a>
<center style="left: 20px; position: relative;"> </br>
<a href="./pin?light1=1"><button type="button" class="button" value="temp">温度<span style="color: red; font-size: 25px;">
00°C</span></button></a>
<a href="./pin?light1=0"><button type="button" class="button" value="humi">湿度<span style="color: green;font-size: 25px;">
00%</span></button></a></br>
</body>

​ 我们移动端上显示的效果类似这种样式,具体还有写修改,可以创建完WiFi进行体验。

​ 对于freeRTOS相关的是运用了ESP32的双核,在每个核上跑一个任务,详细的代码如下:

1
2
3
4
5
6
7
void Task1code( void *pvParameters );
void Task2code( void *pvParameters );
void setup(){
xTaskCreatePinnedToCore(Task1code, "Task1", 10000, NULL, 1, NULL, 1);//
xTaskCreatePinnedToCore(Task2code, "Task2", 10000, NULL, 0, NULL, 0);//
//实现任务的函数名称(task1);任务的任何名称(“ task1”等);分配给任务的堆栈大小,以字为单位;任务输入参数(可以为NULL);任务的优先级(0是最低优先级);任务句柄(可以为NULL);任务将运行的内核ID(0或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
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
#include <soc/rtc_cntl_reg.h>
#include <WiFi.h>
#include <WebServer.h>
#include <ESPmDNS.h>
#include <esp_wifi.h>
#include <HTTPClient.h>
#include "text.h"
#include <Wire.h>
/**********************温湿度定义***************************/
#include <dht11.h>
dht11 DHT11;
#define DHT11PIN 2
int DHT11_STATUS = 0;
int DHT11_COUNT = 0;
/*******************************END**************************************************/
const char* AP_SSID = "ESP32_Config"; //热点名称
String wifi_ssid = "";
String wifi_pass = "";
String scanNetworksID = "";//用于储存扫描到的WiFi

#define ROOT_HTML "<!DOCTYPE html><html><head><title>WIFI Config by lwang</title><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\"></head><style type=\"text/css\">.input{display: block; margin-top: 10px;}.input span{width: 100px; float: left; float: left; height: 36px; line-height: 36px;}.input input{height: 30px;width: 200px;}.btn{width: 120px; height: 35px; background-color: #000000; border:0px; color:#ffffff; margin-top:15px; margin-left:100px;}</style><body><form method=\"GET\" action=\"connect\"><label class=\"input\"><span>WiFi SSID</span><input type=\"text\" name=\"ssid\"></label><label class=\"input\"><span>WiFi PASS</span><input type=\"text\" name=\"pass\"></label><input class=\"btn\" type=\"submit\" name=\"submit\" value=\"Submie\"> <p><span> Nearby wifi:</P></form>"
/****************************************************网页定义*********************************/
// Variable to store the HTTP request
String header;

// Auxiliar variables to store the current output state
String LED= "off";
String ATOMIZER= "off";
String FAN = "off";
String NEW = "REFRESH";
// Assign output variables to GPIO pins
const int output26 = 23;
const int output27 = 27;

// Current time
unsigned long currentTime = millis();
// Previous time
unsigned long previousTime = 0;
// Define timeout time in milliseconds (example: 2000ms = 2s)
const long timeoutTime = 2000;
/*********************************************变量********************************/
typedef struct tool
{
int ledinput;
int faninput;
int wuinput;
}tools;
tools ntools;
bool resetesp = 0;
/******************************************END************************************/
WiFiServer server1(80);
WebServer server(80);

#define RESET_PIN 13 //用于删除WiFi信息

/************************OLED*****************************/
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>

#define SCREEN_WIDTH 128 // 设置OLED宽度,单位:像素
#define SCREEN_HEIGHT 64 // 设置OLED高度,单位:像素
// 自定义重置引脚,虽然教程未使用,但却是Adafruit_SSD1306库文件所必需的
#define OLED_RESET 4
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
const char *ntpServer = "pool.ntp.org";
const long gmtOffset_sec = 8 * 3600;
const int daylightOffset_sec = 0;
/************************OLED*****************************/

void Task1code( void *pvParameters );
void Task2code( void *pvParameters );

void setup()
{
Serial.begin(115200);
pinMode(RESET_PIN, INPUT);
/***************************控制引脚*******************************/
pinMode(33, OUTPUT);
pinMode(5, OUTPUT); // set the fan pin mode

pinMode(25, OUTPUT);
pinMode(22, OUTPUT); // set the LED pin mode

pinMode(26, OUTPUT);
pinMode(23, OUTPUT); //at


pinMode(34, INPUT);
pinMode(35, INPUT); //LED
pinMode(32, INPUT); //
/*******************串口2当做IO口使用*************************/
pinMode(17, OUTPUT);


/************************end******************************/

digitalWrite(25, HIGH);
digitalWrite(22, LOW);

digitalWrite(33, HIGH);
digitalWrite(5, LOW);

digitalWrite(26, HIGH);
digitalWrite(23, LOW);

pinMode(DHT11PIN,OUTPUT);
/*************************DHT11**************************/
digitalWrite(17, HIGH);
/****************************END***********************************/

delay(100);

// We start by connecting to a WiFi network

// 连接WiFi
if (!AutoConfig())
{
wifi_Config();
Serial.println("esp32 reset");
}
Serial.println("");
Serial.println("WiFi connected.");
Serial.println("IP address: ");
Serial.println(WiFi.localIP());
WRITE_PERI_REG(RTC_CNTL_BROWN_OUT_REG, 0);//关闭低电压检测,避免无限重启
xTaskCreatePinnedToCore(Task1code, "Task1", 10000, NULL, 1, NULL, 1);//
xTaskCreatePinnedToCore(Task2code, "Task2", 10000, NULL, 0, NULL, 0);//
//实现任务的函数名称(task1);任务的任何名称(“ task1”等);分配给任务的堆栈大小,以字为单位;任务输入参数(可以为NULL);任务的优先级(0是最低优先级);任务句柄(可以为NULL);任务将运行的内核ID(0或1)
//Serial.begin(115200);
server1.begin();
}

int value = 0;
void loop(){
}
void Task1code(void *pvParameters) {
//setup部分
for (;;)//相当于loop
{
digitalWrite(25, HIGH);
digitalWrite(22, LOW); //LED
digitalWrite(26, HIGH);
digitalWrite(23, LOW); //WU

digitalWrite(33, HIGH); //FAN
digitalWrite(5, LOW);
/*******************串口2DHT11******************************************/
digitalWrite(17, HIGH);
/********************************检测开关状态****************************/
ntools.ledinput = digitalRead(35);
ntools.wuinput = digitalRead(34);
ntools.faninput = digitalRead(32);
delay(2000);
if(ntools.ledinput == HIGH)
{
Serial.println("ntools.ledinput high ");
LED = "on";
}
if(ntools.ledinput == LOW)
{
Serial.println("ntools.ledinput low ");
//digitalWrite(25, HIGH);
LED = "off";
}

if(ntools.faninput == HIGH)
{
Serial.println("ntools.faninput high ");
FAN = "on";
}
if(ntools.faninput == LOW)
{
Serial.println("ntools.faninput low ");

FAN = "off";
}
if(ntools.wuinput == HIGH)
{
Serial.println("ntools.wuinput high ");
ATOMIZER = "on";
}
if(ntools.wuinput == LOW)
{
Serial.println("ntools.wuinput low ");

ATOMIZER = "off";
}

/******************************END***************************************/
/*************************************温湿度定义********************************/
//byte temperature = 0;
//byte humidity = 0;
//int chk = DHT11.read(DHT11PIN);
//Serial.print("temperature =");
//Serial.println((int)DHT11.temperature);
//Serial.print("humidity =");
//Serial.println((int)DHT11.humidity);
/*********************************判断是否重启**************************************/
if(resetesp == 1)
{
resetesp =0;
ESP.restart(); //复位esp32
Serial.println("esp32 restart!!!!");
}
Serial.println(" no no esp32 restart!!!!");
/**********************************是否重置网络************************************/
//用于删除已存WiFi
if (digitalRead(RESET_PIN) == HIGH) {
delay(1000);
esp_wifi_restore();
delay(10);
Serial.println(" esp32 esp_wifi_restore()!!!!");
ESP.restart(); //复位esp32
}
/************************************END****************************************/
/************************************END****************************************/
server.handleClient();
WiFiClient client = server1.available(); // listen for incoming clients
delay(500);
if (client) {
currentTime = millis();
previousTime = currentTime;
Serial.println("New Client."); // print a message out in the serial port
String currentLine = ""; // make a String to hold incoming data from the client
while (client.connected() && currentTime - previousTime <= timeoutTime) { // loop while the client's connected
currentTime = millis();
if (client.available()) { // if there's bytes to read from the client,
char c = client.read(); // read a byte, then
Serial.write(c); // print it out the serial monitor
header += c;
if (c == '\n') { // if the byte is a newline character
// if the current line is blank, you got two newline characters in a row.
// that's the end of the client HTTP request, so send a response:
if (currentLine.length() == 0) {
// HTTP headers always start with a response code (e.g. HTTP/1.1 200 OK)
// and a content-type so the client knows what's coming, then a blank line:
client.println("HTTP/1.1 200 OK");
client.println("Content-type:text/html");
client.println("Connection: close");
client.println();

// turns the GPIOs on and off
if (header.indexOf("GET /LED/on") >= 0) {
Serial.println("GPIO 26 on");
LED = "on";
digitalWrite(22, HIGH);
} else if (header.indexOf("GET /LED/off") >= 0) {
Serial.println("GPIO 26 off");
LED = "off";
digitalWrite(25, LOW);
} else if (header.indexOf("GET /at/on") >= 0) {
ATOMIZER = "on";
digitalWrite(5, HIGH);
} else if (header.indexOf("GET /at/off") >= 0) {
ATOMIZER = "off";
digitalWrite(33, LOW);
}
/*********************************fan***********************************/
else if(header.indexOf("GET /fan/on")>= 0){
FAN = "on";
digitalWrite(23, HIGH);

}else if(header.indexOf("GET /fan/off")>= 0){
FAN= "off";
digitalWrite(26, LOW);

}
/******************************刷新按钮***********************************/
else if(header.indexOf("GET /new") >= 0)
{
Serial.println("new");

}

/******************************END**************************************/
// Display the HTML web page
client.println("<!DOCTYPE html><html>");
client.println("<head><meta name=\"viewport\" content=\"width=device-width, initial-scale=1\">");
client.println("<link rel=\"icon\" href=\"data:,\">");
// CSS to style the on/off buttons
// Feel free to change the background-color and font-size attributes to fit your preferences
client.println("<style>html { font-family: Helvetica; display: inline-block; margin: 0px auto; text-align: center;}");
client.println(".button { background-color: #4CAF50; border: none; color: white; padding: 16px 40px;");
client.println("text-decoration: none; font-size: 30px; margin: 2px; cursor: pointer;}");
client.println(".button2 {background-color: #555555;}</style></head>");

// Web Page Heading
client.println("<body><h1>桌面精灵</h1>");

// Display current state, and ON/OFF buttons for GPIO 26
client.println("<p>LED - State " + LED + "</p>");
// If the output26State is off, it displays the ON button
if (LED=="off") {
client.println("<p><a href=\"/LED/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/LED/off\"><button class=\"button button2\">OFF</button></a></p>");
}

// Display current state, and ON/OFF buttons for GPIO 27
client.println("<p>atomizer - State " + ATOMIZER + "</p>");
// If the output27State is off, it displays the ON button
if (ATOMIZER=="off") {
client.println("<p><a href=\"/at/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/at/off\"><button class=\"button button2\">OFF</button></a></p>");
}
/*****************************Fan*************************************/
client.println("<p>fan - State " + FAN + "</p>");
if (FAN=="off") {
client.println("<p><a href=\"/fan/on\"><button class=\"button\">ON</button></a></p>");
} else {
client.println("<p><a href=\"/fan/off\"><button class=\"button button2\">OFF</button></a></p>");
}

/*******************************温湿度显示**********************************/
if(FAN=="off" || DHT11_STATUS == 0)
{
client.println("<a ><button class=\"button top1\">&#x6E29;&#x6E7F;&#x5EA6;&#x663E;&#x793A;</button>");
client.println("</a><center style=\"position: relative;\"></br><a><button type=\"button\" class=\"button\" value=\"temp\">温度<span style=\"color: red;font-size: 25px;\">");
client.println((int)DHT11.temperature);
client.println("°C</span></button></a><a><button type=\"button\" class=\"button\" value=\"humi\">湿度<span style=\"color: green;font-size: 25px;\">");
client.println((int)DHT11.humidity);
client.println("%</span></button></a>");
client.println("</span></button></a></br>");
}
/*******************************NEW*************************************/
client.println("<p>" + NEW + "</p>");
client.println("<p><a href=\"/new\"><button class=\"button\">NEW</button></a></p>");
/*******************************END*************************************/
client.println("</body></html>");

// The HTTP response ends with another blank line
client.println();
// Break out of the while loop
break;
} else { // if you got a newline, then clear currentLine
currentLine = "";
}
} else if (c != '\r') { // if you got anything else but a carriage return character,
currentLine += c; // add it to the end of the currentLine
}
}
}
// Clear the header variable
header = "";
// Close the connection
client.stop();
Serial.println("Client disconnected.");
Serial.println("");
}
}
}
void Task2code(void *pvParameters) {
//setup部分
Wire.setPins(/*SDA*/18,/*SCL*/19);
//初始化OLED并设置其IIC地址为 0x3C
display.begin(SSD1306_SWITCHCAPVCC, 0x3C);
//清除屏幕
display.clearDisplay();
//设置字体颜色,白色可见
display.setTextColor(WHITE);
//设置字体大小
display.setTextSize(1.5);
//设置光标位置
display.setCursor(0, 0);
WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
configTime(gmtOffset_sec, daylightOffset_sec, ntpServer);
for (;;)//相当于loop
{
// put your main code here, to run repeatedly:、

delay(100);
//清除屏幕
display.clearDisplay();
//设置光标位置
//检测到fan开启状态时,OLED显示动图状态消失
if(FAN=="off")
{
bmp_display();
display.clearDisplay();
delay(500);

printLocalTime();
display.display();
delay(3000);
Serial.println("ononon");
}else
{
display.setCursor(0, 0);
display.print("The fan is turned on and cannot be used.");
display.display();
delay(3000);
Serial.println("offoff");
}
}
}
void printLocalTime()
{

struct tm timeinfo;
if (!getLocalTime(&timeinfo))
{
display.println("Failed to obtain time");
return;
}
/***********************获取温湿度****************************/
int chk = DHT11.read(DHT11PIN);

Serial.print("Read sensor: ");
switch (chk)
{
case DHTLIB_OK:
Serial.println("OK");
DHT11_STATUS = 0;
break;
case DHTLIB_ERROR_CHECKSUM:
Serial.println("Checksum error");
break;
case DHTLIB_ERROR_TIMEOUT:
Serial.println("Time out error");
break;
default:
Serial.println("Unknown error");
break;
}
if((int)DHT11.humidity > 100 || (int)DHT11.temperature > 100 )
{
for(int q = 0 ;q < 10; q++)
{
int chk = DHT11.read(DHT11PIN);
Serial.print("Humidity1 (%): ");
Serial.println((float)DHT11.humidity, 2);

Serial.print("Temperature1 °C): ");
Serial.println((float)DHT11.temperature, 2);
if((int)DHT11.humidity < 100 && (int)DHT11.temperature < 100 )
{
DHT11_STATUS = 0;
break;
}
DHT11_STATUS = 1;
}
DHT11_COUNT++;
if(DHT11_COUNT == 5 )
{
digitalWrite(17, LOW);
delay(4000);
digitalWrite(17, HIGH);
DHT11_COUNT =0;
}

}


Serial.print("Humidity (%): ");
Serial.println((float)DHT11.humidity, 2);

Serial.print("Temperature °C): ");
Serial.println((float)DHT11.temperature, 2);
/***********************获取温湿度END****************************/

display.setCursor(0, 0);
display.println(&timeinfo, "%F"); // 格式化输出
//display.setCursor(80, 0);
// display.println(&timeinfo, "%T"); // 格式化输出
display.setCursor(42, 8);
display.println(&timeinfo, "%A"); // 格式化输出
display.setCursor(0, 16);
if( DHT11_STATUS == 1)
{
display.print("Humidity:");
display.println("error");
display.setCursor(0, 24);
display.print("Temperature:");
display.println("error");


}else
{
display.print("Humidity:");
display.println((float)DHT11.humidity);
display.setCursor(0, 24);
display.print("Temperature:");
display.println((float)DHT11.temperature);
}

display.setCursor(80, 0);
display.println(&timeinfo, "%T"); // 格式化输出
display.display();
display.setCursor(0, 32);
display.print("IP:");
display.println(WiFi.localIP());
display.drawBitmap(0,40,Heart_16x16,16,16,1);
display.setCursor(30, 44);
display.print("JGX Desktop");
display.drawBitmap(112,40,Heart_16x16,16,16,1);
}
////新的一页显示图片
void bmp_display(void)
{
// 显示前清屏
display.stopscroll();
display.clearDisplay();
// 将图片显示在中心位置
display.drawBitmap(0,0,BMP_5, 128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_6, 128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_7, 128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_8,128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_9, 128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_10, 128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_11,128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_12, 128, 64, 1);
display.display();
delay(100);


display.clearDisplay();
display.drawBitmap(0,0,BMP_13,128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_14, 128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_15, 128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_16, 128, 64, 1);
display.display();
delay(100);

display.clearDisplay();
display.drawBitmap(0,0,BMP_17, 128, 64, 1);
display.display();
delay(100);
}
/****************************WiFi***************************************/
//用于配置WiFi
void wifi_Config()
{
Serial.println("scan start");
// 扫描附近WiFi
int n = WiFi.scanNetworks();
Serial.println("scan done");
if (n == 0) {
Serial.println("no networks found");
scanNetworksID = "no networks found";
} else {
Serial.print(n);
Serial.println(" networks found");
for (int i = 0; i < n; ++i) {
// Print SSID and RSSI for each network found
Serial.print(i + 1);
Serial.print(": ");
Serial.print(WiFi.SSID(i));
Serial.print(" (");
Serial.print(WiFi.RSSI(i));
Serial.print(")");
Serial.println((WiFi.encryptionType(i) == WIFI_AUTH_OPEN) ? " " : "*");
scanNetworksID += "<P>" + WiFi.SSID(i) + "</P>";
delay(10);
}
}
Serial.println("");

WiFi.mode(WIFI_AP);//配置为AP模式
boolean result = WiFi.softAP(AP_SSID, ""); //开启WIFI热点
if (result)
{
IPAddress myIP = WiFi.softAPIP();
//打印相关信息
Serial.println("");
Serial.print("Soft-AP IP address = ");
Serial.println(myIP);
Serial.println(String("MAC address = ") + WiFi.softAPmacAddress().c_str());
Serial.println("waiting ...");
} else { //开启热点失败
Serial.println("WiFiAP Failed");
delay(3000);
ESP.restart(); //复位esp32
}

if (MDNS.begin("esp32")) {
Serial.println("MDNS responder started");
}

//首页
server.on("/", []() {
server.send(200, "text/html", ROOT_HTML + scanNetworksID + "</body></html>");
});

//连接
server.on("/connect", []() {

server.send(200, "text/html", "<html><body><font size=\"10\">successd,wifi connecting...<br />Please close this page manually.</font></body></html>");

WiFi.softAPdisconnect(true);
//获取输入的WIFI账户和密码
wifi_ssid = server.arg("ssid");
wifi_pass = server.arg("pass");
server.close();
WiFi.softAPdisconnect();
Serial.println("WiFi Connect SSID:" + wifi_ssid + " PASS:" + wifi_pass);

//设置为STA模式并连接WIFI
WiFi.mode(WIFI_STA);
WiFi.begin(wifi_ssid.c_str(), wifi_pass.c_str());
uint8_t Connect_time = 0; //用于连接计时,如果长时间连接不成功,复位设备
while (WiFi.status() != WL_CONNECTED) { //等待WIFI连接成功
delay(500);
Serial.print(".");
Connect_time ++;
resetesp = 1;
if (Connect_time > 80) { //长时间连接不上,复位设备
Serial.println("Connection timeout, check input is correct or try again later!");
delay(3000);
ESP.restart();
}
}
Serial.println("");
Serial.println("WIFI Config Success");
Serial.printf("SSID:%s", WiFi.SSID().c_str());
Serial.print(" LocalIP:");
Serial.print(WiFi.localIP());
Serial.println("");
});
server.begin();
//server1.begin();
}
//用于上电自动连接WiFi
bool AutoConfig()
{
WiFi.begin();
for (int i = 0; i < 20; i++)
{
int wstatus = WiFi.status();
if (wstatus == WL_CONNECTED)
{
Serial.println("WIFI SmartConfig Success");
Serial.printf("SSID:%s", WiFi.SSID().c_str());
Serial.printf(", PSW:%s\r\n", WiFi.psk().c_str());
Serial.print("LocalIP:");
Serial.print(WiFi.localIP());
Serial.print(" ,GateIP:");
Serial.println(WiFi.gatewayIP());
return true;
}
else
{
Serial.print("WIFI AutoConfig Waiting......");
Serial.println(wstatus);
delay(1000);
}
}
Serial.println("WIFI AutoConfig Faild!" );
return false;
}

​ 其中即为ESP完整的代码部分,中间还有许多未解释说明,这些部分即为ESP32与STM32交互部分。

STM32与ESP32交互

​ 这个制作过程中,出现了一个大问题,由于之间写的温湿度显示是5S自动刷新词语,没改之前,出现开关数据,界面会一直发送,导致STM32与ESP32之间的串口通信会造成中断溢出,卡死在接收端上,后面便采取了输入输出高低电平做控制,在STM32与ESP32上进行设置输入引脚和输出引脚,通过控制功能,使用引脚饿高低变化来达到传递信息的作用,这一步麻烦了许多,在完全调整完毕后,才找到界面一直刷新的原因,由于硬件已经焊上,为了保持硬件,就依旧这么写下去。

​ 交互过程中,满足必须满足使用,在WIFI开启时,语音可以关闭,反之如此。这部分代码如下所示:

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
//STM32
#define WU PAout(4)
#define FAN PAout(5)
#define LED PAout(6)
#define ESP32RESET PAout(7)

#define LEDINPUTSTATUS_H GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_12)
#define LEDINPUTSTATUS_L GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_13)

#define WUINPUTSTATUS_H GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_14)
#define WUINPUTSTATUS_L GPIO_ReadInputDataBit(GPIOB,GPIO_Pin_15)

#define FANINPUTSTATUS_H GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_11)
#define FANINPUTSTATUS_L GPIO_ReadInputDataBit(GPIOA,GPIO_Pin_12)
/******************************ESP32重新设置密码********************************/
if (esp32status == 1)
{
delay_ms(40000);
esp32count++;
if(esp32count > 10)
{
ESP32RESET = 0;
esp32status = 0;
esp32count=0;
}
}
/******************************引脚输入********************************/
if(FANINPUTSTATUS_H == 1)
{
OLED_Clear();
FAN=1;
delay_ms(100);
OLED_ShowString(0,2,"Open the fan",32);
}
if(FANINPUTSTATUS_L == 0)
{
OLED_Clear();
FAN=0;
}
if(WUINPUTSTATUS_H == 1)
{
OLED_Clear();
delay_ms(100);
OLED_DrawBMP(45,1,85,6,jia);
WU=1;
}
if(WUINPUTSTATUS_L == 0)
{
OLED_Clear();
WU=0;
}
if(LEDINPUTSTATUS_H == 1 )
{
OLED_Clear();
delay_ms(100);
OLED_DrawBMP(45,1,93,7,deng);
LED=1;
}
if(LEDINPUTSTATUS_L == 0)
{
OLED_Clear();
LED=0;
}
//ESP32
typedef struct tool
{
int ledinput;
int faninput;
int wuinput;
}tools;
tools ntools;
/********************************检测开关状态****************************/
ntools.ledinput = digitalRead(35);
ntools.wuinput = digitalRead(34);
ntools.faninput = digitalRead(32);
delay(2000);
if(ntools.ledinput == HIGH)
{
Serial.println("ntools.ledinput high ");
LED = "on";
}
if(ntools.ledinput == LOW)
{
Serial.println("ntools.ledinput low ");
LED = "off";
}

if(ntools.faninput == HIGH)
{
Serial.println("ntools.faninput high ");
FAN = "on";
}
if(ntools.faninput == LOW)
{
Serial.println("ntools.faninput low ");

FAN = "off";
}
if(ntools.wuinput == HIGH)
{
Serial.println("ntools.wuinput high ");
ATOMIZER = "on";
}
if(ntools.wuinput == LOW)
{
Serial.println("ntools.wuinput low ");

ATOMIZER = "off";
}

/******************************END***************************************/

​ 到此即为全部代码部分,剩下的后续继续完善。

结尾:

​ 此作品,难度系数中等偏上,需要使用3个编译软件,需要熟悉HTML、C两种语言,以及懂得硬件的处理,软件上实现比较麻烦,需要使用硬件来满足。

​ 制作过程中,处理较为久的部分为页面的制作和交互部分,从连接WiFi到实现界面这个两个地方花费时间较久,问题在于,定义的参数类型不熟悉,使用了同一个参数,如:WebServer server(80)、WiFiServer server(80),最早没有处理明白,具体的即为服务不同,定义错误。

​ 其次即为交互部分,界面一直刷新导致了这个问题,原因在于在写界面是使用了这个http-equiv="Refresh" content="5"(这里不是标准的HTML,ESP32上面的写法),后面一直没有发现导致了,串口处理一直发生问题,也更加熟悉了串口的原理:USART_FLAG_FE 帧错误标志位 、USART_FLAG_PE 奇偶错误标志位、USART_FLAG_ORE查寻串口标志,为0还是1 reset 为0 set 为1 还有清除标志位的作用,由于串口一直接收,导致溢出,标志位没有清楚掉,一直卡在中断中,使用上这个清楚掉了标志位(这一部分,理解还不够深刻,有问题欢迎沟通交流),后面于是采用引脚的办法来传递信息。由于,引发问题的原因发现的较晚,方案改动,没有按照串口方法进行收发,后续有想尝试的欢迎留言一起解决!

​ 这次作品前期后后花费了2个月的时间,从框架,到各个部分的调通以及硬件的确定,收获到了许多东西,自己的经验又多了一些!后面还有继续制作一些小玩意,来分享给大家!

​ 谢谢观看!