mqtt_sn_client_misc.c
Go to the documentation of this file.
1 /**
2  * @file mqtt_sn_client_misc.c
3  * @brief Helper functions for MQTT-SN client
4  *
5  * @section License
6  *
7  * SPDX-License-Identifier: GPL-2.0-or-later
8  *
9  * Copyright (C) 2010-2019 Oryx Embedded SARL. All rights reserved.
10  *
11  * This file is part of CycloneTCP Open.
12  *
13  * This program is free software; you can redistribute it and/or
14  * modify it under the terms of the GNU General Public License
15  * as published by the Free Software Foundation; either version 2
16  * of the License, or (at your option) any later version.
17  *
18  * This program is distributed in the hope that it will be useful,
19  * but WITHOUT ANY WARRANTY; without even the implied warranty of
20  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21  * GNU General Public License for more details.
22  *
23  * You should have received a copy of the GNU General Public License
24  * along with this program; if not, write to the Free Software Foundation,
25  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
26  *
27  * @author Oryx Embedded SARL (www.oryx-embedded.com)
28  * @version 1.9.6
29  **/
30 
31 //Switch to the appropriate trace level
32 #define TRACE_LEVEL MQTT_SN_TRACE_LEVEL
33 
34 //Dependencies
35 #include "core/net.h"
36 #include "mqtt_sn/mqtt_sn_client.h"
40 #include "debug.h"
41 
42 //Check TCP/IP stack configuration
43 #if (MQTT_SN_CLIENT_SUPPORT == ENABLED)
44 
45 
46 /**
47  * @brief Process MQTT-SN client events
48  * @param[in] context Pointer to the MQTT-SN client context
49  * @param[in] timeout Maximum time to wait before returning
50  * @return Error code
51  **/
52 
54  systime_t timeout)
55 {
56  error_t error;
57  systime_t d;
58  systime_t startTime;
59  systime_t currentTime;
60  IpAddr ipAddr;
61  uint16_t port;
62 
63  //Flush buffer
64  context->message.length = 0;
65 
66  //Save current time
67  currentTime = osGetSystemTime();
68  startTime = currentTime;
69 
70  //Initialize status code
71  error = NO_ERROR;
72 
73  //Process events
74  do
75  {
76  //Maximum time to wait for an incoming datagram
77  if(timeCompare(startTime + timeout, currentTime) > 0)
78  d = startTime + timeout - currentTime;
79  else
80  d = 0;
81 
82  //Limit the delay
84 
85  //Wait for an incoming datagram
86  error = mqttSnClientReceiveDatagram(context, &ipAddr, &port,
87  context->message.buffer, MQTT_SN_MAX_MSG_SIZE,
88  &context->message.length, d);
89 
90  //Get current time
91  currentTime = osGetSystemTime();
92 
93  //Any datagram received?
94  if(error == NO_ERROR)
95  {
96  //Terminate the payload with a NULL character
97  context->message.buffer[context->message.length] = '\0';
98 
99  //Process the received MQTT-SN message
100  mqttSnClientProcessMessage(context, &context->message, &ipAddr, port);
101  }
102  else if(error == ERROR_WOULD_BLOCK || error == ERROR_TIMEOUT)
103  {
104  //No datagram has been received
105  error = NO_ERROR;
106  }
107  else
108  {
109  //A communication error has occurred
110  }
111 
112  //Check status code
113  if(!error)
114  {
115  //A keep-alive value of zero has the effect of turning off the keep
116  //alive mechanism
117  if(context->keepAlive != 0)
118  {
119  //Make sure the MQTT-SN client is connected
120  if(context->state == MQTT_SN_CLIENT_STATE_ACTIVE ||
121  context->state == MQTT_SN_CLIENT_STATE_SENDING_REQ ||
122  context->state == MQTT_SN_CLIENT_STATE_RESP_RECEIVED)
123  {
124  //Check whether the keep-alive timer has expired
125  if(timeCompare(currentTime, context->keepAliveTimestamp +
126  context->keepAlive) >= 0)
127  {
128  //Check retransmission counter
129  if(context->keepAliveCounter < MQTT_SN_CLIENT_MAX_KEEP_ALIVE_PROBES)
130  {
131  //Send a PINGREQ message to the gateway
132  error = mqttSnClientSendPingReq(context);
133 
134  //Increment retransmission counter
135  context->keepAliveCounter++;
136  }
137  else
138  {
139  //If a client does not receive a PINGRESP from the gateway
140  //even after multiple retransmissions of the PINGREQ message,
141  //then the gateway is considered offline
142  context->state = MQTT_SN_CLIENT_STATE_DISCONNECTING;
143 
144  //The connection is lost
145  error = ERROR_NOT_CONNECTED;
146  }
147  }
148  }
149  }
150  }
151 
152  //Check whether the timeout has elapsed
153  } while(error == NO_ERROR && context->message.length == 0 && d > 0);
154 
155  //Return status code
156  return error;
157 }
158 
159 
160 /**
161  * @brief Deliver a PUBLISH message to the application
162  * @param[in] context Pointer to the MQTT-SN client context
163  * @param[in] flags Flags
164  * @param[in] topicId Topic identifier
165  * @param[in] data Message payload
166  * @param[in] dataLen Length of the message payload
167  * @return Error code
168  **/
169 
171  MqttSnFlags flags, uint16_t topicId, const uint8_t *data, size_t dataLen)
172 {
173  const char_t *topicName;
174  char_t shortTopicName[3];
176 
177  //Check the type of topic identifier
178  if(flags.topicIdType == MQTT_SN_NORMAL_TOPIC_ID)
179  {
180  //Retrieve the topic name associated with the normal topic ID
182  }
183  else if(flags.topicIdType == MQTT_SN_PREDEFINED_TOPIC_ID)
184  {
185  //Retrieve the topic name associated with the predefined topic ID
187  }
188  else if(flags.topicIdType == MQTT_SN_SHORT_TOPIC_NAME)
189  {
190  //Short topic names are topic names that have a fixed length of two
191  //octets. They are short enough for being carried together with the
192  //data within PUBLISH messages
193  shortTopicName[0] = MSB(topicId);
194  shortTopicName[1] = LSB(topicId);
195  shortTopicName[2] = '\0';
196 
197  //Point to the resulting topic name
198  topicName = shortTopicName;
199  }
200  else
201  {
202  //The value of the TopicIdType flag is not valid
203  topicName = NULL;
204  }
205 
206  //Check whether the topic name has been successfully resolved
207  if(topicName != NULL)
208  {
209  //Any registered callback?
210  if(context->publishCallback != NULL)
211  {
212  //Deliver the message to the application
213  context->publishCallback(context, topicName, data, dataLen,
214  (MqttSnQosLevel) flags.qos, flags.retain);
215  }
216 
217  //Successfull processing
219  }
220  else
221  {
222  //The client has received a PUBLISH message with an unknown topic ID
224  }
225 
226  //The return code indicates whether the PUBLISH message has been accepted
227  //or rejected
228  return returnCode;
229 }
230 
231 
232 /**
233  * @brief Add a new entry to the topic table
234  * @param[in] context Pointer to the MQTT-SN client context
235  * @param[in] topicName Topic name
236  * @param[in] topicId Topic identifier
237  * @return Error code
238  **/
239 
241  const char_t *topicName, uint16_t topicId)
242 {
243  uint_t i;
244 
245  //Make sure the name of the topic name is acceptable
247  return ERROR_INVALID_LENGTH;
248 
249  //Loop through the topic table
250  for(i = 0; i < MQTT_SN_CLIENT_TOPIC_TABLE_SIZE; i++)
251  {
252  //Check whether the topic name has already been registered
253  if(!strcmp(context->topicTable[i].topicName, topicName))
254  {
255  //Update topic identifier
256  context->topicTable[i].topicId = topicId;
257 
258  //We are done
259  return NO_ERROR;
260  }
261  }
262 
263  //Loop through the topic table
264  for(i = 0; i < MQTT_SN_CLIENT_TOPIC_TABLE_SIZE; i++)
265  {
266  //Check whether the current entry is free
267  if(context->topicTable[i].topicName[0] == '\0')
268  {
269  //Save mapping between topic name and topic ID
270  strcpy(context->topicTable[i].topicName, topicName);
271  context->topicTable[i].topicId = topicId;
272 
273  //A new entry has been successfully created
274  return NO_ERROR;
275  }
276  }
277 
278  //The table runs out of entries
279  return ERROR_OUT_OF_RESOURCES;
280 }
281 
282 
283 /**
284  * @brief Remove an entry in the topic table
285  * @param[in] context Pointer to the MQTT-SN client context
286  * @param[in] topicName Topic name
287  * @return Error code
288  **/
289 
291  const char_t *topicName)
292 {
293  uint_t i;
294 
295  //Loop through the list of topics that have been registered
296  for(i = 0; i < MQTT_SN_CLIENT_TOPIC_TABLE_SIZE; i++)
297  {
298  //Matching topic name?
299  if(!strcmp(context->topicTable[i].topicName, topicName))
300  {
301  //Release current entry
302  context->topicTable[i].topicName[0] = '\0';
303 
304  //We are done
305  return NO_ERROR;
306  }
307  }
308 
309  //The specified topic name does not exist
310  return ERROR_NOT_FOUND;
311 }
312 
313 
314 /**
315  * @brief Retrieve the topic name associated with a given topic ID
316  * @param[in] context Pointer to the MQTT-SN client context
317  * @param[in] topicId Topic identifier
318  * @return Topic name
319  **/
320 
322  uint16_t topicId)
323 {
324  uint_t i;
325  const char_t *topicName;
326 
327  //Initialize topic name
328  topicName = NULL;
329 
330  //Valid topic identifier?
332  {
333  //Loop through the list of topics that have been registered
334  for(i = 0; i < MQTT_SN_CLIENT_TOPIC_TABLE_SIZE; i++)
335  {
336  //Matching topic identifier?
337  if(context->topicTable[i].topicId == topicId)
338  {
339  //Retrieve the corresponding topic name
340  topicName = context->topicTable[i].topicName;
341  break;
342  }
343  }
344  }
345 
346  //Return the corresponding topic name
347  return topicName;
348 }
349 
350 
351 /**
352  * @brief Retrieve the topic ID associated with a given topic name
353  * @param[in] context Pointer to the MQTT-SN client context
354  * @param[in] topicName Topic name
355  * @return Topic identifier
356  **/
357 
359  const char_t *topicName)
360 {
361  uint_t i;
362  uint16_t topicId;
363 
364  //Initialize topic identifier
366 
367  //Valid topic name?
368  if(topicName != NULL)
369  {
370  //Loop through the list of registered topics that have been registered
371  for(i = 0; i < MQTT_SN_CLIENT_TOPIC_TABLE_SIZE; i++)
372  {
373  //Matching topic name?
374  if(!strcmp(context->topicTable[i].topicName, topicName))
375  {
376  //Retrieve the corresponding topic identifier
377  topicId = context->topicTable[i].topicId;
378  break;
379  }
380  }
381  }
382 
383  //Return the corresponding topic identifier
384  return topicId;
385 }
386 
387 
388 /**
389  * @brief Retrieve the topic name associated with a predefined topic ID
390  * @param[in] context Pointer to the MQTT-SN client context
391  * @param[in] topicId Predefined topic identifier
392  * @return Topic name
393  **/
394 
396  uint16_t topicId)
397 {
398  uint_t i;
399  const char_t *topicName;
400 
401  //Initialize topic name
402  topicName = NULL;
403 
404  //Valid topic identifier?
406  {
407  //Loop through the list of predefined topics
408  for(i = 0; i < context->predefinedTopicTableSize; i++)
409  {
410  //Matching topic identifier?
411  if(context->predefinedTopicTable[i].topicId == topicId)
412  {
413  //Retrieve the corresponding topic name
414  topicName = context->predefinedTopicTable[i].topicName;
415  break;
416  }
417  }
418  }
419 
420  //Return the corresponding topic name
421  return topicName;
422 }
423 
424 
425 /**
426  * @brief Retrieve the topic ID associated with a predefined topic name
427  * @param[in] context Pointer to the MQTT-SN client context
428  * @param[in] topicName Predefined topic name
429  * @return Topic identifier
430  **/
431 
433  const char_t *topicName)
434 {
435  uint_t i;
436  uint16_t topicId;
437 
438  //Initialize topic identifier
440 
441  //Valid topic name?
442  if(topicName != NULL)
443  {
444  //Loop through the list of predefined topics
445  for(i = 0; i < context->predefinedTopicTableSize; i++)
446  {
447  //Matching topic name?
448  if(!strcmp(context->predefinedTopicTable[i].topicName, topicName))
449  {
450  //Retrieve the corresponding topic identifier
451  topicId = context->predefinedTopicTable[i].topicId;
452  break;
453  }
454  }
455  }
456 
457  //Return the corresponding topic identifier
458  return topicId;
459 }
460 
461 
462 /**
463  * @brief Generate a new message identifier
464  * @param[in] context Pointer to the MQTT-SN client context
465  * @return 16-bit message identifier
466  **/
467 
469 {
470  //Increment message identifier and wrap around if necessary
471  if(context->msgId < UINT16_MAX)
472  context->msgId++;
473  else
474  context->msgId = 1;
475 
476  //Return current value
477  return context->msgId;
478 }
479 
480 
481 /**
482  * @brief Store message ID (QoS 2 message processing)
483  * @param[in] context Pointer to the MQTT-SN client context
484  * @param[in] msgId Message identifier
485  * @return Error code
486  **/
487 
489  uint16_t msgId)
490 {
491  uint_t i;
492 
493  //Loop through the list of message identifiers
494  for(i = 0; i < MQTT_SN_CLIENT_MSG_ID_TABLE_SIZE; i++)
495  {
496  //Check whether the message ID has already been accepted by the client
497  if(context->msgIdTable[i].ownership &&
498  context->msgIdTable[i].msgId == msgId)
499  {
500  //We are done
501  return NO_ERROR;
502  }
503  }
504 
505  //Loop through the list of message identifiers
506  for(i = 0; i < MQTT_SN_CLIENT_MSG_ID_TABLE_SIZE; i++)
507  {
508  //Check whether the current entry is free
509  if(!context->msgIdTable[i].ownership)
510  {
511  //Create a new entry
512  context->msgIdTable[i].msgId = msgId;
513  context->msgIdTable[i].ownership = TRUE;
514 
515  //We are done
516  return NO_ERROR;
517  }
518  }
519 
520  //The table runs out of entries
521  return ERROR_OUT_OF_RESOURCES;
522 }
523 
524 
525 /**
526  * @brief Discard message ID (QoS 2 message processing)
527  * @param[in] context Pointer to the MQTT-SN client context
528  * @param[in] msgId Message identifier
529  * @return Error code
530  **/
531 
533  uint16_t msgId)
534 {
535  uint_t i;
536 
537  //Loop through the list of message identifiers
538  for(i = 0; i < MQTT_SN_CLIENT_MSG_ID_TABLE_SIZE; i++)
539  {
540  //Matching message identifier?
541  if(context->msgIdTable[i].ownership &&
542  context->msgIdTable[i].msgId == msgId)
543  {
544  //Release current entry
545  context->msgIdTable[i].msgId = 0;
546  context->msgIdTable[i].ownership = FALSE;
547 
548  //We are done
549  return NO_ERROR;
550  }
551  }
552 
553  //The specified message identifier does not exist
554  return ERROR_NOT_FOUND;
555 }
556 
557 
558 /**
559  * @brief Check whether the message ID is a duplicate (QoS 2 message processing)
560  * @param[in] context Pointer to the MQTT-SN client context
561  * @param[in] msgId Message identifier
562  * @return TRUE if the message ID is a duplicate, else FALSE
563  **/
564 
566  uint16_t msgId)
567 {
568  uint_t i;
569  bool_t duplicate;
570 
571  //Initialize flag
572  duplicate = FALSE;
573 
574  //Loop through the list of message identifiers
575  for(i = 0; i < MQTT_SN_CLIENT_MSG_ID_TABLE_SIZE; i++)
576  {
577  //Check whether the message ID has already been accepted by the client
578  if(context->msgIdTable[i].ownership &&
579  context->msgIdTable[i].msgId == msgId)
580  {
581  //The message ID is a duplicate
582  duplicate = TRUE;
583  break;
584  }
585  }
586 
587  //Return TRUE if the message ID is a duplicate
588  return duplicate;
589 }
590 
591 
592 /**
593  * @brief Check whether a topic name is a short topic name
594  * @param[in] topicName Topic name
595  * @return TRUE if the specified topic name is a short topic name, else FALSE
596  **/
597 
599 {
600  bool_t res;
601 
602  //Initialize variable
603  res = FALSE;
604 
605  //A short topic name is a topic name that has a fixed length of two octets
606  if(strlen(topicName) == 2)
607  {
608  //Ensure the topic name does not contains wildcard characters
609  if(strchr(topicName, '#') == NULL && strchr(topicName, '+') == NULL)
610  {
611  //The short topic name is a valid
612  res = TRUE;
613  }
614  }
615 
616  //Return TRUE if the specified topic name is a short topic name
617  return res;
618 }
619 
620 #endif
error_t mqttSnClientProcessEvents(MqttSnClientContext *context, systime_t timeout)
Process MQTT-SN client events.
const char_t * mqttSnClientFindPredefTopicId(MqttSnClientContext *context, uint16_t topicId)
Retrieve the topic name associated with a predefined topic ID.
int bool_t
Definition: compiler_port.h:49
@ ERROR_NOT_FOUND
Definition: error.h:145
@ ERROR_WOULD_BLOCK
Definition: error.h:95
@ MQTT_SN_SHORT_TOPIC_NAME
Short topic name.
IP network address.
Definition: ip.h:71
#define MqttSnClientContext
#define TRUE
Definition: os_port.h:50
MqttSnReturnCode
MQTT-SN return codes.
@ MQTT_SN_PREDEFINED_TOPIC_ID
Predefined topic ID.
MqttSnQosLevel
Quality of service level.
@ ERROR_OUT_OF_RESOURCES
Definition: error.h:64
error_t mqttSnClientReceiveDatagram(MqttSnClientContext *context, IpAddr *srcIpAddr, uint16_t *srcPort, void *data, size_t size, size_t *received, systime_t timeout)
Receive a datagram.
error_t mqttSnClientProcessMessage(MqttSnClientContext *context, MqttSnMessage *message, const IpAddr *ipAddr, uint16_t port)
Process incoming MQTT-SN message.
#define MQTT_SN_CLIENT_MSG_ID_TABLE_SIZE
#define MQTT_SN_CLIENT_MAX_KEEP_ALIVE_PROBES
uint16_t msgId
#define MQTT_SN_MAX_MSG_SIZE
MQTT-SN message parsing and formatting.
uint16_t topicId
const uint8_t res[]
uint8_t returnCode
@ MQTT_SN_CLIENT_STATE_SENDING_REQ
#define timeCompare(t1, t2)
Definition: os_port.h:42
__start_packed struct @232 MqttSnFlags
MQTT-SN flags.
error_t mqttSnClientSendPingReq(MqttSnClientContext *context)
Send PINGREQ message.
uint16_t mqttSnClientFindTopicName(MqttSnClientContext *context, const char_t *topicName)
Retrieve the topic ID associated with a given topic name.
bool_t mqttSnClientIsDuplicateMessageId(MqttSnClientContext *context, uint16_t msgId)
Check whether the message ID is a duplicate (QoS 2 message processing)
@ MQTT_SN_NORMAL_TOPIC_ID
Normal topic ID.
error_t mqttSnClientAddTopic(MqttSnClientContext *context, const char_t *topicName, uint16_t topicId)
Add a new entry to the topic table.
#define MQTT_SN_CLIENT_TICK_INTERVAL
#define FALSE
Definition: os_port.h:46
#define MQTT_SN_CLIENT_TOPIC_TABLE_SIZE
error_t
Error codes.
Definition: error.h:42
uint16_t mqttSnClientGenerateMessageId(MqttSnClientContext *context)
Generate a new message identifier.
bool_t mqttSnClientIsShortTopicName(const char_t *topicName)
Check whether a topic name is a short topic name.
@ ERROR_INVALID_LENGTH
Definition: error.h:109
char_t topicName[]
MqttSnReturnCode mqttSnDeliverPublishMessage(MqttSnClientContext *context, MqttSnFlags flags, uint16_t topicId, const uint8_t *data, size_t dataLen)
Deliver a PUBLISH message to the application.
#define MSB(x)
Definition: os_port.h:58
#define LSB(x)
Definition: os_port.h:54
@ MQTT_SN_CLIENT_STATE_RESP_RECEIVED
#define MIN(a, b)
Definition: os_port.h:62
@ MQTT_SN_CLIENT_STATE_DISCONNECTING
Helper functions for MQTT-SN client.
uint16_t port
Definition: dns_common.h:223
uint8_t flags
Definition: tcp.h:314
@ ERROR_TIMEOUT
Definition: error.h:94
char char_t
Definition: compiler_port.h:43
Transport protocol abstraction layer.
@ ERROR_NOT_CONNECTED
Definition: error.h:79
uint16_t mqttSnClientFindPredefTopicName(MqttSnClientContext *context, const char_t *topicName)
Retrieve the topic ID associated with a predefined topic name.
@ MQTT_SN_RETURN_CODE_REJECTED_INVALID_TOPIC_ID
@ MQTT_SN_RETURN_CODE_ACCEPTED
error_t mqttSnClientStoreMessageId(MqttSnClientContext *context, uint16_t msgId)
Store message ID (QoS 2 message processing)
@ MQTT_SN_CLIENT_STATE_ACTIVE
#define MQTT_SN_CLIENT_MAX_TOPIC_NAME_LEN
MQTT-SN client.
unsigned int uint_t
Definition: compiler_port.h:45
TCP/IP stack core.
uint8_t data[]
Definition: dtls_misc.h:176
const char_t * mqttSnClientFindTopicId(MqttSnClientContext *context, uint16_t topicId)
Retrieve the topic name associated with a given topic ID.
error_t mqttSnClientDiscardMessageId(MqttSnClientContext *context, uint16_t msgId)
Discard message ID (QoS 2 message processing)
uint32_t systime_t
Definition: compiler_port.h:46
uint8_t ipAddr[4]
Definition: mib_common.h:187
#define MQTT_SN_INVALID_TOPIC_ID
@ NO_ERROR
Success.
Definition: error.h:44
Debugging facilities.
error_t mqttSnClientDeleteTopic(MqttSnClientContext *context, const char_t *topicName)
Remove an entry in the topic table.
systime_t osGetSystemTime(void)
Retrieve system time.