Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions examples/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ Here is a quick overview of the examples available in the RIOT:
| [sniffer](./networking/misc/sniffer/README.md) | This application is built to run together with the script `./tools/sniffer.py` as a sniffer for (wireless) data traffic. |
| [benckmark_udp](./networking/misc/benchmark_udp/README.md) | This example uses the `benchmark_udp` module to create a stress-test for the RIOT network stack. |
| [sock_tcp_echo](./networking/misc/sock_tcp_echo/README.md) | This is a simple TCP echo server / client that uses the SOCK API. |
| [lwip_ipv4](./networking/misc/lwip_ipv4/README.md) | This is a simple UDP client / server using LWIP for IPv4. |


## Advanced Examples

Expand Down
39 changes: 39 additions & 0 deletions examples/networking/misc/lwip_ipv4/Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# 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

# If this module will be disabled, example set IPv4 192.168.100.11 address.
# Enable next two line to use DHCP
#USEMODULE += lwip_dhcp_auto
#CFLAGS += -DETHARP_SUPPORT_STATIC_ENTRIES=1

#enables UDP LWIP sockets
USEMODULE += sock_udp

#nables ifconfig command, which allows IPv4 address configuration
USEMODULE += shell_cmds_default

include $(RIOTBASE)/Makefile.include
21 changes: 21 additions & 0 deletions examples/networking/misc/lwip_ipv4/Makefile.ci
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
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 \
stm32c0116-dk \
stm32c0316-dk \
stm32f030f4-demo \
stm32f0discovery \
stm32g0316-disco \
stm32l0538-disco \
weact-g030f6 \
#
115 changes: 115 additions & 0 deletions examples/networking/misc/lwip_ipv4/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
# 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.
- `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

The server starts automatically on the RIOT:

```
main(): This is RIOT! (Version: 2024.04-devel-3275-gab2302-examples-lwip_ipv4)
{"IPv4 addresses": ["192.168.100.11"]}
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:

```
main(): This is RIOT! (Version: 2024.04-devel-3275-gab2302-examples-lwip_ipv4)
{"IPv4 addresses": ["192.168.100.11"]}
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

```
167 changes: 167 additions & 0 deletions examples/networking/misc/lwip_ipv4/main.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,167 @@
/*
* SPDX-FileCopyrightText: 2025 Krzysztof Cabaj <kcabaj@gmail.com>
* SPDX-License-Identifier: LGPL-2.1-only
*/

/**
* @ingroup examples
* @{
*
* @file
* @brief lwip_ipv4 - sample application for demonstrating basic LWIP
* IPv4 client/server functions.
*
* @author Krzysztof Cabaj <kcabaj@gmail.com>
*
* @}
*/
#include "stdio.h"
#include "stdlib.h"
#include "shell.h"
#include "net/sock/udp.h"
#include <arpa/inet.h>
#include "thread.h"
#include "mutex.h"

#include "ztimer.h"
#include "lwip/netif.h"
#include <arpa/inet.h>

#ifndef CONFIG_CLIENT_TIMEOUT_SEC
#define CONFIG_CLIENT_TIMEOUT_SEC 3
#endif

#ifndef CONFIG_DHCP_TIMEOUT_SEC
#define CONFIG_DHCP_TIMEOUT_SEC 10
#endif

#ifndef SERVER_PORT
#define SERVER_PORT 4444
#endif

static char server_stack[THREAD_STACKSIZE_DEFAULT];

#define SERVER_PORT 4444

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 = -1;
char buffer[64];

if ((result = sock_udp_create(&sock, NULL, &remote, 0)) != 0) {
printf("sock_udp_create() failed with %d\n", result);
return -1;
}

if (data_len != (size_t)sock_udp_send(&sock, argv[3], data_len, NULL)) {
printf("sock_udp_send() failed with %d\n", (int)data_len);
goto out;
}

if ((result = sock_udp_recv(&sock, buffer, sizeof(buffer) - 1,
CONFIG_CLIENT_TIMEOUT_SEC * US_PER_SEC, &remote)) < 0) {
printf("sock_udp_recv() failed with %d\n", result);
goto out;
}

buffer[result] = 0;
printf("Received %d bytes - %s\n", result, buffer);

out:
sock_udp_close(&sock);
return result;
}

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];

int res = sock_udp_create(&sock, &local, NULL, 0);
if (res) {
printf("Sock_udp_create error!\n");
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You want to print the error.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes I want to print error.
Your comment concerns wrong condition was ! = 0 is < 0?

Copy link
Copy Markdown
Contributor

@benpicco benpicco Aug 22, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I mean print the error code you get from sock_udp_create()

printf("Server stopped.\n");
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() failed with %d\n", result);
}
else {
buffer[result] = 0;
printf("Received %d bytes - %s\n", result, buffer);

error = sock_udp_send(&sock, buffer, result, &remote);
if (error < 0) {
printf("sock_udp_send() failed with %d\n", error);
}
}
}

sock_udp_close(&sock);

return NULL;
}

#define _TEST_ADDR4_LOCAL (0x0b64a8c0U) /* 192.168.100.11 */
#define _TEST_ADDR4_MASK (0x00ffffffU) /* 255.255.255.0 */

int main(void)
{
char line_buf[SHELL_DEFAULT_BUFSIZE];

sys_lock_tcpip_core();
struct netif *iface = netif_find("ET0");

#ifndef MODULE_LWIP_DHCP_AUTO
ip4_addr_t ip, subnet;
ip.addr = _TEST_ADDR4_LOCAL;
subnet.addr = _TEST_ADDR4_MASK;
netif_set_addr(iface, &ip, &subnet, NULL);
#else
printf("Waiting for DHCP address autoconfiguration ...\n");
ztimer_sleep(ZTIMER_MSEC, CONFIG_DHCP_TIMEOUT_SEC * MS_PER_SEC);
#endif

/* print network addresses */
printf("{\"IPv4 addresses\": [\"");
char buffer[16];
inet_ntop(AF_INET, netif_ip_addr4(iface), buffer, 16);
sys_unlock_tcpip_core();
printf("%s\"]}\n", buffer);

thread_create(server_stack, sizeof(server_stack), THREAD_PRIORITY_MAIN - 1, 0, server_thread, NULL, "server");

shell_run(NULL, line_buf, SHELL_DEFAULT_BUFSIZE);

return 0;
}
Loading