samples: net: coap_download: Add sample application for coap_client API

Added a sample application demonstrating how to use the coap_client API
to download a resource via GET request.

Signed-off-by: Matt Rodgers <mrodgers@witekio.com>
This commit is contained in:
Matt Rodgers 2024-07-17 15:31:25 +01:00 committed by Fabio Baltieri
parent 3029bb1fbf
commit e4dff4ad14
6 changed files with 278 additions and 0 deletions

View File

@ -0,0 +1,10 @@
# SPDX-License-Identifier: Apache-2.0
cmake_minimum_required(VERSION 3.20.0)
find_package(Zephyr REQUIRED HINTS $ENV{ZEPHYR_BASE})
project(coap_download)
target_sources(app PRIVATE
src/main.c
)

View File

@ -0,0 +1,19 @@
# Copyright (c) 2024 Witekio
# SPDX-License-Identifier: Apache-2.0
mainmenu "CoAP Download Sample Application"
config NET_SAMPLE_COAP_SERVER_PORT
int "CoAP server port"
default 5683
help
CoAP server port that the application should send requests to.
config NET_SAMPLE_COAP_RESOURCE_PATH
string "The CoAP resource path to download"
default "test.txt"
help
The path (relative to server's root directory) of the CoAP resource
the application should download.
source "Kconfig.zephyr"

View File

@ -0,0 +1,90 @@
.. zephyr:code-sample:: coap-download
:name: CoAP download
:relevant-api: coap_client_interface
Use the CoAP client API to download data via a GET request
Overview
********
This sample demonstrates the use of the CoAP client API to make GET requests to
a CoAP server. If the data to be fetched is larger than a single CoAP packet,
a blockwise transfer will be used to receive the data.
Once the transfer is complete, the sample prints the amount of data received
and the time taken.
Requirements
************
- :ref:`networking_with_host`, :ref:`networking_with_native_sim`
- or, a board with hardware networking (tested on nucleo_h753zi)
- Network connection between the board and host running a CoAP server
Build and Running
*****************
Build the CoAP download sample application like this:
.. zephyr-app-commands::
:zephyr-app: samples/net/sockets/coap_download
:board: <board to use>
:conf: <config file to use>
:goals: build
:compact:
The easiest way to run this sample application is to build and run it as a
native_sim application. Some setup is required as described in
:ref:`networking_with_native_sim`.
Download a CoAP server application, for example `aiocoap`_ (Python), or
`Eclipse Californium`_ (Java) has a demo `Simple File Server`_ application.
Using ``aiocoap``:
.. code-block:: bash
python -m pip install "aiocoap[all]"
mkdir file_root
echo "some test data" > file_root/test.txt
aiocoap-fileserver file_root
You can also change the name of the CoAP resource to request via Kconfig:
.. code-block:: cfg
CONFIG_NET_SAMPLE_COAP_RESOURCE_PATH="resource_name_to_request"
Launch :command:`net-setup.sh` in net-tools:
.. code-block:: bash
./net-setup.sh
Build and run the coap_download sample application for native_sim like this:
.. zephyr-app-commands::
:zephyr-app: samples/net/sockets/coap_download
:host-os: unix
:board: native_sim
:goals: run
:compact:
Sample output
=============
.. code-block:: console
[00:00:00.000,000] <inf> net_config: Initializing network
[00:00:00.000,000] <inf> net_config: IPv4 address: 192.0.2.1
[00:00:00.110,000] <inf> net_config: IPv6 address: 2001:db8::1
[00:00:00.110,000] <inf> net_config: IPv6 address: 2001:db8::1
[00:00:00.110,000] <inf> coap_download: Network L4 is connected
[00:00:00.110,000] <inf> coap_download: Starting CoAP download using IPv4
[00:00:00.180,000] <inf> coap_download: CoAP response, result_code=69, offset=0, len=100
[00:00:00.180,000] <inf> coap_download: CoAP download done, got 100 bytes in 70 ms
[00:00:00.180,000] <inf> coap_download: Starting CoAP download using IPv6
[00:00:00.300,000] <inf> coap_download: CoAP response, result_code=69, offset=0, len=100
[00:00:00.300,000] <inf> coap_download: CoAP download done, got 100 bytes in 120 ms
.. _aiocoap: https://github.com/chrysn/aiocoap
.. _Eclipse Californium: https://github.com/eclipse-californium/californium
.. _Simple File Server: https://github.com/eclipse-californium/californium/tree/main/demo-apps/cf-simplefile-server

View File

@ -0,0 +1,22 @@
# Networking
CONFIG_NETWORKING=y
CONFIG_NET_UDP=y
CONFIG_NET_SOCKETS=y
CONFIG_NET_CONNECTION_MANAGER=y
# Logging
CONFIG_LOG=y
CONFIG_NET_LOG=y
# CoAP
CONFIG_COAP=y
CONFIG_COAP_CLIENT=y
# Network application options and configuration
CONFIG_NET_CONFIG_SETTINGS=y
CONFIG_NET_CONFIG_NEED_IPV6=y
CONFIG_NET_CONFIG_MY_IPV6_ADDR="2001:db8::1"
CONFIG_NET_CONFIG_PEER_IPV6_ADDR="2001:db8::2"
CONFIG_NET_CONFIG_NEED_IPV4=y
CONFIG_NET_CONFIG_MY_IPV4_ADDR="192.0.2.1"
CONFIG_NET_CONFIG_PEER_IPV4_ADDR="192.0.2.2"

View File

@ -0,0 +1,13 @@
sample:
name: CoAP Download Sample
tests:
sample.net.sockets.coap_download:
harness: net
filter: CONFIG_ENTROPY_HAS_DRIVER
depends_on: netif
tags:
- net
- coap
integration_platforms:
- native_sim
- nucleo_h753zi

View File

@ -0,0 +1,124 @@
/*
* Copyright (c) 2024 Witekio
*
* SPDX-License-Identifier: Apache-2.0
*/
#include <zephyr/kernel.h>
#include <zephyr/logging/log.h>
#include <zephyr/net/net_mgmt.h>
#include <zephyr/net/socket.h>
#include <zephyr/net/coap_client.h>
#include <inttypes.h>
#include <errno.h>
LOG_MODULE_REGISTER(coap_download, LOG_LEVEL_INF);
static K_SEM_DEFINE(net_connected_sem, 0, 1);
static K_SEM_DEFINE(coap_done_sem, 0, 1);
static int64_t start_time;
/* This struct contains (potentially large) TX and RX buffers, so allocate statically */
static struct coap_client client = {0};
static void net_event_handler(uint32_t mgmt_event, struct net_if *iface, void *info,
size_t info_length, void *user_data)
{
if (NET_EVENT_L4_CONNECTED == mgmt_event) {
k_sem_give(&net_connected_sem);
}
}
NET_MGMT_REGISTER_EVENT_HANDLER(l4_conn_handler, NET_EVENT_L4_CONNECTED, net_event_handler, NULL);
static void on_coap_response(int16_t result_code, size_t offset, const uint8_t *payload, size_t len,
bool last_block, void *user_data)
{
LOG_INF("CoAP response, result_code=%d, offset=%u, len=%u", result_code, offset, len);
if ((COAP_RESPONSE_CODE_CONTENT == result_code) && last_block) {
int64_t elapsed_time = k_uptime_delta(&start_time);
size_t total_size = offset + len;
LOG_INF("CoAP download done, got %u bytes in %" PRId64 " ms", total_size,
elapsed_time);
k_sem_give(&coap_done_sem);
} else if (COAP_RESPONSE_CODE_CONTENT != result_code) {
LOG_ERR("Error during CoAP download, result_code=%d", result_code);
k_sem_give(&coap_done_sem);
}
}
static void do_coap_download(struct sockaddr *sa)
{
int ret;
int sockfd;
struct coap_client_request request = {.method = COAP_METHOD_GET,
.confirmable = true,
.path = CONFIG_NET_SAMPLE_COAP_RESOURCE_PATH,
.payload = NULL,
.len = 0,
.cb = on_coap_response,
.options = NULL,
.num_options = 0,
.user_data = NULL};
LOG_INF("Starting CoAP download using %s", (AF_INET == sa->sa_family) ? "IPv4" : "IPv6");
sockfd = zsock_socket(sa->sa_family, SOCK_DGRAM, 0);
if (sockfd < 0) {
LOG_ERR("Failed to create socket, err %d", errno);
return;
}
start_time = k_uptime_get();
ret = coap_client_req(&client, sockfd, sa, &request, NULL);
if (ret) {
LOG_ERR("Failed to send CoAP request, err %d", ret);
return;
}
/* Wait for CoAP request to complete */
k_sem_take(&coap_done_sem, K_FOREVER);
zsock_close(sockfd);
}
int main(void)
{
int ret;
ret = coap_client_init(&client, NULL);
if (ret) {
LOG_ERR("Failed to init coap client, err %d", ret);
return ret;
}
k_sem_take(&net_connected_sem, K_FOREVER);
LOG_INF("Network L4 is connected");
struct sockaddr sa;
#if defined(CONFIG_NET_IPV4)
struct sockaddr_in *addr4 = (struct sockaddr_in *)&sa;
addr4->sin_family = AF_INET;
addr4->sin_port = htons(CONFIG_NET_SAMPLE_COAP_SERVER_PORT);
zsock_inet_pton(AF_INET, CONFIG_NET_CONFIG_PEER_IPV4_ADDR, &addr4->sin_addr);
do_coap_download(&sa);
#endif
#if defined(CONFIG_NET_IPV6)
struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&sa;
addr6->sin6_family = AF_INET6;
addr6->sin6_port = htons(CONFIG_NET_SAMPLE_COAP_SERVER_PORT);
zsock_inet_pton(AF_INET6, CONFIG_NET_CONFIG_PEER_IPV6_ADDR, &addr6->sin6_addr);
do_coap_download(&sa);
#endif
return 0;
}