1 /*
2 * Copyright (c) 2024 Witekio
3 *
4 * SPDX-License-Identifier: Apache-2.0
5 */
6
7 #include <zephyr/kernel.h>
8 #include <zephyr/logging/log.h>
9 #include <zephyr/net/net_mgmt.h>
10 #include <zephyr/net/socket.h>
11 #include <zephyr/net/coap_client.h>
12 #include <inttypes.h>
13 #include <errno.h>
14
15 LOG_MODULE_REGISTER(coap_download, LOG_LEVEL_INF);
16
17 static K_SEM_DEFINE(net_connected_sem, 0, 1);
18 static K_SEM_DEFINE(coap_done_sem, 0, 1);
19 static int64_t start_time;
20
21 /* This struct contains (potentially large) TX and RX buffers, so allocate statically */
22 static struct coap_client client = {0};
23
net_event_handler(uint32_t mgmt_event,struct net_if * iface,void * info,size_t info_length,void * user_data)24 static void net_event_handler(uint32_t mgmt_event, struct net_if *iface, void *info,
25 size_t info_length, void *user_data)
26 {
27 if (NET_EVENT_L4_CONNECTED == mgmt_event) {
28 k_sem_give(&net_connected_sem);
29 }
30 }
31
32 NET_MGMT_REGISTER_EVENT_HANDLER(l4_conn_handler, NET_EVENT_L4_CONNECTED, net_event_handler, NULL);
33
on_coap_response(int16_t result_code,size_t offset,const uint8_t * payload,size_t len,bool last_block,void * user_data)34 static void on_coap_response(int16_t result_code, size_t offset, const uint8_t *payload, size_t len,
35 bool last_block, void *user_data)
36 {
37 LOG_INF("CoAP response, result_code=%d, offset=%u, len=%u", result_code, offset, len);
38
39 if ((COAP_RESPONSE_CODE_CONTENT == result_code) && last_block) {
40 int64_t elapsed_time = k_uptime_delta(&start_time);
41 size_t total_size = offset + len;
42
43 LOG_INF("CoAP download done, got %u bytes in %" PRId64 " ms", total_size,
44 elapsed_time);
45 k_sem_give(&coap_done_sem);
46 } else if (COAP_RESPONSE_CODE_CONTENT != result_code) {
47 LOG_ERR("Error during CoAP download, result_code=%d", result_code);
48 k_sem_give(&coap_done_sem);
49 }
50 }
51
do_coap_download(struct sockaddr * sa)52 static void do_coap_download(struct sockaddr *sa)
53 {
54 int ret;
55 int sockfd;
56 struct coap_client_request request = {.method = COAP_METHOD_GET,
57 .confirmable = true,
58 .path = CONFIG_NET_SAMPLE_COAP_RESOURCE_PATH,
59 .payload = NULL,
60 .len = 0,
61 .cb = on_coap_response,
62 .options = NULL,
63 .num_options = 0,
64 .user_data = NULL};
65
66 LOG_INF("Starting CoAP download using %s", (AF_INET == sa->sa_family) ? "IPv4" : "IPv6");
67
68 sockfd = zsock_socket(sa->sa_family, SOCK_DGRAM, 0);
69 if (sockfd < 0) {
70 LOG_ERR("Failed to create socket, err %d", errno);
71 return;
72 }
73
74 start_time = k_uptime_get();
75
76 ret = coap_client_req(&client, sockfd, sa, &request, NULL);
77 if (ret) {
78 LOG_ERR("Failed to send CoAP request, err %d", ret);
79 return;
80 }
81
82 /* Wait for CoAP request to complete */
83 k_sem_take(&coap_done_sem, K_FOREVER);
84
85 zsock_close(sockfd);
86 }
87
main(void)88 int main(void)
89 {
90 int ret;
91
92 ret = coap_client_init(&client, NULL);
93 if (ret) {
94 LOG_ERR("Failed to init coap client, err %d", ret);
95 return ret;
96 }
97
98 k_sem_take(&net_connected_sem, K_FOREVER);
99 LOG_INF("Network L4 is connected");
100
101 struct sockaddr sa;
102
103 #if defined(CONFIG_NET_IPV4)
104 struct sockaddr_in *addr4 = (struct sockaddr_in *)&sa;
105
106 addr4->sin_family = AF_INET;
107 addr4->sin_port = htons(CONFIG_NET_SAMPLE_COAP_SERVER_PORT);
108 zsock_inet_pton(AF_INET, CONFIG_NET_CONFIG_PEER_IPV4_ADDR, &addr4->sin_addr);
109
110 do_coap_download(&sa);
111 #endif
112
113 #if defined(CONFIG_NET_IPV6)
114 struct sockaddr_in6 *addr6 = (struct sockaddr_in6 *)&sa;
115
116 addr6->sin6_family = AF_INET6;
117 addr6->sin6_port = htons(CONFIG_NET_SAMPLE_COAP_SERVER_PORT);
118 zsock_inet_pton(AF_INET6, CONFIG_NET_CONFIG_PEER_IPV6_ADDR, &addr6->sin6_addr);
119
120 do_coap_download(&sa);
121 #endif
122
123 return 0;
124 }
125