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:
parent
3029bb1fbf
commit
e4dff4ad14
|
@ -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
|
||||
)
|
|
@ -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"
|
|
@ -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
|
|
@ -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"
|
|
@ -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
|
|
@ -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;
|
||||
}
|
Loading…
Reference in New Issue