-
Notifications
You must be signed in to change notification settings - Fork 2.1k
examples/lwip_ipv4: add example for LWIP IPv4 client/server #21519
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
# Set the name of your application: | ||
APPLICATION = lwip_ipv4 | ||
|
||
# If no BOARD is found in the environment, use this default: | ||
BOARD ?= native | ||
|
||
# This has to be the absolute path to the RIOT base directory: | ||
RIOTBASE ?= $(CURDIR)/../../../../ | ||
|
||
# Uncomment this to enable code in RIOT that does safety checking | ||
# which is not needed in a production environment but helps in the | ||
# development process: | ||
DEVELHELP = 1 | ||
|
||
# Change this to 0 to show compiler invocation lines by default: | ||
QUIET ?= 1 | ||
|
||
# Modules to include: | ||
USEMODULE += shell | ||
|
||
USEMODULE += netdev_default | ||
|
||
#enables LWIP IPv4 stack | ||
USEMODULE += ipv4_addr | ||
USEMODULE += lwip_arp | ||
USEMODULE += lwip_ipv4 | ||
|
||
#enables UDP LWIP sockets | ||
USEMODULE += sock_udp | ||
|
||
#nables ifconfig command, which allows IPv4 address configuration | ||
USEMODULE += shell_cmds_default | ||
|
||
include $(RIOTBASE)/Makefile.include |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,19 @@ | ||
BOARD_INSUFFICIENT_MEMORY := \ | ||
bluepill-stm32f030c8 \ | ||
i-nucleo-lrwan1 \ | ||
nucleo-c031c6 \ | ||
nucleo-f030r8 \ | ||
nucleo-f031k6 \ | ||
nucleo-f042k6 \ | ||
nucleo-l011k4 \ | ||
nucleo-l031k6 \ | ||
nucleo-l053r8 \ | ||
samd10-xmini \ | ||
slstk3400a \ | ||
stk3200 \ | ||
stm32f030f4-demo \ | ||
stm32f0discovery \ | ||
stm32g0316-disco \ | ||
stm32l0538-disco \ | ||
weact-g030f6 \ | ||
# |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,116 @@ | ||
# LWIP IPv4 client and server example | ||
|
||
This example shows how to send and receive IPv4 UDP datagrams using LWIP | ||
network library. | ||
|
||
# Shell commands | ||
|
||
The following commands are available: | ||
- `ifconfig`: allows manual IPv4 address configuration. | ||
- `server`: run simple server which receives UDP datagrams. | ||
- `client`: sends UDP datagram to given IPv4 address. | ||
|
||
# Usage on BOARD=native | ||
|
||
This instruction, using native board, utilize tap interface and allows | ||
communication between RIOT system and host Linux. | ||
|
||
In the following sections we describe how to: | ||
- configure tap interface using RIOT `tapsetup` tool. | ||
- send data from the Linux host to the RIOT. | ||
- send data from the RIOT to the Linux host. | ||
|
||
## Network configuration | ||
|
||
Create `tap0` interface and `tapbr0` using RIOT `tapsetup` tool. | ||
Commands should be executed on the Linux host: | ||
|
||
``` | ||
$sudo ./dist/tools/tapsetup/tapsetup -c 1 | ||
$sudo ip addr add 192.168.100.100/24 dev tapbr0 | ||
``` | ||
|
||
Compile and manually configure IPv4 address on the RIOT. | ||
|
||
``` | ||
$make all | ||
$./bin/native64/lwip_ipv4.elf tap0 | ||
RIOT native interrupts/signals initialized. | ||
TZ not set, setting UTC | ||
RIOT native64 board initialized. | ||
RIOT native hardware initialization complete. | ||
|
||
main(): This is RIOT! (Version: 2024.04-devel-2705-g39b6f-examples-lwip_ipv4) | ||
|
||
> ifconfig add ET0 192.168.100.11/24 | ||
ifconfig add ET0 192.168.100.11/24 | ||
> ifconfig | ||
ifconfig | ||
Iface ET0 HWaddr: 8e:9d:7d:a1:fd:cd Link: up State: up | ||
Link type: wired | ||
inet addr: 192.168.100.11 mask: 255.255.255.0 gw: 0.0.0.0 | ||
> | ||
``` | ||
|
||
Check connectivity from the Linux host machine to the RIOT: | ||
|
||
``` | ||
$ping 192.168.100.11 | ||
PING 192.168.100.11 (192.168.100.11) 56(84) bytes of data. | ||
64 bytes from 192.168.100.11: icmp_seq=1 ttl=255 time=0.240 ms | ||
64 bytes from 192.168.100.11: icmp_seq=2 ttl=255 time=0.210 ms | ||
... | ||
``` | ||
|
||
## RIOT LWIP IPv4 server | ||
|
||
Run server on the RIOT using command: | ||
|
||
``` | ||
> server 4444 | ||
server 4444 | ||
Server started. | ||
``` | ||
|
||
Send UDP datagram from the Linux host using `nc` tool: | ||
|
||
``` | ||
$ nc -u 192.168.100.11 4444 | ||
Msg from Linux | ||
Msg from Linux | ||
``` | ||
|
||
Observe results on the RIOT: | ||
|
||
``` | ||
> server 4444 | ||
server 4444 | ||
Server started. | ||
Received 15 bytes - Msg from Linux | ||
|
||
> | ||
``` | ||
|
||
## RIOT LWIP IPv4 client | ||
|
||
Run server on the Linux host using `nc` tool: | ||
|
||
``` | ||
$ nc -ul -p 4444 | ||
``` | ||
|
||
Send UDP datagram from the RIOT: | ||
|
||
``` | ||
> client 192.168.100.100 4444 Msg_from_RIOT | ||
client 192.168.100.100 4444 Msg_from_RIOT | ||
> | ||
``` | ||
|
||
Observe result on the Linux host: | ||
|
||
``` | ||
$nc -ul -p 4444 | ||
Msg_from_RIOT | ||
|
||
``` |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,174 @@ | ||
/* | ||
* Copyright (C) 2025 Krzysztof Cabaj <[email protected]> | ||
* | ||
* This file is subject to the terms and conditions of the GNU Lesser | ||
* General Public License v2.1. See the file LICENSE in the top level | ||
* directory for more details. | ||
*/ | ||
|
||
/** | ||
* @ingroup examples | ||
* @{ | ||
* | ||
* @file | ||
* @brief lwip_ipv4 - sample application for demonstrating basic LWIP | ||
* IPv4 client/server functions. | ||
* | ||
* @author Krzysztof Cabaj <[email protected]> | ||
* | ||
* @} | ||
*/ | ||
#include "stdio.h" | ||
#include "stdlib.h" | ||
#include "shell.h" | ||
#include "net/sock/udp.h" | ||
#include <arpa/inet.h> | ||
#include "thread.h" | ||
#include "mutex.h" | ||
|
||
#define CLIENT_TIMEOUT 3 /* in seconds */ | ||
|
||
static char server_stack[THREAD_STACKSIZE_DEFAULT]; | ||
static mutex_t server_mutex = MUTEX_INIT; | ||
static int server_running = 0; | ||
static uint16_t server_port; | ||
|
||
static int _client_cmd(int argc, char **argv) | ||
{ | ||
uint32_t dest_ip; | ||
|
||
if (argc < 4) { | ||
printf("usage: %s <destination IP> <port> <text>\n", argv[0]); | ||
return -1; | ||
} | ||
|
||
if (inet_pton(AF_INET, argv[1], &dest_ip) != 1) { | ||
printf("\"%s\" - is not a valid IPv4 address!\n", argv[1]); | ||
return -1; | ||
} | ||
|
||
uint16_t port = atoi(argv[2]); | ||
size_t data_len = strlen(argv[3]); | ||
sock_udp_t sock; | ||
sock_udp_ep_t remote = { .addr = { .ipv4_u32 = dest_ip }, | ||
.family = AF_INET, | ||
.port = port }; | ||
int result; | ||
char buffer[64]; | ||
|
||
if ((result = sock_udp_create(&sock, NULL, &remote, SOCK_FLAGS_REUSE_EP)) != 0) { | ||
printf("Error sock_udp_create - result %d!\n", result); | ||
return -1; | ||
} | ||
|
||
if (data_len != (size_t)sock_udp_send(&sock, argv[3], data_len, NULL)) { | ||
printf("Error sock_udp_send! Error code %d.\n", (int)data_len); | ||
sock_udp_close(&sock); | ||
return -1; | ||
} | ||
|
||
krzysztof-cabaj marked this conversation as resolved.
Show resolved
Hide resolved
|
||
if ((result = sock_udp_recv(&sock, buffer, sizeof(buffer) - 1, CLIENT_TIMEOUT * US_PER_SEC, &remote)) < 0) { | ||
printf("Sock_udp_recv error! Error code %d\n", result); | ||
sock_udp_close(&sock); | ||
return -1; | ||
} | ||
|
||
buffer[result] = 0; | ||
printf("Received %d bytes - %s\n", result, buffer); | ||
|
||
sock_udp_close(&sock); | ||
|
||
return 0; | ||
} | ||
|
||
SHELL_COMMAND(client, "Send UDP datagram", _client_cmd); | ||
|
||
void *server_thread(void *arg) | ||
{ | ||
(void)arg; | ||
sock_udp_t sock; | ||
sock_udp_ep_t local = { .family = AF_INET, | ||
.port = server_port }; | ||
sock_udp_ep_t remote; | ||
int result; | ||
int error; | ||
char buffer[64]; | ||
|
||
if (sock_udp_create(&sock, &local, NULL, SOCK_FLAGS_REUSE_EP) < 0) { | ||
printf("Sock_udp_create error!\n"); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. You want to print the error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes I want to print error. |
||
printf("Server stopped.\n"); | ||
|
||
mutex_lock(&server_mutex); | ||
server_running = 0; | ||
mutex_unlock(&server_mutex); | ||
Comment on lines
+101
to
+103
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do you need that for? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. My idea is that if due to some errors this thread ends - we could start server once again. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But why the need for a mutex? |
||
|
||
return NULL; | ||
} | ||
|
||
printf("Server started.\n"); | ||
|
||
while(1) { | ||
if ((result = sock_udp_recv(&sock, buffer, sizeof(buffer) - 1, SOCK_NO_TIMEOUT, &remote)) < 0) { | ||
printf("Sock_udp_recv error! Error code %d.\n", result); | ||
printf("Server stopped.\n"); | ||
|
||
sock_udp_close(&sock); | ||
|
||
mutex_lock(&server_mutex); | ||
server_running = 0; | ||
mutex_unlock(&server_mutex); | ||
|
||
return NULL; | ||
} | ||
|
||
buffer[result] = 0; | ||
printf("Received %d bytes - %s\n", result, buffer); | ||
|
||
if ( (error = sock_udp_send(&sock, buffer, result, &remote)) != result) { | ||
printf("Sock_udp_send error! Error code %d.\n", error); | ||
} | ||
} | ||
|
||
sock_udp_close(&sock); | ||
|
||
return NULL; | ||
} | ||
|
||
static int _server_cmd(int argc, char **argv) | ||
{ | ||
int is_running; | ||
if (argc < 2) { | ||
printf("usage: %s <port>\n", argv[0]); | ||
return -1; | ||
} | ||
|
||
mutex_lock(&server_mutex); | ||
is_running = server_running; | ||
mutex_unlock(&server_mutex); | ||
|
||
if(is_running == 1) { | ||
printf("Server already running!\n"); | ||
return -1; | ||
} | ||
|
||
server_port = atoi(argv[1]); | ||
|
||
mutex_lock(&server_mutex); | ||
server_running = 1; | ||
mutex_unlock(&server_mutex); | ||
|
||
thread_create(server_stack, sizeof(server_stack), THREAD_PRIORITY_MAIN - 1, 0, server_thread, NULL, "server"); | ||
|
||
return -1; | ||
} | ||
|
||
SHELL_COMMAND(server, "Starts server which receives UDP datagrams", _server_cmd); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It would be more convenient for testing if the server did auto-start. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would prefer do not start server automatically. |
||
|
||
int main(void) | ||
{ | ||
char line_buf[SHELL_DEFAULT_BUFSIZE]; | ||
|
||
shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE); | ||
|
||
return 0; | ||
} |
Uh oh!
There was an error while loading. Please reload this page.