1 /*
2  * Copyright (c) 2025 Linumiz GmbH
3  *
4  * SPDX-License-Identifier: Apache-2.0
5  */
6 
7 #undef _POSIX_C_SOURCE
8 #define _POSIX_C_SOURCE 200809L /* for strdup() */
9 #include <stdio.h>
10 #include <time.h>
11 
12 #include <zephyr/posix/sys/socket.h>
13 #include <zephyr/posix/arpa/inet.h>
14 #include <zephyr/posix/netinet/in.h>
15 #include <zephyr/posix/unistd.h>
16 #include <zephyr/posix/netdb.h>
17 
18 #include <zephyr/net/net_if.h>
19 #include <zephyr/net/net_core.h>
20 #include <zephyr/net/net_context.h>
21 #include <zephyr/net/net_mgmt.h>
22 #include <zephyr/net/sntp.h>
23 #include <zephyr/net/ocpp.h>
24 #include <zephyr/random/random.h>
25 #include <zephyr/zbus/zbus.h>
26 
27 #include "net_sample_common.h"
28 
29 LOG_MODULE_REGISTER(main, LOG_LEVEL_INF);
30 
31 #define NO_OF_CONN 2
32 K_KERNEL_STACK_ARRAY_DEFINE(cp_stk, NO_OF_CONN, 2 * 1024);
33 
34 static struct k_thread tinfo[NO_OF_CONN];
35 static k_tid_t tid[NO_OF_CONN];
36 static char idtag[NO_OF_CONN][25];
37 
dns_query(const char * host,uint16_t port,int family,int socktype,struct sockaddr * addr,socklen_t * addrlen)38 static int dns_query(const char *host, uint16_t port, int family, int socktype,
39 			  struct sockaddr *addr, socklen_t *addrlen)
40 {
41 	struct addrinfo hints = {
42 		.ai_family = family,
43 		.ai_socktype = socktype,
44 	};
45 
46 	struct addrinfo *res = NULL;
47 	char addr_str[INET6_ADDRSTRLEN] = {0};
48 	int ret;
49 
50 	/* Perform DNS query */
51 	ret = getaddrinfo(host, NULL, &hints, &res);
52 	if (ret < 0) {
53 		LOG_ERR("getaddrinfo failed (%d, errno %d)", ret, errno);
54 		return ret;
55 	}
56 
57 	/* Store the first result */
58 	*addr = *res->ai_addr;
59 	*addrlen = res->ai_addrlen;
60 	/* Free the allocated memory */
61 	freeaddrinfo(res);
62 	/* Store the port */
63 	net_sin(addr)->sin_port = htons(port);
64 	/* Print the found address */
65 	inet_ntop(addr->sa_family, &net_sin(addr)->sin_addr, addr_str, sizeof(addr_str));
66 	LOG_INF("%s -> %s", host, addr_str);
67 
68 	return 0;
69 }
70 
ocpp_get_time_from_sntp(void)71 static int ocpp_get_time_from_sntp(void)
72 {
73 	struct sntp_ctx ctx;
74 	struct sntp_time stime;
75 	struct sockaddr addr;
76 	socklen_t addrlen;
77 	struct timespec tv;
78 	int ret;
79 
80 	ret = dns_query(CONFIG_NET_SAMPLE_SNTP_SERVER, CONFIG_NET_SAMPLE_SNTP_SERVER_PORT,
81 				   AF_INET, SOCK_DGRAM, &addr, &addrlen);
82 	if (ret != 0) {
83 		LOG_ERR("Failed to lookup SNTP server (%d)", ret);
84 		return ret;
85 	}
86 
87 	ret = sntp_init(&ctx, &addr, addrlen);
88 	if (ret < 0) {
89 		LOG_ERR("Failed to init SNTP IPv4 ctx: %d", ret);
90 		return ret;
91 	}
92 
93 	ret = sntp_query(&ctx, 4 * MSEC_PER_SEC, &stime);
94 	if (ret < 0) {
95 		LOG_ERR("SNTP IPv4 request failed: %d", ret);
96 		return ret;
97 	}
98 
99 	LOG_INF("SNTP success, epoch seconds: %llu\n", stime.seconds);
100 	tv.tv_sec = stime.seconds;
101 	clock_settime(CLOCK_REALTIME, &tv);
102 	sntp_close(&ctx);
103 	return 0;
104 }
105 
106 ZBUS_CHAN_DEFINE(ch_event, /* Name */
107 		 union ocpp_io_value,
108 		 NULL,			/* Validator */
109 		 NULL,			/* User data */
110 		 ZBUS_OBSERVERS_EMPTY,	/* observers */
111 		 ZBUS_MSG_INIT(0) /* Initial value {0} */
112 );
113 
114 ZBUS_SUBSCRIBER_DEFINE(cp_thread0, 5);
115 ZBUS_SUBSCRIBER_DEFINE(cp_thread1, 5);
116 struct zbus_observer *obs[NO_OF_CONN] = {(struct zbus_observer *)&cp_thread0,
117 					 (struct zbus_observer *)&cp_thread1};
118 
119 static void ocpp_cp_entry(void *p1, void *p2, void *p3);
user_notify_cb(enum ocpp_notify_reason reason,union ocpp_io_value * io,void * user_data)120 static int user_notify_cb(enum ocpp_notify_reason reason,
121 			  union ocpp_io_value *io,
122 			  void *user_data)
123 {
124 	static int wh = 6 + NO_OF_CONN;
125 	int idx;
126 	int i;
127 
128 	switch (reason) {
129 	case OCPP_USR_GET_METER_VALUE:
130 		if (OCPP_OMM_ACTIVE_ENERGY_TO_EV == io->meter_val.mes) {
131 			snprintf(io->meter_val.val, CISTR50, "%u",
132 				 wh + io->meter_val.id_con);
133 
134 			wh++;
135 			LOG_DBG("mtr reading val %s con %d", io->meter_val.val,
136 				io->meter_val.id_con);
137 
138 			return 0;
139 		}
140 		break;
141 
142 	case OCPP_USR_START_CHARGING:
143 		if (io->start_charge.id_con < 0) {
144 			for (i = 0; i < NO_OF_CONN; i++) {
145 				if (tid[i] == NULL) {
146 					break;
147 				}
148 			}
149 
150 			if (i >= NO_OF_CONN) {
151 				return -EBUSY;
152 			}
153 			idx = i;
154 		} else {
155 			idx = io->start_charge.id_con - 1;
156 		}
157 
158 		if (tid[idx] == NULL) {
159 			LOG_INF("Remote start charging idtag %s connector %d\n",
160 				idtag[idx], idx + 1);
161 
162 			strncpy(idtag[idx], io->start_charge.idtag,
163 				sizeof(idtag[0]));
164 
165 			tid[idx] = k_thread_create(&tinfo[idx], cp_stk[idx],
166 						   sizeof(cp_stk[idx]), ocpp_cp_entry,
167 						   (void *)(uintptr_t)(idx + 1), idtag[idx],
168 						   obs[idx], 7, 0, K_NO_WAIT);
169 
170 			return 0;
171 		}
172 		break;
173 
174 	case OCPP_USR_STOP_CHARGING:
175 		zbus_chan_pub(&ch_event, io, K_MSEC(100));
176 		return 0;
177 
178 	case OCPP_USR_UNLOCK_CONNECTOR:
179 		LOG_INF("unlock connector %d\n", io->unlock_con.id_con);
180 		return 0;
181 	}
182 
183 	return -ENOTSUP;
184 }
185 
ocpp_cp_entry(void * p1,void * p2,void * p3)186 static void ocpp_cp_entry(void *p1, void *p2, void *p3)
187 {
188 	int ret;
189 	int idcon = (int)(uintptr_t)p1;
190 	char *idtag = (char *)p2;
191 	struct zbus_observer *obs = (struct zbus_observer *)p3;
192 	ocpp_session_handle_t sh = NULL;
193 	enum ocpp_auth_status status;
194 	const uint32_t timeout_ms = 500;
195 
196 	ret = ocpp_session_open(&sh);
197 	if (ret < 0) {
198 		LOG_ERR("ocpp open ses idcon %d> res %d\n", idcon, ret);
199 		return;
200 	}
201 
202 	while (1) {
203 		/* Avoid quick retry since authorization request is possible only
204 		 * after Bootnotification process (handled in lib) completed.
205 		 */
206 
207 		k_sleep(K_SECONDS(5));
208 		ret = ocpp_authorize(sh,
209 				     idtag,
210 				     &status,
211 				     timeout_ms);
212 		if (ret < 0) {
213 			LOG_ERR("ocpp auth %d> idcon %d status %d\n",
214 				ret, idcon, status);
215 		} else {
216 			LOG_INF("ocpp auth %d> idcon %d status %d\n",
217 				ret, idcon, status);
218 			break;
219 		}
220 	}
221 
222 	if (status != OCPP_AUTH_ACCEPTED) {
223 		LOG_ERR("ocpp start idcon %d> not authorized status %d\n",
224 			idcon, status);
225 		return;
226 	}
227 
228 	ret = ocpp_start_transaction(sh, sys_rand32_get(), idcon, timeout_ms);
229 	if (ret == 0) {
230 		const struct zbus_channel *chan;
231 		union ocpp_io_value io;
232 
233 		LOG_INF("ocpp start charging connector id %d\n", idcon);
234 		memset(&io, 0xff, sizeof(io));
235 
236 		/* wait for stop charging event from main or remote CS */
237 		zbus_chan_add_obs(&ch_event, obs, K_SECONDS(1));
238 		do {
239 			zbus_sub_wait(obs, &chan, K_FOREVER);
240 			zbus_chan_read(chan, &io, K_SECONDS(1));
241 
242 			if (io.stop_charge.id_con == idcon) {
243 				break;
244 			}
245 
246 		} while (1);
247 	}
248 
249 	ret = ocpp_stop_transaction(sh, sys_rand32_get(), timeout_ms);
250 	if (ret < 0) {
251 		LOG_ERR("ocpp stop txn idcon %d> %d\n", idcon, ret);
252 		return;
253 	}
254 
255 	LOG_INF("ocpp stop charging connector id %d\n", idcon);
256 	k_sleep(K_SECONDS(1));
257 	ocpp_session_close(sh);
258 	tid[idcon - 1] = NULL;
259 	k_sleep(K_SECONDS(1));
260 	k_thread_abort(k_current_get());
261 }
262 
ocpp_getaddrinfo(char * server,int port,char ** ip)263 static int ocpp_getaddrinfo(char *server, int port, char **ip)
264 {
265 	int ret;
266 	uint8_t retry = 5;
267 	char addr_str[INET_ADDRSTRLEN];
268 	struct sockaddr_storage b;
269 	struct addrinfo *result = NULL;
270 	struct addrinfo *addr;
271 	struct addrinfo hints = {
272 		.ai_family = AF_INET,
273 		.ai_socktype = SOCK_STREAM
274 	};
275 
276 	LOG_INF("cs server %s %d", server, port);
277 	do {
278 		ret = getaddrinfo(server, NULL, &hints, &result);
279 		if (ret == -EAGAIN) {
280 			LOG_ERR("ERROR: getaddrinfo %d, rebind", ret);
281 			k_sleep(K_SECONDS(1));
282 		} else if (ret != 0) {
283 			LOG_ERR("ERROR: getaddrinfo failed %d", ret);
284 			return ret;
285 		}
286 	} while (--retry && ret);
287 
288 	addr = result;
289 	while (addr != NULL) {
290 		/* IPv4 Address. */
291 		if (addr->ai_addrlen == sizeof(struct sockaddr_in)) {
292 			struct sockaddr_in *broker =
293 				((struct sockaddr_in *)&b);
294 
295 			broker->sin_addr.s_addr =
296 				((struct sockaddr_in *)addr->ai_addr)
297 				->sin_addr.s_addr;
298 			broker->sin_family = AF_INET;
299 			broker->sin_port = htons(port);
300 
301 			inet_ntop(AF_INET, &broker->sin_addr, addr_str,
302 				  sizeof(addr_str));
303 
304 			*ip = strdup(addr_str);
305 			LOG_INF("IPv4 Address %s", addr_str);
306 			break;
307 		}
308 
309 		LOG_ERR("error: ai_addrlen = %u should be %u or %u",
310 			(unsigned int)addr->ai_addrlen,
311 			(unsigned int)sizeof(struct sockaddr_in),
312 			(unsigned int)sizeof(struct sockaddr_in6));
313 
314 		addr = addr->ai_next;
315 	}
316 
317 	/* Free the address. */
318 	freeaddrinfo(result);
319 
320 	return 0;
321 }
322 
main(void)323 int main(void)
324 {
325 	int ret;
326 	int i;
327 	char *ip = NULL;
328 
329 	struct ocpp_cp_info cpi = { "basic", "zephyr", .num_of_con = NO_OF_CONN };
330 	struct ocpp_cs_info csi = {NULL,
331 				   CONFIG_NET_SAMPLE_OCPP_WS_PATH,
332 				   CONFIG_NET_SAMPLE_OCPP_PORT,
333 				   AF_INET};
334 
335 	printk("OCPP sample %s\n", CONFIG_BOARD);
336 
337 	wait_for_network();
338 
339 	ret = ocpp_getaddrinfo(CONFIG_NET_SAMPLE_OCPP_SERVER, CONFIG_NET_SAMPLE_OCPP_PORT, &ip);
340 	if (ret < 0) {
341 		return ret;
342 	}
343 
344 	csi.cs_ip = ip;
345 
346 	ocpp_get_time_from_sntp();
347 
348 	ret = ocpp_init(&cpi,
349 			&csi,
350 			user_notify_cb,
351 			NULL);
352 	if (ret < 0) {
353 		LOG_ERR("ocpp init failed %d\n", ret);
354 		return ret;
355 	}
356 
357 	/* Spawn threads for each connector */
358 	for (i = 0; i < NO_OF_CONN; i++) {
359 		snprintf(idtag[i], sizeof(idtag[0]), "ZepId%02d", i);
360 
361 		tid[i] = k_thread_create(&tinfo[i], cp_stk[i],
362 					 sizeof(cp_stk[i]),
363 					 ocpp_cp_entry, (void *)(uintptr_t)(i + 1),
364 					 idtag[i], obs[i], 7, 0, K_NO_WAIT);
365 	}
366 
367 	/* Active charging session */
368 	k_sleep(K_SECONDS(30));
369 
370 	/* Send stop charging to thread */
371 	for (i = 0; i < NO_OF_CONN; i++) {
372 		union ocpp_io_value io = {0};
373 
374 		io.stop_charge.id_con = i + 1;
375 
376 		zbus_chan_pub(&ch_event, &io, K_MSEC(100));
377 		k_sleep(K_SECONDS(1));
378 	}
379 
380 	/* User could trigger remote start/stop transaction from CS server */
381 	k_sleep(K_SECONDS(1200));
382 
383 	return 0;
384 }
385