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:
Producing thread
Processing thread
Input thread, when
LWCELL_CFG_INPUT_USE_PROCESS
is enabled andlwcell_input_process()
function is called
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.
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
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
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}