1 /*
2  * Copyright (c) 2018 Intel Corporation
3  * Copyright (c) 2023 Basalte bv
4  *
5  * SPDX-License-Identifier: Apache-2.0
6  */
7 
8 #include <zephyr/logging/log.h>
9 LOG_MODULE_DECLARE(net_coap_service_sample);
10 
11 #include <zephyr/kernel.h>
12 #include <zephyr/sys/printk.h>
13 #include <zephyr/net/coap_service.h>
14 
15 static int obs_counter;
16 
17 static void update_counter(struct k_work *work);
18 K_WORK_DELAYABLE_DEFINE(obs_work, update_counter);
19 
send_notification_packet(struct coap_resource * resource,const struct sockaddr * addr,socklen_t addr_len,uint16_t age,uint16_t id,const uint8_t * token,uint8_t tkl,bool is_response)20 static int send_notification_packet(struct coap_resource *resource,
21 				    const struct sockaddr *addr,
22 				    socklen_t addr_len,
23 				    uint16_t age, uint16_t id,
24 				    const uint8_t *token, uint8_t tkl,
25 				    bool is_response)
26 {
27 	uint8_t data[CONFIG_COAP_SERVER_MESSAGE_SIZE];
28 	struct coap_packet response;
29 	char payload[14];
30 	uint8_t type;
31 	int r;
32 
33 	if (is_response) {
34 		type = COAP_TYPE_ACK;
35 	} else {
36 		type = COAP_TYPE_CON;
37 	}
38 
39 	if (!is_response) {
40 		id = coap_next_id();
41 	}
42 
43 	r = coap_packet_init(&response, data, sizeof(data),
44 			     COAP_VERSION_1, type, tkl, token,
45 			     COAP_RESPONSE_CODE_CONTENT, id);
46 	if (r < 0) {
47 		return r;
48 	}
49 
50 	if (age >= 2U) {
51 		r = coap_append_option_int(&response, COAP_OPTION_OBSERVE, age);
52 		if (r < 0) {
53 			return r;
54 		}
55 	}
56 
57 	r = coap_append_option_int(&response, COAP_OPTION_CONTENT_FORMAT,
58 				   COAP_CONTENT_FORMAT_TEXT_PLAIN);
59 	if (r < 0) {
60 		return r;
61 	}
62 
63 	r = coap_packet_append_payload_marker(&response);
64 	if (r < 0) {
65 		return r;
66 	}
67 
68 	/* The response that coap-client expects */
69 	r = snprintk((char *) payload, sizeof(payload),
70 		     "Counter: %d\n", obs_counter);
71 	if (r < 0) {
72 		return r;
73 	}
74 
75 	r = coap_packet_append_payload(&response, (uint8_t *)payload,
76 				       strlen(payload));
77 	if (r < 0) {
78 		return r;
79 	}
80 
81 	k_work_reschedule(&obs_work, K_SECONDS(5));
82 
83 	r = coap_resource_send(resource, &response, addr, addr_len, NULL);
84 
85 	return r;
86 }
87 
obs_get(struct coap_resource * resource,struct coap_packet * request,struct sockaddr * addr,socklen_t addr_len)88 static int obs_get(struct coap_resource *resource,
89 		   struct coap_packet *request,
90 		   struct sockaddr *addr, socklen_t addr_len)
91 {
92 	uint8_t token[COAP_TOKEN_MAX_LEN];
93 	uint16_t id;
94 	uint8_t code;
95 	uint8_t type;
96 	uint8_t tkl;
97 	int r;
98 
99 	r = coap_resource_parse_observe(resource, request, addr);
100 
101 	code = coap_header_get_code(request);
102 	type = coap_header_get_type(request);
103 	id = coap_header_get_id(request);
104 	tkl = coap_header_get_token(request, token);
105 
106 	LOG_INF("*******");
107 	LOG_INF("type: %u code %u id %u", type, code, id);
108 	LOG_INF("*******");
109 
110 	return send_notification_packet(resource, addr, addr_len,
111 					r == 0 ? resource->age : 0,
112 					id, token, tkl, true);
113 }
114 
obs_notify(struct coap_resource * resource,struct coap_observer * observer)115 static void obs_notify(struct coap_resource *resource,
116 		       struct coap_observer *observer)
117 {
118 	send_notification_packet(resource,
119 				 &observer->addr,
120 				 sizeof(observer->addr),
121 				 resource->age, 0,
122 				 observer->token, observer->tkl, false);
123 }
124 
125 static const char * const obs_path[] = { "obs", NULL };
126 COAP_RESOURCE_DEFINE(obs, coap_server,
127 {
128 	.get = obs_get,
129 	.path = obs_path,
130 	.notify = obs_notify,
131 });
132 
update_counter(struct k_work * work)133 static void update_counter(struct k_work *work)
134 {
135 	obs_counter++;
136 
137 	coap_resource_notify(&obs);
138 
139 	k_work_reschedule(&obs_work, K_SECONDS(5));
140 }
141