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_PROCESS
is 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) 2023 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
80 uint16_t conn_timeout; /*!< Connection timeout in units of seconds when
81 netconn is in server (listen) mode.
82 Connection will be automatically closed if there is no
83 data exchange in time. Set to `0` when timeout feature is disabled. */
84
85#if LWESP_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__
86 uint32_t rcv_timeout; /*!< Receive timeout in unit of milliseconds */
87#endif
88} lwesp_netconn_t;
89
90static uint8_t recv_closed = 0xFF, recv_not_present = 0xFF;
91static lwesp_netconn_t* listen_api; /*!< Main connection in listening mode */
92static lwesp_netconn_t* netconn_list; /*!< Linked list of netconn entries */
93
94/**
95 * \brief Flush all mboxes and clear possible used memories
96 * \param[in] nc: Pointer to netconn to flush
97 * \param[in] protect: Set to 1 to protect against multi-thread access
98 */
99static void
100flush_mboxes(lwesp_netconn_t* nc, uint8_t protect) {
101 lwesp_pbuf_p pbuf;
102 lwesp_netconn_t* new_nc;
103 if (protect) {
104 lwesp_core_lock();
105 }
106 if (lwesp_sys_mbox_isvalid(&nc->mbox_receive)) {
107 while (lwesp_sys_mbox_getnow(&nc->mbox_receive, (void**)&pbuf)) {
108 if (nc->mbox_receive_entries > 0) {
109 --nc->mbox_receive_entries;
110 }
111 if (pbuf != NULL && (uint8_t*)pbuf != (uint8_t*)&recv_closed) {
112 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING,
113 "[LWESP NETCONN] flush mboxes. Clearing pbuf 0x%p\r\n", (void*)pbuf);
114 lwesp_pbuf_free_s(&pbuf); /* Free received data buffers */
115 }
116 }
117 lwesp_sys_mbox_delete(&nc->mbox_receive); /* Delete message queue */
118 lwesp_sys_mbox_invalid(&nc->mbox_receive); /* Invalid handle */
119 }
120 if (lwesp_sys_mbox_isvalid(&nc->mbox_accept)) {
121 while (lwesp_sys_mbox_getnow(&nc->mbox_accept, (void**)&new_nc)) {
122 if (new_nc != NULL && (uint8_t*)new_nc != (uint8_t*)&recv_closed
123 && (uint8_t*)new_nc != (uint8_t*)&recv_not_present) {
124 lwesp_netconn_close(new_nc); /* Close netconn connection */
125 }
126 }
127 lwesp_sys_mbox_delete(&nc->mbox_accept); /* Delete message queue */
128 lwesp_sys_mbox_invalid(&nc->mbox_accept); /* Invalid handle */
129 }
130 if (protect) {
131 lwesp_core_unlock();
132 }
133}
134
135/**
136 * \brief Callback function for every server connection
137 * \param[in] evt: Pointer to callback structure
138 * \return Member of \ref lwespr_t enumeration
139 */
140static lwespr_t
141netconn_evt(lwesp_evt_t* evt) {
142 lwesp_conn_p conn;
143 lwesp_netconn_t* nc = NULL;
144 uint8_t close = 0;
145
146 conn = lwesp_conn_get_from_evt(evt); /* Get connection from event */
147 switch (lwesp_evt_get_type(evt)) {
148 /*
149 * A new connection has been active
150 * and should be handled by netconn API
151 */
152 case LWESP_EVT_CONN_ACTIVE: { /* A new connection active is active */
153 if (lwesp_conn_is_client(conn)) { /* Was connection started by us? */
154 nc = lwesp_conn_get_arg(conn); /* Argument should be already set */
155 if (nc != NULL) {
156 nc->conn = conn; /* Save actual connection */
157 nc->conn_val_id = conn->val_id; /* Get value ID */
158 } else {
159 close = 1; /* Close this connection, invalid netconn */
160 }
161
162 /* Is the connection server type and we have known listening API? */
163 } else if (lwesp_conn_is_server(conn) && listen_api != NULL) {
164 /*
165 * Create a new netconn structure
166 * and set it as connection argument.
167 */
168 nc = lwesp_netconn_new(LWESP_NETCONN_TYPE_TCP); /* Create new API */
169 LWESP_DEBUGW(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING, nc == NULL,
170 "[LWESP NETCONN] Cannot create new structure for incoming server connection!\r\n");
171
172 if (nc != NULL) {
173 nc->conn = conn; /* Set connection handle */
174 nc->conn_val_id = conn->val_id;
175 lwesp_conn_set_arg(conn, nc); /* Set argument for connection */
176
177 /*
178 * In case there is no listening connection,
179 * simply close the connection
180 */
181 if (!lwesp_sys_mbox_isvalid(&listen_api->mbox_accept)
182 || !lwesp_sys_mbox_putnow(&listen_api->mbox_accept, nc)) {
183 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING,
184 "[LWESP NETCONN] Accept MBOX is invalid or it cannot insert new nc!\r\n");
185 close = 1;
186 }
187 } else {
188 close = 1;
189 }
190 } else {
191 LWESP_DEBUGW(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING, listen_api == NULL,
192 "[LWESP NETCONN] Closing connection as there is no listening API in netconn!\r\n");
193 close = 1; /* Close the connection at this point */
194 }
195
196 /* Decide if some events want to close the connection */
197 if (close) {
198 if (nc != NULL) {
199 lwesp_conn_set_arg(conn, NULL); /* Reset argument */
200 lwesp_netconn_delete(nc); /* Free memory for API */
201 }
202 lwesp_conn_close(conn, 0); /* Close the connection */
203 close = 0;
204 }
205 break;
206 }
207
208 /*
209 * We have a new data received which
210 * should have netconn structure as argument
211 */
212 case LWESP_EVT_CONN_RECV: {
213 lwesp_pbuf_p pbuf;
214
215 nc = lwesp_conn_get_arg(conn); /* Get API from connection */
216 pbuf = lwesp_evt_conn_recv_get_buff(evt); /* Get received buff */
217
218#if !LWESP_CFG_CONN_MANUAL_TCP_RECEIVE
219 lwesp_conn_recved(conn, pbuf); /* Notify stack about received data */
220#endif /* !LWESP_CFG_CONN_MANUAL_TCP_RECEIVE */
221
222 lwesp_pbuf_ref(pbuf); /* Increase reference counter */
223 LWESP_DEBUGW(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE, nc == NULL,
224 "[LWESP NETCONN] Data receive -> netconn is NULL!\r\n");
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 if (nc == NULL || nc->conn_val_id != conn->val_id || !lwesp_sys_mbox_isvalid(&nc->mbox_receive)
230 || !lwesp_sys_mbox_putnow(&nc->mbox_receive, pbuf)) {
231 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN,
232 "[LWESP NETCONN] Could not put receive packet. Ignoring more data for receive!\r\n");
233 lwesp_pbuf_free_s(&pbuf); /* Free pbuf */
234 return lwespOKIGNOREMORE; /* Return OK to free the memory and ignore further data */
235 }
236 ++nc->mbox_receive_entries; /* Increase number of packets in receive mbox */
237#if LWESP_CFG_CONN_MANUAL_TCP_RECEIVE
238 /* Check against 1 less to still allow potential close event to be written to queue */
239 if (nc->mbox_receive_entries >= (LWESP_CFG_NETCONN_RECEIVE_QUEUE_LEN - 1)) {
240 conn->status.f.receive_blocked = 1; /* Block reading more data */
241 }
242#endif /* LWESP_CFG_CONN_MANUAL_TCP_RECEIVE */
243
244 ++nc->rcv_packets; /* Increase number of packets received */
245 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE,
246 "[LWESP NETCONN] Received pbuf contains %d bytes. Handle written to receive mbox\r\n",
247 (int)lwesp_pbuf_length(pbuf, 0));
248 break;
249 }
250
251 /* Connection was just closed */
252 case LWESP_EVT_CONN_CLOSE: {
253 nc = lwesp_conn_get_arg(conn); /* Get API from connection */
254
255 /*
256 * In case we have a netconn available,
257 * simply write pointer to received variable to indicate closed state
258 */
259 if (nc != NULL && nc->conn_val_id == conn->val_id && lwesp_sys_mbox_isvalid(&nc->mbox_receive)) {
260 if (lwesp_sys_mbox_putnow(&nc->mbox_receive, (void*)&recv_closed)) {
261 ++nc->mbox_receive_entries;
262 }
263 }
264 break;
265 }
266 default: return lwespERR;
267 }
268 return lwespOK;
269}
270
271/**
272 * \brief Global event callback function
273 * \param[in] evt: Callback information and data
274 * \return \ref lwespOK on success, member of \ref lwespr_t otherwise
275 */
276static lwespr_t
277lwesp_evt(lwesp_evt_t* evt) {
278 switch (lwesp_evt_get_type(evt)) {
279 case LWESP_EVT_WIFI_DISCONNECTED: { /* Wifi disconnected event */
280 if (listen_api != NULL) { /* Check if listen API active */
281 lwesp_sys_mbox_putnow(&listen_api->mbox_accept, &recv_closed);
282 }
283 break;
284 }
285 case LWESP_EVT_DEVICE_PRESENT: { /* Device present event */
286 if (listen_api != NULL && !lwesp_device_is_present()) { /* Check if device present */
287 lwesp_sys_mbox_putnow(&listen_api->mbox_accept, &recv_not_present);
288 }
289 }
290 default: break;
291 }
292 return lwespOK;
293}
294
295/**
296 * \brief Create new netconn connection
297 * \param[in] type: Netconn connection type
298 * \return New netconn connection on success, `NULL` otherwise
299 */
300lwesp_netconn_p
301lwesp_netconn_new(lwesp_netconn_type_t type) {
302 lwesp_netconn_t* a;
303 static uint8_t first = 1;
304
305 /* Register only once! */
306 lwesp_core_lock();
307 if (first) {
308 first = 0;
309 lwesp_evt_register(lwesp_evt); /* Register global event function */
310 }
311 lwesp_core_unlock();
312 a = lwesp_mem_calloc(1, sizeof(*a)); /* Allocate memory for core object */
313 if (a != NULL) {
314 a->type = type; /* Save netconn type */
315 a->conn_timeout = 0; /* Default connection timeout */
316 if (!lwesp_sys_mbox_create(&a->mbox_accept, LWESP_CFG_NETCONN_ACCEPT_QUEUE_LEN)) {
317 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_DANGER,
318 "[LWESP NETCONN] Cannot create accept MBOX\r\n");
319 goto free_ret;
320 }
321 if (!lwesp_sys_mbox_create(&a->mbox_receive, LWESP_CFG_NETCONN_RECEIVE_QUEUE_LEN)) {
322 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_DANGER,
323 "[LWESP NETCONN] Cannot create receive MBOX\r\n");
324 goto free_ret;
325 }
326 lwesp_core_lock();
327 a->next = netconn_list; /* Add it to beginning of the list */
328 netconn_list = a;
329 lwesp_core_unlock();
330 }
331 return a;
332free_ret:
333 if (lwesp_sys_mbox_isvalid(&a->mbox_accept)) {
334 lwesp_sys_mbox_delete(&a->mbox_accept);
335 lwesp_sys_mbox_invalid(&a->mbox_accept);
336 }
337 if (lwesp_sys_mbox_isvalid(&a->mbox_receive)) {
338 lwesp_sys_mbox_delete(&a->mbox_receive);
339 lwesp_sys_mbox_invalid(&a->mbox_receive);
340 }
341 if (a != NULL) {
342 lwesp_mem_free_s((void**)&a);
343 }
344 return NULL;
345}
346
347/**
348 * \brief Delete netconn connection
349 * \param[in] nc: Netconn handle
350 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
351 */
352lwespr_t
353lwesp_netconn_delete(lwesp_netconn_p nc) {
354 LWESP_ASSERT(nc != NULL);
355
356 lwesp_core_lock();
357 if (nc->conn != NULL) {
358 /* No NC for any incoming connections or anything else... */
359 lwesp_conn_set_arg(nc->conn, NULL);
360 }
361 flush_mboxes(nc, 0); /* Clear mboxes */
362
363 /* Stop listening on netconn */
364 if (nc == listen_api) {
365 listen_api = NULL;
366 lwesp_core_unlock();
367 lwesp_set_server(0, nc->listen_port, 0, 0, NULL, NULL, NULL, 1);
368 lwesp_core_lock();
369 }
370
371 /* Remove netconn from linkedlist */
372 if (nc == netconn_list) {
373 netconn_list = netconn_list->next; /* Remove first from linked list */
374 } else if (netconn_list != NULL) {
375 lwesp_netconn_p tmp, prev;
376 /* Find element on the list */
377 for (prev = netconn_list, tmp = netconn_list->next; tmp != NULL; prev = tmp, tmp = tmp->next) {
378 if (nc == tmp) {
379 prev->next = tmp->next; /* Remove tmp from linked list */
380 break;
381 }
382 }
383 }
384 if (nc->conn != NULL) {
385 /*
386 * First delete the connection argument,
387 * then close the connection.
388 */
389 if (lwesp_conn_is_active(nc->conn)) {
390 lwesp_conn_close(nc->conn, 1);
391 }
392 nc->conn = NULL;
393 }
394 lwesp_core_unlock();
395
396 lwesp_mem_free_s((void**)&nc);
397 return lwespOK;
398}
399
400/**
401 * \brief Connect to server as client
402 * \param[in] nc: Netconn handle
403 * \param[in] host: Pointer to host, such as domain name or IP address in string format
404 * \param[in] port: Target port to use
405 * \return \ref lwespOK if successfully connected, member of \ref lwespr_t otherwise
406 */
407lwespr_t
408lwesp_netconn_connect(lwesp_netconn_p nc, const char* host, lwesp_port_t port) {
409 lwespr_t res;
410
411 LWESP_ASSERT(nc != NULL);
412 LWESP_ASSERT(host != NULL);
413 LWESP_ASSERT(port > 0);
414
415 /*
416 * Start a new connection as client and:
417 *
418 * - Set current netconn structure as argument
419 * - Set netconn callback function for connection management
420 * - Start connection in blocking mode
421 */
422 res = lwesp_conn_start(NULL, (lwesp_conn_type_t)nc->type, host, port, nc, netconn_evt, 1);
423 return res;
424}
425
426/**
427 * \brief Connect to server as client, allow keep-alive option
428 * \param[in] nc: Netconn handle
429 * \param[in] host: Pointer to host, such as domain name or IP address in string format
430 * \param[in] port: Target port to use
431 * \param[in] keep_alive: Keep alive period seconds
432 * \param[in] local_ip: Local ip in connected command
433 * \param[in] local_port: Local port address
434 * \param[in] mode: UDP mode
435 * \return \ref lwespOK if successfully connected, member of \ref lwespr_t otherwise
436 */
437lwespr_t
438lwesp_netconn_connect_ex(lwesp_netconn_p nc, const char* host, lwesp_port_t port, uint16_t keep_alive,
439 const char* local_ip, lwesp_port_t local_port, uint8_t mode) {
440 lwesp_conn_start_t cs = {0};
441 lwespr_t res;
442
443 LWESP_ASSERT(nc != NULL);
444 LWESP_ASSERT(host != NULL);
445 LWESP_ASSERT(port > 0);
446
447 /*
448 * Start a new connection as client and:
449 *
450 * - Set current netconn structure as argument
451 * - Set netconn callback function for connection management
452 * - Start connection in blocking mode
453 */
454 cs.type = (lwesp_conn_type_t)nc->type;
455 cs.remote_host = host;
456 cs.remote_port = port;
457 cs.local_ip = local_ip;
458 if (NETCONN_IS_TCP(nc) || NETCONN_IS_SSL(nc)) {
459 cs.ext.tcp_ssl.keep_alive = keep_alive;
460 } else {
461 cs.ext.udp.local_port = local_port;
462 cs.ext.udp.mode = mode;
463 }
464 res = lwesp_conn_startex(NULL, &cs, nc, netconn_evt, 1);
465 return res;
466}
467
468/**
469 * \brief Bind a connection to specific port, can be only used for server connections
470 * \param[in] nc: Netconn handle
471 * \param[in] port: Port used to bind a connection to
472 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
473 */
474lwespr_t
475lwesp_netconn_bind(lwesp_netconn_p nc, lwesp_port_t port) {
476 lwespr_t res = lwespOK;
477
478 LWESP_ASSERT(nc != NULL);
479
480 /*
481 * Protection is not needed as it is expected
482 * that this function is called only from single
483 * thread for single netconn connection,
484 * thus it is considered reentrant
485 */
486
487 nc->listen_port = port;
488
489 return res;
490}
491
492/**
493 * \brief Set timeout value in units of seconds when connection is in listening mode
494 * If new connection is accepted, it will be automatically closed after `seconds` elapsed
495 * without any data exchange.
496 * \note Call this function before you put connection to listen mode with \ref lwesp_netconn_listen
497 * \param[in] nc: Netconn handle used for listen mode
498 * \param[in] timeout: Time in units of seconds. Set to `0` to disable timeout feature
499 * \return \ref lwespOK on success, member of \ref lwespr_t otherwise
500 */
501lwespr_t
502lwesp_netconn_set_listen_conn_timeout(lwesp_netconn_p nc, uint16_t timeout) {
503 lwespr_t res = lwespOK;
504 LWESP_ASSERT(nc != NULL);
505
506 /*
507 * Protection is not needed as it is expected
508 * that this function is called only from single
509 * thread for single netconn connection,
510 * thus it is reentrant in this case
511 */
512
513 nc->conn_timeout = timeout;
514
515 return res;
516}
517
518/**
519 * \brief Listen on previously binded connection
520 * \param[in] nc: Netconn handle used to listen for new connections
521 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
522 */
523lwespr_t
524lwesp_netconn_listen(lwesp_netconn_p nc) {
525 return lwesp_netconn_listen_with_max_conn(nc, LWESP_CFG_MAX_CONNS);
526}
527
528/**
529 * \brief Listen on previously binded connection with max allowed connections at a time
530 * \param[in] nc: Netconn handle used to listen for new connections
531 * \param[in] max_connections: Maximal number of connections server can accept at a time
532 * This parameter may not be larger than \ref LWESP_CFG_MAX_CONNS
533 * \return \ref lwespOK on success, member of \ref lwespr_t otherwise
534 */
535lwespr_t
536lwesp_netconn_listen_with_max_conn(lwesp_netconn_p nc, uint16_t max_connections) {
537 lwespr_t res;
538
539 LWESP_ASSERT(nc != NULL);
540 LWESP_ASSERT(NETCONN_IS_TCP(nc));
541
542 /* Enable server on port and set default netconn callback */
543 if ((res = lwesp_set_server(1, nc->listen_port, LWESP_U16(LWESP_MIN(max_connections, LWESP_CFG_MAX_CONNS)),
544 nc->conn_timeout, netconn_evt, NULL, NULL, 1))
545 == lwespOK) {
546 lwesp_core_lock();
547 listen_api = nc; /* Set current main API in listening state */
548 lwesp_core_unlock();
549 }
550 return res;
551}
552
553/**
554 * \brief Accept a new connection
555 * \param[in] nc: Netconn handle used as base connection to accept new clients
556 * \param[out] client: Pointer to netconn handle to save new connection to
557 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
558 */
559lwespr_t
560lwesp_netconn_accept(lwesp_netconn_p nc, lwesp_netconn_p* client) {
561 lwesp_netconn_t* tmp;
562 uint32_t time;
563
564 LWESP_ASSERT(nc != NULL);
565 LWESP_ASSERT(client != NULL);
566 LWESP_ASSERT(NETCONN_IS_TCP(nc));
567 LWESP_ASSERT(nc == listen_api);
568
569 *client = NULL;
570 time = lwesp_sys_mbox_get(&nc->mbox_accept, (void**)&tmp, 0);
571 if (time == LWESP_SYS_TIMEOUT) {
572 return lwespTIMEOUT;
573 }
574 if ((uint8_t*)tmp == (uint8_t*)&recv_closed) {
575 lwesp_core_lock();
576 listen_api = NULL; /* Disable listening at this point */
577 lwesp_core_unlock();
578 return lwespERRWIFINOTCONNECTED; /* Wifi disconnected */
579 } else if ((uint8_t*)tmp == (uint8_t*)&recv_not_present) {
580 lwesp_core_lock();
581 listen_api = NULL; /* Disable listening at this point */
582 lwesp_core_unlock();
583 return lwespERRNODEVICE; /* Device not present */
584 }
585 *client = tmp; /* Set new pointer */
586 return lwespOK; /* We have a new connection */
587}
588
589/**
590 * \brief Write data to connection output buffers
591 * \note This function may only be used on TCP or SSL connections
592 * \param[in] nc: Netconn handle used to write data to
593 * \param[in] data: Pointer to data to write
594 * \param[in] btw: Number of bytes to write
595 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
596 */
597lwespr_t
598lwesp_netconn_write(lwesp_netconn_p nc, const void* data, size_t btw) {
599 size_t len, sent;
600 const uint8_t* d = data;
601 lwespr_t res;
602
603 LWESP_ASSERT(nc != NULL);
604 LWESP_ASSERT(NETCONN_IS_TCP(nc) || NETCONN_IS_SSL(nc));
605 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
606
607 /*
608 * Several steps are done in write process
609 *
610 * 1. Check if buffer is set and check if there is something to write to it.
611 * 1. In case buffer will be full after copy, send it and free memory.
612 * 2. Check how many bytes we can write directly without need to copy
613 * 3. Try to allocate a new buffer and copy remaining input data to it
614 * 4. In case buffer allocation fails, send data directly (may have impact on speed and effectivenes)
615 */
616
617 /* Step 1 */
618 if (nc->buff.buff != NULL) { /* Is there a write buffer ready to accept more data? */
619 len = LWESP_MIN(nc->buff.len - nc->buff.ptr, btw); /* Get number of bytes we can write to buffer */
620 if (len > 0) {
621 LWESP_MEMCPY(&nc->buff.buff[nc->buff.ptr], data, len); /* Copy memory to temporary write buffer */
622 d += len;
623 nc->buff.ptr += len;
624 btw -= len;
625 }
626
627 /* Step 1.1 */
628 if (nc->buff.ptr == nc->buff.len) {
629 res = lwesp_conn_send(nc->conn, nc->buff.buff, nc->buff.len, &sent, 1);
630
631 lwesp_mem_free_s((void**)&nc->buff.buff);
632 if (res != lwespOK) {
633 return res;
634 }
635 } else {
636 return lwespOK; /* Buffer is not full yet */
637 }
638 }
639
640 /* Step 2 */
641 if (btw >= LWESP_CFG_CONN_MAX_DATA_LEN) {
642 size_t rem;
643 rem = btw % LWESP_CFG_CONN_MAX_DATA_LEN; /* Get remaining bytes for max data length */
644 res = lwesp_conn_send(nc->conn, d, btw - rem, &sent, 1); /* Write data directly */
645 if (res != lwespOK) {
646 return res;
647 }
648 d += sent; /* Advance in data pointer */
649 btw -= sent; /* Decrease remaining data to send */
650 }
651
652 if (btw == 0) { /* Sent everything? */
653 return lwespOK;
654 }
655
656 /* Step 3 */
657 if (nc->buff.buff == NULL) { /* Check if we should allocate a new buffer */
658 nc->buff.buff = lwesp_mem_malloc(sizeof(*nc->buff.buff) * LWESP_CFG_CONN_MAX_DATA_LEN);
659 nc->buff.len = LWESP_CFG_CONN_MAX_DATA_LEN; /* Save buffer length */
660 nc->buff.ptr = 0; /* Save buffer pointer */
661 }
662
663 /* Step 4 */
664 if (nc->buff.buff != NULL) { /* Memory available? */
665 LWESP_MEMCPY(&nc->buff.buff[nc->buff.ptr], d, btw); /* Copy data to buffer */
666 nc->buff.ptr += btw;
667 } else { /* Still no memory available? */
668 return lwesp_conn_send(nc->conn, data, btw, NULL, 1); /* Simply send directly blocking */
669 }
670 return lwespOK;
671}
672
673/**
674 * \brief Extended version of \ref lwesp_netconn_write with additional
675 * option to set custom flags.
676 *
677 * \note It is recommended to use this for full features support
678 *
679 * \param[in] nc: Netconn handle used to write data to
680 * \param[in] data: Pointer to data to write
681 * \param[in] btw: Number of bytes to write
682 * \param flags: Bitwise-ORed set of flags for netconn.
683 * Flags start with \ref LWESP_NETCONN_FLAG_xxx
684 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
685 */
686lwespr_t
687lwesp_netconn_write_ex(lwesp_netconn_p nc, const void* data, size_t btw, uint16_t flags) {
688 lwespr_t res = lwesp_netconn_write(nc, data, btw);
689 if (res == lwespOK) {
690 if (flags & LWESP_NETCONN_FLAG_FLUSH) {
691 res = lwesp_netconn_flush(nc);
692 }
693 }
694 return res;
695}
696
697/**
698 * \brief Flush buffered data on netconn TCP/SSL connection
699 * \note This function may only be used on TCP/SSL connection
700 * \param[in] nc: Netconn handle to flush data
701 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
702 */
703lwespr_t
704lwesp_netconn_flush(lwesp_netconn_p nc) {
705 LWESP_ASSERT(nc != NULL);
706 LWESP_ASSERT(NETCONN_IS_TCP(nc) || NETCONN_IS_SSL(nc));
707 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
708
709 /*
710 * In case we have data in write buffer,
711 * flush them out to network
712 */
713 if (nc->buff.buff != NULL) { /* Check remaining data */
714 if (nc->buff.ptr > 0) { /* Do we have data in current buffer? */
715 lwesp_conn_send(nc->conn, nc->buff.buff, nc->buff.ptr, NULL, 1); /* Send data */
716 }
717 lwesp_mem_free_s((void**)&nc->buff.buff);
718 }
719 return lwespOK;
720}
721
722/**
723 * \brief Send data on UDP connection to default IP and port
724 * \param[in] nc: Netconn handle used to send
725 * \param[in] data: Pointer to data to write
726 * \param[in] btw: Number of bytes to write
727 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
728 */
729lwespr_t
730lwesp_netconn_send(lwesp_netconn_p nc, const void* data, size_t btw) {
731 LWESP_ASSERT(nc != NULL);
732 LWESP_ASSERT(nc->type == LWESP_NETCONN_TYPE_UDP);
733 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
734
735 return lwesp_conn_send(nc->conn, data, btw, NULL, 1);
736}
737
738/**
739 * \brief Send data on UDP connection to specific IP and port
740 * \note Use this function in case of UDP type netconn
741 * \param[in] nc: Netconn handle used to send
742 * \param[in] ip: Pointer to IP address
743 * \param[in] port: Port number used to send data
744 * \param[in] data: Pointer to data to write
745 * \param[in] btw: Number of bytes to write
746 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
747 */
748lwespr_t
749lwesp_netconn_sendto(lwesp_netconn_p nc, const lwesp_ip_t* ip, lwesp_port_t port, const void* data, size_t btw) {
750 LWESP_ASSERT(nc != NULL);
751 LWESP_ASSERT(nc->type == LWESP_NETCONN_TYPE_UDP);
752 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
753
754 return lwesp_conn_sendto(nc->conn, ip, port, data, btw, NULL, 1);
755}
756
757/**
758 * \brief Receive data from connection
759 * \param[in] nc: Netconn handle used to receive from
760 * \param[in] pbuf: Pointer to pointer to save new receive buffer to.
761 * When function returns, user must check for valid pbuf value `pbuf != NULL`
762 * \return \ref lwespOK when new data ready
763 * \return \ref lwespCLOSED when connection closed by remote side
764 * \return \ref lwespTIMEOUT when receive timeout occurs
765 * \return Any other member of \ref lwespr_t otherwise
766 */
767lwespr_t
768lwesp_netconn_receive(lwesp_netconn_p nc, lwesp_pbuf_p* pbuf) {
769 LWESP_ASSERT(nc != NULL);
770 LWESP_ASSERT(pbuf != NULL);
771
772 *pbuf = NULL;
773#if LWESP_CFG_NETCONN_RECEIVE_TIMEOUT
774 /*
775 * Wait for new received data for up to specific timeout
776 * or throw error for timeout notification
777 */
778 if (nc->rcv_timeout == LWESP_NETCONN_RECEIVE_NO_WAIT) {
779 if (!lwesp_sys_mbox_getnow(&nc->mbox_receive, (void**)pbuf)) {
780 return lwespTIMEOUT;
781 }
782 } else if (lwesp_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, nc->rcv_timeout) == LWESP_SYS_TIMEOUT) {
783 return lwespTIMEOUT;
784 }
785#else /* LWESP_CFG_NETCONN_RECEIVE_TIMEOUT */
786 /* Forever wait for new receive packet */
787 lwesp_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, 0);
788#endif /* !LWESP_CFG_NETCONN_RECEIVE_TIMEOUT */
789
790 lwesp_core_lock();
791 if (nc->mbox_receive_entries > 0) {
792 --nc->mbox_receive_entries;
793 }
794 lwesp_core_unlock();
795
796 /* Check if connection closed */
797 if ((uint8_t*)(*pbuf) == (uint8_t*)&recv_closed) {
798 *pbuf = NULL; /* Reset pbuf */
799 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING,
800 "[LWESP NETCONN] netcon_receive: Got object handle for close event\r\n");
801 return lwespCLOSED;
802 }
803#if LWESP_CFG_CONN_MANUAL_TCP_RECEIVE
804 else {
805 lwesp_core_lock();
806 nc->conn->status.f.receive_blocked = 0; /* Resume reading more data */
807 lwesp_conn_recved(nc->conn, *pbuf); /* Notify stack about received data */
808 lwesp_core_unlock();
809 }
810#endif /* LWESP_CFG_CONN_MANUAL_TCP_RECEIVE */
811 LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING,
812 "[LWESP NETCONN] netcon_receive: Got pbuf object handle at 0x%p. Len/Tot_len: %u/%u\r\n", (void*)*pbuf,
813 (unsigned)lwesp_pbuf_length(*pbuf, 0), (unsigned)lwesp_pbuf_length(*pbuf, 1));
814 return lwespOK; /* We have data available */
815}
816
817/**
818 * \brief Close a netconn connection
819 * \param[in] nc: Netconn handle to close
820 * \return \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
821 */
822lwespr_t
823lwesp_netconn_close(lwesp_netconn_p nc) {
824 lwesp_conn_p conn;
825
826 LWESP_ASSERT(nc != NULL);
827 LWESP_ASSERT(nc->conn != NULL);
828 LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
829
830 lwesp_netconn_flush(nc); /* Flush data and ignore result */
831 conn = nc->conn;
832 nc->conn = NULL;
833
834 lwesp_conn_set_arg(conn, NULL); /* Reset argument */
835 lwesp_conn_close(conn, 1); /* Close the connection */
836 flush_mboxes(nc, 1); /* Flush message queues */
837 return lwespOK;
838}
839
840/**
841 * \brief Get connection number used for netconn
842 * \param[in] nc: Netconn handle
843 * \return `-1` on failure, connection number between `0` and \ref LWESP_CFG_MAX_CONNS otherwise
844 */
845int8_t
846lwesp_netconn_get_connnum(lwesp_netconn_p nc) {
847 if (nc != NULL && nc->conn != NULL) {
848 return lwesp_conn_getnum(nc->conn);
849 }
850 return -1;
851}
852
853#if LWESP_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__
854
855/**
856 * \brief Set timeout value for receiving data.
857 *
858 * When enabled, \ref lwesp_netconn_receive will only block for up to
859 * \e timeout value and will return if no new data within this time
860 *
861 * \param[in] nc: Netconn handle
862 * \param[in] timeout: Timeout in units of milliseconds.
863 * Set to `0` to disable timeout feature. Function blocks until data receive or connection closed
864 * Set to `> 0` to set maximum milliseconds to wait before timeout
865 * Set to \ref LWESP_NETCONN_RECEIVE_NO_WAIT to enable non-blocking receive
866 */
867void
868lwesp_netconn_set_receive_timeout(lwesp_netconn_p nc, uint32_t timeout) {
869 nc->rcv_timeout = timeout;
870}
871
872/**
873 * \brief Get netconn receive timeout value
874 * \param[in] nc: Netconn handle
875 * \return Timeout in units of milliseconds.
876 * If value is `0`, timeout is disabled (wait forever)
877 */
878uint32_t
879lwesp_netconn_get_receive_timeout(lwesp_netconn_p nc) {
880 return nc->rcv_timeout;
881}
882
883#endif /* LWESP_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__ */
884
885/**
886 * \brief Get netconn connection handle
887 * \param[in] nc: Netconn handle
888 * \return ESP connection handle
889 */
890lwesp_conn_p
891lwesp_netconn_get_conn(lwesp_netconn_p nc) {
892 return nc->conn;
893}
894
895/**
896 * \brief Get netconn connection type
897 * \param[in] nc: Netconn handle
898 * \return ESP connection type
899 */
900lwesp_netconn_type_t
901lwesp_netconn_get_type(lwesp_netconn_p nc) {
902 return nc->type;
903}
904
905#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}