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

Connection specific event

This events are subset of global event callback. They work exactly the same way as global, but only receive events related to connections.

Tip

Connection related events start with LWESP_EVT_CONN_*, such as LWESP_EVT_CONN_RECV. Check Event management for list of all connection events.

Connection events callback function is set for 2 cases:

  • Each client (when application starts connection) sets event callback function when trying to connect with lwesp_conn_start() 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}