forked from raspberrypi/pico-examples
-
Notifications
You must be signed in to change notification settings - Fork 1
/
picow_tcp_client.c
256 lines (225 loc) · 7.66 KB
/
picow_tcp_client.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
/**
* Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
*
* SPDX-License-Identifier: BSD-3-Clause
*/
#include <string.h>
#include <time.h>
#include "pico/stdlib.h"
#include "pico/cyw43_arch.h"
#include "lwip/pbuf.h"
#include "lwip/tcp.h"
#if !defined(TEST_TCP_SERVER_IP)
#error TEST_TCP_SERVER_IP not defined
#endif
#define TCP_PORT 4242
#define DEBUG_printf printf
#define BUF_SIZE 2048
#define TEST_ITERATIONS 10
#define POLL_TIME_S 5
#if 0
static void dump_bytes(const uint8_t *bptr, uint32_t len) {
unsigned int i = 0;
printf("dump_bytes %d", len);
for (i = 0; i < len;) {
if ((i & 0x0f) == 0) {
printf("\n");
} else if ((i & 0x07) == 0) {
printf(" ");
}
printf("%02x ", bptr[i++]);
}
printf("\n");
}
#define DUMP_BYTES dump_bytes
#else
#define DUMP_BYTES(A,B)
#endif
typedef struct TCP_CLIENT_T_ {
struct tcp_pcb *tcp_pcb;
ip_addr_t remote_addr;
uint8_t buffer[BUF_SIZE];
int buffer_len;
int sent_len;
bool complete;
int run_count;
bool connected;
} TCP_CLIENT_T;
static err_t tcp_client_close(void *arg) {
TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg;
err_t err = ERR_OK;
if (state->tcp_pcb != NULL) {
tcp_arg(state->tcp_pcb, NULL);
tcp_poll(state->tcp_pcb, NULL, 0);
tcp_sent(state->tcp_pcb, NULL);
tcp_recv(state->tcp_pcb, NULL);
tcp_err(state->tcp_pcb, NULL);
err = tcp_close(state->tcp_pcb);
if (err != ERR_OK) {
DEBUG_printf("close failed %d, calling abort\n", err);
tcp_abort(state->tcp_pcb);
err = ERR_ABRT;
}
state->tcp_pcb = NULL;
}
return err;
}
// Called with results of operation
static err_t tcp_result(void *arg, int status) {
TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg;
if (status == 0) {
DEBUG_printf("test success\n");
} else {
DEBUG_printf("test failed %d\n", status);
}
state->complete = true;
return tcp_client_close(arg);
}
static err_t tcp_client_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) {
TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg;
DEBUG_printf("tcp_client_sent %u\n", len);
state->sent_len += len;
if (state->sent_len >= BUF_SIZE) {
state->run_count++;
if (state->run_count >= TEST_ITERATIONS) {
tcp_result(arg, 0);
return ERR_OK;
}
// We should receive a new buffer from the server
state->buffer_len = 0;
state->sent_len = 0;
DEBUG_printf("Waiting for buffer from server\n");
}
return ERR_OK;
}
static err_t tcp_client_connected(void *arg, struct tcp_pcb *tpcb, err_t err) {
TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg;
if (err != ERR_OK) {
printf("connect failed %d\n", err);
return tcp_result(arg, err);
}
state->connected = true;
DEBUG_printf("Waiting for buffer from server\n");
return ERR_OK;
}
static err_t tcp_client_poll(void *arg, struct tcp_pcb *tpcb) {
DEBUG_printf("tcp_client_poll\n");
return tcp_result(arg, -1); // no response is an error?
}
static void tcp_client_err(void *arg, err_t err) {
if (err != ERR_ABRT) {
DEBUG_printf("tcp_client_err %d\n", err);
tcp_result(arg, err);
}
}
err_t tcp_client_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg;
if (!p) {
return tcp_result(arg, -1);
}
// this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you
// can use this method to cause an assertion in debug mode, if this method is called when
// cyw43_arch_lwip_begin IS needed
cyw43_arch_lwip_check();
if (p->tot_len > 0) {
DEBUG_printf("recv %d err %d\n", p->tot_len, err);
for (struct pbuf *q = p; q != NULL; q = q->next) {
DUMP_BYTES(q->payload, q->len);
}
// Receive the buffer
const uint16_t buffer_left = BUF_SIZE - state->buffer_len;
state->buffer_len += pbuf_copy_partial(p, state->buffer + state->buffer_len,
p->tot_len > buffer_left ? buffer_left : p->tot_len, 0);
tcp_recved(tpcb, p->tot_len);
}
pbuf_free(p);
// If we have received the whole buffer, send it back to the server
if (state->buffer_len == BUF_SIZE) {
DEBUG_printf("Writing %d bytes to server\n", state->buffer_len);
err_t err = tcp_write(tpcb, state->buffer, state->buffer_len, TCP_WRITE_FLAG_COPY);
if (err != ERR_OK) {
DEBUG_printf("Failed to write data %d\n", err);
return tcp_result(arg, -1);
}
}
return ERR_OK;
}
static bool tcp_client_open(void *arg) {
TCP_CLIENT_T *state = (TCP_CLIENT_T*)arg;
DEBUG_printf("Connecting to %s port %u\n", ip4addr_ntoa(&state->remote_addr), TCP_PORT);
state->tcp_pcb = tcp_new_ip_type(IP_GET_TYPE(&state->remote_addr));
if (!state->tcp_pcb) {
DEBUG_printf("failed to create pcb\n");
return false;
}
tcp_arg(state->tcp_pcb, state);
tcp_poll(state->tcp_pcb, tcp_client_poll, POLL_TIME_S * 2);
tcp_sent(state->tcp_pcb, tcp_client_sent);
tcp_recv(state->tcp_pcb, tcp_client_recv);
tcp_err(state->tcp_pcb, tcp_client_err);
state->buffer_len = 0;
// cyw43_arch_lwip_begin/end should be used around calls into lwIP to ensure correct locking.
// You can omit them if you are in a callback from lwIP. Note that when using pico_cyw_arch_poll
// these calls are a no-op and can be omitted, but it is a good practice to use them in
// case you switch the cyw43_arch type later.
cyw43_arch_lwip_begin();
err_t err = tcp_connect(state->tcp_pcb, &state->remote_addr, TCP_PORT, tcp_client_connected);
cyw43_arch_lwip_end();
return err == ERR_OK;
}
// Perform initialisation
static TCP_CLIENT_T* tcp_client_init(void) {
TCP_CLIENT_T *state = calloc(1, sizeof(TCP_CLIENT_T));
if (!state) {
DEBUG_printf("failed to allocate state\n");
return NULL;
}
ip4addr_aton(TEST_TCP_SERVER_IP, &state->remote_addr);
return state;
}
void run_tcp_client_test(void) {
TCP_CLIENT_T *state = tcp_client_init();
if (!state) {
return;
}
if (!tcp_client_open(state)) {
tcp_result(state, -1);
return;
}
while(!state->complete) {
// the following #ifdef is only here so this same example can be used in multiple modes;
// you do not need it in your code
#if PICO_CYW43_ARCH_POLL
// if you are using pico_cyw43_arch_poll, then you must poll periodically from your
// main loop (not from a timer) to check for Wi-Fi driver or lwIP work that needs to be done.
cyw43_arch_poll();
// you can poll as often as you like, however if you have nothing else to do you can
// choose to sleep until either a specified time, or cyw43_arch_poll() has work to do:
cyw43_arch_wait_for_work_until(make_timeout_time_ms(1000));
#else
// if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work
// is done via interrupt in the background. This sleep is just an example of some (blocking)
// work you might be doing.
sleep_ms(1000);
#endif
}
free(state);
}
int main() {
stdio_init_all();
if (cyw43_arch_init()) {
DEBUG_printf("failed to initialise\n");
return 1;
}
cyw43_arch_enable_sta_mode();
printf("Connecting to Wi-Fi...\n");
if (cyw43_arch_wifi_connect_timeout_ms(WIFI_SSID, WIFI_PASSWORD, CYW43_AUTH_WPA2_AES_PSK, 30000)) {
printf("failed to connect.\n");
return 1;
} else {
printf("Connected.\n");
}
run_tcp_client_test();
cyw43_arch_deinit();
return 0;
}