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

Tip

Implementation of Netconn API leverages lwgsm_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            lwgsm_netconn.c
  3 * \brief           API functions for sequential calls
  4 */
  5
  6/*
  7 * Copyright (c) 2023 Tilen MAJERLE
  8 *
  9 * Permission is hereby granted, free of charge, to any person
 10 * obtaining a copy of this software and associated documentation
 11 * files (the "Software"), to deal in the Software without restriction,
 12 * including without limitation the rights to use, copy, modify, merge,
 13 * publish, distribute, sublicense, and/or sell copies of the Software,
 14 * and to permit persons to whom the Software is furnished to do so,
 15 * subject to the following conditions:
 16 *
 17 * The above copyright notice and this permission notice shall be
 18 * included in all copies or substantial portions of the Software.
 19 *
 20 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
 21 * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
 22 * OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE
 23 * AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
 24 * HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
 25 * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
 26 * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
 27 * OTHER DEALINGS IN THE SOFTWARE.
 28 *
 29 * This file is part of LwGSM - Lightweight GSM-AT library.
 30 *
 31 * Author:          Tilen MAJERLE <tilen@majerle.eu>
 32 * Version:         v0.1.1
 33 */
 34#include "lwgsm/lwgsm_netconn.h"
 35#include "lwgsm/lwgsm_conn.h"
 36#include "lwgsm/lwgsm_mem.h"
 37#include "lwgsm/lwgsm_private.h"
 38
 39#if LWGSM_CFG_NETCONN || __DOXYGEN__
 40
 41/* Check conditions */
 42#if !LWGSM_CFG_CONN
 43#error "LWGSM_CFG_CONN must be enabled for NETCONN API!"
 44#endif /* !LWGSM_CFG_CONN */
 45
 46#if LWGSM_CFG_NETCONN_RECEIVE_QUEUE_LEN < 2
 47#error "LWGSM_CFG_NETCONN_RECEIVE_QUEUE_LEN must be greater or equal to 2"
 48#endif /* LWGSM_CFG_NETCONN_RECEIVE_QUEUE_LEN < 2 */
 49
 50/**
 51 * \brief           Sequential API structure
 52 */
 53typedef struct lwgsm_netconn {
 54    struct lwgsm_netconn* next; /*!< Linked list entry */
 55
 56    lwgsm_netconn_type_t type; /*!< Netconn type */
 57
 58    size_t rcv_packets; /*!< Number of received packets so far on this connection */
 59    lwgsm_conn_p conn;  /*!< Pointer to actual connection */
 60
 61    lwgsm_sys_mbox_t mbox_receive; /*!< Message queue for receive mbox */
 62
 63    lwgsm_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 LWGSM_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__
 71    uint32_t rcv_timeout; /*!< Receive timeout in unit of milliseconds */
 72#endif
 73} lwgsm_netconn_t;
 74
 75static uint8_t recv_closed = 0xFF;
 76static lwgsm_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(lwgsm_netconn_t* nc, uint8_t protect) {
 85    lwgsm_pbuf_p pbuf;
 86    if (protect) {
 87        lwgsm_core_lock();
 88    }
 89    if (lwgsm_sys_mbox_isvalid(&nc->mbox_receive)) {
 90        while (lwgsm_sys_mbox_getnow(&nc->mbox_receive, (void**)&pbuf)) {
 91            if (pbuf != NULL && (uint8_t*)pbuf != (uint8_t*)&recv_closed) {
 92                lwgsm_pbuf_free(pbuf); /* Free received data buffers */
 93            }
 94        }
 95        lwgsm_sys_mbox_delete(&nc->mbox_receive);  /* Delete message queue */
 96        lwgsm_sys_mbox_invalid(&nc->mbox_receive); /* Invalid handle */
 97    }
 98    if (protect) {
 99        lwgsm_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 lwgsmr_t enumeration
107 */
108static lwgsmr_t
109netconn_evt(lwgsm_evt_t* evt) {
110    lwgsm_conn_p conn;
111    lwgsm_netconn_t* nc = NULL;
112    uint8_t close = 0;
113
114    conn = lwgsm_conn_get_from_evt(evt); /* Get connection from event */
115    switch (lwgsm_evt_get_type(evt)) {
116        /*
117         * A new connection has been active
118         * and should be handled by netconn API
119         */
120        case LWGSM_EVT_CONN_ACTIVE: {          /* A new connection active is active */
121            if (lwgsm_conn_is_client(conn)) {  /* Was connection started by us? */
122                nc = lwgsm_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                LWGSM_DEBUGF(LWGSM_CFG_DBG_NETCONN | LWGSM_DBG_TYPE_TRACE | LWGSM_DBG_LVL_WARNING,
130                             "[LWGSM 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                    lwgsm_conn_set_arg(conn, NULL); /* Reset argument */
138                    lwgsm_netconn_delete(nc);       /* Free memory for API */
139                }
140                lwgsm_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 LWGSM_EVT_CONN_RECV: {
151            lwgsm_pbuf_p pbuf;
152
153            nc = lwgsm_conn_get_arg(conn);            /* Get API from connection */
154            pbuf = lwgsm_evt_conn_recv_get_buff(evt); /* Get received buff */
155
156            lwgsm_conn_recved(conn, pbuf); /* Notify stack about received data */
157
158            lwgsm_pbuf_ref(pbuf); /* Increase reference counter */
159            if (nc == NULL || !lwgsm_sys_mbox_isvalid(&nc->mbox_receive)
160                || !lwgsm_sys_mbox_putnow(&nc->mbox_receive, pbuf)) {
161                LWGSM_DEBUGF(LWGSM_CFG_DBG_NETCONN, "[LWGSM NETCONN] Ignoring more data for receive!\r\n");
162                lwgsm_pbuf_free(pbuf);    /* Free pbuf */
163                return lwgsmOKIGNOREMORE; /* Return OK to free the memory and ignore further data */
164            }
165            ++nc->rcv_packets; /* Increase number of received packets */
166            LWGSM_DEBUGF(LWGSM_CFG_DBG_NETCONN | LWGSM_DBG_TYPE_TRACE,
167                         "[LWGSM NETCONN] Received pbuf contains %d bytes. Handle written to receive mbox\r\n",
168                         (int)lwgsm_pbuf_length(pbuf, 0));
169            break;
170        }
171
172        /* Connection was just closed */
173        case LWGSM_EVT_CONN_CLOSE: {
174            nc = lwgsm_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 && lwgsm_sys_mbox_isvalid(&nc->mbox_receive)) {
181                lwgsm_sys_mbox_putnow(&nc->mbox_receive, (void*)&recv_closed);
182            }
183
184            break;
185        }
186        default:
187            return lwgsmERR;
188    }
189    return lwgsmOK;
190}
191
192/**
193 * \brief           Global event callback function
194 * \param[in]       evt: Callback information and data
195 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t otherwise
196 */
197static lwgsmr_t
198lwgsm_evt(lwgsm_evt_t* evt) {
199    switch (lwgsm_evt_get_type(evt)) {
200        default:
201            break;
202    }
203    return lwgsmOK;
204}
205
206/**
207 * \brief           Create new netconn connection
208 * \param[in]       type: Netconn connection type
209 * \return          New netconn connection on success, `NULL` otherwise
210 */
211lwgsm_netconn_p
212lwgsm_netconn_new(lwgsm_netconn_type_t type) {
213    lwgsm_netconn_t* a;
214    static uint8_t first = 1;
215
216    /* Register only once! */
217    lwgsm_core_lock();
218    if (first) {
219        first = 0;
220        lwgsm_evt_register(lwgsm_evt); /* Register global event function */
221    }
222    lwgsm_core_unlock();
223    a = lwgsm_mem_calloc(1, sizeof(*a)); /* Allocate memory for core object */
224    if (a != NULL) {
225        a->type = type;      /* Save netconn type */
226        a->conn_timeout = 0; /* Default connection timeout */
227        if (!lwgsm_sys_mbox_create(
228                &a->mbox_receive,
229                LWGSM_CFG_NETCONN_RECEIVE_QUEUE_LEN)) { /* Allocate memory for receiving message box */
230            LWGSM_DEBUGF(LWGSM_CFG_DBG_NETCONN | LWGSM_DBG_TYPE_TRACE | LWGSM_DBG_LVL_DANGER,
231                         "[LWGSM NETCONN] Cannot create receive MBOX\r\n");
232            goto free_ret;
233        }
234        lwgsm_core_lock();
235        if (netconn_list == NULL) { /* Add new netconn to the existing list */
236            netconn_list = a;
237        } else {
238            a->next = netconn_list; /* Add it to beginning of the list */
239            netconn_list = a;
240        }
241        lwgsm_core_unlock();
242    }
243    return a;
244free_ret:
245    if (lwgsm_sys_mbox_isvalid(&a->mbox_receive)) {
246        lwgsm_sys_mbox_delete(&a->mbox_receive);
247        lwgsm_sys_mbox_invalid(&a->mbox_receive);
248    }
249    if (a != NULL) {
250        lwgsm_mem_free_s((void**)&a);
251    }
252    return NULL;
253}
254
255/**
256 * \brief           Delete netconn connection
257 * \param[in]       nc: Netconn handle
258 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
259 */
260lwgsmr_t
261lwgsm_netconn_delete(lwgsm_netconn_p nc) {
262    LWGSM_ASSERT(nc != NULL);
263
264    lwgsm_core_lock();
265    flush_mboxes(nc, 0); /* Clear mboxes */
266
267    /* Remove netconn from linkedlist */
268    if (netconn_list == nc) {
269        netconn_list = netconn_list->next; /* Remove first from linked list */
270    } else if (netconn_list != NULL) {
271        lwgsm_netconn_p tmp, prev;
272        /* Find element on the list */
273        for (prev = netconn_list, tmp = netconn_list->next; tmp != NULL; prev = tmp, tmp = tmp->next) {
274            if (nc == tmp) {
275                prev->next = tmp->next; /* Remove tmp from linked list */
276                break;
277            }
278        }
279    }
280    lwgsm_core_unlock();
281
282    lwgsm_mem_free_s((void**)&nc);
283    return lwgsmOK;
284}
285
286/**
287 * \brief           Connect to server as client
288 * \param[in]       nc: Netconn handle
289 * \param[in]       host: Pointer to host, such as domain name or IP address in string format
290 * \param[in]       port: Target port to use
291 * \return          \ref lwgsmOK if successfully connected, member of \ref lwgsmr_t otherwise
292 */
293lwgsmr_t
294lwgsm_netconn_connect(lwgsm_netconn_p nc, const char* host, lwgsm_port_t port) {
295    lwgsmr_t res;
296
297    LWGSM_ASSERT(nc != NULL);
298    LWGSM_ASSERT(host != NULL);
299    LWGSM_ASSERT(port > 0);
300
301    /*
302     * Start a new connection as client and:
303     *
304     *  - Set current netconn structure as argument
305     *  - Set netconn callback function for connection management
306     *  - Start connection in blocking mode
307     */
308    res = lwgsm_conn_start(NULL, (lwgsm_conn_type_t)nc->type, host, port, nc, netconn_evt, 1);
309    return res;
310}
311
312/**
313 * \brief           Write data to connection output buffers
314 * \note            This function may only be used on TCP or SSL connections
315 * \param[in]       nc: Netconn handle used to write data to
316 * \param[in]       data: Pointer to data to write
317 * \param[in]       btw: Number of bytes to write
318 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
319 */
320lwgsmr_t
321lwgsm_netconn_write(lwgsm_netconn_p nc, const void* data, size_t btw) {
322    size_t len, sent;
323    const uint8_t* d = data;
324    lwgsmr_t res;
325
326    LWGSM_ASSERT(nc != NULL);
327    LWGSM_ASSERT(nc->type == LWGSM_NETCONN_TYPE_TCP || nc->type == LWGSM_NETCONN_TYPE_SSL);
328    LWGSM_ASSERT(lwgsm_conn_is_active(nc->conn));
329
330    /*
331     * Several steps are done in write process
332     *
333     * 1. Check if buffer is set and check if there is something to write to it.
334     *    1. In case buffer will be full after copy, send it and free memory.
335     * 2. Check how many bytes we can write directly without need to copy
336     * 3. Try to allocate a new buffer and copy remaining input data to it
337     * 4. In case buffer allocation fails, send data directly (may affect on speed and effectivenes)
338     */
339
340    /* Step 1 */
341    if (nc->buff.buff != NULL) {                           /* Is there a write buffer ready to accept more data? */
342        len = LWGSM_MIN(nc->buff.len - nc->buff.ptr, btw); /* Get number of bytes we can write to buffer */
343        if (len > 0) {
344            LWGSM_MEMCPY(&nc->buff.buff[nc->buff.ptr], data, len); /* Copy memory to temporary write buffer */
345            d += len;
346            nc->buff.ptr += len;
347            btw -= len;
348        }
349
350        /* Step 1.1 */
351        if (nc->buff.ptr == nc->buff.len) {
352            res = lwgsm_conn_send(nc->conn, nc->buff.buff, nc->buff.len, &sent, 1);
353
354            lwgsm_mem_free_s((void**)&nc->buff.buff);
355            if (res != lwgsmOK) {
356                return res;
357            }
358        } else {
359            return lwgsmOK; /* Buffer is not yet full yet */
360        }
361    }
362
363    /* Step 2 */
364    if (btw >= LWGSM_CFG_CONN_MAX_DATA_LEN) {
365        size_t rem;
366        rem = btw % LWGSM_CFG_CONN_MAX_DATA_LEN;                 /* Get remaining bytes for max data length */
367        res = lwgsm_conn_send(nc->conn, d, btw - rem, &sent, 1); /* Write data directly */
368        if (res != lwgsmOK) {
369            return res;
370        }
371        d += sent;   /* Advance in data pointer */
372        btw -= sent; /* Decrease remaining data to send */
373    }
374
375    if (btw == 0) { /* Sent everything? */
376        return lwgsmOK;
377    }
378
379    /* Step 3 */
380    if (nc->buff.buff == NULL) { /* Check if we should allocate a new buffer */
381        nc->buff.buff = lwgsm_mem_malloc(sizeof(*nc->buff.buff) * LWGSM_CFG_CONN_MAX_DATA_LEN);
382        nc->buff.len = LWGSM_CFG_CONN_MAX_DATA_LEN; /* Save buffer length */
383        nc->buff.ptr = 0;                           /* Save buffer pointer */
384    }
385
386    /* Step 4 */
387    if (nc->buff.buff != NULL) {                            /* Memory available? */
388        LWGSM_MEMCPY(&nc->buff.buff[nc->buff.ptr], d, btw); /* Copy data to buffer */
389        nc->buff.ptr += btw;
390    } else {                                                  /* Still no memory available? */
391        return lwgsm_conn_send(nc->conn, data, btw, NULL, 1); /* Simply send directly blocking */
392    }
393    return lwgsmOK;
394}
395
396/**
397 * \brief           Flush buffered data on netconn \e TCP/SSL connection
398 * \note            This function may only be used on \e TCP/SSL connection
399 * \param[in]       nc: Netconn handle to flush data
400 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
401 */
402lwgsmr_t
403lwgsm_netconn_flush(lwgsm_netconn_p nc) {
404    LWGSM_ASSERT(nc != NULL);
405    LWGSM_ASSERT(nc->type == LWGSM_NETCONN_TYPE_TCP || nc->type == LWGSM_NETCONN_TYPE_SSL);
406    LWGSM_ASSERT(lwgsm_conn_is_active(nc->conn));
407
408    /*
409     * In case we have data in write buffer,
410     * flush them out to network
411     */
412    if (nc->buff.buff != NULL) {                                             /* Check remaining data */
413        if (nc->buff.ptr > 0) {                                              /* Do we have data in current buffer? */
414            lwgsm_conn_send(nc->conn, nc->buff.buff, nc->buff.ptr, NULL, 1); /* Send data */
415        }
416        lwgsm_mem_free_s((void**)&nc->buff.buff);
417    }
418    return lwgsmOK;
419}
420
421/**
422 * \brief           Send data on \e UDP connection to default IP and port
423 * \param[in]       nc: Netconn handle used to send
424 * \param[in]       data: Pointer to data to write
425 * \param[in]       btw: Number of bytes to write
426 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
427 */
428lwgsmr_t
429lwgsm_netconn_send(lwgsm_netconn_p nc, const void* data, size_t btw) {
430    LWGSM_ASSERT(nc != NULL);
431    LWGSM_ASSERT(nc->type == LWGSM_NETCONN_TYPE_UDP);
432    LWGSM_ASSERT(lwgsm_conn_is_active(nc->conn));
433
434    return lwgsm_conn_send(nc->conn, data, btw, NULL, 1);
435}
436
437/**
438 * \brief           Send data on \e UDP connection to specific IP and port
439 * \note            Use this function in case of UDP type netconn
440 * \param[in]       nc: Netconn handle used to send
441 * \param[in]       ip: Pointer to IP address
442 * \param[in]       port: Port number used to send data
443 * \param[in]       data: Pointer to data to write
444 * \param[in]       btw: Number of bytes to write
445 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
446 */
447lwgsmr_t
448lwgsm_netconn_sendto(lwgsm_netconn_p nc, const lwgsm_ip_t* ip, lwgsm_port_t port, const void* data, size_t btw) {
449    LWGSM_ASSERT(nc != NULL);
450    LWGSM_ASSERT(nc->type == LWGSM_NETCONN_TYPE_UDP);
451    LWGSM_ASSERT(lwgsm_conn_is_active(nc->conn));
452
453    return lwgsm_conn_sendto(nc->conn, ip, port, data, btw, NULL, 1);
454}
455
456/**
457 * \brief           Receive data from connection
458 * \param[in]       nc: Netconn handle used to receive from
459 * \param[in]       pbuf: Pointer to pointer to save new receive buffer to.
460 *                     When function returns, user must check for valid pbuf value `pbuf != NULL`
461 * \return          \ref lwgsmOK when new data ready,
462 * \return          \ref lwgsmCLOSED when connection closed by remote side,
463 * \return          \ref lwgsmTIMEOUT when receive timeout occurs
464 * \return          Any other member of \ref lwgsmr_t otherwise
465 */
466lwgsmr_t
467lwgsm_netconn_receive(lwgsm_netconn_p nc, lwgsm_pbuf_p* pbuf) {
468    LWGSM_ASSERT(nc != NULL);
469    LWGSM_ASSERT(pbuf != NULL);
470
471    *pbuf = NULL;
472#if LWGSM_CFG_NETCONN_RECEIVE_TIMEOUT
473    /*
474     * Wait for new received data for up to specific timeout
475     * or throw error for timeout notification
476     */
477    if (lwgsm_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, nc->rcv_timeout) == LWGSM_SYS_TIMEOUT) {
478        return lwgsmTIMEOUT;
479    }
480#else  /* LWGSM_CFG_NETCONN_RECEIVE_TIMEOUT */
481    /* Forever wait for new receive packet */
482    lwgsm_sys_mbox_get(&nc->mbox_receive, (void**)pbuf, 0);
483#endif /* !LWGSM_CFG_NETCONN_RECEIVE_TIMEOUT */
484
485    /* Check if connection closed */
486    if ((uint8_t*)(*pbuf) == (uint8_t*)&recv_closed) {
487        *pbuf = NULL; /* Reset pbuf */
488        return lwgsmCLOSED;
489    }
490    return lwgsmOK; /* We have data available */
491}
492
493/**
494 * \brief           Close a netconn connection
495 * \param[in]       nc: Netconn handle to close
496 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t enumeration otherwise
497 */
498lwgsmr_t
499lwgsm_netconn_close(lwgsm_netconn_p nc) {
500    lwgsm_conn_p conn;
501
502    LWGSM_ASSERT(nc != NULL);
503    LWGSM_ASSERT(nc->conn != NULL);
504    LWGSM_ASSERT(lwgsm_conn_is_active(nc->conn));
505
506    lwgsm_netconn_flush(nc); /* Flush data and ignore result */
507    conn = nc->conn;
508    nc->conn = NULL;
509
510    lwgsm_conn_set_arg(conn, NULL); /* Reset argument */
511    lwgsm_conn_close(conn, 1);      /* Close the connection */
512    flush_mboxes(nc, 1);            /* Flush message queues */
513    return lwgsmOK;
514}
515
516/**
517 * \brief           Get connection number used for netconn
518 * \param[in]       nc: Netconn handle
519 * \return          `-1` on failure, connection number between `0` and \ref LWGSM_CFG_MAX_CONNS otherwise
520 */
521int8_t
522lwgsm_netconn_getconnnum(lwgsm_netconn_p nc) {
523    if (nc != NULL && nc->conn != NULL) {
524        return lwgsm_conn_getnum(nc->conn);
525    }
526    return -1;
527}
528
529#if LWGSM_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__
530
531/**
532 * \brief           Set timeout value for receiving data.
533 *
534 * When enabled, \ref lwgsm_netconn_receive will only block for up to
535 * \e timeout value and will return if no new data within this time
536 *
537 * \param[in]       nc: Netconn handle
538 * \param[in]       timeout: Timeout in units of milliseconds.
539 *                  Set to `0` to disable timeout for \ref lwgsm_netconn_receive function
540 */
541void
542lwgsm_netconn_set_receive_timeout(lwgsm_netconn_p nc, uint32_t timeout) {
543    nc->rcv_timeout = timeout;
544}
545
546/**
547 * \brief           Get netconn receive timeout value
548 * \param[in]       nc: Netconn handle
549 * \return          Timeout in units of milliseconds.
550 *                  If value is `0`, timeout is disabled (wait forever)
551 */
552uint32_t
553lwgsm_netconn_get_receive_timeout(lwgsm_netconn_p nc) {
554    return nc->rcv_timeout; /* Return receive timeout */
555}
556
557#endif /* LWGSM_CFG_NETCONN_RECEIVE_TIMEOUT || __DOXYGEN__ */
558
559#endif /* LWGSM_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 LWGSM_EVT_CONN_*, such as LWGSM_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 lwgsm_conn_start() function

An example of client with its dedicated event callback function
  1#include "client.h"
  2#include "lwgsm/lwgsm.h"
  3#include "lwgsm/lwgsm_network_api.h"
  4
  5/* Host parameter */
  6#define CONN_HOST           "example.com"
  7#define CONN_PORT           80
  8
  9static lwgsmr_t   conn_callback_func(lwgsm_evt_t* evt);
 10
 11/**
 12 * \brief           Request data for connection
 13 */
 14static const
 15uint8_t req_data[] = ""
 16                     "GET / HTTP/1.1\r\n"
 17                     "Host: " CONN_HOST "\r\n"
 18                     "Connection: close\r\n"
 19                     "\r\n";
 20
 21/**
 22 * \brief           Start a new connection(s) as client
 23 */
 24void
 25client_connect(void) {
 26    lwgsmr_t res;
 27
 28    /* Attach to GSM network */
 29    lwgsm_network_request_attach();
 30
 31    /* Start a new connection as client in non-blocking mode */
 32    if ((res = lwgsm_conn_start(NULL, LWGSM_CONN_TYPE_TCP, "example.com", 80, NULL, conn_callback_func, 0)) == lwgsmOK) {
 33        printf("Connection to " CONN_HOST " started...\r\n");
 34    } else {
 35        printf("Cannot start connection to " CONN_HOST "!\r\n");
 36    }
 37}
 38
 39/**
 40 * \brief           Event callback function for connection-only
 41 * \param[in]       evt: Event information with data
 42 * \return          \ref lwgsmOK on success, member of \ref lwgsmr_t otherwise
 43 */
 44static lwgsmr_t
 45conn_callback_func(lwgsm_evt_t* evt) {
 46    lwgsm_conn_p conn;
 47    lwgsmr_t res;
 48    uint8_t conn_num;
 49
 50    conn = lwgsm_conn_get_from_evt(evt);          /* Get connection handle from event */
 51    if (conn == NULL) {
 52        return lwgsmERR;
 53    }
 54    conn_num = lwgsm_conn_getnum(conn);           /* Get connection number for identification */
 55    switch (lwgsm_evt_get_type(evt)) {
 56        case LWGSM_EVT_CONN_ACTIVE: {             /* Connection just active */
 57            printf("Connection %d active!\r\n", (int)conn_num);
 58            res = lwgsm_conn_send(conn, req_data, sizeof(req_data) - 1, NULL, 0); /* Start sending data in non-blocking mode */
 59            if (res == lwgsmOK) {
 60                printf("Sending request data to server...\r\n");
 61            } else {
 62                printf("Cannot send request data to server. Closing connection manually...\r\n");
 63                lwgsm_conn_close(conn, 0);        /* Close the connection */
 64            }
 65            break;
 66        }
 67        case LWGSM_EVT_CONN_CLOSE: {              /* Connection closed */
 68            if (lwgsm_evt_conn_close_is_forced(evt)) {
 69                printf("Connection %d closed by client!\r\n", (int)conn_num);
 70            } else {
 71                printf("Connection %d closed by remote side!\r\n", (int)conn_num);
 72            }
 73            break;
 74        }
 75        case LWGSM_EVT_CONN_SEND: {               /* Data send event */
 76            lwgsmr_t res = lwgsm_evt_conn_send_get_result(evt);
 77            if (res == lwgsmOK) {
 78                printf("Data sent successfully on connection %d...waiting to receive data from remote side...\r\n", (int)conn_num);
 79            } else {
 80                printf("Error while sending data on connection %d!\r\n", (int)conn_num);
 81            }
 82            break;
 83        }
 84        case LWGSM_EVT_CONN_RECV: {               /* Data received from remote side */
 85            lwgsm_pbuf_p pbuf = lwgsm_evt_conn_recv_get_buff(evt);
 86            lwgsm_conn_recved(conn, pbuf);        /* Notify stack about received pbuf */
 87            printf("Received %d bytes on connection %d..\r\n", (int)lwgsm_pbuf_length(pbuf, 1), (int)conn_num);
 88            break;
 89        }
 90        case LWGSM_EVT_CONN_ERROR: {              /* Error connecting to server */
 91            const char* host = lwgsm_evt_conn_error_get_host(evt);
 92            lwgsm_port_t port = lwgsm_evt_conn_error_get_port(evt);
 93            printf("Error connecting to %s:%d\r\n", host, (int)port);
 94            break;
 95        }
 96        default:
 97            break;
 98    }
 99    return lwgsmOK;
100}

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 (lwgsm_sms_send("+0123456789", "text", NULL, NULL, 1 /* 1 means blocking call */) == lwgsmOK) {
 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}