Many of the libraries used in this course, like the MQTT library (Lesson 4) and the CoAP library (Lesson 5) don’t use the socket API directly, meaning the application needs to handle that. In this exercise, we will use the socket API to create a socket and connect to an echo server over UDP.
1. In the GitHub repository for this course, go to the base code for this exercise, found in l3/l3_e1
, of whichever version directory you are using.
2. Set the necessary networking configurations
2.1 Configure the Zephyr networking API
Enable Zephyr native networking API and disable the Zephyr native IP stack.
CONFIG_NETWORKING=y
CONFIG_NET_NATIVE=n
Kconfig2.2 Enable and configure the BSD sockets compatible API
Enable the BSD socket like API on top of Zephyr’s native networking API and socket offloading (so all socket API calls are sent to the modem).
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_OFFLOAD=y
Kconfig3. Include the header file for the socket API
#include <zephyr/net/socket.h>
C4. Define the hostname and port for the echo server.
This is a UDP based echo server that we are running specifically for this course.
#define SERVER_HOSTNAME "udp-echo.nordicsemi.academy"
#define SERVER_PORT "2444"
CEcho server: An echo server is a server running an application that sends back (or “echos”) all received messages. So when a client connects to an echo server and sends a message, that message will be received back.
5. Initialize variables for resolving the server address, creating the socket and receiving messages from the server.
5.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;
CAs we mentioned in Socket API, using struct sockaddr_storage
instead of struct sockaddr
is good practice as it promotes protocol-family independence (see here for a good explanation).
5.2 Declare the receive buffer for receiving messages from the UDP echo server.
static uint8_t recv_buf[MESSAGE_SIZE];
C6. In the function server_resolve()
, we will resolve the IP address of the server, retrieve the relevant information and print the address to console.
6.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("ERROR: getaddrinfo failed %d\n", err);
return -EIO;
}
if (result == NULL) {
LOG_INF("ERROR: Address not found\n");
return -ENOENT;
}
C6.2. 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;
C6.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 found %s", ipv4_addr);
C6.4 Free the memory allocated for the addrinfo
structure result
, using freeadrinfo()
.
freeaddrinfo(result);
C7. In the function server_connect()
, create a 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;
}
C8. Then connect the socket 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;
}
C9. 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 (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);
}
C10. 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);
C11. Build the exercise and flash it to the board as we have done in the previous lessons.
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***
[00:00:00.184,369] <inf> Lesson3_Exercise1: Initializing modem library
[00:00:00.533,599] <inf> Lesson3_Exercise1: Connecting to LTE network
[00:00:01.551,269] <inf> Lesson3_Exercise1: RRC mode: Connected
[00:00:03.590,515] <inf> Lesson3_Exercise1: Network registration status: Connected - roaming
[00:00:03.590,667] <inf> Lesson3_Exercise1: Connected to LTE network
[00:00:03.744,842] <inf> Lesson3_Exercise1: IPv4 Address found 20.56.165.163
[00:00:03.745,300] <inf> Lesson3_Exercise1: Successfully connected to server
[00:00:03.745,300] <inf> Lesson3_Exercise1: Press button 1 on your DK or Thingy:91 to send your message
[00:00:10.218,750] <inf> Lesson3_Exercise1: RRC mode: Idle
Terminal12. Press button 1 a few times and observe the following output.
[00:00:15.826,538] <inf> Lesson3_Exercise1: Successfully sent message: Hi from nRF91 Series device
[00:00:15.919,647] <inf> Lesson3_Exercise1: RRC mode: Connected
[00:00:16.175,781] <inf> Lesson3_Exercise1: Data received from the server: (Time: 2022-10-19 13:25:25 Message: Hi from nRF91 Series device)
[00:00:19.410,858] <inf> Lesson3_Exercise1: Successfully sent message: Hi from nRF91 Series device
[00:00:19.525,909] <inf> Lesson3_Exercise1: Data received from the server: (Time: 2022-10-19 13:25:28 Message: Hi from nRF91 Series device)
[00:00:26.020,172] <inf> Lesson3_Exercise1: RRC mode: Idle
[00:00:27.688,018] <inf> Lesson3_Exercise1: Successfully sent message: Hi from nRF91 Series device
[00:00:27.779,083] <inf> Lesson3_Exercise1: RRC mode: Connected
[00:00:28.170,227] <inf> Lesson3_Exercise1: Data received from the server: (Time: 2022-10-19 13:25:37 Message: Hi from nRF91 Series device)
[00:00:34.812,500] <inf> Lesson3_Exercise1: RRC mode: Idle
TerminalThe solution for this exercise can be found in l3/l3_e1_sol
, of whichever version directory you are using.
Many of the libraries used in this course, like the MQTT library (Lesson 4) and the CoAP library (Lesson 5) don’t use the socket API directly, meaning the application needs to handle that. In this exercise, we will use the socket API to create a socket and connect to an echo server over UDP.
1. In the GitHub repository for this course, go to the base code for this exercise, found in l3/l3_e1
, of whichever version directory you are using.
2. Set the necessary networking configurations
2.1 Configure the Zephyr networking API
Enable Zephyr native networking API and disable the Zephyr native IP stack.
CONFIG_NETWORKING=y
CONFIG_NET_NATIVE=n
Kconfig2.2 Enable and configure the BSD sockets compatible API
Enable the BSD socket like API on top of Zephyr’s native networking API, socket offloading (so all socket API calls are sent to the modem), and POSIX names for the socket API (to disable the _zsock
prefix).
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_OFFLOAD=y
CONFIG_NET_SOCKETS_POSIX_NAMES=y
Kconfig3. Include the header file for the socket API
#include <zephyr/net/socket.h>
C4. Define the hostname and port for the echo server.
This is a UDP based echo server that we are running specifically for this course.
#define SERVER_HOSTNAME "udp-echo.nordicsemi.academy"
#define SERVER_PORT "2444"
CEcho server: An echo server is a server running an application that sends back (or “echos”) all received messages. So when a client connects to an echo server and sends a message, that message will be received back.
5. Initialize variables for resolving the server address, creating the socket and receiving messages from the server.
5.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;
CAs we mentioned in Socket API, using struct sockaddr_storage
instead of struct sockaddr
is good practice as it promotes protocol-family independence (see here for a good explanation).
5.2 Declare the receive buffer for receiving messages from the UDP echo server.
static uint8_t recv_buf[MESSAGE_SIZE];
C6. In the function server_resolve()
, we will resolve the IP address of the server, retrieve the relevant information and print the address to console.
6.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("ERROR: getaddrinfo failed %d\n", err);
return -EIO;
}
if (result == NULL) {
LOG_INF("ERROR: Address not found\n");
return -ENOENT;
}
C6.2. 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;
C6.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 found %s", ipv4_addr);
C6.4 Free the memory allocated for the addrinfo
structure result
, using freeadrinfo()
.
freeaddrinfo(result);
C7. In the function server_connect()
, create a 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;
}
C8. Then connect the socket 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;
}
C9. 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 (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);
}
C10. 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);
C11. Build the exercise and flash it to the board as we have done in the previous lessons.
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***
[00:00:00.184,369] <inf> Lesson3_Exercise1: Initializing modem library
[00:00:00.533,599] <inf> Lesson3_Exercise1: Connecting to LTE network
[00:00:01.551,269] <inf> Lesson3_Exercise1: RRC mode: Connected
[00:00:03.590,515] <inf> Lesson3_Exercise1: Network registration status: Connected - roaming
[00:00:03.590,667] <inf> Lesson3_Exercise1: Connected to LTE network
[00:00:03.744,842] <inf> Lesson3_Exercise1: IPv4 Address found 20.56.165.163
[00:00:03.745,300] <inf> Lesson3_Exercise1: Successfully connected to server
[00:00:03.745,300] <inf> Lesson3_Exercise1: Press button 1 on your DK or Thingy:91 to send your message
[00:00:10.218,750] <inf> Lesson3_Exercise1: RRC mode: Idle
Terminal12. Press button 1 a few times and observe the following output.
[00:00:15.826,538] <inf> Lesson3_Exercise1: Successfully sent message: Hi from nRF91 Series device
[00:00:15.919,647] <inf> Lesson3_Exercise1: RRC mode: Connected
[00:00:16.175,781] <inf> Lesson3_Exercise1: Data received from the server: (Time: 2022-10-19 13:25:25 Message: Hi from nRF91 Series device)
[00:00:19.410,858] <inf> Lesson3_Exercise1: Successfully sent message: Hi from nRF91 Series device
[00:00:19.525,909] <inf> Lesson3_Exercise1: Data received from the server: (Time: 2022-10-19 13:25:28 Message: Hi from nRF91 Series device)
[00:00:26.020,172] <inf> Lesson3_Exercise1: RRC mode: Idle
[00:00:27.688,018] <inf> Lesson3_Exercise1: Successfully sent message: Hi from nRF91 Series device
[00:00:27.779,083] <inf> Lesson3_Exercise1: RRC mode: Connected
[00:00:28.170,227] <inf> Lesson3_Exercise1: Data received from the server: (Time: 2022-10-19 13:25:37 Message: Hi from nRF91 Series device)
[00:00:34.812,500] <inf> Lesson3_Exercise1: RRC mode: Idle
TerminalThe solution for this exercise can be found in l3/l3_e1_sol
, of whichever version directory you are using.
Many of the libraries used in this course, like the MQTT library (Lesson 4) and the CoAP library (Lesson 5) don’t use the socket API directly, meaning the application needs to handle that. In this exercise, we will use the socket API to create a socket and connect to an echo server over UDP.
1. In the GitHub repository for this course, go to the base code for this exercise, found in l3/l3_e1
, of whichever version directory you are using.
2. Set the necessary networking configurations
2.1 Configure the Zephyr networking API
Enable Zephyr native networking API and disable the Zephyr native IP stack.
CONFIG_NETWORKING=y
CONFIG_NET_NATIVE=n
Kconfig2.2 Enable and configure the BSD sockets compatible API
Enable the BSD socket like API on top of Zephyr’s native networking API, socket offloading (so all socket API calls are sent to the modem), and POSIX names for the socket API (to disable the _zsock
prefix).
CONFIG_NET_SOCKETS=y
CONFIG_NET_SOCKETS_OFFLOAD=y
CONFIG_NET_SOCKETS_POSIX_NAMES=y
Kconfig3. Include the header file for the socket API
#include <zephyr/net/socket.h>
C4. Define the hostname and port for the echo server.
This is a UDP based echo server that we are running specifically for this course.
#define SERVER_HOSTNAME "udp-echo.nordicsemi.academy"
#define SERVER_PORT "2444"
CEcho server: An echo server is a server running an application that sends back (or “echos”) all received messages. So when a client connects to an echo server and sends a message, that message will be received back.
5. Initialize variables for resolving the server address, creating the socket and receiving messages from the server.
5.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;
CAs we mentioned in Socket API, using struct sockaddr_storage
instead of struct sockaddr
is good practice as it promotes protocol-family independence (see here for a good explanation).
5.2 Declare the receive buffer for receiving messages from the UDP echo server.
static uint8_t recv_buf[MESSAGE_SIZE];
C6. In the function server_resolve()
, we will resolve the IP address of the server, retrieve the relevant information and print the address to console.
6.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("ERROR: getaddrinfo failed %d\n", err);
return -EIO;
}
if (result == NULL) {
LOG_INF("ERROR: Address not found\n");
return -ENOENT;
}
C6.2. 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;
C6.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 found %s", ipv4_addr);
C6.4 Free the memory allocated for the addrinfo
structure result
, using freeadrinfo()
.
freeaddrinfo(result);
C7. In the function server_connect()
, create a 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;
}
C8. Then connect the socket 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;
}
C9. 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 (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);
}
C10. 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);
C11. Build the exercise and flash it to the board as we have done in the previous lessons.
*** Booting nRF Connect SDK 2.6.1-3758bcbfa5cd ***
[00:00:00.184,369] <inf> Lesson3_Exercise1: Initializing modem library
[00:00:00.533,599] <inf> Lesson3_Exercise1: Connecting to LTE network
[00:00:01.551,269] <inf> Lesson3_Exercise1: RRC mode: Connected
[00:00:03.590,515] <inf> Lesson3_Exercise1: Network registration status: Connected - roaming
[00:00:03.590,667] <inf> Lesson3_Exercise1: Connected to LTE network
[00:00:03.744,842] <inf> Lesson3_Exercise1: IPv4 Address found 20.56.165.163
[00:00:03.745,300] <inf> Lesson3_Exercise1: Successfully connected to server
[00:00:03.745,300] <inf> Lesson3_Exercise1: Press button 1 on your DK or Thingy:91 to send your message
[00:00:10.218,750] <inf> Lesson3_Exercise1: RRC mode: Idle
Terminal12. Press button 1 a few times and observe the following output.
[00:00:15.826,538] <inf> Lesson3_Exercise1: Successfully sent message: Hi from nRF91 Series device
[00:00:15.919,647] <inf> Lesson3_Exercise1: RRC mode: Connected
[00:00:16.175,781] <inf> Lesson3_Exercise1: Data received from the server: (Time: 2022-10-19 13:25:25 Message: Hi from nRF91 Series device)
[00:00:19.410,858] <inf> Lesson3_Exercise1: Successfully sent message: Hi from nRF91 Series device
[00:00:19.525,909] <inf> Lesson3_Exercise1: Data received from the server: (Time: 2022-10-19 13:25:28 Message: Hi from nRF91 Series device)
[00:00:26.020,172] <inf> Lesson3_Exercise1: RRC mode: Idle
[00:00:27.688,018] <inf> Lesson3_Exercise1: Successfully sent message: Hi from nRF91 Series device
[00:00:27.779,083] <inf> Lesson3_Exercise1: RRC mode: Connected
[00:00:28.170,227] <inf> Lesson3_Exercise1: Data received from the server: (Time: 2022-10-19 13:25:37 Message: Hi from nRF91 Series device)
[00:00:34.812,500] <inf> Lesson3_Exercise1: RRC mode: Idle
TerminalThe solution for this exercise can be found in l3/l3_e1_sol
, of whichever version directory you are using.