STM32F1网络编程-HTTP客户端请求(基于W5500网卡)

HTTP客户端请求(基于W5500网卡)

HTTP是超文本传输协议(Hyper Text Transfer Protocol,HTTP)是一个简单的请求-响应协议,它通常运行在TCP之上。本文将详细介绍如何在W5500驱动基础上实现HTTP请求。

W5500中的HTTP请求主要由如下步骤组成:

  • 1)初始化HTTP客户端
  • 2)创建HTTP客户的TCP连接
  • 3)向远程服务发送HTTP请求头
  • 4)读取远程服务器HTTP响应数据

关于W5500的驱动移植及IP设置、获取,请参考:

关于TCP客户的通信过程,请参考:

关于远程主机IP查询,请参考:

1、HTTP客户端实现

在STM32CubeIDE工程的Application目录中分别创建http_client.hhttp_client.c文件。HTTP客户端实现的基本定义如下:

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
/\*
\* http\_client.h
\*
\* Created on: 2022年6月29日
\* Author: jenson
\*/

#ifndef HTTP\_CLIENT\_H\_
#define HTTP\_CLIENT\_H\_

#include <stdint.h>


// HTTP client debug message enable
#define \_HTTPCLIENT\_DEBUG\_ 1

// HTTP请求数据缓冲大小
#ifndef DATA\_BUF\_SIZE
#define DATA\_BUF\_SIZE 2048
#endif

// HTTP随机端口范围
#define HTTP\_CLIENT\_PORT\_MIN 0xB000 // 45056
#define HTTP\_CLIENT\_PORT\_MAX 0xFFFF // 65535

/\*\*
\* HTTP方法
\*\*/
#define HTTP\_GET "GET"
#define HTTP\_HEAD "HEAD"
#define HTTP\_POST "POST"
#define HTTP\_PUT "PUT"
#define HTTP\_DELETE "DELETE"


/\*\*
\* HTTP Content-Type
\*/
#define HTTP\_CTYPE\_MULTIPART\_FORM "multipart/form-data"
#define HTTP\_CTYPE\_APP\_HTTP\_FORM "Application/x-www-form-urlencoded"
#define HTTP\_CTYPE\_APP\_JS "Application/javascript"
#define HTTP\_CTYPE\_APP\_JSON "Application/json"
#define HTTP\_CTYPE\_APP\_XML "Application/xml"
#define HTTP\_CTYPE\_TEXT\_PLAIN "text/plain"
#define HTTP\_CTYPE\_TEXT\_HTML "text/html"
#define HTTP\_CTYPE\_TEXT\_CSS "text/css"
#define HTTP\_CTYPE\_TEXT\_JS "text/javascript"
#define HTTP\_CTYPE\_TEXT\_XML "text/xml"


/\*\*
\* HTTP客户端返回值
\*/
// 失败
#define HTTPC\_FAILED 0
// 成功
#define HTTPC\_SUCCESS 1
// 连接
#define HTTPC\_CONNECTED 2

#define HTTPC\_FALSE 0
#define HTTPC\_TRUE 1


/\*\*
\* HTTP客户端初始化宏定义
\*/
#define HttpRequest\_multipart\_post\_initializer {(uint8\_t \*)HTTP\_POST, NULL, NULL, (uint8\_t \*)HTTP\_CTYPE\_MULTIPART\_FORM, (uint8\_t \*)"keep-alive", 0}
#define HttpRequest\_get\_initializer {(uint8\_t \*)HTTP\_GET, NULL, NULL, NULL, (uint8\_t \*)"keep-alive", 0}


/\*\*
\* HTTP Boundary String for Multipart/form data
\*/
#define formDataBoundary "----WebKitFormBoundaryE8pT6qqUHgRhSDDC" // example boundary string for multipart form data


// HTTP客户端
typedef struct \_\_HttpRequest {
uint8\_t \* method; // 请求方法
uint8\_t \* uri; // 请求URI
uint8\_t \* host; // 远程主机
uint8\_t \* content_type; // 内容类型
uint8\_t \* connection; // 连接
uint32\_t content_length; // 内容长度
} \_\_attribute\_\_((packed)) HttpRequest;

// HTTP客户端状态
extern uint8\_t httpc_isSockOpen; // socket打开状态
extern uint8\_t httpc_isConnected; // socket连接状态
extern uint16\_t httpc_isReceived; // 接收到数据

// extern: HTTP request structure
extern HttpRequest request;

/\*\*
\* HTTP客户端方法
\*/

// HTTP客户端socket处理,一般在主函数循环中调用
uint8\_t httpc\_connection\_handler();

/\*\*
\* HTTP客户端初始化
\*/
uint8\_t httpc\_init(uint8\_t sock, uint8\_t \* ip, uint16\_t port, uint8\_t \* sbuf, uint8\_t \* rbuf);
/\*\*
\*HTTP客户端连接
\*/
uint8\_t httpc\_connect();
/\*\*
\* 断开HTTP客户端连接
\*/
uint8\_t httpc\_disconnect(void);

/\*\*
\* 自定义HTTP请求头
\*
\*/
uint16\_t httpc\_add\_customHeader\_field(uint8\_t \* customHeader_buf, const char \* name, const char \* value);

/\*\*
\* 发送HTTP请求头
\*/
uint16\_t httpc\_send\_header(HttpRequest \* req, uint8\_t \* buf, uint8\_t \* customHeader_buf, uint16\_t content_len);

/\*\*
\* 发送HTTP请求body,在httpc\_send\_header之后调用
\*/
uint16\_t httpc\_send\_body(uint8\_t \* buf, uint16\_t len);

/\*\*
\* 发送完整HTTP
\*/
uint16\_t httpc\_send(HttpRequest \* req, uint8\_t \* buf, uint8\_t \* body, uint16\_t content_len);

/\*\*
\*接收HTTP响应数据
\*/
uint16\_t httpc\_recv(uint8\_t \* buf, uint16\_t len);


#endif /\* HTTP\_CLIENT\_H\_ \*/

http_client.c中添加如下代码:

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
/\*
\* http\_client.c
\*
\* Created on: 2022年6月29日
\* Author: jenson
\*/

#include <stdlib.h>
#include <string.h>

#include "wizchip\_conf.h"
#include "socket.h"
#include "http\_client.h"
#include <stdio.h>

uint8\_t \*httpc_send_buf;
uint8\_t \*httpc_recv_buf;

// 初始化HTTP客户端(GET请求方法)
HttpRequest request = HttpRequest_get_initializer;

static int8\_t httpsock = 0;
static uint8\_t dest_ip[4] = { 0, };
static uint16\_t dest_port = 0;
static uint16\_t httpc_any_port = 0;

uint8\_t httpc_isSockOpen = HTTPC_FALSE;
uint8\_t httpc_isConnected = HTTPC_FALSE;
uint16\_t httpc_isReceived = HTTPC_FALSE;



2)辅助函数实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 生成一个随机端口
uint16\_t get\_httpc\_any\_port(void);

uint16\_t get\_httpc\_any\_port(void) {
if (httpc_any_port) {
if ((httpc_any_port >= HTTP_CLIENT_PORT_MIN)
&& (httpc_any_port < HTTP_CLIENT_PORT_MAX)) {
httpc_any_port++;
} else {
httpc_any_port = 0;
}
}

if (httpc_any_port < HTTP_CLIENT_PORT_MIN) {
// todo: get random seed value
httpc_any_port = (rand() % 10000) + 46000; // 46000 ~ 55999
}

return httpc_any_port;
}

3)HTTP初始化

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
uint8\_t httpc\_init(uint8\_t sock, uint8\_t \*ip, uint16\_t port, uint8\_t \*sbuf,
uint8\_t \*rbuf) {
uint8\_t ret = HTTPC_FALSE;

if (sock <= _WIZCHIP_SOCK_NUM_) {
// W5500最大支付Socket数量为8个,编号为 (0 ~ 7)
httpsock = sock;

// 接收和发送缓冲
httpc_send_buf = sbuf;
httpc_recv_buf = rbuf;

// 远程主机IP
dest_ip[0] = ip[0];
dest_ip[1] = ip[1];
dest_ip[2] = ip[2];
dest_ip[3] = ip[3];
dest_port = port;

ret = HTTPC_TRUE;
}

return ret;
}

uint8\_t httpc\_connect() {
uint8\_t ret = HTTPC_FALSE;

if (httpsock >= 0) {
if (httpc_isSockOpen == HTTPC_TRUE) {
// 连接socket
ret = connect(httpsock, dest_ip, dest_port);
if (ret == SOCK_OK)
ret = HTTPC_TRUE;
}
}

return ret;
}

uint8\_t httpc\_disconnect(void) {
uint8\_t ret = HTTPC_FALSE;

if (httpc_isConnected == HTTPC_TRUE) {
#ifdef \_HTTPCLIENT\_DEBUG\_
printf(" > HTTP CLIENT: Try to disconnect\r\n");
#endif
// 断开socket
ret = disconnect(httpsock);
if (ret == SOCK_OK) {
ret = HTTPC_TRUE;
#ifdef \_HTTPCLIENT\_DEBUG\_
printf(" > HTTP CLIENT: Disconnected\r\n");
#endif
}
}

return ret;
}

4)HTTP客户端的socket状态处理

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
uint8\_t httpc\_connection\_handler() {
uint8\_t ret = HTTPC_FALSE;

uint16\_t source_port;

#ifdef \_HTTPCLIENT\_DEBUG\_
uint8\_t destip[4] = {0, };
uint16\_t destport = 0;
#endif
// 查询当前socket状态
uint8\_t state = getSn\_SR(httpsock);
switch (state) {
case SOCK_INIT: // socket初始化
// state: connect
ret = HTTPC_TRUE;
break;

case SOCK_ESTABLISHED: // socket连接建立
if (getSn\_IR(httpsock) & Sn_IR_CON) {
#ifdef \_HTTPCLIENT\_DEBUG\_
// Serial debug message printout
getsockopt(httpsock, SO_DESTIP, &destip);
getsockopt(httpsock, SO_DESTPORT, &destport);
printf(" > HTTP CLIENT: CONNECTED TO - %d.%d.%d.%d : %d\r\n",destip[0], destip[1], destip[2], destip[3], destport);
#endif
httpc_isConnected = HTTPC_TRUE;

setSn\_IR(httpsock, Sn_IR_CON);
}

httpc_isReceived = getSn\_RX\_RSR(httpsock);
ret = HTTPC_CONNECTED;
break;

case SOCK_CLOSE_WAIT: // socket等待关闭
disconnect(httpsock);
break;

case SOCK_FIN_WAIT:
case SOCK_CLOSED: // socket关闭
httpc_isSockOpen = HTTPC_FALSE;
httpc_isConnected = HTTPC_FALSE;
// 生成随机端口
source_port = get\_httpc\_any\_port();
#ifdef \_HTTPCLIENT\_DEBUG\_
printf(" > HTTP CLIENT: source\_port = %d\r\n", source_port);
#endif
// 创建一个TCPsocket
if (socket(httpsock, Sn_MR_TCP, source_port, Sn_MR_ND) == httpsock) {
if (httpc_isSockOpen == HTTPC_FALSE) {
#ifdef \_HTTPCLIENT\_DEBUG\_
printf(" > HTTP CLIENT: SOCKOPEN\r\n");
#endif
httpc_isSockOpen = HTTPC_TRUE;
}
}

break;

default:
break;
}

return ret;
}

5)HTTP数据发送与接收

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
uint16\_t httpc\_add\_customHeader\_field(uint8\_t \*customHeader_buf,
const char \*name, const char \*value) {
uint16\_t len = 0;

if ((strlen((char\*) name) + strlen((char\*) value)) > DATA_BUF_SIZE)
return 0;

if (strlen((char\*) customHeader_buf) == 0) {
len = sprintf((char\*) customHeader_buf, "%s: %s\r\n", name, value);
} else {
len = sprintf((char\*) customHeader_buf, "%s%s: %s\r\n",
customHeader_buf, name, value);
}

return len;
}


uint16\_t httpc\_send\_header(HttpRequest \*req, uint8\_t \*buf,
uint8\_t \*customHeader_buf, uint16\_t content_len) {
uint16\_t len;

if (httpc_isConnected == HTTPC_TRUE) {
memset(buf, 0x00, DATA_BUF_SIZE);

len = sprintf((char\*) buf, "%s %s HTTP/1.1\r\n", req->method, req->uri);
len += sprintf((char\*) buf + len, "Host: %s\r\n", req->host);
len += sprintf((char\*) buf + len, "Connection: %s\r\n",
req->connection);

// HTTP content type: POST / PUT
if (content_len > 0) {
len += sprintf((char\*) buf + len, "Content-Length: %d\r\n",
content_len);

if (strcmp((char\*) req->content_type, HTTP_CTYPE_MULTIPART_FORM)
== 0) {
// HTTP content type: multipart/form-data
len += sprintf((char\*) buf + len,
"Content-Type: %s; boundary=%s\r\n", req->content_type,
formDataBoundary);
} else {
// 其他HTTP Content-Type
len += sprintf((char\*) buf + len, "Content-Type: %s\r\n",
req->content_type); // HTTP content type: others
}
}

// 自定义请求头
if (customHeader_buf != NULL) {
if ((strlen((char\*) customHeader_buf) + len + 2) <= DATA_BUF_SIZE) {
len += sprintf((char\*) buf + len, "%s", customHeader_buf);
}
}

len += sprintf((char\*) buf + len, "\r\n");

#ifdef \_HTTPCLIENT\_DEBUG\_
printf(" >> HTTP Request header - Method: %s, URI: %s, Content-Length: %d\r\n", req->method, req->uri, content_len);
printf("%s", buf);
#endif
// 发送数据
send(httpsock, buf, len);
} else {
len = HTTPC_FAILED;
}

return len;
}

uint16\_t httpc\_send\_body(uint8\_t \*buf, uint16\_t len) {
uint16\_t sentlen = 0;

#ifdef \_HTTPCLIENT\_DEBUG\_
uint16\_t i;
#endif

if (httpc_isConnected == HTTPC_TRUE) {
do {
// 发送数据
sentlen += send(httpsock, buf, len);
} while (sentlen < len);

#ifdef \_HTTPCLIENT\_DEBUG\_
if(sentlen > 0)
{
printf(" >> HTTP Request Body\r\n");
for(i = 0; i < sentlen; i++) printf("%c", buf[i]);
printf("\r\n");
}
#endif
} else {
sentlen = HTTPC_FAILED;
}

return sentlen;
}


uint16\_t httpc\_send(HttpRequest \*req, uint8\_t \*buf, uint8\_t \*body,
uint16\_t content_len) {
uint16\_t i;
uint16\_t len;
uint8\_t http_header_generated = HTTPC_FAILED;

if (httpc_isConnected == HTTPC_TRUE) {
do {
memset(buf, 0x00, DATA_BUF_SIZE);

/\* HTTP 请求头 \*/
len = sprintf((char\*) buf, "%s %s HTTP/1.1\r\n", req->method,
req->uri);
len += sprintf((char\*) buf + len, "Host: %s\r\n", req->host);
len += sprintf((char\*) buf + len, "Connection: %s\r\n",
req->connection);

// HTTP content type: POST / PUT
if (content_len > 0) {
len += sprintf((char\*) buf + len, "Content-Length: %d\r\n",
content_len);

if (strcmp((char\*) req->content_type, HTTP_CTYPE_MULTIPART_FORM)
== 0) {
// HTTP content type: multipart/form-data
len += sprintf((char\*) buf + len,
"Content-Type: %s; boundary=%s\r\n",
req->content_type, formDataBoundary);
} else {
// HTTP content type: others
len += sprintf((char\*) buf + len, "Content-Type: %s\r\n",
req->content_type); // HTTP content type: others
}
}
len += sprintf((char\*) buf + len, "\r\n");

// 避免缓存溢出
if ((len + content_len) > DATA_BUF_SIZE) {
content_len = DATA_BUF_SIZE - len;

} else {
http_header_generated = HTTPC_SUCCESS;
}
} while (http_header_generated != HTTPC_SUCCESS);

/\* HTTP 请求 body \*/
for (i = 0; i < content_len; i++) {
buf[len++] = body[i];
}

//#ifdef \_HTTPCLIENT\_DEBUG\_
printf(" >> HTTP Request - Method: %s, URI: %s, Content-Length: %d\r\n",
req->method, req->uri, content_len);
for (i = 0; i < len; i++)
printf("%c", buf[i]);
printf("\r\n");
//#endif
// 发送数据
send(httpsock, buf, len);
} else {
len = HTTPC_FAILED;
}

return len;
}

uint16\_t httpc\_recv(uint8\_t \*buf, uint16\_t len) {
uint16\_t recvlen;

if (httpc_isConnected == HTTPC_TRUE) {
if (len > DATA_BUF_SIZE)
len = DATA_BUF_SIZE;
recvlen = recv(httpsock, buf, len);
} else {
recvlen = HTTPC_FAILED;
}

return recvlen;
}

2、HTTP客户端测试

main.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
/\* Private includes ----------------------------------------------------------\*/
/\* USER CODE BEGIN Includes \*/
#include <stdio.h>
#include "w5500\_port.h"
#include <string.h> // memcmp
#include "http\_client.h"
/\* USER CODE END Includes \*/

/\* Private define ------------------------------------------------------------\*/
/\* USER CODE BEGIN PD \*/
#define DATA\_BUF\_SIZE 2048
#define SOCK\_DNS 4
/\* USER CODE END PD \*/

/\* USER CODE BEGIN PV \*/
#define \_MAIN\_DEBUG\_

//uint8\_t httpc\_isConnected = HTTPC\_FALSE;
//uint16\_t httpc\_isReceived = HTTPC\_FALSE;
typedef enum {
OFF = 0, ON = 1
} OnOff_State_Type;
uint8\_t flag_process_dhcp_success = OFF;
uint8\_t flag_process_dns_success = OFF;
uint8\_t dns_server[4] = { 8, 8, 8, 8 };
int8\_t Domain_IP[4] = { 0, };
uint8\_t Domain_name[] = "api.seniverse.com";
uint8\_t URI[] = "/v3/weather/now.json?key=自己的APPKey&location=beijing&language=zh-Hans&unit=c";
uint8\_t flag_sent_http_request = DISABLE;

uint8\_t g_send_buf[DATA_BUF_SIZE];
uint8\_t g_recv_buf[DATA_BUF_SIZE];

uint8\_t data_buf_nds[DATA_BUF_SIZE]; // TX Buffer for applications

/\* USER CODE END PV \*/


/\* Private user code ---------------------------------------------------------\*/
/\* USER CODE BEGIN 0 \*/

// 查询远程主机的IP
int8\_t process\_dns(void) {
int8\_t ret = 0;
uint8\_t dns_retry = 0;

#ifdef \_MAIN\_DEBUG\_
printf(" - DNS Client running\r\n");
#endif
DNS\_init(SOCK_DNS, data_buf_nds);

while (1) {

if ((ret = DNS\_run(dns_server, (uint8\_t\*) Domain_name, Domain_IP))
== 1) {
#ifdef \_MAIN\_DEBUG\_
printf(" - DNS Success\r\n");
#endif
break;
} else {
dns_retry++;
#ifdef \_MAIN\_DEBUG\_
if (dns_retry <= 2)
printf(" - DNS Timeout occurred and retry [%d]\r\n", dns_retry);
#endif
}

if (dns_retry > 2) {
#ifdef \_MAIN\_DEBUG\_
printf(" - DNS Failed\r\n\r\n");
#endif
break;
}

#ifdef \_\_USE\_DHCP\_\_
DHCP\_run();
#endif
}
return ret;
}

// 处理HTTP请求
void process\_http\_request(void) {
httpc\_init(0, Domain_IP, 80, g_send_buf, g_recv_buf);
while (1) {
httpc\_connection\_handler();
if (httpc_isSockOpen) {
httpc\_connect();
}
if (httpc_isConnected) {
if (!flag_sent_http_request) {
// 发送HTTP请求
request.method = (uint8\_t\*) HTTP_GET;
request.uri = (uint8\_t\*) URI;
request.host = (uint8\_t\*) Domain_name;

httpc\_send(&request, g_recv_buf, g_send_buf, 0);

flag_sent_http_request = ENABLE;
}

// 接收HTTP请求响应数据
if (httpc_isReceived > 0) {
int len = httpc\_recv(g_recv_buf, httpc_isReceived);

printf(" >> HTTP Response - Received len: %d\r\n", len);
printf(
"======================================================\r\n");
for (uint32\_t i = 0; i < len; i++)
printf("%c", g_recv_buf[i]);
printf("\r\n");
printf(
"======================================================\r\n");
}
}
}
}
/\* USER CODE END 0 \*/

int main(void) {
/\* USER CODE BEGIN 1 \*/

/\* USER CODE END 1 \*/

/\* MCU Configuration--------------------------------------------------------\*/

/\* Reset of all peripherals, Initializes the Flash interface and the Systick. \*/
HAL\_Init();

/\* USER CODE BEGIN Init \*/

/\* USER CODE END Init \*/

/\* Configure the system clock \*/
SystemClock\_Config();

/\* USER CODE BEGIN SysInit \*/

/\* USER CODE END SysInit \*/

/\* Initialize all configured peripherals \*/
MX\_GPIO\_Init();
MX\_USART1\_UART\_Init();
MX\_SPI1\_Init();
/\* USER CODE BEGIN 2 \*/
printf("-----STM32 W5500 Ethernet Demo-----\r\n");
w5500\_restart(); // hardware restart through RESET pin
wiznet\_register();
w5500\_network\_init();
wiznet\_chip\_config();

process\_dns();
process\_http\_request();

/\* USER CODE END 2 \*/

/\* Infinite loop \*/
/\* USER CODE BEGIN WHILE \*/
while (1) {
/\* USER CODE END WHILE \*/
HAL\_Delay(5000);
/\* USER CODE BEGIN 3 \*/
}
/\* USER CODE END 3 \*/
}

注意,本次实例使用的天气API服务为心知天气,请自行注册使用。

程序运行结果如下:

在这里插入图片描述

文章来源: https://iotsmart.blog.csdn.net/article/details/125943316