Events and callback functions
Library uses events to notify application layer for (possible, but not limited to) unexpected events. This concept is used aswell for commands with longer executing time, such as scanning access points or when application starts new connection as client mode.
There are 3 types of events/callbacks available:
Global event callback function, assigned when initializing library
Connection specific event callback function, to process only events related to connection, such as connection error, data send, data receive, connection closed
API function call based event callback function
Every callback is always called from protected area of middleware (when exclusing access is granted to single thread only),
and it can be called from one of these 3 threads:
Producing thread
Processing thread
Input thread, when
LWESP_CFG_INPUT_USE_PROCESSis enabled andlwesp_input_process()function is called
Tip
Check Inter thread communication for more details about Producing and Processing thread.
Global event callback
Global event callback function is assigned at library initialization. It is used by the application to receive any kind of event, except the one related to connection:
ESP station successfully connected to access point
ESP physical device reset has been detected
Restore operation finished
New station has connected to access point
and many more..
Tip
Check Event management section for different kind of events
By default, global event function is single function.
If the application tries to split different events with different callback functions,
it is possible to do so by using lwesp_evt_register() function to register a new,
custom, event function.
Tip
Implementation of Netconn API leverages lwesp_evt_register() to
receive event when station disconnected from wifi access point.
Check its source file for actual implementation.
1/**
2 * \file lwesp_netconn.c
3 * \brief API functions for sequential calls
4 */
5
6/*
7 * Copyright (c) 2024 Tilen MAJERLE
8 *
9 * Permission is hereby granted, free of charge, to any person
10 * obtaining a copy of this software and associated documentation
11 * files (the "Software"), to deal in the Software without restriction,
12 * including without limitation the rights to use, copy, modify, merge,
13 * publish, distribute, sublicense, and/or sell copies of the Software,
14 * and to permit persons to whom the Software is furnished to do so,
15 * subject to the following conditions:
16 *
17 * The above copyright notice and this permission notice shall be
18 * included in all copies or substantial portions of the Software.
19 *
20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
27 * OTHER DEALINGS IN THE SOFTWARE.
28 *
29 * This file is part of LwESP - Lightweight ESP-AT parser library.
30 *
31 * Author: Tilen MAJERLE <tilen@majerle.eu>
32 * Version: v1.1.2-dev
33 */
34#include "lwesp/lwesp_netconn.h"
35#include "lwesp/lwesp_conn.h"
36#include "lwesp/lwesp_mem.h"
37#include "lwesp/lwesp_private.h"
38
39#if LWESP_CFG_NETCONN || __DOXYGEN__
40
41/* Check conditions */
42#if LWESP_CFG_NETCONN_RECEIVE_QUEUE_LEN < 2
43#error "LWESP_CFG_NETCONN_RECEIVE_QUEUE_LEN must be greater or equal to 2"
44#endif /* LWESP_CFG_NETCONN_RECEIVE_QUEUE_LEN < 2 */
45
46#if LWESP_CFG_NETCONN_ACCEPT_QUEUE_LEN < 2
47#error "LWESP_CFG_NETCONN_ACCEPT_QUEUE_LEN must be greater or equal to 2"
48#endif /* LWESP_CFG_NETCONN_ACCEPT_QUEUE_LEN < 2 */
49
50/* Check for IP status */
51#if LWESP_CFG_IPV6
52#define NETCONN_IS_TCP(nc) ((nc)->type == LWESP_NETCONN_TYPE_TCP || (nc)->type == LWESP_NETCONN_TYPE_TCPV6)
53#define NETCONN_IS_SSL(nc) ((nc)->type == LWESP_NETCONN_TYPE_SSL || (nc)->type == LWESP_NETCONN_TYPE_SSLV6)
54#define NETCONN_IS_UDP(nc) ((nc)->type == LWESP_NETCONN_TYPE_UDP || (nc)->type == LWESP_NETCONN_TYPE_UDPV6)
55#else
56#define NETCONN_IS_TCP(nc) ((nc)->type == LWESP_NETCONN_TYPE_TCP)
57#define NETCONN_IS_SSL(nc) ((nc)->type == LWESP_NETCONN_TYPE_SSL)
58#define NETCONN_IS_UDP(nc) ((nc)->type == LWESP_NETCONN_TYPE_UDP)
59#endif /* LWESP_CFG_IPV6 */
60
61/**
62 * \brief Sequential API structure
63 */
64typedef struct lwesp_netconn {
65 struct lwesp_netconn* next; /*!< Linked list entry */
66
67 lwesp_netconn_type_t type; /*!< Netconn type */
68 lwesp_port_t listen_port; /*!< Port on which we are listening */
69
70 size_t rcv_packets; /*!< Number of received packets so far on this connection */
71 lwesp_conn_p conn; /*!< Pointer to actual connection */
72 uint16_t conn_val_id; /*!< Connection validation ID that changes between every connection active/closed operation */
73
74 lwesp_sys_mbox_t mbox_accept; /*!< List of active connections waiting to be processed */
75 lwesp_sys_mbox_t mbox_receive; /*!< Message queue for receive mbox */
76 size_t mbox_receive_entries; /*!< Number of entries written to receive mbox */
77
78 lwesp_linbuff_t buff; /*!< Linear buffer structure */
79 uint16_t conn_timeout; /*!< Connection timeout in units of seconds when
80 netconn is in server (listen) mode.
81 Connection will be automatically closed if there is no
82 data exchange in time. Set to `0` when timeout feature is disabled. */
83
84#if LWESP_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__
85 uint32_t rcv_timeout; /*!< Receive timeout in unit of milliseconds */
86#endif
87} lwesp_netconn_t;
88
89static uint8_t recv_closed = 0xFF, recv_not_present = 0xFF;
90static lwesp_netconn_t* listen_api; /*!< Main connection in listening mode */
91static lwesp_netconn_t* netconn_list; /*!< Linked list of netconn entries */
92
93/**
94 * \brief Flush all mboxes and clear possible used memories
95 * \param[in] nc: Pointer to netconn to flush
96 * \param[in] protect: Set to 1 to protect against multi-thread access
97 */
98static void
99flush_mboxes(lwesp_netconn_t* nc, uint8_t protect) {
100 lwesp_pbuf_p pbuf;
101 lwesp_netconn_t* new_nc;
102 if (protect) {
103 lwesp_core_lock();
104 }
105 if (lwesp_sys_mbox_isvalid(&nc->mbox_receive)) {
106 while (lwesp_sys_mbox_getnow(&nc->mbox_receive, (void**)&pbuf)) {
107 if (nc->mbox_receive_entries > 0) {
108 --nc->mbox_receive_entries;
109 }
110 if (pbuf != NULL && (uint8_t*)pbuf != (uint8_t*)&recv_closed) {
111 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING,
112 "[LWESP NETCONN] flush mboxes. Clearing pbuf 0x%p\r\n", (void*)pbuf);
113 lwesp_pbuf_free_s(&pbuf); /* Free received data buffers */
114 }
115 }
116 lwesp_sys_mbox_delete(&nc->mbox_receive); /* Delete message queue */
117 lwesp_sys_mbox_invalid(&nc->mbox_receive); /* Invalid handle */
118 }
119 if (lwesp_sys_mbox_isvalid(&nc->mbox_accept)) {
120 while (lwesp_sys_mbox_getnow(&nc->mbox_accept, (void**)&new_nc)) {
121 if (new_nc != NULL && (uint8_t*)new_nc != (uint8_t*)&recv_closed
122 && (uint8_t*)new_nc != (uint8_t*)&recv_not_present) {
123 lwesp_netconn_close(new_nc); /* Close netconn connection */
124 }
125 }
126 lwesp_sys_mbox_delete(&nc->mbox_accept); /* Delete message queue */
127 lwesp_sys_mbox_invalid(&nc->mbox_accept); /* Invalid handle */
128 }
129 if (protect) {
130 lwesp_core_unlock();
131 }
132}
133
134/**
135 * \brief Callback function for every server connection
136 * \param[in] evt: Pointer to callback structure
137 * \return Member of \ref lwespr_t enumeration
138 */
139static lwespr_t
140netconn_evt(lwesp_evt_t* evt) {
141 lwesp_conn_p conn;
142 lwesp_netconn_t* nc = NULL;
143 uint8_t close = 0;
144
145 conn = lwesp_conn_get_from_evt(evt); /* Get connection from event */
146 switch (lwesp_evt_get_type(evt)) {
147 /*
148 * A new connection has been active
149 * and should be handled by netconn API
150 */
151 case LWESP_EVT_CONN_ACTIVE: { /* A new connection active is active */
152 if (lwesp_conn_is_client(conn)) { /* Was connection started by us? */
153 nc = lwesp_conn_get_arg(conn); /* Argument should be already set */
154 if (nc != NULL) {
155 nc->conn = conn; /* Save actual connection */
156 nc->conn_val_id = conn->val_id; /* Get value ID */
157 } else {
158 close = 1; /* Close this connection, invalid netconn */
159 }
160
161 /* Is the connection server type and we have known listening API? */
162 } else if (lwesp_conn_is_server(conn) && listen_api != NULL) {
163 /*
164 * Create a new netconn structure
165 * and set it as connection argument.
166 */
167 nc = lwesp_netconn_new(LWESP_NETCONN_TYPE_TCP); /* Create new API */
168 LWESP_DEBUGW(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING, nc == NULL,
169 "[LWESP NETCONN] Cannot create new structure for incoming server connection!\r\n");
170
171 if (nc != NULL) {
172 nc->conn = conn; /* Set connection handle */
173 nc->conn_val_id = conn->val_id;
174 lwesp_conn_set_arg(conn, nc); /* Set argument for connection */
175
176 /*
177 * In case there is no listening connection,
178 * simply close the connection
179 */
180 if (!lwesp_sys_mbox_isvalid(&listen_api->mbox_accept)
181 || !lwesp_sys_mbox_putnow(&listen_api->mbox_accept, nc)) {
182 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING,
183 "[LWESP NETCONN] Accept MBOX is invalid or it cannot insert new nc!\r\n");
184 close = 1;
185 }
186 } else {
187 close = 1;
188 }
189 } else {
190 LWESP_DEBUGW(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING, listen_api == NULL,
191 "[LWESP NETCONN] Closing connection as there is no listening API in netconn!\r\n");
192 close = 1; /* Close the connection at this point */
193 }
194
195 /* Decide if some events want to close the connection */
196 if (close) {
197 if (nc != NULL) {
198 lwesp_conn_set_arg(conn, NULL); /* Reset argument */
199 lwesp_netconn_delete(nc); /* Free memory for API */
200 }
201 lwesp_conn_close(conn, 0); /* Close the connection */
202 close = 0;
203 }
204 break;
205 }
206
207 /*
208 * We have a new data received which
209 * should have netconn structure as argument
210 */
211 case LWESP_EVT_CONN_RECV: {
212 lwesp_pbuf_p pbuf;
213
214 nc = lwesp_conn_get_arg(conn); /* Get API from connection */
215 pbuf = lwesp_evt_conn_recv_get_buff(evt); /* Get received buff */
216
217#if !LWESP_CFG_CONN_MANUAL_TCP_RECEIVE
218 lwesp_conn_recved(conn, pbuf); /* Notify stack about received data */
219#endif /* !LWESP_CFG_CONN_MANUAL_TCP_RECEIVE */
220
221 lwesp_pbuf_ref(pbuf); /* Increase reference counter */
222 LWESP_DEBUGW(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE, nc == NULL,
223 "[LWESP NETCONN] Data receive -> netconn is NULL!\r\n");
224 if (nc != NULL) {
225 LWESP_DEBUGW(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE, nc->conn_val_id != conn->val_id,
226 "[LWESP NETCONN] Connection validation ID does not match connection val_id!\r\n");
227 LWESP_DEBUGW(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE, !lwesp_sys_mbox_isvalid(&nc->mbox_receive),
228 "[LWESP NETCONN] Receive mbox is not valid!\r\n");
229 }
230 if (nc == NULL || nc->conn_val_id != conn->val_id || !lwesp_sys_mbox_isvalid(&nc->mbox_receive)
231 || !lwesp_sys_mbox_putnow(&nc->mbox_receive, pbuf)) {
232 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN,
233 "[LWESP NETCONN] Could not put receive packet. Ignoring more data for receive!\r\n");
234 lwesp_pbuf_free_s(&pbuf); /* Free pbuf */
235 return lwespOKIGNOREMORE; /* Return OK to free the memory and ignore further data */
236 }
237 ++nc->mbox_receive_entries; /* Increase number of packets in receive mbox */
238#if LWESP_CFG_CONN_MANUAL_TCP_RECEIVE
239 /* Check against 1 less to still allow potential close event to be written to queue */
240 if (nc->mbox_receive_entries >= (LWESP_CFG_NETCONN_RECEIVE_QUEUE_LEN - 1)) {
241 conn->status.f.receive_blocked = 1; /* Block reading more data */
242 }
243#endif /* LWESP_CFG_CONN_MANUAL_TCP_RECEIVE */
244
245 ++nc->rcv_packets; /* Increase number of packets received */
246 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE,
247 "[LWESP NETCONN] Received pbuf contains %d bytes. Handle written to receive mbox\r\n",
248 (int)lwesp_pbuf_length(pbuf, 0));
249 break;
250 }
251
252 /* Connection was just closed */
253 case LWESP_EVT_CONN_CLOSE: {
254 nc = lwesp_conn_get_arg(conn); /* Get API from connection */
255
256 /*
257 * In case we have a netconn available,
258 * simply write pointer to received variable to indicate closed state
259 */
260 if (nc != NULL && nc->conn_val_id == conn->val_id && lwesp_sys_mbox_isvalid(&nc->mbox_receive)) {
261 if (lwesp_sys_mbox_putnow(&nc->mbox_receive, (void*)&recv_closed)) {
262 ++nc->mbox_receive_entries;
263 }
264 }
265 break;
266 }
267 default: return lwespERR;
268 }
269 return lwespOK;
270}
271
272/**
273 * \brief Global event callback function
274 * \param[in] evt: Callback information and data
275 * \return \ref lwespOK on success, member of \ref lwespr_t otherwise
276 */
277static lwespr_t
278lwesp_evt(lwesp_evt_t* evt) {
279 switch (lwesp_evt_get_type(evt)) {
280 case LWESP_EVT_WIFI_DISCONNECTED: { /* Wifi disconnected event */
281 if (listen_api != NULL) { /* Check if listen API active */
282 lwesp_sys_mbox_putnow(&listen_api->mbox_accept, &recv_closed);
283 }
284 break;
285 }
286 case LWESP_EVT_DEVICE_PRESENT: { /* Device present event */
287 if (listen_api != NULL && !lwesp_device_is_present()) { /* Check if device present */
288 lwesp_sys_mbox_putnow(&listen_api->mbox_accept, &recv_not_present);
289 }
290 }
291 default: break;
292 }
293 return lwespOK;
294}
295
296/**
297 * \brief Create new netconn connection
298 * \param[in] type: Netconn connection type
299 * \return New netconn connection on success, `NULL` otherwise
300 */
301lwesp_netconn_p
302lwesp_netconn_new(lwesp_netconn_type_t type) {
303 lwesp_netconn_t* a;
304 static uint8_t first = 1;
305
306 /* Register only once! */
307 lwesp_core_lock();
308 if (first) {
309 first = 0;
310 lwesp_evt_register(lwesp_evt); /* Register global event function */
311 }
312 lwesp_core_unlock();
313 a = lwesp_mem_calloc(1, sizeof(*a)); /* Allocate memory for core object */
314 if (a != NULL) {
315 a->type = type; /* Save netconn type */
316 a->conn_timeout = 0; /* Default connection timeout */
317 if (!lwesp_sys_mbox_create(&a->mbox_accept, LWESP_CFG_NETCONN_ACCEPT_QUEUE_LEN)) {
318 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_DANGER,
319 "[LWESP NETCONN] Cannot create accept MBOX\r\n");
320 goto free_ret;
321 }
322 if (!lwesp_sys_mbox_create(&a->mbox_receive, LWESP_CFG_NETCONN_RECEIVE_QUEUE_LEN)) {
323 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_DANGER,
324 "[LWESP NETCONN] Cannot create receive MBOX\r\n");
325 goto free_ret;
326 }
327 lwesp_core_lock();
328 a->next = netconn_list; /* Add it to beginning of the list */
329 netconn_list = a;
330 lwesp_core_unlock();
331 }
332 return a;
333free_ret:
334 if (lwesp_sys_mbox_isvalid(&a->mbox_accept)) {
335 lwesp_sys_mbox_delete(&a->mbox_accept);
336 lwesp_sys_mbox_invalid(&a->mbox_accept);
337 }
338 if (lwesp_sys_mbox_isvalid(&a->mbox_receive)) {
339 lwesp_sys_mbox_delete(&a->mbox_receive);
340 lwesp_sys_mbox_invalid(&a->mbox_receive);
341 }
342 if (a != NULL) {
343 lwesp_mem_free_s((void**)&a);
344 }
345 return NULL;
346}
347
348/**
349 * \brief Delete netconn connection
350 * \param[in] nc: Netconn handle
351 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
352 */
353lwespr_t
354lwesp_netconn_delete(lwesp_netconn_p nc) {
355 LWESP_ASSERT(nc != NULL);
356
357 lwesp_core_lock();
358 if (nc->conn != NULL) {
359 /* No NC for any incoming connections or anything else... */
360 lwesp_conn_set_arg(nc->conn, NULL);
361 }
362 flush_mboxes(nc, 0); /* Clear mboxes */
363
364 /* Stop listening on netconn */
365 if (nc == listen_api) {
366 listen_api = NULL;
367 lwesp_core_unlock();
368 lwesp_set_server(0, nc->listen_port, 0, 0, NULL, NULL, NULL, 1);
369 lwesp_core_lock();
370 }
371
372 /* Remove netconn from linkedlist */
373 if (nc == netconn_list) {
374 netconn_list = netconn_list->next; /* Remove first from linked list */
375 } else if (netconn_list != NULL) {
376 lwesp_netconn_p tmp, prev;
377 /* Find element on the list */
378 for (prev = netconn_list, tmp = netconn_list->next; tmp != NULL; prev = tmp, tmp = tmp->next) {
379 if (nc == tmp) {
380 prev->next = tmp->next; /* Remove tmp from linked list */
381 break;
382 }
383 }
384 }
385 if (nc->conn != NULL) {
386 /*
387 * First delete the connection argument,
388 * then close the connection.
389 */
390 if (lwesp_conn_is_active(nc->conn)) {
391 lwesp_conn_close(nc->conn, 1);
392 }
393 nc->conn = NULL;
394 }
395 lwesp_core_unlock();
396
397 lwesp_mem_free_s((void**)&nc);
398 return lwespOK;
399}
400
401/**
402 * \brief Connect to server as client
403 * \param[in] nc: Netconn handle
404 * \param[in] host: Pointer to host, such as domain name or IP address in string format
405 * \param[in] port: Target port to use
406 * \return \ref lwespOK if successfully connected, member of \ref lwespr_t otherwise
407 */
408lwespr_t
409lwesp_netconn_connect(lwesp_netconn_p nc, const char* host, lwesp_port_t port) {
410 lwespr_t res;
411
412 LWESP_ASSERT(nc != NULL);
413 LWESP_ASSERT(host != NULL);
414 LWESP_ASSERT(port > 0);
415
416 /*
417 * Start a new connection as client and:
418 *
419 * - Set current netconn structure as argument
420 * - Set netconn callback function for connection management
421 * - Start connection in blocking mode
422 */
423 res = lwesp_conn_start(NULL, (lwesp_conn_type_t)nc->type, host, port, nc, netconn_evt, 1);
424 return res;
425}
426
427/**
428 * \brief Connect to server as client, allow keep-alive option
429 * \param[in] nc: Netconn handle
430 * \param[in] host: Pointer to host, such as domain name or IP address in string format
431 * \param[in] port: Target port to use
432 * \param[in] keep_alive: Keep alive period seconds
433 * \param[in] local_ip: Local ip in connected command
434 * \param[in] local_port: Local port address
435 * \param[in] mode: UDP mode
436 * \return \ref lwespOK if successfully connected, member of \ref lwespr_t otherwise
437 */
438lwespr_t
439lwesp_netconn_connect_ex(lwesp_netconn_p nc, const char* host, lwesp_port_t port, uint16_t keep_alive,
440 const char* local_ip, lwesp_port_t local_port, uint8_t mode) {
441 lwesp_conn_start_t cs = {0};
442 lwespr_t res;
443
444 LWESP_ASSERT(nc != NULL);
445 LWESP_ASSERT(host != NULL);
446 LWESP_ASSERT(port > 0);
447
448 /*
449 * Start a new connection as client and:
450 *
451 * - Set current netconn structure as argument
452 * - Set netconn callback function for connection management
453 * - Start connection in blocking mode
454 */
455 cs.type = (lwesp_conn_type_t)nc->type;
456 cs.remote_host = host;
457 cs.remote_port = port;
458 cs.local_ip = local_ip;
459 if (NETCONN_IS_TCP(nc) || NETCONN_IS_SSL(nc)) {
460 cs.ext.tcp_ssl.keep_alive = keep_alive;
461 } else {
462 cs.ext.udp.local_port = local_port;
463 cs.ext.udp.mode = mode;
464 }
465 res = lwesp_conn_startex(NULL, &cs, nc, netconn_evt, 1);
466 return res;
467}
468
469/**
470 * \brief Bind a connection to specific port, can be only used for server connections
471 * \param[in] nc: Netconn handle
472 * \param[in] port: Port used to bind a connection to
473 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
474 */
475lwespr_t
476lwesp_netconn_bind(lwesp_netconn_p nc, lwesp_port_t port) {
477 lwespr_t res = lwespOK;
478
479 LWESP_ASSERT(nc != NULL);
480
481 /*
482 * Protection is not needed as it is expected
483 * that this function is called only from single
484 * thread for single netconn connection,
485 * thus it is considered reentrant
486 */
487
488 nc->listen_port = port;
489
490 return res;
491}
492
493/**
494 * \brief Set timeout value in units of seconds when connection is in listening mode
495 * If new connection is accepted, it will be automatically closed after `seconds` elapsed
496 * without any data exchange.
497 * \note Call this function before you put connection to listen mode with \ref lwesp_netconn_listen
498 * \param[in] nc: Netconn handle used for listen mode
499 * \param[in] timeout: Time in units of seconds. Set to `0` to disable timeout feature
500 * \return \ref lwespOK on success, member of \ref lwespr_t otherwise
501 */
502lwespr_t
503lwesp_netconn_set_listen_conn_timeout(lwesp_netconn_p nc, uint16_t timeout) {
504 lwespr_t res = lwespOK;
505 LWESP_ASSERT(nc != NULL);
506
507 /*
508 * Protection is not needed as it is expected
509 * that this function is called only from single
510 * thread for single netconn connection,
511 * thus it is reentrant in this case
512 */
513
514 nc->conn_timeout = timeout;
515
516 return res;
517}
518
519/**
520 * \brief Listen on previously binded connection
521 * \param[in] nc: Netconn handle used to listen for new connections
522 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
523 */
524lwespr_t
525lwesp_netconn_listen(lwesp_netconn_p nc) {
526 return lwesp_netconn_listen_with_max_conn(nc, LWESP_CFG_MAX_CONNS);
527}
528
529/**
530 * \brief Listen on previously binded connection with max allowed connections at a time
531 * \param[in] nc: Netconn handle used to listen for new connections
532 * \param[in] max_connections: Maximal number of connections server can accept at a time
533 * This parameter may not be larger than \ref LWESP_CFG_MAX_CONNS
534 * \return \ref lwespOK on success, member of \ref lwespr_t otherwise
535 */
536lwespr_t
537lwesp_netconn_listen_with_max_conn(lwesp_netconn_p nc, uint16_t max_connections) {
538 lwespr_t res;
539
540 LWESP_ASSERT(nc != NULL);
541 LWESP_ASSERT(NETCONN_IS_TCP(nc));
542
543 /* Enable server on port and set default netconn callback */
544 if ((res = lwesp_set_server(1, nc->listen_port, LWESP_U16(LWESP_MIN(max_connections, LWESP_CFG_MAX_CONNS)),
545 nc->conn_timeout, netconn_evt, NULL, NULL, 1))
546 == lwespOK) {
547 lwesp_core_lock();
548 listen_api = nc; /* Set current main API in listening state */
549 lwesp_core_unlock();
550 }
551 return res;
552}
553
554/**
555 * \brief Accept a new connection
556 * \param[in] nc: Netconn handle used as base connection to accept new clients
557 * \param[out] client: Pointer to netconn handle to save new connection to
558 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
559 */
560lwespr_t
561lwesp_netconn_accept(lwesp_netconn_p nc, lwesp_netconn_p* client) {
562 lwesp_netconn_t* tmp;
563 uint32_t time;
564
565 LWESP_ASSERT(nc != NULL);
566 LWESP_ASSERT(client != NULL);
567 LWESP_ASSERT(NETCONN_IS_TCP(nc));
568 LWESP_ASSERT(nc == listen_api);
569
570 *client = NULL;
571 time = lwesp_sys_mbox_get(&nc->mbox_accept, (void**)&tmp, 0);
572 if (time == LWESP_SYS_TIMEOUT) {
573 return lwespTIMEOUT;
574 }
575 if ((uint8_t*)tmp == (uint8_t*)&recv_closed) {
576 lwesp_core_lock();
577 listen_api = NULL; /* Disable listening at this point */
578 lwesp_core_unlock();
579 return lwespERRWIFINOTCONNECTED; /* Wifi disconnected */
580 } else if ((uint8_t*)tmp == (uint8_t*)&recv_not_present) {
581 lwesp_core_lock();
582 listen_api = NULL; /* Disable listening at this point */
583 lwesp_core_unlock();
584 return lwespERRNODEVICE; /* Device not present */
585 }
586 *client = tmp; /* Set new pointer */
587 return lwespOK; /* We have a new connection */
588}
589
590/**
591 * \brief Write data to connection output buffers
592 * \note This function may only be used on TCP or SSL connections
593 * \param[in] nc: Netconn handle used to write data to
594 * \param[in] data: Pointer to data to write
595 * \param[in] btw: Number of bytes to write
596 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
597 */
598lwespr_t
599lwesp_netconn_write(lwesp_netconn_p nc, const void* data, size_t btw) {
600 size_t len, sent;
601 const uint8_t* d = data;
602 lwespr_t res;
603
604 LWESP_ASSERT(nc != NULL);
605 LWESP_ASSERT(NETCONN_IS_TCP(nc) || NETCONN_IS_SSL(nc));
606 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
607
608 /*
609 * Several steps are done in write process
610 *
611 * 1. Check if buffer is set and check if there is something to write to it.
612 * 1. In case buffer will be full after copy, send it and free memory.
613 * 2. Check how many bytes we can write directly without need to copy
614 * 3. Try to allocate a new buffer and copy remaining input data to it
615 * 4. In case buffer allocation fails, send data directly (may have impact on speed and effectivenes)
616 */
617
618 /* Step 1 */
619 if (nc->buff.buff != NULL) { /* Is there a write buffer ready to accept more data? */
620 len = LWESP_MIN(nc->buff.len - nc->buff.ptr, btw); /* Get number of bytes we can write to buffer */
621 if (len > 0) {
622 LWESP_MEMCPY(&nc->buff.buff[nc->buff.ptr], data, len); /* Copy memory to temporary write buffer */
623 d += len;
624 nc->buff.ptr += len;
625 btw -= len;
626 }
627
628 /* Step 1.1 */
629 if (nc->buff.ptr == nc->buff.len) {
630 res = lwesp_conn_send(nc->conn, nc->buff.buff, nc->buff.len, &sent, 1);
631
632 lwesp_mem_free_s((void**)&nc->buff.buff);
633 if (res != lwespOK) {
634 return res;
635 }
636 } else {
637 return lwespOK; /* Buffer is not full yet */
638 }
639 }
640
641 /* Step 2 */
642 if (btw >= LWESP_CFG_CONN_MAX_DATA_LEN) {
643 size_t rem;
644 rem = btw % LWESP_CFG_CONN_MAX_DATA_LEN; /* Get remaining bytes for max data length */
645 res = lwesp_conn_send(nc->conn, d, btw - rem, &sent, 1); /* Write data directly */
646 if (res != lwespOK) {
647 return res;
648 }
649 d += sent; /* Advance in data pointer */
650 btw -= sent; /* Decrease remaining data to send */
651 }
652
653 if (btw == 0) { /* Sent everything? */
654 return lwespOK;
655 }
656
657 /* Step 3 */
658 if (nc->buff.buff == NULL) { /* Check if we should allocate a new buffer */
659 nc->buff.buff = lwesp_mem_malloc(sizeof(*nc->buff.buff) * LWESP_CFG_CONN_MAX_DATA_LEN);
660 nc->buff.len = LWESP_CFG_CONN_MAX_DATA_LEN; /* Save buffer length */
661 nc->buff.ptr = 0; /* Save buffer pointer */
662 }
663
664 /* Step 4 */
665 if (nc->buff.buff != NULL) { /* Memory available? */
666 LWESP_MEMCPY(&nc->buff.buff[nc->buff.ptr], d, btw); /* Copy data to buffer */
667 nc->buff.ptr += btw;
668 } else { /* Still no memory available? */
669 return lwesp_conn_send(nc->conn, data, btw, NULL, 1); /* Simply send directly blocking */
670 }
671 return lwespOK;
672}
673
674/**
675 * \brief Extended version of \ref lwesp_netconn_write with additional
676 * option to set custom flags.
677 *
678 * \note It is recommended to use this for full features support
679 *
680 * \param[in] nc: Netconn handle used to write data to
681 * \param[in] data: Pointer to data to write
682 * \param[in] btw: Number of bytes to write
683 * \param flags: Bitwise-ORed set of flags for netconn.
684 * Flags start with \ref LWESP_NETCONN_FLAG_xxx
685 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
686 */
687lwespr_t
688lwesp_netconn_write_ex(lwesp_netconn_p nc, const void* data, size_t btw, uint16_t flags) {
689 lwespr_t res = lwesp_netconn_write(nc, data, btw);
690 if (res == lwespOK) {
691 if (flags & LWESP_NETCONN_FLAG_FLUSH) {
692 res = lwesp_netconn_flush(nc);
693 }
694 }
695 return res;
696}
697
698/**
699 * \brief Flush buffered data on netconn TCP/SSL connection
700 * \note This function may only be used on TCP/SSL connection
701 * \param[in] nc: Netconn handle to flush data
702 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
703 */
704lwespr_t
705lwesp_netconn_flush(lwesp_netconn_p nc) {
706 LWESP_ASSERT(nc != NULL);
707 LWESP_ASSERT(NETCONN_IS_TCP(nc) || NETCONN_IS_SSL(nc));
708 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
709
710 /*
711 * In case we have data in write buffer,
712 * flush them out to network
713 */
714 if (nc->buff.buff != NULL) { /* Check remaining data */
715 if (nc->buff.ptr > 0) { /* Do we have data in current buffer? */
716 lwesp_conn_send(nc->conn, nc->buff.buff, nc->buff.ptr, NULL, 1); /* Send data */
717 }
718 lwesp_mem_free_s((void**)&nc->buff.buff);
719 }
720 return lwespOK;
721}
722
723/**
724 * \brief Send data on UDP connection to default IP and port
725 * \param[in] nc: Netconn handle used to send
726 * \param[in] data: Pointer to data to write
727 * \param[in] btw: Number of bytes to write
728 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
729 */
730lwespr_t
731lwesp_netconn_send(lwesp_netconn_p nc, const void* data, size_t btw) {
732 LWESP_ASSERT(nc != NULL);
733 LWESP_ASSERT(nc->type == LWESP_NETCONN_TYPE_UDP);
734 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
735
736 return lwesp_conn_send(nc->conn, data, btw, NULL, 1);
737}
738
739/**
740 * \brief Send data on UDP connection to specific IP and port
741 * \note Use this function in case of UDP type netconn
742 * \param[in] nc: Netconn handle used to send
743 * \param[in] ip: Pointer to IP address
744 * \param[in] port: Port number used to send data
745 * \param[in] data: Pointer to data to write
746 * \param[in] btw: Number of bytes to write
747 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
748 */
749lwespr_t
750lwesp_netconn_sendto(lwesp_netconn_p nc, const lwesp_ip_t* ip, lwesp_port_t port, const void* data, size_t btw) {
751 LWESP_ASSERT(nc != NULL);
752 LWESP_ASSERT(nc->type == LWESP_NETCONN_TYPE_UDP);
753 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
754
755 return lwesp_conn_sendto(nc->conn, ip, port, data, btw, NULL, 1);
756}
757
758/**
759 * \brief Receive data from connection
760 * \param[in] nc: Netconn handle used to receive from
761 * \param[in] pbuf: Pointer to pointer to save new receive buffer to.
762 * When function returns, user must check for valid pbuf value `pbuf != NULL`
763 * \return \ref lwespOK when new data ready
764 * \return \ref lwespCLOSED when connection closed by remote side
765 * \return \ref lwespTIMEOUT when receive timeout occurs
766 * \return Any other member of \ref lwespr_t otherwise
767 */
768lwespr_t
769lwesp_netconn_receive(lwesp_netconn_p nc, lwesp_pbuf_p* pbuf) {
770 LWESP_ASSERT(nc != NULL);
771 LWESP_ASSERT(pbuf != NULL);
772
773 *pbuf = NULL;
774#if LWESP_CFG_NETCONN_RECEIVE_TIMEOUT
775 /*
776 * Wait for new received data for up to specific timeout
777 * or throw error for timeout notification
778 */
779 if (nc->rcv_timeout == LWESP_NETCONN_RECEIVE_NO_WAIT) {
780 if (!lwesp_sys_mbox_getnow(&nc->mbox_receive, (void**)pbuf)) {
781 return lwespTIMEOUT;
782 }
783 } else if (lwesp_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, nc->rcv_timeout) == LWESP_SYS_TIMEOUT) {
784 return lwespTIMEOUT;
785 }
786#else /* LWESP_CFG_NETCONN_RECEIVE_TIMEOUT */
787 /* Forever wait for new receive packet */
788 lwesp_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, 0);
789#endif /* !LWESP_CFG_NETCONN_RECEIVE_TIMEOUT */
790
791 lwesp_core_lock();
792 if (nc->mbox_receive_entries > 0) {
793 --nc->mbox_receive_entries;
794 }
795 lwesp_core_unlock();
796
797 /* Check if connection closed */
798 if ((uint8_t*)(*pbuf) == (uint8_t*)&recv_closed) {
799 *pbuf = NULL; /* Reset pbuf */
800 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING,
801 "[LWESP NETCONN] netcon_receive: Got object handle for close event\r\n");
802 return lwespCLOSED;
803 }
804#if LWESP_CFG_CONN_MANUAL_TCP_RECEIVE
805 else {
806 lwesp_core_lock();
807 nc->conn->status.f.receive_blocked = 0; /* Resume reading more data */
808 lwesp_conn_recved(nc->conn, *pbuf); /* Notify stack about received data */
809 lwesp_core_unlock();
810 }
811#endif /* LWESP_CFG_CONN_MANUAL_TCP_RECEIVE */
812 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING,
813 "[LWESP NETCONN] netcon_receive: Got pbuf object handle at 0x%p. Len/Tot_len: %u/%u\r\n", (void*)*pbuf,
814 (unsigned)lwesp_pbuf_length(*pbuf, 0), (unsigned)lwesp_pbuf_length(*pbuf, 1));
815 return lwespOK; /* We have data available */
816}
817
818/**
819 * \brief Close a netconn connection
820 * \param[in] nc: Netconn handle to close
821 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
822 */
823lwespr_t
824lwesp_netconn_close(lwesp_netconn_p nc) {
825 lwesp_conn_p conn;
826
827 LWESP_ASSERT(nc != NULL);
828 LWESP_ASSERT(nc->conn != NULL);
829 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
830
831 lwesp_netconn_flush(nc); /* Flush data and ignore result */
832 conn = nc->conn;
833 nc->conn = NULL;
834
835 lwesp_conn_set_arg(conn, NULL); /* Reset argument */
836 lwesp_conn_close(conn, 1); /* Close the connection */
837 flush_mboxes(nc, 1); /* Flush message queues */
838 return lwespOK;
839}
840
841/**
842 * \brief Get connection number used for netconn
843 * \param[in] nc: Netconn handle
844 * \return `-1` on failure, connection number between `0` and \ref LWESP_CFG_MAX_CONNS otherwise
845 */
846int8_t
847lwesp_netconn_get_connnum(lwesp_netconn_p nc) {
848 if (nc != NULL && nc->conn != NULL) {
849 return lwesp_conn_getnum(nc->conn);
850 }
851 return -1;
852}
853
854#if LWESP_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__
855
856/**
857 * \brief Set timeout value for receiving data.
858 *
859 * When enabled, \ref lwesp_netconn_receive will only block for up to
860 * \e timeout value and will return if no new data within this time
861 *
862 * \param[in] nc: Netconn handle
863 * \param[in] timeout: Timeout in units of milliseconds.
864 * Set to `0` to disable timeout feature. Function blocks until data receive or connection closed
865 * Set to `> 0` to set maximum milliseconds to wait before timeout
866 * Set to \ref LWESP_NETCONN_RECEIVE_NO_WAIT to enable non-blocking receive
867 */
868void
869lwesp_netconn_set_receive_timeout(lwesp_netconn_p nc, uint32_t timeout) {
870 nc->rcv_timeout = timeout;
871}
872
873/**
874 * \brief Get netconn receive timeout value
875 * \param[in] nc: Netconn handle
876 * \return Timeout in units of milliseconds.
877 * If value is `0`, timeout is disabled (wait forever)
878 */
879uint32_t
880lwesp_netconn_get_receive_timeout(lwesp_netconn_p nc) {
881 return nc->rcv_timeout;
882}
883
884#endif /* LWESP_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__ */
885
886/**
887 * \brief Get netconn connection handle
888 * \param[in] nc: Netconn handle
889 * \return ESP connection handle
890 */
891lwesp_conn_p
892lwesp_netconn_get_conn(lwesp_netconn_p nc) {
893 return nc->conn;
894}
895
896/**
897 * \brief Get netconn connection type
898 * \param[in] nc: Netconn handle
899 * \return ESP connection type
900 */
901lwesp_netconn_type_t
902lwesp_netconn_get_type(lwesp_netconn_p nc) {
903 return nc->type;
904}
905
906#endif /* LWESP_CFG_NETCONN || __DOXYGEN__ */
Connection specific event
This events are subset of global event callback. They work exactly the same way as global, but only receive events related to connections.
Tip
Connection related events start with LWESP_EVT_CONN_*, such as LWESP_EVT_CONN_RECV.
Check Event management for list of all connection events.
Connection events callback function is set for 2 cases:
Each client (when application starts connection) sets event callback function when trying to connect with
lwesp_conn_start()functionApplication sets global event callback function when enabling server mode with
lwesp_set_server()function
1#include "client.h"
2#include "lwesp/lwesp.h"
3
4/* Host parameter */
5#define CONN_HOST "example.com"
6#define CONN_PORT 80
7
8static lwespr_t conn_callback_func(lwesp_evt_t* evt);
9
10/**
11 * \brief Request data for connection
12 */
13static const
14uint8_t req_data[] = ""
15 "GET / HTTP/1.1\r\n"
16 "Host: " CONN_HOST "\r\n"
17 "Connection: close\r\n"
18 "\r\n";
19
20/**
21 * \brief Start a new connection(s) as client
22 */
23void
24client_connect(void) {
25 lwespr_t res;
26
27 /* Start a new connection as client in non-blocking mode */
28 if ((res = lwesp_conn_start(NULL, LWESP_CONN_TYPE_TCP, "example.com", 80, NULL, conn_callback_func, 0)) == lwespOK) {
29 printf("Connection to " CONN_HOST " started...\r\n");
30 } else {
31 printf("Cannot start connection to " CONN_HOST "!\r\n");
32 }
33
34 /* Start 2 more */
35 lwesp_conn_start(NULL, LWESP_CONN_TYPE_TCP, CONN_HOST, CONN_PORT, NULL, conn_callback_func, 0);
36
37 /*
38 * An example of connection which should fail in connecting.
39 * When this is the case, \ref LWESP_EVT_CONN_ERROR event should be triggered
40 * in callback function processing
41 */
42 lwesp_conn_start(NULL, LWESP_CONN_TYPE_TCP, CONN_HOST, 10, NULL, conn_callback_func, 0);
43}
44
45/**
46 * \brief Event callback function for connection-only
47 * \param[in] evt: Event information with data
48 * \return \ref lwespOK on success, member of \ref lwespr_t otherwise
49 */
50static lwespr_t
51conn_callback_func(lwesp_evt_t* evt) {
52 lwesp_conn_p conn;
53 lwespr_t res;
54 uint8_t conn_num;
55
56 conn = lwesp_conn_get_from_evt(evt); /* Get connection handle from event */
57 if (conn == NULL) {
58 return lwespERR;
59 }
60 conn_num = lwesp_conn_getnum(conn); /* Get connection number for identification */
61 switch (lwesp_evt_get_type(evt)) {
62 case LWESP_EVT_CONN_ACTIVE: { /* Connection just active */
63 printf("Connection %d active!\r\n", (int)conn_num);
64 res = lwesp_conn_send(conn, req_data, sizeof(req_data) - 1, NULL, 0); /* Start sending data in non-blocking mode */
65 if (res == lwespOK) {
66 printf("Sending request data to server...\r\n");
67 } else {
68 printf("Cannot send request data to server. Closing connection manually...\r\n");
69 lwesp_conn_close(conn, 0); /* Close the connection */
70 }
71 break;
72 }
73 case LWESP_EVT_CONN_CLOSE: { /* Connection closed */
74 if (lwesp_evt_conn_close_is_forced(evt)) {
75 printf("Connection %d closed by client!\r\n", (int)conn_num);
76 } else {
77 printf("Connection %d closed by remote side!\r\n", (int)conn_num);
78 }
79 break;
80 }
81 case LWESP_EVT_CONN_SEND: { /* Data send event */
82 lwespr_t res = lwesp_evt_conn_send_get_result(evt);
83 if (res == lwespOK) {
84 printf("Data sent successfully on connection %d...waiting to receive data from remote side...\r\n", (int)conn_num);
85 } else {
86 printf("Error while sending data on connection %d!\r\n", (int)conn_num);
87 }
88 break;
89 }
90 case LWESP_EVT_CONN_RECV: { /* Data received from remote side */
91 lwesp_pbuf_p pbuf = lwesp_evt_conn_recv_get_buff(evt);
92 lwesp_conn_recved(conn, pbuf); /* Notify stack about received pbuf */
93 printf("Received %d bytes on connection %d..\r\n", (int)lwesp_pbuf_length(pbuf, 1), (int)conn_num);
94 break;
95 }
96 case LWESP_EVT_CONN_ERROR: { /* Error connecting to server */
97 const char* host = lwesp_evt_conn_error_get_host(evt);
98 lwesp_port_t port = lwesp_evt_conn_error_get_port(evt);
99 printf("Error connecting to %s:%d\r\n", host, (int)port);
100 break;
101 }
102 default:
103 break;
104 }
105 return lwespOK;
106}
API call event
API function call event function is special type of event and is linked to command execution. It is especially useful when dealing with non-blocking commands to understand when specific command execution finished and when next operation could start.
Every API function, which directly operates with AT command on physical device layer,
has optional 2 parameters for API call event:
Callback function, called when command finished
Custom user parameter for callback function
Below is an example code for DNS resolver. It uses custom API callback function with custom argument, used to distinguis domain name (when multiple domains are to be resolved).
1/*
2 * This snippet shows how to use ESP's DNS module to
3 * obtain IP address from domain name
4 */
5#include "dns.h"
6#include "lwesp/lwesp.h"
7
8/* Host to resolve */
9#define DNS_HOST1 "example.com"
10#define DNS_HOST2 "example.net"
11
12/**
13 * \brief Variable to hold result of DNS resolver
14 */
15static lwesp_ip_t ip;
16
17/**
18 * \brief Function to print actual resolved IP address
19 */
20static void
21prv_print_ip(void) {
22 if (0) {
23#if LWESP_CFG_IPV6
24 } else if (ip.type == LWESP_IPTYPE_V6) {
25 printf("IPv6: %04X:%04X:%04X:%04X:%04X:%04X:%04X:%04X\r\n",
26 (unsigned)ip.addr.ip6.addr[0], (unsigned)ip.addr.ip6.addr[1], (unsigned)ip.addr.ip6.addr[2],
27 (unsigned)ip.addr.ip6.addr[3], (unsigned)ip.addr.ip6.addr[4], (unsigned)ip.addr.ip6.addr[5],
28 (unsigned)ip.addr.ip6.addr[6], (unsigned)ip.addr.ip6.addr[7]);
29#endif /* LWESP_CFG_IPV6 */
30 } else {
31 printf("IPv4: %d.%d.%d.%d\r\n",
32 (int)ip.addr.ip4.addr[0], (int)ip.addr.ip4.addr[1], (int)ip.addr.ip4.addr[2], (int)ip.addr.ip4.addr[3]);
33 }
34}
35
36/**
37 * \brief Event callback function for API call,
38 * called when API command finished with execution
39 */
40static void
41prv_dns_resolve_evt(lwespr_t res, void* arg) {
42 LWESP_UNUSED(arg);
43 /* Check result of command */
44 if (res == lwespOK) {
45 /* Print actual resolved IP */
46 prv_print_ip();
47 }
48}
49
50/**
51 * \brief Start DNS resolver
52 */
53void
54dns_start(void) {
55 /* Use DNS protocol to get IP address of domain name */
56
57 /* Get IP with non-blocking mode */
58 if (lwesp_dns_gethostbyname(DNS_HOST2, &ip, prv_dns_resolve_evt, DNS_HOST2, 0) == lwespOK) {
59 printf("Request for DNS record for " DNS_HOST2 " has started\r\n");
60 } else {
61 printf("Could not start command for DNS\r\n");
62 }
63
64 /* Get IP with blocking mode */
65 if (lwesp_dns_gethostbyname(DNS_HOST1, &ip, prv_dns_resolve_evt, DNS_HOST1, 1) == lwespOK) {
66 /* Print actual resolved IP */
67 prv_print_ip();
68 } else {
69 printf("Could not retrieve IP address for " DNS_HOST1 "\r\n");
70 }
71}