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