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:

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.

Netconn API module 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() function

  • Application sets global event callback function when enabling server mode with lwesp_set_server() function

An example of client with its dedicated event callback 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).

Simple example for API call event, using DNS module
 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}