Browse Source

Initial basic TCP server

Based on the Pi example
Brian Starkey 2 years ago
parent
commit
04bd83cae5
4 changed files with 442 additions and 0 deletions
  1. 24
    0
      CMakeLists.txt
  2. 90
    0
      lwipopts.h
  3. 255
    0
      main.c
  4. 73
    0
      pico_sdk_import.cmake

+ 24
- 0
CMakeLists.txt View File

@@ -0,0 +1,24 @@
1
+cmake_minimum_required(VERSION 3.13)
2
+
3
+include(pico_sdk_import.cmake)
4
+
5
+project(picowota C CXX ASM)
6
+set(CMAKE_C_STANDARD 11)
7
+set(CMAKE_CXX_STANDARD 17)
8
+pico_sdk_init()
9
+
10
+add_executable(picowota
11
+	main.c
12
+	creds.c
13
+)
14
+
15
+pico_enable_stdio_usb(picowota 1)
16
+
17
+pico_add_extra_outputs(picowota)
18
+
19
+target_include_directories(picowota PRIVATE ${CMAKE_CURRENT_LIST_DIR})
20
+
21
+target_link_libraries(picowota
22
+	pico_cyw43_arch_lwip_threadsafe_background
23
+	pico_stdlib
24
+)

+ 90
- 0
lwipopts.h View File

@@ -0,0 +1,90 @@
1
+#ifndef _LWIPOPTS_EXAMPLE_COMMONH_H
2
+#define _LWIPOPTS_EXAMPLE_COMMONH_H
3
+
4
+
5
+// Common settings used in most of the pico_w examples
6
+// (see https://www.nongnu.org/lwip/2_1_x/group__lwip__opts.html for details)
7
+
8
+// allow override in some examples
9
+#ifndef NO_SYS
10
+#define NO_SYS                      1
11
+#endif
12
+// allow override in some examples
13
+#ifndef LWIP_SOCKET
14
+#define LWIP_SOCKET                 0
15
+#endif
16
+#if PICO_CYW43_ARCH_POLL
17
+#define MEM_LIBC_MALLOC             1
18
+#else
19
+// MEM_LIBC_MALLOC is incompatible with non polling versions
20
+#define MEM_LIBC_MALLOC             0
21
+#endif
22
+#define MEM_ALIGNMENT               4
23
+#define MEM_SIZE                    4000
24
+#define MEMP_NUM_TCP_SEG            32
25
+#define MEMP_NUM_ARP_QUEUE          10
26
+#define PBUF_POOL_SIZE              24
27
+#define LWIP_ARP                    1
28
+#define LWIP_ETHERNET               1
29
+#define LWIP_ICMP                   1
30
+#define LWIP_RAW                    1
31
+#define TCP_WND                     (8 * TCP_MSS)
32
+#define TCP_MSS                     1460
33
+#define TCP_SND_BUF                 (8 * TCP_MSS)
34
+#define TCP_SND_QUEUELEN            ((4 * (TCP_SND_BUF) + (TCP_MSS - 1)) / (TCP_MSS))
35
+#define LWIP_NETIF_STATUS_CALLBACK  1
36
+#define LWIP_NETIF_LINK_CALLBACK    1
37
+#define LWIP_NETIF_HOSTNAME         1
38
+#define LWIP_NETCONN                0
39
+#define MEM_STATS                   0
40
+#define SYS_STATS                   0
41
+#define MEMP_STATS                  0
42
+#define LINK_STATS                  0
43
+// #define ETH_PAD_SIZE                2
44
+#define LWIP_CHKSUM_ALGORITHM       3
45
+#define LWIP_DHCP                   1
46
+#define LWIP_IPV4                   1
47
+#define LWIP_TCP                    1
48
+#define LWIP_UDP                    1
49
+#define LWIP_DNS                    1
50
+#define LWIP_TCP_KEEPALIVE          1
51
+#define LWIP_NETIF_TX_SINGLE_PBUF   1
52
+#define DHCP_DOES_ARP_CHECK         0
53
+#define LWIP_DHCP_DOES_ACD_CHECK    0
54
+
55
+#ifndef NDEBUG
56
+#define LWIP_DEBUG                  1
57
+#define LWIP_STATS                  1
58
+#define LWIP_STATS_DISPLAY          1
59
+#endif
60
+
61
+#define ETHARP_DEBUG                LWIP_DBG_OFF
62
+#define NETIF_DEBUG                 LWIP_DBG_OFF
63
+#define PBUF_DEBUG                  LWIP_DBG_OFF
64
+#define API_LIB_DEBUG               LWIP_DBG_OFF
65
+#define API_MSG_DEBUG               LWIP_DBG_OFF
66
+#define SOCKETS_DEBUG               LWIP_DBG_OFF
67
+#define ICMP_DEBUG                  LWIP_DBG_OFF
68
+#define INET_DEBUG                  LWIP_DBG_OFF
69
+#define IP_DEBUG                    LWIP_DBG_OFF
70
+#define IP_REASS_DEBUG              LWIP_DBG_OFF
71
+#define RAW_DEBUG                   LWIP_DBG_OFF
72
+#define MEM_DEBUG                   LWIP_DBG_OFF
73
+#define MEMP_DEBUG                  LWIP_DBG_OFF
74
+#define SYS_DEBUG                   LWIP_DBG_OFF
75
+#define TCP_DEBUG                   LWIP_DBG_OFF
76
+#define TCP_INPUT_DEBUG             LWIP_DBG_OFF
77
+#define TCP_OUTPUT_DEBUG            LWIP_DBG_OFF
78
+#define TCP_RTO_DEBUG               LWIP_DBG_OFF
79
+#define TCP_CWND_DEBUG              LWIP_DBG_OFF
80
+#define TCP_WND_DEBUG               LWIP_DBG_OFF
81
+#define TCP_FR_DEBUG                LWIP_DBG_OFF
82
+#define TCP_QLEN_DEBUG              LWIP_DBG_OFF
83
+#define TCP_RST_DEBUG               LWIP_DBG_OFF
84
+#define UDP_DEBUG                   LWIP_DBG_OFF
85
+#define TCPIP_DEBUG                 LWIP_DBG_OFF
86
+#define PPP_DEBUG                   LWIP_DBG_OFF
87
+#define SLIP_DEBUG                  LWIP_DBG_OFF
88
+#define DHCP_DEBUG                  LWIP_DBG_OFF
89
+
90
+#endif /* __LWIPOPTS_H__ */

+ 255
- 0
main.c View File

@@ -0,0 +1,255 @@
1
+/**
2
+ * Copyright (c) 2022 Brian Starkey <stark3y@gmail.com>
3
+ *
4
+ * Based on the Pico W tcp_server example:
5
+ * Copyright (c) 2022 Raspberry Pi (Trading) Ltd.
6
+ *
7
+ * SPDX-License-Identifier: BSD-3-Clause
8
+ */
9
+
10
+#include <string.h>
11
+#include <stdlib.h>
12
+
13
+#include "pico/stdlib.h"
14
+#include "pico/cyw43_arch.h"
15
+
16
+#include "lwip/pbuf.h"
17
+#include "lwip/tcp.h"
18
+
19
+extern const char *wifi_ssid;
20
+extern const char *wifi_pass;
21
+
22
+#define TCP_PORT 4242
23
+#define DEBUG_printf printf
24
+#define BUF_SIZE 2048
25
+#define POLL_TIME_S 5
26
+
27
+typedef struct TCP_SERVER_T_ {
28
+	struct tcp_pcb *server_pcb;
29
+	struct tcp_pcb *client_pcb;
30
+	bool complete;
31
+	uint8_t buffer_recv[BUF_SIZE];
32
+	int sent_len;
33
+} TCP_SERVER_T;
34
+
35
+static TCP_SERVER_T* tcp_server_init(void) {
36
+	TCP_SERVER_T *state = calloc(1, sizeof(TCP_SERVER_T));
37
+	if (!state) {
38
+		DEBUG_printf("failed to allocate state\n");
39
+		return NULL;
40
+	}
41
+	return state;
42
+}
43
+
44
+static err_t tcp_server_close(void *arg) {
45
+	TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
46
+	err_t err = ERR_OK;
47
+	if (state->client_pcb != NULL) {
48
+		tcp_arg(state->client_pcb, NULL);
49
+		tcp_poll(state->client_pcb, NULL, 0);
50
+		tcp_sent(state->client_pcb, NULL);
51
+		tcp_recv(state->client_pcb, NULL);
52
+		tcp_err(state->client_pcb, NULL);
53
+		err = tcp_close(state->client_pcb);
54
+		if (err != ERR_OK) {
55
+			DEBUG_printf("close failed %d, calling abort\n", err);
56
+			tcp_abort(state->client_pcb);
57
+			err = ERR_ABRT;
58
+		}
59
+		state->client_pcb = NULL;
60
+	}
61
+	if (state->server_pcb) {
62
+		tcp_arg(state->server_pcb, NULL);
63
+		tcp_close(state->server_pcb);
64
+		state->server_pcb = NULL;
65
+	}
66
+	return err;
67
+}
68
+
69
+static err_t tcp_server_result(void *arg, int status) {
70
+	TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
71
+	if (status == 0) {
72
+		DEBUG_printf("completed normally\n");
73
+	} else {
74
+		DEBUG_printf("error %d\n", status);
75
+	}
76
+	state->complete = true;
77
+	return tcp_server_close(arg);
78
+}
79
+
80
+static err_t tcp_server_sent(void *arg, struct tcp_pcb *tpcb, u16_t len) {
81
+	TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
82
+	DEBUG_printf("tcp_server_sent %u\n", len);
83
+	state->sent_len += len;
84
+
85
+	if (state->sent_len >= strlen("hello\n")) {
86
+		DEBUG_printf("Sending done\n");
87
+	}
88
+
89
+	return ERR_OK;
90
+}
91
+
92
+err_t tcp_server_send_data(void *arg, struct tcp_pcb *tpcb)
93
+{
94
+	TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
95
+
96
+	state->sent_len = 0;
97
+	DEBUG_printf("Writing to client\n");
98
+	// this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you
99
+	// can use this method to cause an assertion in debug mode, if this method is called when
100
+	// cyw43_arch_lwip_begin IS needed
101
+	cyw43_arch_lwip_check();
102
+	err_t err = tcp_write(tpcb, "hello\n", strlen("hello\n"), TCP_WRITE_FLAG_COPY);
103
+	if (err != ERR_OK) {
104
+		DEBUG_printf("Failed to write data %d\n", err);
105
+		return tcp_server_result(arg, -1);
106
+	}
107
+	return ERR_OK;
108
+}
109
+
110
+err_t tcp_server_recv(void *arg, struct tcp_pcb *tpcb, struct pbuf *p, err_t err) {
111
+	TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
112
+	if (!p) {
113
+		return tcp_server_result(arg, 0);
114
+	}
115
+	// this method is callback from lwIP, so cyw43_arch_lwip_begin is not required, however you
116
+	// can use this method to cause an assertion in debug mode, if this method is called when
117
+	// cyw43_arch_lwip_begin IS needed
118
+	cyw43_arch_lwip_check();
119
+	if (p->tot_len > 0) {
120
+		DEBUG_printf("tcp_server_recv %d err %d\n", p->tot_len, err);
121
+
122
+		// Receive the buffer
123
+		pbuf_copy_partial(p, state->buffer_recv, p->tot_len, 0);
124
+
125
+		state->buffer_recv[p->tot_len] = '\0';
126
+		printf("%s\n", state->buffer_recv);
127
+		tcp_recved(tpcb, p->tot_len);
128
+	}
129
+	pbuf_free(p);
130
+
131
+	return ERR_OK;
132
+}
133
+
134
+static err_t tcp_server_poll(void *arg, struct tcp_pcb *tpcb) {
135
+	DEBUG_printf("tcp_server_poll_fn\n");
136
+	return ERR_OK;
137
+}
138
+
139
+static void tcp_server_err(void *arg, err_t err) {
140
+	if (err != ERR_ABRT) {
141
+		DEBUG_printf("tcp_client_err_fn %d\n", err);
142
+		tcp_server_result(arg, err);
143
+	} else {
144
+		DEBUG_printf("tcp_client_err_fn abort %d\n", err);
145
+		tcp_server_result(arg, err);
146
+	}
147
+}
148
+
149
+static err_t tcp_server_accept(void *arg, struct tcp_pcb *client_pcb, err_t err) {
150
+	TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
151
+	if (err != ERR_OK || client_pcb == NULL) {
152
+		DEBUG_printf("Failure in accept\n");
153
+		tcp_server_result(arg, err);
154
+		return ERR_VAL;
155
+	}
156
+	DEBUG_printf("Client connected\n");
157
+
158
+	state->client_pcb = client_pcb;
159
+	tcp_arg(client_pcb, state);
160
+	tcp_sent(client_pcb, tcp_server_sent);
161
+	tcp_recv(client_pcb, tcp_server_recv);
162
+	tcp_poll(client_pcb, tcp_server_poll, POLL_TIME_S * 2);
163
+	tcp_err(client_pcb, tcp_server_err);
164
+
165
+	return tcp_server_send_data(arg, state->client_pcb);
166
+}
167
+
168
+static bool tcp_server_open(void *arg) {
169
+	TCP_SERVER_T *state = (TCP_SERVER_T*)arg;
170
+	DEBUG_printf("Starting server at %s on port %u\n", ip4addr_ntoa(netif_ip4_addr(netif_list)), TCP_PORT);
171
+
172
+	struct tcp_pcb *pcb = tcp_new_ip_type(IPADDR_TYPE_ANY);
173
+	if (!pcb) {
174
+		DEBUG_printf("failed to create pcb\n");
175
+		return false;
176
+	}
177
+
178
+	err_t err = tcp_bind(pcb, NULL, TCP_PORT);
179
+	if (err) {
180
+		DEBUG_printf("failed to bind to port %d\n");
181
+		return false;
182
+	}
183
+
184
+	state->server_pcb = tcp_listen_with_backlog(pcb, 1);
185
+	if (!state->server_pcb) {
186
+		DEBUG_printf("failed to listen\n");
187
+		if (pcb) {
188
+			tcp_close(pcb);
189
+		}
190
+		return false;
191
+	}
192
+
193
+	tcp_arg(state->server_pcb, state);
194
+	tcp_accept(state->server_pcb, tcp_server_accept);
195
+
196
+	return true;
197
+}
198
+
199
+void run_tcp_server(void) {
200
+	TCP_SERVER_T *state = tcp_server_init();
201
+	if (!state) {
202
+		return;
203
+	}
204
+
205
+	if (!tcp_server_open(state)) {
206
+		tcp_server_result(state, -1);
207
+		return;
208
+	}
209
+
210
+	// Block until the connection is closed
211
+	while(!state->complete) {
212
+		// the following #ifdef is only here so this same example can be used in multiple modes;
213
+		// you do not need it in your code
214
+#if PICO_CYW43_ARCH_POLL
215
+		// if you are using pico_cyw43_arch_poll, then you must poll periodically from your
216
+		// main loop (not from a timer) to check for WiFi driver or lwIP work that needs to be done.
217
+		cyw43_arch_poll();
218
+		sleep_ms(1);
219
+#else
220
+		// if you are not using pico_cyw43_arch_poll, then WiFI driver and lwIP work
221
+		// is done via interrupt in the background. This sleep is just an example of some (blocking)
222
+		// work you might be doing.
223
+		sleep_ms(1000);
224
+#endif
225
+	}
226
+	free(state);
227
+}
228
+
229
+int main() {
230
+	stdio_init_all();
231
+
232
+	sleep_ms(1000);
233
+
234
+	if (cyw43_arch_init()) {
235
+		printf("failed to initialise\n");
236
+		return 1;
237
+	}
238
+
239
+	cyw43_arch_enable_sta_mode();
240
+
241
+	printf("Connecting to WiFi...\n");
242
+	if (cyw43_arch_wifi_connect_timeout_ms(wifi_ssid, wifi_pass, CYW43_AUTH_WPA2_AES_PSK, 30000)) {
243
+		printf("failed to connect.\n");
244
+		return 1;
245
+	} else {
246
+		printf("Connected.\n");
247
+	}
248
+
249
+	for ( ; ; ) {
250
+		run_tcp_server();
251
+	}
252
+
253
+	cyw43_arch_deinit();
254
+	return 0;
255
+}

+ 73
- 0
pico_sdk_import.cmake View File

@@ -0,0 +1,73 @@
1
+# This is a copy of <PICO_SDK_PATH>/external/pico_sdk_import.cmake
2
+
3
+# This can be dropped into an external project to help locate this SDK
4
+# It should be include()ed prior to project()
5
+
6
+if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH))
7
+    set(PICO_SDK_PATH $ENV{PICO_SDK_PATH})
8
+    message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')")
9
+endif ()
10
+
11
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT))
12
+    set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT})
13
+    message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')")
14
+endif ()
15
+
16
+if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH))
17
+    set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH})
18
+    message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')")
19
+endif ()
20
+
21
+set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK")
22
+set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable")
23
+set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK")
24
+
25
+if (NOT PICO_SDK_PATH)
26
+    if (PICO_SDK_FETCH_FROM_GIT)
27
+        include(FetchContent)
28
+        set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR})
29
+        if (PICO_SDK_FETCH_FROM_GIT_PATH)
30
+            get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}")
31
+        endif ()
32
+        # GIT_SUBMODULES_RECURSE was added in 3.17
33
+        if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0")
34
+            FetchContent_Declare(
35
+                    pico_sdk
36
+                    GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
37
+                    GIT_TAG master
38
+                    GIT_SUBMODULES_RECURSE FALSE
39
+            )
40
+        else ()
41
+            FetchContent_Declare(
42
+                    pico_sdk
43
+                    GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk
44
+                    GIT_TAG master
45
+            )
46
+        endif ()
47
+
48
+        if (NOT pico_sdk)
49
+            message("Downloading Raspberry Pi Pico SDK")
50
+            FetchContent_Populate(pico_sdk)
51
+            set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR})
52
+        endif ()
53
+        set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE})
54
+    else ()
55
+        message(FATAL_ERROR
56
+                "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git."
57
+                )
58
+    endif ()
59
+endif ()
60
+
61
+get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}")
62
+if (NOT EXISTS ${PICO_SDK_PATH})
63
+    message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found")
64
+endif ()
65
+
66
+set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake)
67
+if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE})
68
+    message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK")
69
+endif ()
70
+
71
+set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE)
72
+
73
+include(${PICO_SDK_INIT_CMAKE_FILE})

Loading…
Cancel
Save