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:

  • GSM station successfully connected to access point

  • GSM 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 lwcell_evt_register() function to register a new, custom, event function.

Tip

Implementation of Netconn API leverages lwcell_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            lwcell_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 LwCELL - Lightweight cellular modem AT library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v0.1.1
 33 */
 34#include "lwcell/lwcell_netconn.h"
 35#include "lwcell/lwcell_conn.h"
 36#include "lwcell/lwcell_mem.h"
 37#include "lwcell/lwcell_private.h"
 38
 39#if LWCELL_CFG_NETCONN || __DOXYGEN__
 40
 41/* Check conditions */
 42#if !LWCELL_CFG_CONN
 43#error "LWCELL_CFG_CONN must be enabled for NETCONN API!"
 44#endif /* !LWCELL_CFG_CONN */
 45
 46#if LWCELL_CFG_NETCONN_RECEIVE_QUEUE_LEN < 2
 47#error "LWCELL_CFG_NETCONN_RECEIVE_QUEUE_LEN must be greater or equal to 2"
 48#endif /* LWCELL_CFG_NETCONN_RECEIVE_QUEUE_LEN < 2 */
 49
 50/**
 51 * \brief           Sequential API structure
 52 */
 53typedef struct lwcell_netconn {
 54    struct lwcell_netconn* next;    /*!< Linked list entry */
 55
 56    lwcell_netconn_type_t type;     /*!< Netconn type */
 57
 58    size_t rcv_packets;            /*!< Number of received packets so far on this connection */
 59    lwcell_conn_p conn;             /*!< Pointer to actual connection */
 60
 61    lwcell_sys_mbox_t mbox_receive; /*!< Message queue for receive mbox */
 62
 63    lwcell_linbuff_t buff;          /*!< Linear buffer structure */
 64
 65    uint16_t conn_timeout;         /*!< Connection timeout in units of seconds when
 66                                                    netconn is in server (listen) mode.
 67                                                    Connection will be automatically closed if there is no
 68                                                    data exchange in time. Set to `0` when timeout feature is disabled. */
 69
 70#if LWCELL_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__
 71    uint32_t rcv_timeout; /*!< Receive timeout in unit of milliseconds */
 72#endif
 73} lwcell_netconn_t;
 74
 75static uint8_t recv_closed = 0xFF;
 76static lwcell_netconn_t* netconn_list; /*!< Linked list of netconn entries */
 77
 78/**
 79 * \brief           Flush all mboxes and clear possible used memories
 80 * \param[in]       nc: Pointer to netconn to flush
 81 * \param[in]       protect: Set to 1 to protect against multi-thread access
 82 */
 83static void
 84flush_mboxes(lwcell_netconn_t* nc, uint8_t protect) {
 85    lwcell_pbuf_p pbuf;
 86    if (protect) {
 87        lwcell_core_lock();
 88    }
 89    if (lwcell_sys_mbox_isvalid(&nc->mbox_receive)) {
 90        while (lwcell_sys_mbox_getnow(&nc->mbox_receive, (void**)&pbuf)) {
 91            if (pbuf != NULL && (uint8_t*)pbuf != (uint8_t*)&recv_closed) {
 92                lwcell_pbuf_free_s(&pbuf); /* Free received data buffers */
 93            }
 94        }
 95        lwcell_sys_mbox_delete(&nc->mbox_receive);  /* Delete message queue */
 96        lwcell_sys_mbox_invalid(&nc->mbox_receive); /* Invalid handle */
 97    }
 98    if (protect) {
 99        lwcell_core_unlock();
100    }
101}
102
103/**
104 * \brief           Callback function for every server connection
105 * \param[in]       evt: Pointer to callback structure
106 * \return          Member of \ref lwcellr_t enumeration
107 */
108static lwcellr_t
109netconn_evt(lwcell_evt_t* evt) {
110    lwcell_conn_p conn;
111    lwcell_netconn_t* nc = NULL;
112    uint8_t close = 0;
113
114    conn = lwcell_conn_get_from_evt(evt); /* Get connection from event */
115    switch (lwcell_evt_get_type(evt)) {
116        /*
117         * A new connection has been active
118         * and should be handled by netconn API
119         */
120        case LWCELL_EVT_CONN_ACTIVE: {          /* A new connection active is active */
121            if (lwcell_conn_is_client(conn)) {  /* Was connection started by us? */
122                nc = lwcell_conn_get_arg(conn); /* Argument should be already set */
123                if (nc != NULL) {
124                    nc->conn = conn;           /* Save actual connection */
125                } else {
126                    close = 1;                 /* Close this connection, invalid netconn */
127                }
128            } else {
129                LWCELL_DEBUGF(LWCELL_CFG_DBG_NETCONN | LWCELL_DBG_TYPE_TRACE | LWCELL_DBG_LVL_WARNING,
130                             "[LWCELL NETCONN] Closing connection, it is not in client mode!\r\n");
131                close = 1; /* Close the connection at this point */
132            }
133
134            /* Decide if some events want to close the connection */
135            if (close) {
136                if (nc != NULL) {
137                    lwcell_conn_set_arg(conn, NULL); /* Reset argument */
138                    lwcell_netconn_delete(nc);       /* Free memory for API */
139                }
140                lwcell_conn_close(conn, 0);          /* Close the connection */
141                close = 0;
142            }
143            break;
144        }
145
146        /*
147         * We have a new data received which
148         * should have netconn structure as argument
149         */
150        case LWCELL_EVT_CONN_RECV: {
151            lwcell_pbuf_p pbuf;
152
153            nc = lwcell_conn_get_arg(conn);            /* Get API from connection */
154            pbuf = lwcell_evt_conn_recv_get_buff(evt); /* Get received buff */
155
156            lwcell_conn_recved(conn, pbuf);            /* Notify stack about received data */
157
158            lwcell_pbuf_ref(pbuf);                     /* Increase reference counter */
159            if (nc == NULL || !lwcell_sys_mbox_isvalid(&nc->mbox_receive)
160                || !lwcell_sys_mbox_putnow(&nc->mbox_receive, pbuf)) {
161                LWCELL_DEBUGF(LWCELL_CFG_DBG_NETCONN, "[LWCELL NETCONN] Ignoring more data for receive!\r\n");
162                lwcell_pbuf_free_s(&pbuf); /* Free pbuf */
163                return lwcellOKIGNOREMORE; /* Return OK to free the memory and ignore further data */
164            }
165            ++nc->rcv_packets;            /* Increase number of received packets */
166            LWCELL_DEBUGF(LWCELL_CFG_DBG_NETCONN | LWCELL_DBG_TYPE_TRACE,
167                         "[LWCELL NETCONN] Received pbuf contains %d bytes. Handle written to receive mbox\r\n",
168                         (int)lwcell_pbuf_length(pbuf, 0));
169            break;
170        }
171
172        /* Connection was just closed */
173        case LWCELL_EVT_CONN_CLOSE: {
174            nc = lwcell_conn_get_arg(conn); /* Get API from connection */
175
176            /*
177             * In case we have a netconn available,
178             * simply write pointer to received variable to indicate closed state
179             */
180            if (nc != NULL && lwcell_sys_mbox_isvalid(&nc->mbox_receive)) {
181                lwcell_sys_mbox_putnow(&nc->mbox_receive, (void*)&recv_closed);
182            }
183
184            break;
185        }
186        default: return lwcellERR;
187    }
188    return lwcellOK;
189}
190
191/**
192 * \brief           Global event callback function
193 * \param[in]       evt: Callback information and data
194 * \return          \ref lwcellOK on success, member of \ref lwcellr_t otherwise
195 */
196static lwcellr_t
197lwcell_evt(lwcell_evt_t* evt) {
198    switch (lwcell_evt_get_type(evt)) {
199        default: break;
200    }
201    return lwcellOK;
202}
203
204/**
205 * \brief           Create new netconn connection
206 * \param[in]       type: Netconn connection type
207 * \return          New netconn connection on success, `NULL` otherwise
208 */
209lwcell_netconn_p
210lwcell_netconn_new(lwcell_netconn_type_t type) {
211    lwcell_netconn_t* a;
212    static uint8_t first = 1;
213
214    /* Register only once! */
215    lwcell_core_lock();
216    if (first) {
217        first = 0;
218        lwcell_evt_register(lwcell_evt); /* Register global event function */
219    }
220    lwcell_core_unlock();
221    a = lwcell_mem_calloc(1, sizeof(*a)); /* Allocate memory for core object */
222    if (a != NULL) {
223        a->type = type;                  /* Save netconn type */
224        a->conn_timeout = 0;             /* Default connection timeout */
225        if (!lwcell_sys_mbox_create(
226                &a->mbox_receive,
227                LWCELL_CFG_NETCONN_RECEIVE_QUEUE_LEN)) { /* Allocate memory for receiving message box */
228            LWCELL_DEBUGF(LWCELL_CFG_DBG_NETCONN | LWCELL_DBG_TYPE_TRACE | LWCELL_DBG_LVL_DANGER,
229                         "[LWCELL NETCONN] Cannot create receive MBOX\r\n");
230            goto free_ret;
231        }
232        lwcell_core_lock();
233        if (netconn_list == NULL) { /* Add new netconn to the existing list */
234            netconn_list = a;
235        } else {
236            a->next = netconn_list; /* Add it to beginning of the list */
237            netconn_list = a;
238        }
239        lwcell_core_unlock();
240    }
241    return a;
242free_ret:
243    if (lwcell_sys_mbox_isvalid(&a->mbox_receive)) {
244        lwcell_sys_mbox_delete(&a->mbox_receive);
245        lwcell_sys_mbox_invalid(&a->mbox_receive);
246    }
247    if (a != NULL) {
248        lwcell_mem_free_s((void**)&a);
249    }
250    return NULL;
251}
252
253/**
254 * \brief           Delete netconn connection
255 * \param[in]       nc: Netconn handle
256 * \return          \ref lwcellOK on success, member of \ref lwcellr_t enumeration otherwise
257 */
258lwcellr_t
259lwcell_netconn_delete(lwcell_netconn_p nc) {
260    LWCELL_ASSERT(nc != NULL);
261
262    lwcell_core_lock();
263    flush_mboxes(nc, 0); /* Clear mboxes */
264
265    /* Remove netconn from linkedlist */
266    if (netconn_list == nc) {
267        netconn_list = netconn_list->next; /* Remove first from linked list */
268    } else if (netconn_list != NULL) {
269        lwcell_netconn_p tmp, prev;
270        /* Find element on the list */
271        for (prev = netconn_list, tmp = netconn_list->next; tmp != NULL; prev = tmp, tmp = tmp->next) {
272            if (nc == tmp) {
273                prev->next = tmp->next; /* Remove tmp from linked list */
274                break;
275            }
276        }
277    }
278    lwcell_core_unlock();
279
280    lwcell_mem_free_s((void**)&nc);
281    return lwcellOK;
282}
283
284/**
285 * \brief           Connect to server as client
286 * \param[in]       nc: Netconn handle
287 * \param[in]       host: Pointer to host, such as domain name or IP address in string format
288 * \param[in]       port: Target port to use
289 * \return          \ref lwcellOK if successfully connected, member of \ref lwcellr_t otherwise
290 */
291lwcellr_t
292lwcell_netconn_connect(lwcell_netconn_p nc, const char* host, lwcell_port_t port) {
293    lwcellr_t res;
294
295    LWCELL_ASSERT(nc != NULL);
296    LWCELL_ASSERT(host != NULL);
297    LWCELL_ASSERT(port > 0);
298
299    /*
300     * Start a new connection as client and:
301     *
302     *  - Set current netconn structure as argument
303     *  - Set netconn callback function for connection management
304     *  - Start connection in blocking mode
305     */
306    res = lwcell_conn_start(NULL, (lwcell_conn_type_t)nc->type, host, port, nc, netconn_evt, 1);
307    return res;
308}
309
310/**
311 * \brief           Write data to connection output buffers
312 * \note            This function may only be used on TCP or SSL connections
313 * \param[in]       nc: Netconn handle used to write data to
314 * \param[in]       data: Pointer to data to write
315 * \param[in]       btw: Number of bytes to write
316 * \return          \ref lwcellOK on success, member of \ref lwcellr_t enumeration otherwise
317 */
318lwcellr_t
319lwcell_netconn_write(lwcell_netconn_p nc, const void* data, size_t btw) {
320    size_t len, sent;
321    const uint8_t* d = data;
322    lwcellr_t res;
323
324    LWCELL_ASSERT(nc != NULL);
325    LWCELL_ASSERT(nc->type == LWCELL_NETCONN_TYPE_TCP || nc->type == LWCELL_NETCONN_TYPE_SSL);
326    LWCELL_ASSERT(lwcell_conn_is_active(nc->conn));
327
328    /*
329     * Several steps are done in write process
330     *
331     * 1. Check if buffer is set and check if there is something to write to it.
332     *    1. In case buffer will be full after copy, send it and free memory.
333     * 2. Check how many bytes we can write directly without need to copy
334     * 3. Try to allocate a new buffer and copy remaining input data to it
335     * 4. In case buffer allocation fails, send data directly (may affect on speed and effectivenes)
336     */
337
338    /* Step 1 */
339    if (nc->buff.buff != NULL) {                           /* Is there a write buffer ready to accept more data? */
340        len = LWCELL_MIN(nc->buff.len - nc->buff.ptr, btw); /* Get number of bytes we can write to buffer */
341        if (len > 0) {
342            LWCELL_MEMCPY(&nc->buff.buff[nc->buff.ptr], data, len); /* Copy memory to temporary write buffer */
343            d += len;
344            nc->buff.ptr += len;
345            btw -= len;
346        }
347
348        /* Step 1.1 */
349        if (nc->buff.ptr == nc->buff.len) {
350            res = lwcell_conn_send(nc->conn, nc->buff.buff, nc->buff.len, &sent, 1);
351
352            lwcell_mem_free_s((void**)&nc->buff.buff);
353            if (res != lwcellOK) {
354                return res;
355            }
356        } else {
357            return lwcellOK; /* Buffer is not yet full yet */
358        }
359    }
360
361    /* Step 2 */
362    if (btw >= LWCELL_CFG_CONN_MAX_DATA_LEN) {
363        size_t rem;
364        rem = btw % LWCELL_CFG_CONN_MAX_DATA_LEN;                 /* Get remaining bytes for max data length */
365        res = lwcell_conn_send(nc->conn, d, btw - rem, &sent, 1); /* Write data directly */
366        if (res != lwcellOK) {
367            return res;
368        }
369        d += sent;   /* Advance in data pointer */
370        btw -= sent; /* Decrease remaining data to send */
371    }
372
373    if (btw == 0) { /* Sent everything? */
374        return lwcellOK;
375    }
376
377    /* Step 3 */
378    if (nc->buff.buff == NULL) {                    /* Check if we should allocate a new buffer */
379        nc->buff.buff = lwcell_mem_malloc(sizeof(*nc->buff.buff) * LWCELL_CFG_CONN_MAX_DATA_LEN);
380        nc->buff.len = LWCELL_CFG_CONN_MAX_DATA_LEN; /* Save buffer length */
381        nc->buff.ptr = 0;                           /* Save buffer pointer */
382    }
383
384    /* Step 4 */
385    if (nc->buff.buff != NULL) {                              /* Memory available? */
386        LWCELL_MEMCPY(&nc->buff.buff[nc->buff.ptr], d, btw);   /* Copy data to buffer */
387        nc->buff.ptr += btw;
388    } else {                                                  /* Still no memory available? */
389        return lwcell_conn_send(nc->conn, data, btw, NULL, 1); /* Simply send directly blocking */
390    }
391    return lwcellOK;
392}
393
394/**
395 * \brief           Extended version of \ref lwcell_netconn_write with additional
396 *                  option to set custom flags.
397 * 
398 * \note            It is recommended to use this for full features support 
399 * 
400 * \param[in]       nc: Netconn handle used to write data to
401 * \param[in]       data: Pointer to data to write
402 * \param[in]       btw: Number of bytes to write
403 * \param           flags: Bitwise-ORed set of flags for netconn.
404 *                      Flags start with \ref LWCELL_NETCONN_FLAG_xxx
405 * \return          \ref lwcellOK on success, member of \ref lwcellr_t enumeration otherwise
406 */
407lwcellr_t
408lwcell_netconn_write_ex(lwcell_netconn_p nc, const void* data, size_t btw, uint16_t flags) {
409    lwcellr_t res = lwcell_netconn_write(nc, data, btw);
410    if (res == lwcellOK) {
411        if (flags & LWCELL_NETCONN_FLAG_FLUSH) {
412            res = lwcell_netconn_flush(nc);
413        }
414    }
415    return res;
416}
417
418/**
419 * \brief           Flush buffered data on netconn \e TCP/SSL connection
420 * \note            This function may only be used on \e TCP/SSL connection
421 * \param[in]       nc: Netconn handle to flush data
422 * \return          \ref lwcellOK on success, member of \ref lwcellr_t enumeration otherwise
423 */
424lwcellr_t
425lwcell_netconn_flush(lwcell_netconn_p nc) {
426    LWCELL_ASSERT(nc != NULL);
427    LWCELL_ASSERT(nc->type == LWCELL_NETCONN_TYPE_TCP || nc->type == LWCELL_NETCONN_TYPE_SSL);
428    LWCELL_ASSERT(lwcell_conn_is_active(nc->conn));
429
430    /*
431     * In case we have data in write buffer,
432     * flush them out to network
433     */
434    if (nc->buff.buff != NULL) {                                             /* Check remaining data */
435        if (nc->buff.ptr > 0) {                                              /* Do we have data in current buffer? */
436            lwcell_conn_send(nc->conn, nc->buff.buff, nc->buff.ptr, NULL, 1); /* Send data */
437        }
438        lwcell_mem_free_s((void**)&nc->buff.buff);
439    }
440    return lwcellOK;
441}
442
443/**
444 * \brief           Send data on \e UDP connection to default IP and port
445 * \param[in]       nc: Netconn handle used to send
446 * \param[in]       data: Pointer to data to write
447 * \param[in]       btw: Number of bytes to write
448 * \return          \ref lwcellOK on success, member of \ref lwcellr_t enumeration otherwise
449 */
450lwcellr_t
451lwcell_netconn_send(lwcell_netconn_p nc, const void* data, size_t btw) {
452    LWCELL_ASSERT(nc != NULL);
453    LWCELL_ASSERT(nc->type == LWCELL_NETCONN_TYPE_UDP);
454    LWCELL_ASSERT(lwcell_conn_is_active(nc->conn));
455
456    return lwcell_conn_send(nc->conn, data, btw, NULL, 1);
457}
458
459/**
460 * \brief           Send data on \e UDP connection to specific IP and port
461 * \note            Use this function in case of UDP type netconn
462 * \param[in]       nc: Netconn handle used to send
463 * \param[in]       ip: Pointer to IP address
464 * \param[in]       port: Port number used to send data
465 * \param[in]       data: Pointer to data to write
466 * \param[in]       btw: Number of bytes to write
467 * \return          \ref lwcellOK on success, member of \ref lwcellr_t enumeration otherwise
468 */
469lwcellr_t
470lwcell_netconn_sendto(lwcell_netconn_p nc, const lwcell_ip_t* ip, lwcell_port_t port, const void* data, size_t btw) {
471    LWCELL_ASSERT(nc != NULL);
472    LWCELL_ASSERT(nc->type == LWCELL_NETCONN_TYPE_UDP);
473    LWCELL_ASSERT(lwcell_conn_is_active(nc->conn));
474
475    return lwcell_conn_sendto(nc->conn, ip, port, data, btw, NULL, 1);
476}
477
478/**
479 * \brief           Receive data from connection
480 * \param[in]       nc: Netconn handle used to receive from
481 * \param[in]       pbuf: Pointer to pointer to save new receive buffer to.
482 *                     When function returns, user must check for valid pbuf value `pbuf != NULL`
483 * \return          \ref lwcellOK when new data ready,
484 * \return          \ref lwcellCLOSED when connection closed by remote side,
485 * \return          \ref lwcellTIMEOUT when receive timeout occurs
486 * \return          Any other member of \ref lwcellr_t otherwise
487 */
488lwcellr_t
489lwcell_netconn_receive(lwcell_netconn_p nc, lwcell_pbuf_p* pbuf) {
490    LWCELL_ASSERT(nc != NULL);
491    LWCELL_ASSERT(pbuf != NULL);
492
493    *pbuf = NULL;
494#if LWCELL_CFG_NETCONN_RECEIVE_TIMEOUT
495    /*
496     * Wait for new received data for up to specific timeout
497     * or throw error for timeout notification
498     */
499    if (nc->rcv_timeout == LWCELL_NETCONN_RECEIVE_NO_WAIT) {
500        if (!lwcell_sys_mbox_getnow(&nc->mbox_receive, (void**)pbuf)) {
501            return lwcellTIMEOUT;
502        }
503    } else if (lwcell_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, nc->rcv_timeout) == LWCELL_SYS_TIMEOUT) {
504        return lwcellTIMEOUT;
505    }
506#else  /* LWCELL_CFG_NETCONN_RECEIVE_TIMEOUT */
507    /* Forever wait for new receive packet */
508    lwcell_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, 0);
509#endif /* !LWCELL_CFG_NETCONN_RECEIVE_TIMEOUT */
510
511    /* Check if connection closed */
512    if ((uint8_t*)(*pbuf) == (uint8_t*)&recv_closed) {
513        *pbuf = NULL; /* Reset pbuf */
514        return lwcellCLOSED;
515    }
516    return lwcellOK; /* We have data available */
517}
518
519/**
520 * \brief           Close a netconn connection
521 * \param[in]       nc: Netconn handle to close
522 * \return          \ref lwcellOK on success, member of \ref lwcellr_t enumeration otherwise
523 */
524lwcellr_t
525lwcell_netconn_close(lwcell_netconn_p nc) {
526    lwcell_conn_p conn;
527
528    LWCELL_ASSERT(nc != NULL);
529    LWCELL_ASSERT(nc->conn != NULL);
530    LWCELL_ASSERT(lwcell_conn_is_active(nc->conn));
531
532    lwcell_netconn_flush(nc); /* Flush data and ignore result */
533    conn = nc->conn;
534    nc->conn = NULL;
535
536    lwcell_conn_set_arg(conn, NULL); /* Reset argument */
537    lwcell_conn_close(conn, 1);      /* Close the connection */
538    flush_mboxes(nc, 1);            /* Flush message queues */
539    return lwcellOK;
540}
541
542/**
543 * \brief           Get connection number used for netconn
544 * \param[in]       nc: Netconn handle
545 * \return          `-1` on failure, connection number between `0` and \ref LWCELL_CFG_MAX_CONNS otherwise
546 */
547int8_t
548lwcell_netconn_getconnnum(lwcell_netconn_p nc) {
549    if (nc != NULL && nc->conn != NULL) {
550        return lwcell_conn_getnum(nc->conn);
551    }
552    return -1;
553}
554
555#if LWCELL_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__
556
557/**
558 * \brief           Set timeout value for receiving data.
559 *
560 * When enabled, \ref lwcell_netconn_receive will only block for up to
561 * \e timeout value and will return if no new data within this time
562 *
563 * \param[in]       nc: Netconn handle
564 * \param[in]       timeout: Timeout in units of milliseconds.
565 *                      Set to `0` to disable timeout feature. Function blocks until data receive or connection closed
566 *                      Set to `> 0` to set maximum milliseconds to wait before timeout
567 *                      Set to \ref LWCELL_NETCONN_RECEIVE_NO_WAIT to enable non-blocking receive
568 */
569void
570lwcell_netconn_set_receive_timeout(lwcell_netconn_p nc, uint32_t timeout) {
571    nc->rcv_timeout = timeout;
572}
573
574/**
575 * \brief           Get netconn receive timeout value
576 * \param[in]       nc: Netconn handle
577 * \return          Timeout in units of milliseconds.
578 *                  If value is `0`, timeout is disabled (wait forever)
579 */
580uint32_t
581lwcell_netconn_get_receive_timeout(lwcell_netconn_p nc) {
582    return nc->rcv_timeout; /* Return receive timeout */
583}
584
585#endif /* LWCELL_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__ */
586
587#endif /* LWCELL_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 LWCELL_EVT_CONN_*, such as LWCELL_EVT_CONN_RECV. Check Event management for list of all connection events.

Connection events callback function is set when client (application starts connection) starts a new connection with lwcell_conn_start() function

An example of client with its dedicated event callback function
  1#include "client.h"
  2#include "lwcell/lwcell.h"
  3#include "lwcell/lwcell_network_api.h"
  4
  5#if LWCELL_CFG_CONN
  6
  7/* Host parameter */
  8#define CONN_HOST "example.com"
  9#define CONN_PORT 80
 10
 11static lwcellr_t conn_callback_func(lwcell_evt_t* evt);
 12
 13/**
 14 * \brief           Request data for connection
 15 */
 16static const uint8_t req_data[] = ""
 17                                  "GET / HTTP/1.1\r\n"
 18                                  "Host: " CONN_HOST "\r\n"
 19                                  "Connection: close\r\n"
 20                                  "\r\n";
 21
 22/**
 23 * \brief           Start a new connection(s) as client
 24 */
 25void
 26client_connect(void) {
 27    lwcellr_t res;
 28
 29    /* Attach to GSM network */
 30    lwcell_network_request_attach();
 31
 32    /* Start a new connection as client in non-blocking mode */
 33    if ((res = lwcell_conn_start(NULL, LWCELL_CONN_TYPE_TCP, "example.com", 80, NULL, conn_callback_func, 0))
 34        == lwcellOK) {
 35        printf("Connection to " CONN_HOST " started...\r\n");
 36    } else {
 37        printf("Cannot start connection to " CONN_HOST "!\r\n");
 38    }
 39}
 40
 41/**
 42 * \brief           Event callback function for connection-only
 43 * \param[in]       evt: Event information with data
 44 * \return          \ref lwcellOK on success, member of \ref lwcellr_t otherwise
 45 */
 46static lwcellr_t
 47conn_callback_func(lwcell_evt_t* evt) {
 48    lwcell_conn_p conn;
 49    lwcellr_t res;
 50    uint8_t conn_num;
 51
 52    conn = lwcell_conn_get_from_evt(evt); /* Get connection handle from event */
 53    if (conn == NULL) {
 54        return lwcellERR;
 55    }
 56    conn_num = lwcell_conn_getnum(conn); /* Get connection number for identification */
 57    switch (lwcell_evt_get_type(evt)) {
 58        case LWCELL_EVT_CONN_ACTIVE: { /* Connection just active */
 59            printf("Connection %d active!\r\n", (int)conn_num);
 60            res = lwcell_conn_send(conn, req_data, sizeof(req_data) - 1, NULL,
 61                                   0); /* Start sending data in non-blocking mode */
 62            if (res == lwcellOK) {
 63                printf("Sending request data to server...\r\n");
 64            } else {
 65                printf("Cannot send request data to server. Closing connection manually...\r\n");
 66                lwcell_conn_close(conn, 0); /* Close the connection */
 67            }
 68            break;
 69        }
 70        case LWCELL_EVT_CONN_CLOSE: { /* Connection closed */
 71            if (lwcell_evt_conn_close_is_forced(evt)) {
 72                printf("Connection %d closed by client!\r\n", (int)conn_num);
 73            } else {
 74                printf("Connection %d closed by remote side!\r\n", (int)conn_num);
 75            }
 76            break;
 77        }
 78        case LWCELL_EVT_CONN_SEND: { /* Data send event */
 79            lwcellr_t res = lwcell_evt_conn_send_get_result(evt);
 80            if (res == lwcellOK) {
 81                printf("Data sent successfully on connection %d...waiting to receive data from remote side...\r\n",
 82                       (int)conn_num);
 83            } else {
 84                printf("Error while sending data on connection %d!\r\n", (int)conn_num);
 85            }
 86            break;
 87        }
 88        case LWCELL_EVT_CONN_RECV: { /* Data received from remote side */
 89            lwcell_pbuf_p pbuf = lwcell_evt_conn_recv_get_buff(evt);
 90            lwcell_conn_recved(conn, pbuf); /* Notify stack about received pbuf */
 91            printf("Received %d bytes on connection %d..\r\n", (int)lwcell_pbuf_length(pbuf, 1), (int)conn_num);
 92            break;
 93        }
 94        case LWCELL_EVT_CONN_ERROR: { /* Error connecting to server */
 95            const char* host = lwcell_evt_conn_error_get_host(evt);
 96            lwcell_port_t port = lwcell_evt_conn_error_get_port(evt);
 97            printf("Error connecting to %s:%d\r\n", host, (int)port);
 98            break;
 99        }
100        default: break;
101    }
102    return lwcellOK;
103}
104
105#endif /* LWCELL_CFG_CONN */

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 SMS send. It uses custom API callback function to notify application when command has been executed successfully

Simple example for API call event, using DNS module
 1/* Somewhere in thread function */
 2
 3/* Get device hostname in blocking mode */
 4/* Function returns actual result */
 5if (lwcell_sms_send("+0123456789", "text", NULL, NULL, 1 /* 1 means blocking call */) == lwcellOK) {
 6    /* At this point we have valid result from device */
 7    printf("SMS sent successfully\r\n");
 8} else {
 9    printf("Error trying to send SMS..\r\n");
10}