Feedback
Feedback

If you are having issues with the exercises, please create a ticket on DevZone: devzone.nordicsemi.com
Click or drag files to this area to upload. You can upload up to 2 files.

Exercise 1

Pinging an echo server

In this exercise, we will learn how to set a UDP socket to ping an echo server and print its response on the terminal.

Exercise steps

In the GitHub repository for this course, go to the base code for this exercise, found in lesson3/wififund_less3_exer1.

1. Set the necessary networking configurations

1.1 Configure the Zephyr networking API

Enable Zephyr native networking API through CONFIG_NETWORKING and the Zephyr native IP stack through CONFIG_NET_NATIVE.

CONFIG_NETWORKING=y
CONFIG_NET_NATIVE=y

1.2 Enable and configure the BSD sockets compatible API.

Enable the BSD socket-like API on top of Zephyr’s native networking API and POSIX names for the socket API (to disable the _zsock prefix).

CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_OFFLOAD=n
CONFIG_NET_SOCKETS_POSIX_NAMES=y
CONFIG_POSIX_MAX_FDS=16

1.3 Enable the relevant networking configurations

Enable IPv4 and IPv6 support for sending and receiving IP network packets. We are using UDP as our transport layer protocol, so we also need to enable this.

CONFIG_NET_DHCPV4 enables the DHCPv4 client so the device can be assigned an IPv4 address from the DHCP server.

Enabling DNS resolver enables the device to resolve the address of the echo server using the hostname.

CONFIG_NET_L2_ETHERNET=y
CONFIG_NET_IPV6=y
CONFIG_NET_IPV4=y
CONFIG_NET_UDP=y
CONFIG_NET_DHCPV4=y
CONFIG_DNS_RESOLVER=y

2. Include the header file for the socket API.

#include <zephyr/net/socket.h>

3. Define the hostname and port for the echo server.

This is a UDP based echo server that we are running specifically for the Nordic Developer Academy courses.

4. Initialize variables for resolving the server address, creating the socket and receiving messages from the server.

4.1 Declare the structure for the socket used when creating the socket and the server address structure, used when connecting the socket.

static int sock;
static struct sockaddr_storage server;

Using struct sockaddr_storage instead of struct sockaddr is good practice as it promotes protocol-family independence (see here for a good explanation).

4.2 Declare the receive buffer for receiving messages from the UDP echo server.

static uint8_t recv_buf[MESSAGE_SIZE];

5. Resolve the IP address of the server.

In the function server_resolve(), we will resolve the IP address of the server, retrieve the relevant information and print the address to console.

5.1 Call getaddrinfo() to get the IP address of the echo server.

Create the empty structure addrinfo result and the structure addrinfo hints and specify the family – IPv4 (AF_INET) and the socket type – UDP (SOCK_DGRAM) and then call getaddrinfo() with the hostname, and the other parameters to get the address.

int err;
struct addrinfo *result;
struct addrinfo hints = {
	.ai_family = AF_INET,
	.ai_socktype = SOCK_DGRAM
};
	
err = getaddrinfo(SERVER_HOSTNAME, SERVER_PORT, &hints, &result);
if (err != 0) {
	LOG_INF("getaddrinfo() failed, err: %d", err);
	return -EIO;
}

if (result == NULL) {
	LOG_INF("Error, address not found");
	return -ENOENT;
}

5.2 Retrieve the relevant information from the result structure.

After getaddrinfo() is called, retrieve the relevant information from addrinfo result.

Create a pointer server4 of type struct sockaddr_in to point to server. Then set the address in server4 to point to the address from result. The family and port we already know, as AF_INET and SERVER_PORT.

struct sockaddr_in *server4 = ((struct sockaddr_in *)&server);
server4->sin_addr.s_addr =
 ((struct sockaddr_in *)result->ai_addr)->sin_addr.s_addr;
server4->sin_family = AF_INET;
server4->sin_port = ((struct sockaddr_in *)result->ai_addr)->sin_port;

5.3 Convert the address into a string and print it.

Convert the network address structure in server4->sin_addr.s_addr into a character string in ipv4_addr to print on the console.

char ipv4_addr[NET_IPV4_ADDR_LEN];
inet_ntop(AF_INET, &server4->sin_addr.s_addr, ipv4_addr,
 sizeof(ipv4_addr));
LOG_INF("IPv4 address of server found %s", ipv4_addr);

5.4 Free the memory allocated for the addrinfo structure result, using freeaddrinfo().

freeaddrinfo(result);

6. Create a UDP socket.

In the function server_connect(), create an IPv4 UDP socket.

sock = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
	LOG_INF("Failed to create socket: %d.\n", errno);
	return -errno;
}

7. Connect the socket to the server.

Now let’s connect the socket we created to the echo server using the structure server.

err = connect(sock, (struct sockaddr *)&server, sizeof(struct sockaddr_in));
if (err < 0) {
	LOG_INF("Connect failed : %d\n", errno);
	return -errno;
}

8. Send a message every time button 1 is pressed.

In button_handler(), whenever button 1 is pressed call send() with the socket and message.

if (has_changed & DK_BTN1_MSK && button_state & DK_BTN1_MSK) {
	int err = send(sock, MESSAGE_TO_SEND, SSTRLEN(MESSAGE_TO_SEND), 0);
	if (err < 0) {
		LOG_INF("Failed to send message, %d", errno);
		return;
	} LOG_INF("Successfully sent message: %s", MESSAGE_TO_SEND);
}

9. Resolve the server name and connect to the server.

In main(), call server_resolve() to resolve the IP address of the server, then call server_connect() to connect to it.

if (server_resolve() != 0) {
	LOG_INF("Failed to resolve server name");
	return 0;
}
	
if (server_connect() != 0) {
	LOG_INF("Failed to initialize client");
	return 0;
}

10. Listen for incoming messages.

In the while-loop in main, call recv() to listen to received messages.

If recv() returns with a positive integer, then the string is null-terminated and we print the received message.

received = recv(sock, recv_buf, sizeof(recv_buf) - 1, 0);

if (received < 0) {
	LOG_ERR("Socket error: %d, exit", errno);
	break;
}

if (received == 0) {
	LOG_ERR("Empty datagram");
	break;
}

recv_buf[received] = 0;
LOG_INF("Data received from the server: (%s)", recv_buf);

11. Build and flash the application to your board.

This exercise uses the PSA backend for storing the Wi-Fi credentials. Therefore, you must build with TF-M.

BoardBuild with TF-M
nRF7002 DKnrf7002dk_nrf5340_cpuapp_ns
nRF5340 DK + nRF7002 EKnrf5340dk_nrf5340_cpuapp_ns

12. Connect to a Wi-Fi network.

Connect to your Wi-Fi network by running the following commands in the terminal

If the connection was successful, you should see the following log output

13. Press button 1 on the board a couple times and observe the following output.

Register an account
Already have an account? Log in
(All fields are required unless specified optional)

  • 8 or more characters
  • Upper and lower case letters
  • At least one number or special character

Forgot your password?
Enter the email associated with your account, and we will send you a link to reset your password.