ESP32异步网络请求

ESP32发起异步网络请求

之前做的GMartix在请求粉丝数量的时候,采用的是同步请求方式,同步请求会导致请求的时候产生阻塞。
很不够优雅,显得会卡住。所以这次做新作品,一定要试试异步请求,避免这种尴尬的阻塞!

异步请求库AsyncTCP-esphome

异步请求的操作由AsyncTCP-esphome提供。

示例代码

main.cpp
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
#include <Arduino.h>
#include <AsyncTCP.h>
#include <WiFi.h>
#include <Wire.h>

#define ssid "XXXXXX"
#define password "XXXXXX"

WiFiClient client;

String response;

void asyncReqeust() {
static AsyncClient *aClient;
if (aClient) {
// aClient already exists
// aClient 繁忙
return;
}
aClient = new AsyncClient();
if (!aClient) {
// could not allocate aClient
// aClient 无法创建
return;
}
aClient->onError(
[&](void *arg, AsyncClient *client, int error) {
Serial.println("getFollowersAsync:Connect Error");
aClient = NULL;
delete client;
},
NULL);

aClient->onConnect(
[&](void *arg, AsyncClient *client) {
Serial.println("getFollowersAsync:Connected");
aClient->onError(NULL, NULL);

// 连接断开的动作
client->onDisconnect(
[&](void *arg, AsyncClient *c) {
// finish = true;
aClient = NULL;
delete c;
Serial.println("getFollowersAsync:Disconnected");
},
NULL);

// 接收到服务器数据时候的动作
client->onData(
[&](void *arg, AsyncClient *c, void *data, size_t len) {
uint8_t *d = (uint8_t *)data;
for (size_t i = 0; i < len; i++) {
Serial.write(d[i]);
}
},
NULL);

// send the request
// 发送请求
client->write(
"GET /x/relation/stat?vmid=14374079 HTTP/1.1\r\nHost: "
"api.bilibili.com\r\nContent-Type: application/json; "
"charset=utf-8\r\n\r\n");
},
NULL);
if (!aClient->connect("api.bilibili.com", 80)) {
Serial.println("Connect Fail");
AsyncClient *client = aClient;
aClient = NULL;
delete client;
}
}

void setup() {
Serial.begin(115200);
client.setTimeout(1);
WiFi.begin(ssid, password);
WiFi.setAutoReconnect(true);
delay(3000);
asyncReqeust();
}

void loop() {}

正确连接WiFi后,打开串口就可以看到输出了

TFT_eSPI 自定义字体

向TFT_eSPI添加一种新的字体吧

还是在填TinyMonitor(暂定)的坑,现在需要更加好看的字体来显示订阅数,本来打算给需要用到的数字制作成图片然后取摸的,结果发现TFT_eSPI支持自定义字体。

准备工具

名称 获取方式
FontConvert.exe 下载链接
你想要添加的字体文件 看你如何获取

转换方式

1
Fontconvert.exe 字体文件名带后缀 字体大小 最小ASCII值  最大ASCII值 > 目标文件名称.h

举个例子,你需要自定义的字体文件名称叫做Furore.otf,然后把它复制到Fontconvert.exe同级文件夹里面,然后只需要A到Z,命令就是

1
Fontconvert.exe .\Furore.otf 12 65 90 > furore12.h

65是A的ASCII编码,90是Z的ASCII编码。

使用方式

复制生成的.h文件到TFT_eSPI/Fonts/Custom文件夹内,然后修改User_Custom_Fonts.h文件,在#ifdef LOAD_GFXFF和#endif中导入生成的furore12.h

使用时候将字体设置成新增字体即可,比如

1
2
eSprite->setFreeFont(&Furore28pt7b);
eSprite->drawString("FURORE", 0, 120, 1);

附件

这次就不贴图了,牵涉到的文件已经打包处理。字体是Furore,Free Font,请放心食用。Furore.zip

参考资料

ESP-32 播放GIF动图

让你的ESP32显示一张动图吧

最近在填TinyMonitor(暂定)的坑,突然想增加更加高级的动态效果,而我实在是想不出什么高级的动画效果了,于是决定借助一些GIF来实现一些有趣的效果。

然后就发现了这个AnimatedGIF,使用起来也灰常简单。

animation-preview

因为我TinyMonitor(暂定)驱动用的是TFT_eSPI
(至于如何驱动屏幕,可以参考之前的文章ESP32驱动ST7789液晶屏)

稍作修改后的代码

代码基于AnimatedGIF自带的example–TFT_eSPI_memory.ino稍作修改.

main.cpp
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
// TFT_eSPI_memory
//
// Example sketch which shows how to display an
// animated GIF image stored in FLASH memory
//
// written by Larry Bank
// [email protected]
//
// Adapted by Bodmer for the TFT_eSPI Arduino library:
// https://github.com/Bodmer/TFT_eSPI
//
// To display a GIF from memory, a single callback function
// must be provided - GIFDRAW
// This function is called after each scan line is decoded
// and is passed the 8-bit pixels, RGB565 palette and info
// about how and where to display the line. The palette entries
// can be in little-endian or big-endian order; this is specified
// in the begin() method.
//
// The AnimatedGIF class doesn't allocate or free any memory, but the
// instance data occupies about 22.5K of RAM.

//#define USE_DMA // ESP32 ~1.25x single frame rendering performance boost
// for badgers.h
// Note: Do not use SPI DMA if reading GIF images from SPI SD card on same bus
// as TFT
#define NORMAL_SPEED // Comment out for rame rate for render speed test

// Load GIF library
#include <AnimatedGIF.h>
AnimatedGIF gif;

// Example AnimatedGIF library images
#include "loading.h"
// ESP32 40MHz SPI single frame rendering performance
// Note: no DMA performance gain on smaller images or transparent pixel GIFs
#define GIF_IMAGE angry_80px_gif // No DMA 63 fps, DMA: 71fps

#include <SPI.h>
#include <TFT_eSPI.h>
#define DISPLAY_WIDTH tft.width()
#define DISPLAY_HEIGHT tft.height()
#define BUFFER_SIZE \
60 // Optimum is >= GIF width or integral division of width

#ifdef USE_DMA
uint16_t usTemp[2][BUFFER_SIZE]; // Global to support DMA use
#else
uint16_t usTemp[1][BUFFER_SIZE]; // Global to support DMA use
#endif
bool dmaBuf = 0;
TFT_eSPI tft = TFT_eSPI();

// Draw a line of image directly on the LCD
void GIFDraw(GIFDRAW *pDraw) {
uint8_t *s;
uint16_t *d, *usPalette;
int x, y, iWidth, iCount;

// Display bounds chech and cropping
iWidth = pDraw->iWidth;
if (iWidth + pDraw->iX > DISPLAY_WIDTH) iWidth = DISPLAY_WIDTH - pDraw->iX;
usPalette = pDraw->pPalette;
y = pDraw->iY + pDraw->y; // current line
if (y >= DISPLAY_HEIGHT || pDraw->iX >= DISPLAY_WIDTH || iWidth < 1) return;

// Old image disposal
s = pDraw->pPixels;
if (pDraw->ucDisposalMethod == 2) // restore to background color
{
for (x = 0; x < iWidth; x++) {
if (s[x] == pDraw->ucTransparent) s[x] = pDraw->ucBackground;
}
pDraw->ucHasTransparency = 0;
}

// Apply the new pixels to the main image
if (pDraw->ucHasTransparency) // if transparency used
{
// uint8_t *pEnd, c, ucTransparent = pDraw->ucTransparent;
uint8_t *pEnd, c, ucTransparent = TFT_BLACK;
pEnd = s + iWidth;
x = 0;
iCount = 0; // count non-transparent pixels
while (x < iWidth) {
c = ucTransparent - 1;
d = &usTemp[0][0];
while (c != ucTransparent && s < pEnd && iCount < BUFFER_SIZE) {
c = *s++;
if (c == ucTransparent) // done, stop
{
s--; // back up to treat it like transparent
} else // opaque
{
*d++ = usPalette[c];
iCount++;
}
} // while looking for opaque pixels
if (iCount) // any opaque pixels?
{
// DMA would degrtade performance here due to short line segments
tft.setAddrWindow(pDraw->iX + x, y, iCount, 1);
tft.pushPixels(usTemp, iCount);
x += iCount;
iCount = 0;
}
// no, look for a run of transparent pixels
c = ucTransparent;
while (c == ucTransparent && s < pEnd) {
c = *s++;
if (c == ucTransparent)
x++;
else
s--;
}
}
} else {
s = pDraw->pPixels;

// Unroll the first pass to boost DMA performance
// Translate the 8-bit pixels through the RGB565 palette (already byte
// reversed)
if (iWidth <= BUFFER_SIZE)
for (iCount = 0; iCount < iWidth; iCount++)
usTemp[dmaBuf][iCount] = usPalette[*s++];
else
for (iCount = 0; iCount < BUFFER_SIZE; iCount++)
usTemp[dmaBuf][iCount] = usPalette[*s++];

#ifdef USE_DMA // 71.6 fps (ST7796 84.5 fps)
tft.dmaWait();
tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
tft.pushPixelsDMA(&usTemp[dmaBuf][0], iCount);
dmaBuf = !dmaBuf;
#else // 57.0 fps
tft.setAddrWindow(pDraw->iX, y, iWidth, 1);
tft.pushPixels(&usTemp[0][0], iCount);
#endif

iWidth -= iCount;
// Loop if pixel buffer smaller than width
while (iWidth > 0) {
// Translate the 8-bit pixels through the RGB565 palette (already byte
// reversed)
if (iWidth <= BUFFER_SIZE)
for (iCount = 0; iCount < iWidth; iCount++)
usTemp[dmaBuf][iCount] = usPalette[*s++];
else
for (iCount = 0; iCount < BUFFER_SIZE; iCount++)
usTemp[dmaBuf][iCount] = usPalette[*s++];

#ifdef USE_DMA
tft.dmaWait();
tft.pushPixelsDMA(&usTemp[dmaBuf][0], iCount);
dmaBuf = !dmaBuf;
#else
tft.pushPixels(&usTemp[0][0], iCount);
#endif
iWidth -= iCount;
}
}
} /* GIFDraw() */

void setup() {
Serial.begin(115200);

tft.begin();
#ifdef USE_DMA
tft.initDMA();
#endif
tft.setRotation(1);
tft.fillScreen(TFT_BLACK);

gif.begin(BIG_ENDIAN_PIXELS);

}

#ifdef NORMAL_SPEED // Render at rate that is GIF controlled
void loop() {
// Put your main code here, to run repeatedly:
if (gif.open((uint8_t *)GIF_IMAGE, sizeof(GIF_IMAGE), GIFDraw)) {
Serial.printf("Successfully opened GIF; Canvas size = %d x %d\n",
gif.getCanvasWidth(), gif.getCanvasHeight());
tft.startWrite(); // The TFT chip slect is locked low
while (gif.playFrame(true, NULL)) {
yield();
}
gif.close();
tft.endWrite(); // Release TFT chip select for other SPI devices
}
}
#else // Test maximum rendering speed
void loop() {
long lTime = micros();
int iFrames = 0;

if (gif.open((uint8_t *)GIF_IMAGE, sizeof(GIF_IMAGE), GIFDraw)) {
tft.startWrite(); // For DMA the TFT chip slect is locked low
while (gif.playFrame(false, NULL)) {
// Each loop renders one frame
iFrames++;
yield();
}
gif.close();
tft.endWrite(); // Release TFT chip select for other SPI devices
lTime = micros() - lTime;
Serial.print(iFrames / (lTime / 1000000.0));
Serial.println(" fps");
}
}
#endif

下面为loading.h的内容位于Gist,因为内容太长影响排版,就只贴一个连接了。

如何对GIF文件转换成可用的格式

loading.h的制作方法如下.

1
xxd -i angry_80px.gif >> loading.h
名称 描述
xxd 以2进制或者16进制显示文件内容
angry_80px.gif 输入文件
loading.h 输出文件

XXD是一个Linux命令,windows下默认没有这个命令

参考资料

© 2025 Do U Find IT? All Rights Reserved.
Theme by hiero