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) 2026 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, "[LWESP NETCONN] Could not put receive packet. Ignoring more data "
233                                                    "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, "[LWESP NETCONN] Cannot "
319                                                                                              "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, "[LWESP NETCONN] Cannot "
324                                                                                              "create receive "
325                                                                                              "MBOX\r\n");
326            goto free_ret;
327        }
328        lwesp_core_lock();
329        a->next = netconn_list; /* Add it to beginning of the list */
330        netconn_list = a;
331        lwesp_core_unlock();
332    }
333    return a;
334free_ret:
335    if (lwesp_sys_mbox_isvalid(&a->mbox_accept)) {
336        lwesp_sys_mbox_delete(&a->mbox_accept);
337        lwesp_sys_mbox_invalid(&a->mbox_accept);
338    }
339    if (lwesp_sys_mbox_isvalid(&a->mbox_receive)) {
340        lwesp_sys_mbox_delete(&a->mbox_receive);
341        lwesp_sys_mbox_invalid(&a->mbox_receive);
342    }
343    if (a != NULL) {
344        lwesp_mem_free_s((void**)&a);
345    }
346    return NULL;
347}
348
349/**
350 * \brief           Delete netconn connection
351 * \param[in]       nc: Netconn handle
352 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
353 */
354lwespr_t
355lwesp_netconn_delete(lwesp_netconn_p nc) {
356    LWESP_ASSERT(nc != NULL);
357
358    lwesp_core_lock();
359    if (nc->conn != NULL) {
360        /* No NC for any incoming connections or anything else... */
361        lwesp_conn_set_arg(nc->conn, NULL);
362    }
363    flush_mboxes(nc, 0); /* Clear mboxes */
364
365    /* Stop listening on netconn */
366    if (nc == listen_api) {
367        listen_api = NULL;
368        lwesp_core_unlock();
369        lwesp_set_server(0, nc->listen_port, 0, 0, NULL, NULL, NULL, 1);
370        lwesp_core_lock();
371    }
372
373    /* Remove netconn from linkedlist */
374    if (nc == netconn_list) {
375        netconn_list = netconn_list->next; /* Remove first from linked list */
376    } else if (netconn_list != NULL) {
377        lwesp_netconn_p tmp, prev;
378        /* Find element on the list */
379        for (prev = netconn_list, tmp = netconn_list->next; tmp != NULL; prev = tmp, tmp = tmp->next) {
380            if (nc == tmp) {
381                prev->next = tmp->next; /* Remove tmp from linked list */
382                break;
383            }
384        }
385    }
386    if (nc->conn != NULL) {
387        /*
388         * First delete the connection argument,
389         * then close the connection.
390         */
391        if (lwesp_conn_is_active(nc->conn)) {
392            lwesp_conn_close(nc->conn, 1);
393        }
394        nc->conn = NULL;
395    }
396    lwesp_core_unlock();
397
398    lwesp_mem_free_s((void**)&nc);
399    return lwespOK;
400}
401
402/**
403 * \brief           Connect to server as client
404 * \param[in]       nc: Netconn handle
405 * \param[in]       host: Pointer to host, such as domain name or IP address in string format
406 * \param[in]       port: Target port to use
407 * \return          \ref lwespOK if successfully connected, member of \ref lwespr_t otherwise
408 */
409lwespr_t
410lwesp_netconn_connect(lwesp_netconn_p nc, const char* host, lwesp_port_t port) {
411    lwespr_t res;
412
413    LWESP_ASSERT(nc != NULL);
414    LWESP_ASSERT(host != NULL);
415    LWESP_ASSERT(port > 0);
416
417    /*
418     * Start a new connection as client and:
419     *
420     *  - Set current netconn structure as argument
421     *  - Set netconn callback function for connection management
422     *  - Start connection in blocking mode
423     */
424    res = lwesp_conn_start(NULL, (lwesp_conn_type_t)nc->type, host, port, nc, netconn_evt, 1);
425    return res;
426}
427
428/**
429 * \brief           Connect to server as client, allow keep-alive option
430 * \param[in]       nc: Netconn handle
431 * \param[in]       host: Pointer to host, such as domain name or IP address in string format
432 * \param[in]       port: Target port to use
433 * \param[in]       keep_alive: Keep alive period seconds
434 * \param[in]       local_ip: Local ip in connected command
435 * \param[in]       local_port: Local port address
436 * \param[in]       mode: UDP mode
437 * \return          \ref lwespOK if successfully connected, member of \ref lwespr_t otherwise
438 */
439lwespr_t
440lwesp_netconn_connect_ex(lwesp_netconn_p nc, const char* host, lwesp_port_t port, uint16_t keep_alive,
441                         const char* local_ip, lwesp_port_t local_port, uint8_t mode) {
442    lwesp_conn_start_t cs = {0};
443    lwespr_t res;
444
445    LWESP_ASSERT(nc != NULL);
446    LWESP_ASSERT(host != NULL);
447    LWESP_ASSERT(port > 0);
448
449    /*
450     * Start a new connection as client and:
451     *
452     *  - Set current netconn structure as argument
453     *  - Set netconn callback function for connection management
454     *  - Start connection in blocking mode
455     */
456    cs.type = (lwesp_conn_type_t)nc->type;
457    cs.remote_host = host;
458    cs.remote_port = port;
459    cs.local_ip = local_ip;
460    if (NETCONN_IS_TCP(nc) || NETCONN_IS_SSL(nc)) {
461        cs.ext.tcp_ssl.keep_alive = keep_alive;
462    } else {
463        cs.ext.udp.local_port = local_port;
464        cs.ext.udp.mode = mode;
465    }
466    res = lwesp_conn_startex(NULL, &cs, nc, netconn_evt, 1);
467    return res;
468}
469
470/**
471 * \brief           Bind a connection to specific port, can be only used for server connections
472 * \param[in]       nc: Netconn handle
473 * \param[in]       port: Port used to bind a connection to
474 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
475 */
476lwespr_t
477lwesp_netconn_bind(lwesp_netconn_p nc, lwesp_port_t port) {
478    lwespr_t res = lwespOK;
479
480    LWESP_ASSERT(nc != NULL);
481
482    /*
483     * Protection is not needed as it is expected
484     * that this function is called only from single
485     * thread for single netconn connection,
486     * thus it is considered reentrant
487     */
488
489    nc->listen_port = port;
490
491    return res;
492}
493
494/**
495 * \brief           Set timeout value in units of seconds when connection is in listening mode
496 *                  If new connection is accepted, it will be automatically closed after `seconds` elapsed
497 *                  without any data exchange.
498 * \note            Call this function before you put connection to listen mode with \ref lwesp_netconn_listen
499 * \param[in]       nc: Netconn handle used for listen mode
500 * \param[in]       timeout: Time in units of seconds. Set to `0` to disable timeout feature
501 * \return          \ref lwespOK on success, member of \ref lwespr_t otherwise
502 */
503lwespr_t
504lwesp_netconn_set_listen_conn_timeout(lwesp_netconn_p nc, uint16_t timeout) {
505    lwespr_t res = lwespOK;
506    LWESP_ASSERT(nc != NULL);
507
508    /*
509     * Protection is not needed as it is expected
510     * that this function is called only from single
511     * thread for single netconn connection,
512     * thus it is reentrant in this case
513     */
514
515    nc->conn_timeout = timeout;
516
517    return res;
518}
519
520/**
521 * \brief           Listen on previously binded connection
522 * \param[in]       nc: Netconn handle used to listen for new connections
523 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
524 */
525lwespr_t
526lwesp_netconn_listen(lwesp_netconn_p nc) {
527    return lwesp_netconn_listen_with_max_conn(nc, LWESP_CFG_MAX_CONNS);
528}
529
530/**
531 * \brief           Listen on previously binded connection with max allowed connections at a time
532 * \param[in]       nc: Netconn handle used to listen for new connections
533 * \param[in]       max_connections: Maximal number of connections server can accept at a time
534 *                      This parameter may not be larger than \ref LWESP_CFG_MAX_CONNS
535 * \return          \ref lwespOK on success, member of \ref lwespr_t otherwise
536 */
537lwespr_t
538lwesp_netconn_listen_with_max_conn(lwesp_netconn_p nc, uint16_t max_connections) {
539    lwespr_t res;
540
541    LWESP_ASSERT(nc != NULL);
542    LWESP_ASSERT(NETCONN_IS_TCP(nc));
543
544    /* Enable server on port and set default netconn callback */
545    if ((res = lwesp_set_server(1, nc->listen_port, LWESP_U16(LWESP_MIN(max_connections, LWESP_CFG_MAX_CONNS)),
546                                nc->conn_timeout, netconn_evt, NULL, NULL, 1))
547        == lwespOK) {
548        lwesp_core_lock();
549        listen_api = nc; /* Set current main API in listening state */
550        lwesp_core_unlock();
551    }
552    return res;
553}
554
555/**
556 * \brief           Accept a new connection
557 * \param[in]       nc: Netconn handle used as base connection to accept new clients
558 * \param[out]      client: Pointer to netconn handle to save new connection to
559 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
560 */
561lwespr_t
562lwesp_netconn_accept(lwesp_netconn_p nc, lwesp_netconn_p* client) {
563    lwesp_netconn_t* tmp;
564    uint32_t time;
565
566    LWESP_ASSERT(nc != NULL);
567    LWESP_ASSERT(client != NULL);
568    LWESP_ASSERT(NETCONN_IS_TCP(nc));
569    LWESP_ASSERT(nc == listen_api);
570
571    *client = NULL;
572    time = lwesp_sys_mbox_get(&nc->mbox_accept, (void**)&tmp, 0);
573    if (time == LWESP_SYS_TIMEOUT) {
574        return lwespTIMEOUT;
575    }
576    if ((uint8_t*)tmp == (uint8_t*)&recv_closed) {
577        lwesp_core_lock();
578        listen_api = NULL; /* Disable listening at this point */
579        lwesp_core_unlock();
580        return lwespERRWIFINOTCONNECTED; /* Wifi disconnected */
581    } else if ((uint8_t*)tmp == (uint8_t*)&recv_not_present) {
582        lwesp_core_lock();
583        listen_api = NULL; /* Disable listening at this point */
584        lwesp_core_unlock();
585        return lwespERRNODEVICE; /* Device not present */
586    }
587    *client = tmp;  /* Set new pointer */
588    return lwespOK; /* We have a new connection */
589}
590
591/**
592 * \brief           Write data to connection output buffers
593 * \note            This function may only be used on TCP or SSL connections
594 * \param[in]       nc: Netconn handle used to write data to
595 * \param[in]       data: Pointer to data to write
596 * \param[in]       btw: Number of bytes to write
597 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
598 */
599lwespr_t
600lwesp_netconn_write(lwesp_netconn_p nc, const void* data, size_t btw) {
601    size_t len, sent;
602    const uint8_t* d = data;
603    lwespr_t res;
604
605    LWESP_ASSERT(nc != NULL);
606    LWESP_ASSERT(NETCONN_IS_TCP(nc) || NETCONN_IS_SSL(nc));
607    LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
608
609    /*
610     * Several steps are done in write process
611     *
612     * 1. Check if buffer is set and check if there is something to write to it.
613     *    1. In case buffer will be full after copy, send it and free memory.
614     * 2. Check how many bytes we can write directly without need to copy
615     * 3. Try to allocate a new buffer and copy remaining input data to it
616     * 4. In case buffer allocation fails, send data directly (may have impact on speed and effectivenes)
617     */
618
619    /* Step 1 */
620    if (nc->buff.buff != NULL) {                           /* Is there a write buffer ready to accept more data? */
621        len = LWESP_MIN(nc->buff.len - nc->buff.ptr, btw); /* Get number of bytes we can write to buffer */
622        if (len > 0) {
623            LWESP_MEMCPY(&nc->buff.buff[nc->buff.ptr], data, len); /* Copy memory to temporary write buffer */
624            d += len;
625            nc->buff.ptr += len;
626            btw -= len;
627        }
628
629        /* Step 1.1 */
630        if (nc->buff.ptr == nc->buff.len) {
631            res = lwesp_conn_send(nc->conn, nc->buff.buff, nc->buff.len, &sent, 1);
632
633            lwesp_mem_free_s((void**)&nc->buff.buff);
634            if (res != lwespOK) {
635                return res;
636            }
637        } else {
638            return lwespOK; /* Buffer is not full yet */
639        }
640    }
641
642    /* Step 2 */
643    if (btw >= LWESP_CFG_CONN_MAX_DATA_LEN) {
644        size_t rem;
645        rem = btw % LWESP_CFG_CONN_MAX_DATA_LEN;                 /* Get remaining bytes for max data length */
646        res = lwesp_conn_send(nc->conn, d, btw - rem, &sent, 1); /* Write data directly */
647        if (res != lwespOK) {
648            return res;
649        }
650        d += sent;   /* Advance in data pointer */
651        btw -= sent; /* Decrease remaining data to send */
652    }
653
654    if (btw == 0) { /* Sent everything? */
655        return lwespOK;
656    }
657
658    /* Step 3 */
659    if (nc->buff.buff == NULL) { /* Check if we should allocate a new buffer */
660        nc->buff.buff = lwesp_mem_malloc(sizeof(*nc->buff.buff) * LWESP_CFG_CONN_MAX_DATA_LEN);
661        nc->buff.len = LWESP_CFG_CONN_MAX_DATA_LEN; /* Save buffer length */
662        nc->buff.ptr = 0;                           /* Save buffer pointer */
663    }
664
665    /* Step 4 */
666    if (nc->buff.buff != NULL) {                            /* Memory available? */
667        LWESP_MEMCPY(&nc->buff.buff[nc->buff.ptr], d, btw); /* Copy data to buffer */
668        nc->buff.ptr += btw;
669    } else {                                                  /* Still no memory available? */
670        return lwesp_conn_send(nc->conn, data, btw, NULL, 1); /* Simply send directly blocking */
671    }
672    return lwespOK;
673}
674
675/**
676 * \brief           Extended version of \ref lwesp_netconn_write with additional
677 *                  option to set custom flags.
678 *
679 * \note            It is recommended to use this for full features support
680 *
681 * \param[in]       nc: Netconn handle used to write data to
682 * \param[in]       data: Pointer to data to write
683 * \param[in]       btw: Number of bytes to write
684 * \param           flags: Bitwise-ORed set of flags for netconn.
685 *                      Flags start with \ref LWESP_NETCONN_FLAG_xxx
686 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
687 */
688lwespr_t
689lwesp_netconn_write_ex(lwesp_netconn_p nc, const void* data, size_t btw, uint16_t flags) {
690    lwespr_t res = lwesp_netconn_write(nc, data, btw);
691    if (res == lwespOK) {
692        if (flags & LWESP_NETCONN_FLAG_FLUSH) {
693            res = lwesp_netconn_flush(nc);
694        }
695    }
696    return res;
697}
698
699/**
700 * \brief           Flush buffered data on netconn TCP/SSL connection
701 * \note            This function may only be used on TCP/SSL connection
702 * \param[in]       nc: Netconn handle to flush data
703 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
704 */
705lwespr_t
706lwesp_netconn_flush(lwesp_netconn_p nc) {
707    LWESP_ASSERT(nc != NULL);
708    LWESP_ASSERT(NETCONN_IS_TCP(nc) || NETCONN_IS_SSL(nc));
709    LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
710
711    /*
712     * In case we have data in write buffer,
713     * flush them out to network
714     */
715    if (nc->buff.buff != NULL) {                                             /* Check remaining data */
716        if (nc->buff.ptr > 0) {                                              /* Do we have data in current buffer? */
717            lwesp_conn_send(nc->conn, nc->buff.buff, nc->buff.ptr, NULL, 1); /* Send data */
718        }
719        lwesp_mem_free_s((void**)&nc->buff.buff);
720    }
721    return lwespOK;
722}
723
724/**
725 * \brief           Send data on UDP connection to default IP and port
726 * \param[in]       nc: Netconn handle used to send
727 * \param[in]       data: Pointer to data to write
728 * \param[in]       btw: Number of bytes to write
729 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
730 */
731lwespr_t
732lwesp_netconn_send(lwesp_netconn_p nc, const void* data, size_t btw) {
733    LWESP_ASSERT(nc != NULL);
734    LWESP_ASSERT(nc->type == LWESP_NETCONN_TYPE_UDP);
735    LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
736
737    return lwesp_conn_send(nc->conn, data, btw, NULL, 1);
738}
739
740/**
741 * \brief           Send data on UDP connection to specific IP and port
742 * \note            Use this function in case of UDP type netconn
743 * \param[in]       nc: Netconn handle used to send
744 * \param[in]       ip: Pointer to IP address
745 * \param[in]       port: Port number used to send data
746 * \param[in]       data: Pointer to data to write
747 * \param[in]       btw: Number of bytes to write
748 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
749 */
750lwespr_t
751lwesp_netconn_sendto(lwesp_netconn_p nc, const lwesp_ip_t* ip, lwesp_port_t port, const void* data, size_t btw) {
752    LWESP_ASSERT(nc != NULL);
753    LWESP_ASSERT(nc->type == LWESP_NETCONN_TYPE_UDP);
754    LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
755
756    return lwesp_conn_sendto(nc->conn, ip, port, data, btw, NULL, 1);
757}
758
759/**
760 * \brief           Receive data from connection
761 * \param[in]       nc: Netconn handle used to receive from
762 * \param[in]       pbuf: Pointer to pointer to save new receive buffer to.
763 *                     When function returns, user must check for valid pbuf value `pbuf != NULL`
764 * \return          \ref lwespOK when new data ready
765 * \return          \ref lwespCLOSED when connection closed by remote side
766 * \return          \ref lwespTIMEOUT when receive timeout occurs
767 * \return          Any other member of \ref lwespr_t otherwise
768 */
769lwespr_t
770lwesp_netconn_receive(lwesp_netconn_p nc, lwesp_pbuf_p* pbuf) {
771    LWESP_ASSERT(nc != NULL);
772    LWESP_ASSERT(pbuf != NULL);
773
774    *pbuf = NULL;
775#if LWESP_CFG_NETCONN_RECEIVE_TIMEOUT
776    /*
777     * Wait for new received data for up to specific timeout
778     * or throw error for timeout notification
779     */
780    if (nc->rcv_timeout == LWESP_NETCONN_RECEIVE_NO_WAIT) {
781        if (!lwesp_sys_mbox_getnow(&nc->mbox_receive, (void**)pbuf)) {
782            return lwespTIMEOUT;
783        }
784    } else if (lwesp_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, nc->rcv_timeout) == LWESP_SYS_TIMEOUT) {
785        return lwespTIMEOUT;
786    }
787#else  /* LWESP_CFG_NETCONN_RECEIVE_TIMEOUT */
788    /* Forever wait for new receive packet */
789    lwesp_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, 0);
790#endif /* !LWESP_CFG_NETCONN_RECEIVE_TIMEOUT */
791
792    lwesp_core_lock();
793    if (nc->mbox_receive_entries > 0) {
794        --nc->mbox_receive_entries;
795    }
796    lwesp_core_unlock();
797
798    /* Check if connection closed */
799    if ((uint8_t*)(*pbuf) == (uint8_t*)&recv_closed) {
800        *pbuf = NULL; /* Reset pbuf */
801        LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING, "[LWESP NETCONN] "
802                                                                                           "netcon_receive: Got object "
803                                                                                           "handle for close "
804                                                                                           "event\r\n");
805        return lwespCLOSED;
806    }
807#if LWESP_CFG_CONN_MANUAL_TCP_RECEIVE
808    else {
809        lwesp_core_lock();
810        nc->conn->status.f.receive_blocked = 0; /* Resume reading more data */
811        lwesp_conn_recved(nc->conn, *pbuf);     /* Notify stack about received data */
812        lwesp_core_unlock();
813    }
814#endif /* LWESP_CFG_CONN_MANUAL_TCP_RECEIVE */
815    LWESP_DEBUGF(LWESP_CFG_DBG_NETCONN | LWESP_DBG_TYPE_TRACE | LWESP_DBG_LVL_WARNING,
816                 "[LWESP NETCONN] netcon_receive: Got pbuf object handle at 0x%p. Len/Tot_len: %u/%u\r\n", (void*)*pbuf,
817                 (unsigned)lwesp_pbuf_length(*pbuf, 0), (unsigned)lwesp_pbuf_length(*pbuf, 1));
818    return lwespOK; /* We have data available */
819}
820
821/**
822 * \brief           Close a netconn connection
823 * \param[in]       nc: Netconn handle to close
824 * \return          \ref lwespOK on success, member of \ref lwespr_t enumeration otherwise
825 */
826lwespr_t
827lwesp_netconn_close(lwesp_netconn_p nc) {
828    lwesp_conn_p conn;
829
830    LWESP_ASSERT(nc != NULL);
831    LWESP_ASSERT(nc->conn != NULL);
832    LWESP_ASSERT(lwesp_conn_is_active(nc->conn));
833
834    lwesp_netconn_flush(nc); /* Flush data and ignore result */
835    conn = nc->conn;
836    nc->conn = NULL;
837
838    lwesp_conn_set_arg(conn, NULL); /* Reset argument */
839    lwesp_conn_close(conn, 1);      /* Close the connection */
840    flush_mboxes(nc, 1);            /* Flush message queues */
841    return lwespOK;
842}
843
844/**
845 * \brief           Get connection number used for netconn
846 * \param[in]       nc: Netconn handle
847 * \return          `-1` on failure, connection number between `0` and \ref LWESP_CFG_MAX_CONNS otherwise
848 */
849int8_t
850lwesp_netconn_get_connnum(lwesp_netconn_p nc) {
851    if (nc != NULL && nc->conn != NULL) {
852        return lwesp_conn_getnum(nc->conn);
853    }
854    return -1;
855}
856
857#if LWESP_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__
858
859/**
860 * \brief           Set timeout value for receiving data.
861 *
862 * When enabled, \ref lwesp_netconn_receive will only block for up to
863 * \e timeout value and will return if no new data within this time
864 *
865 * \param[in]       nc: Netconn handle
866 * \param[in]       timeout: Timeout in units of milliseconds.
867 *                      Set to `0` to disable timeout feature. Function blocks until data receive or connection closed
868 *                      Set to `> 0` to set maximum milliseconds to wait before timeout
869 *                      Set to \ref LWESP_NETCONN_RECEIVE_NO_WAIT to enable non-blocking receive
870 */
871void
872lwesp_netconn_set_receive_timeout(lwesp_netconn_p nc, uint32_t timeout) {
873    nc->rcv_timeout = timeout;
874}
875
876/**
877 * \brief           Get netconn receive timeout value
878 * \param[in]       nc: Netconn handle
879 * \return          Timeout in units of milliseconds.
880 *                  If value is `0`, timeout is disabled (wait forever)
881 */
882uint32_t
883lwesp_netconn_get_receive_timeout(lwesp_netconn_p nc) {
884    return nc->rcv_timeout;
885}
886
887#endif /* LWESP_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__ */
888
889/**
890 * \brief           Get netconn connection handle
891 * \param[in]       nc: Netconn handle
892 * \return          ESP connection handle
893 */
894lwesp_conn_p
895lwesp_netconn_get_conn(lwesp_netconn_p nc) {
896    return nc->conn;
897}
898
899/**
900 * \brief           Get netconn connection type
901 * \param[in]       nc: Netconn handle
902 * \return          ESP connection type
903 */
904lwesp_netconn_type_t
905lwesp_netconn_get_type(lwesp_netconn_p nc) {
906    return nc->type;
907}
908
909#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}