diff options
author | Ian Craggs | 2014-10-10 22:04:08 +0000 |
---|---|---|
committer | Ian Craggs | 2014-10-10 22:04:08 +0000 |
commit | 0768b17824ccc49048e2737868b3704e88483d81 (patch) | |
tree | 66f740d57f8905bfa769521f8fa6931935011cb8 /apps | |
parent | 36fa1d8e880ed2d0b9263434051b3b0656c232cb (diff) | |
download | org.eclipse.paho.mqtt-sn.apps-0768b17824ccc49048e2737868b3704e88483d81.tar.gz org.eclipse.paho.mqtt-sn.apps-0768b17824ccc49048e2737868b3704e88483d81.tar.xz org.eclipse.paho.mqtt-sn.apps-0768b17824ccc49048e2737868b3704e88483d81.zip |
Change-Id: Id5cdc96987e20b290a0e860d7a928a208bfa57d1
Diffstat (limited to 'apps')
-rw-r--r-- | apps/MQTTSN-C-Client/readme.txt | 34 | ||||
-rw-r--r-- | apps/MQTTSN-C-Client/src/application.c | 322 | ||||
-rw-r--r-- | apps/MQTTSN-C-Client/src/mqttsn/gp_api.h | 193 | ||||
-rw-r--r-- | apps/MQTTSN-C-Client/src/mqttsn/mqtts-core.c | 1407 | ||||
-rw-r--r-- | apps/MQTTSN-C-Client/src/mqttsn/mqtts-timer.c | 181 | ||||
-rw-r--r-- | apps/MQTTSN-C-Client/src/mqttsn/mqtts-timer.h | 60 | ||||
-rw-r--r-- | apps/MQTTSN-C-Client/src/mqttsn/mqtts_api.h | 426 |
7 files changed, 2623 insertions, 0 deletions
diff --git a/apps/MQTTSN-C-Client/readme.txt b/apps/MQTTSN-C-Client/readme.txt new file mode 100644 index 0000000..e227ee4 --- /dev/null +++ b/apps/MQTTSN-C-Client/readme.txt @@ -0,0 +1,34 @@ +This package provides a reference implementation of the client side of the MQTT-SN (MQTT for Sensor Networks) +protocol. + +The reference implementation consists of the following files:
- application.c This is a sample application which demonstrates how the MQTT-SN client API could be used.
- mqtts/mqtts_api.h This file defines the MQTT-SN client API, which includes verbs such as connect, + disconnect, register, publish, etc. for communicating with the gateway/broker. + The use of these verbs is explained below. It also defines the various parameters + values required for the operation of the MQTT-SN protocol.
- mqtts/mqtts-core.c This file provides the platform-neutral (generic) reference implementation of + the client. It contains the client handling of the MQTT-SN protocol.
- mqtts/mqtts-timer.c This is the implementation of timer functions which are required by the MQTT-SN protocol.
- mqtts/mqtts-timer.h The header file for the timer functions.
- mqtts/gp_api.h This header file defines the interface between the platform-neutral client implementation + and a generic platform. To port the reference implementation to a specific hardware platform, + the functions defined in this header file have to be implemented. +
To port the client reference implementation to a specific hardware platform, the following functions need to be +implemented by the platform (see also file gp_api.h):
1. unsigned char gp_timer_new(void (*timeout_funccb)(void));
This function is called by the client to request the creation of a new timer. The pointer to the function +to be called back when the timer times out is given as parameter. If the timer could be created, +the platform assigns to it a one-byte identity and returns that identity to the MQTT-SN client. The client +will use this identity later on to start, stop, and end the timer. If the timer could not be created, +the value 0xFF should returned.
2. void gp_timer_start(unsigned char id, unsigned char msb, unsigned char lsb);
This function is called by the client to request the start of a timer. The byte id is the identity of the +timer that should be started (this id was returned to the client when the timer was created). The two-byte long +time-out value (in seconds) is indicated by msb and lsb, with msb containing the most significant byte and lsb +the least significant byte of the value.
3. void gp_timer_stop(unsigned char id);
This function is called by the client to request the stop a timer. The identity of the timer to be stopped +is indicated by the parameter id.
4. void gp_timer_end(unsigned char id);
This function is called by the client to indicate that it does not need a timer anymore. The identity of the +timer to be freed is indicated by the parameter id.
5. void gp_network_msg_send(unsigned char *msg, unsigned char *dest, unsigned char length);
This function is called by the client when it wants to send a MQTT-SN message. The pointer *msg points to +the first byte of the array which contains the MQTT-SN message to be sent. The length of the message is indicated +by this first byte. The array containing the destination address is pointed by *dest and its length is indicated +by the parameter length.
6. void gp_network_msg_broadcast(unsigned char *msg, unsigned char radius);
This function is called by the client when it wants to broadcast a MQTT-SN message. The pointer *msg points +to the first byte of the array containing the MQTT-SN message to be broadcasted. The length of the MQTT-SN +message is indicated by this first byte. The parameter radius indicates the broadcast radius.
7. void gpcb_network_msg_received(unsigned char *msg, unsigned char *sender, unsigned char length);
This callback function is called by the platform when it receives a MQTT-SN message. The first byte of the +message received is indicated by the pointer *msg; this first byte also contains the length of the message. +The address of the sender is indicated by the pointer *sender, and its length by the parameter length. +The platform can release the buffer containing the message when this function returns.
8. unsigned char gp_byte_get(unsigned char *msg, unsigned char index);
This function is called by the client to get a byte of a MQTT-SN message just received by the platform +and indicated to it by the callback function gpcb_network_msg_received(…). The pointer *msg is the one +in the callback function, and the parameter index is the index of the byte to get. Note that the first +byte of the MQTT-SN message has the index 1! Note also that the client will call this function only +while it processes the callback function gpcb_network_msg_received(…). +
\ No newline at end of file diff --git a/apps/MQTTSN-C-Client/src/application.c b/apps/MQTTSN-C-Client/src/application.c new file mode 100644 index 0000000..f3e48de --- /dev/null +++ b/apps/MQTTSN-C-Client/src/application.c @@ -0,0 +1,322 @@ +/******************************************************************************* + * Copyright (c) 2008, 2013 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * Description : MQTT-SN application template + * + */ + + +#include <stdlib.h> +#include <stdio.h> + +#include "mqttsn/mqtts_api.h" +#include "mqttsn/gp_api.h" + + + +static mqtts_CONNECT_Parms conn_p; +static mqtts_REGISTER_Parms reg_p; +static mqtts_PUBLISH_Parms pub_p; +static mqtts_SUBSCRIBE_Parms sub_p; +static mqtts_UNSUBSCRIBE_Parms usub_p; + +/* Codes and functions that are inside an (#if MQTTS_DEBUG, #endif)-block are + * optional. They could e.g. used to exchange debug information with + * the hw device via a serial port + */ +/**** function prototype */ +void appInit(void); + +/** + * Programming model: appRun() will be called regularly by the operating system + * (e.g. by a round-robin scheduler) of the hw device + **/ +void appRun(void) { + static unsigned char initiated=0; + // init app only once + if (initiated==0) { + initiated=1; + appInit(); + } else { + /* define here what app has to do regularly */ + } +} + + +/** + * Initialization: start mqtts client and ask it to connect to a gw + */ + +void appInit(void) { + + /* CONNECT parameters */ + conn_p.flagCleanSession=0; + conn_p.flagWill =0; + conn_p.flagWillQOS=0; + conn_p.flagWillRetain=0; + + conn_p.flpProtocolID=0x01; + conn_p.flpDuration[0] = 0; /* Keep Alive Timer MostSignificantByte */ + conn_p.flpDuration[1] = 15; /* Keep Alive Timer LSB = 15 sec */ + + conn_p.vlpClientID = (unsigned char *)"mqttsSampleAppl"; + conn_p.vlpClientID_length = 15; + + conn_p.vlpWillMsg = NULL; + conn_p.vlpWillMsg_length = 0; + conn_p.vlpWillTopic = NULL; + conn_p.vlpWillTopic_length = 0; + + /* start mqtts and ask it to connect to a gw */ + mqtts_startStack(); + if (mqtts_connect(&conn_p)!= MQTTS_OK) { +#if MQTTS_DEBUG + gp_debug((unsigned char*)"connect error\r\n",15); +#endif + } + +} + +#if MQTTS_DEBUG +/** + * appHandleCmd() could be called e.g. when a character is typed in a terminal + * and sent to the hw device via a serial port. + * Its aim is to demonstrate the capabilities of mqtts, e.g + * requesting the sending of a REGISTER message + * requesting the sending of a PUBLISH message + * etc. + */ +void appHandleCmd(unsigned char cmd) { //cmd: character typed in terminal + + switch(cmd) { + case '\r': + case '\n': + gp_debug((unsigned char*)"sample app\r\n", 12); + break; + + case 'a': + gp_debug((unsigned char*)"start stack\r\n",13); + mqtts_startStack(); + break; + + case 't': + gp_debug((unsigned char*)"stop stack\r\n",12); + mqtts_stopStack(); + break; + + case 'c': /* ask mqtts to connect to a gw */ + if (mqtts_connect(&conn_p)== MQTTS_OK) { + gp_debug((unsigned char*)"waiting for ready\r\n",19); + } else { + gp_debug((unsigned char*)"connect error!\r\n",16); + } + break; + + case 'd': + gp_debug((unsigned char*)"sending disconnect ... \r\n",25); + if (mqtts_disconnect()!=MQTTS_OK) { + gp_debug((unsigned char*)"disc error!\r\n",13); + } + break; + + case 'r': + gp_debug((unsigned char*)"send REGISTER, topic: wsn/data\r\n",32); + reg_p.vlpTopic = (unsigned char *)"wsn/data"; + reg_p.vlpTopic_length = 8; + mqtts_register(®_p); + break; + + case 'p': + gp_debug((unsigned char*)"send PUBLISH \"PUB QoS 0\" with QoS 0\r\n",37); + pub_p.flagQOS = 0; + pub_p.flagRetain = 0; + pub_p.flagTopicIdType = 0; + pub_p.vlpData = (unsigned char *)"PUB QoS 0"; + pub_p.vlpData_length = 5; + mqtts_publish(&pub_p); + break; + + case 'q': + gp_debug((unsigned char*)"send PUBLISH \"PUB QoS 1\" with QoS 1\r\n",37); + pub_p.flagQOS = 1; + pub_p.vlpData = (unsigned char *)"PUB QoS 1"; + pub_p.vlpData_length = 5; + mqtts_publish(&pub_p); + break; + + case 's': + gp_debug((unsigned char*)"send SUBSCRIBE to topic: wsn/cmd\r\n",34); + sub_p.flagQOS = 0; + sub_p.flagTopicIdType = 0; + sub_p.vlpTopic = (unsigned char *)"wsn/cmd"; + sub_p.vlpTopic_length = 7; + mqtts_subscribe(&sub_p); + break; + + case 'u': + gp_debug((unsigned char*)"send UNSUBSCRIBE topic: wsn/cmd\r\n",33); + usub_p.flagTopicIdType= 0; + usub_p.vlpTopic= (unsigned char *)"wsn/cmd"; + usub_p.vlpTopic_length = 7; + mqtts_unsubscribe(&usub_p); + break; + + default: + gp_debug((unsigned char*)"unknown cmd\r\n",13); + } + +} +#endif + +/***************************************************** + * mqtts callback functions + *****************************************************/ + +/** + * CONNECT sent to gw, client is waiting for answer from gw */ +void mqttscb_connect_sent(void) +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"CONNECT sent\r\n",14); +#endif +} + +/** + * client is now connected to a gw, app can now start publishing, ... */ +void mqttscb_connected(void) +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"connected\r\n",11); +#endif +} + +/** + * client is disconnected, app has to call mqtts_connect() again */ +void mqttscb_disconnected(unsigned char returnCode) +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"disconnected\r\n",15); +#endif +} + +/** + * REGACK received, app can now use topic id for publishing */ +void mqttscb_regack_received( + unsigned char topicID_1, + unsigned char topicID_2, + unsigned char returnCode) +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"REGACK received\r\n",17); +#endif + + pub_p.flpTopicID[0] = topicID_1; + pub_p.flpTopicID[1] = topicID_2; + +} + +/** + * PUBLISH received from gw/broker */ +unsigned char mqttscb_publish_received( + unsigned char dup, + unsigned char qos, + unsigned char topicID_0, /* Topic ID[0] */ + unsigned char topicID_1, /* Topic ID[1] */ + unsigned char *data, + unsigned char data_len) +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"PUBLISH received\r\n",18); +#endif + return 0; +} + +/** + * PUBACK received, only for PUBLISH with QoS 1 */ +void mqttscb_puback_received( + unsigned char topicId_0, + unsigned char topicId_1, + unsigned char returnCode) +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"PUBACK received\r\n",17); +#endif +} + +/** + * PUBCOMP received, only for PUBLISH with QoS 2 */ +void mqttscb_pubcomp_received() +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"PUBCOMP received\r\n",18); +#endif +} + +/** + * SUBACK received */ +void mqttscb_suback_received( + unsigned char qos, + unsigned char topicID_1, + unsigned char topicID_2, + unsigned char returnCode) +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"SUBACK received\r\n",17); +#endif +} + +/** + * UNSUBACK received */ +void mqttscb_unsuback_received() +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"UNSUBACK received\r\n",19); +#endif +} + +/** + * REGISTER received */ +void mqttscb_register_received( + unsigned char topicID_0, /* Topic ID[0] */ + unsigned char topicID_1, /* Topic ID[1] */ + unsigned char *topic, + unsigned char topic_len) +{ +#if MQTTS_DEBUG + gp_debug((unsigned char*)"REGISTER received\r\n",19); +#endif +} + +/** + * WILLTOPICRESP received */ +void mqttscb_willtopicresp_received() { + +} + + +/** + * WILLMSGRESP received */ +void mqttscb_willmsgresp_received() { + +} + +/*********************************************** + * + * END OF FILE + * + */ + diff --git a/apps/MQTTSN-C-Client/src/mqttsn/gp_api.h b/apps/MQTTSN-C-Client/src/mqttsn/gp_api.h new file mode 100644 index 0000000..edf0a32 --- /dev/null +++ b/apps/MQTTSN-C-Client/src/mqttsn/gp_api.h @@ -0,0 +1,193 @@ +/******************************************************************************* + * Copyright (c) 2008, 2013 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + + +/** + * + * + * Description : Generic platform (gp) API header file + * + * + */ + + + + +#ifndef _MQTTS_GP_API_H +#define _MQTTS_GP_API_H + + + +/************************************************************************* + * The following functions are called by the mqtts client and have to be + * implemented by the generic platform (gp): + * 1. Timer related functions: + * * gp_timer_new + * * gp_timer_start + * * gp_timer_stop + * * gp_timer_end + * 2. Messages related functions: + * * gp_network_msg_send + * * gp_network_msg_broadcast + * * gp_byte_get + * 3. Help/Utility functions (optional, need not be implemented) + * * gp_debug (e.g. to print debug info) + * + * There is only one callback function from gp, namely: + * gpcb_network_msg_received() + *************************************************************************/ + + +/** + * request gp to create a new timer + * + * Parameters: + * timeout_funccb callback-function when time-out happens + * Return: + * id of timer created + */ +unsigned char gp_timer_new(void (*timeout_funccb)(void)); + + +/** + * request gp to start indicated timer with the indicated timer value in seconds + * Parameters: + * id id of timer to be started + * msb most significant byte of timer value (in seconds) + * lsb least significant byte of timer value + * Return: + * none + */ +void gp_timer_start(unsigned char id, + unsigned char msb, + unsigned char lsb); + + +/** + * request gp to stop indicated timer + * + * Parameters: + * id id of timer to be stopped + * + * Return: + * none + * + */ +void gp_timer_stop(unsigned char id); + +/** + * request gp to end indicated timer (id cannot be used anymore) + * + * Parameters: id id of timer to be ended + * + * Return: + * none + * + */ +void gp_timer_end(unsigned char id); + +/** + * request gp to send a message + * + * Parameters: + * *msg pointer to first byte to be sent + * (message's length is in first byte to send!) + * *dest pointer to destination address + * length length of destination address + * + * Return: + * none + * + */ +void gp_network_msg_send(unsigned char *msg, + unsigned char *dest, + unsigned char length); + +/** + * request gp to broadcast a message + * + * Parameters: + * *msg pointer to first byte to be broadcasted + * (message's length is in first byte to send!) + * radius broadcast radius (0 means all network broadcast) + * + * Return: + * none + * + */ +void gp_network_msg_broadcast(unsigned char *msg, + unsigned char radius); + + +/** + * get a byte from a message just received + * only called within the handling of gpcb_network_msg_received() + * + * Parameters: + * *msg pointer to the mqtts message + * (as received in gpcb_network_msg_received()) + * index index of the byte to get + * index=1 => mqtts msg length field! + * + * Return: + * requested byte + * + */ +unsigned char gp_byte_get(unsigned char *msg, unsigned char index); + + +/** + * Optional function: print/display e.g. debug information + * + * Parameters: + * *s pointer to string or array to be displayed + * length length of string/array to be displayed + * + * Return: + * none + * + */ +#if MQTTS_DEBUG +//void gp_debug(char *s, ...); +void gp_debug(unsigned char *s, unsigned char length); +#endif + + +/************************************************************************* + * callback functions (implemented by mqtts, see mqtts-core.c) + *************************************************************************/ + +/** + * indicate the reception of a mqtts message + * to be called by gp to give the msg to mqtts client + * + * Parameters: + * *msg pointer to message received + * *sender pointer to sender's address + * length length of sender's address + * + * Return: + * + * NOTE 1: use gp_byte_get(msg,i) to get msg's byte #i ! + * NOTE 2: mqtts length field is in msg[1], not in msg[0] ! + */ +void gpcb_network_msg_received( + unsigned char *msg, + unsigned char *sender, + unsigned char length); + + +#endif diff --git a/apps/MQTTSN-C-Client/src/mqttsn/mqtts-core.c b/apps/MQTTSN-C-Client/src/mqttsn/mqtts-core.c new file mode 100644 index 0000000..dc1594c --- /dev/null +++ b/apps/MQTTSN-C-Client/src/mqttsn/mqtts-core.c @@ -0,0 +1,1407 @@ +/******************************************************************************* + * Copyright (c) 2008, 2013 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * + * Description : MQTT-SN client core implementation + * + * + */ + + +#include <stdlib.h> +#include <string.h> + +#include "mqtts_api.h" +#include "gp_api.h" + +#include "mqtts-timer.h" + + + +/** + * MQTT-S Message types */ +#define ADVERTISE 0x00 +#define SEARCHGW 0x01 +#define GWINFO 0x02 + +#define CONNECT 0x04 +#define CONNACK 0x05 +#define WILLTOPICREQ 0x06 +#define WILLTOPIC 0x07 +#define WILLMSGREQ 0x08 +#define WILLMSG 0x09 +#define REGISTER 0x0A +#define REGACK 0x0B +#define PUBLISH 0x0C +#define PUBACK 0x0D +#define PUBCOMP 0x0E +#define PUBREC 0x0F +#define PUBREL 0x10 + +#define SUBSCRIBE 0x12 +#define SUBACK 0x13 +#define UNSUBSCRIBE 0x14 +#define UNSUBACK 0x15 +#define PINGREQ 0x16 +#define PINGRESP 0x17 +#define DISCONNECT 0x18 + +#define WILLTOPICUPD 0x1A +#define WILLTOPICRESP 0x1B +#define WILLMSGUPD 0x1C +#define WILLMSGRESP 0x1D + + +/** + * Flags binary masks */ +#define EMPTY_MASK 0x00 /* binary : 0000 0000 */ +#define DUP_FLAG_MASK 0x80 /* binary : 1000 0000 */ +#define QOS_MIN1_FLAG_MASK 0x60 /* binary : 0110 0000 */ +#define QOS2_FLAG_MASK 0x40 /* binary : 0100 0000 */ +#define QOS1_FLAG_MASK 0x20 /* binary : 0010 0000 */ +#define QOS0_FLAG_MASK 0x00 /* binary : 0000 0000 */ +#define RETAIN_FLAG_MASK 0x10 /* binary : 0001 0000 */ +#define WILL_FLAG_MASK 0x08 /* binary : 0000 1000 */ +#define CLEANSS_FLAG_MASK 0x04 /* binary : 0000 0100 */ +#define TOPICSHORT_FLAG_MASK 0x02 /* binary : 0000 0010 */ +#define TOPICIDPRE_FLAG_MASK 0x01 /* binary : 0000 0001 */ +#define TOPICID_FLAG_MASK 0x00 /* binary : 0000 0000 */ + +#define GET_DUP_FLAG_MASK 0x80 /* binary : 1000 0000 */ +#define GET_QOS_FLAG_MASK 0x60 /* binary : 0110 0000 */ + + +/** + * mqtts client's state */ +static unsigned char mqtts_state = MQTTS_STATE_NOT_ACTIVE; +/** + * MsgIg: incremented after having sent a message */ +static unsigned char msgId0=0; +static unsigned char msgId1=0; + +static unsigned char myGwAddr[MQTTS_MAX_NETWORK_ADDRESS_LENGTH]={NULL}; +static unsigned char myGwAddrLength=0; +static unsigned char myGwId=0; + +static unsigned char isConnected=0; +static unsigned char lostGw=0; + +static unsigned char broadcastRadius; +/* backupMsg: contains msg which will be retransmitted in case of ACK time-out */ +static unsigned char backupMsg[MQTTS_MAX_MSG_SIZE]; + +static mqtts_CONNECT_Parms* conn_parms; + +#define HEADER_LENGTH 2 +/* + * macros + */ +#define msg_new(size) unsigned char msg[(size)] +#define msg_set_length(len) *((msg)+0)=(len) /*msg[0]=len*/ +#define msg_get_length(msg) *((msg)+0) +#define msg_set_type(type) *((msg)+1)=(type) /*msg[1]=type*/ + + +/************************* + * functions prototypes + *************************/ +static void mqtts_connecting(void); +static void mqtts_willtopic(void); +static void mqtts_willmsg(void); +static void mqtts_regack(unsigned char topicId_1, unsigned char topicId_2, + unsigned char msgId1, unsigned char msgId2, unsigned char retCode ); +static void mqtts_puback(unsigned char topicID_1, unsigned char topicID_2, + unsigned char msgID_1, unsigned char msgID_2, unsigned char retCode ); +static void mqtts_pingresp(void); +static void mqtts_pubrel(unsigned char msgID_1, unsigned char msgID_2 ); +static void gwinfo_received(unsigned char *msg, unsigned char *sender, + unsigned char sender_len); + +/**************************************************** + * stack internal functions + ****************************************************/ + +/** + * called when we lose a gw, e.g. after multiple ack time-outs */ +void lost_gw(void) { + myGwAddrLength=0; + mqtts_timer_stop_keep_alive(); + + /* if we have sent a DISC to the gw then we could go state WAITING_CONNECT + * otherwise we have start searching for gw */ + if (mqtts_state == MQTTS_STATE_DISCONNECTING) { + mqtts_state = MQTTS_STATE_WAITING_CONNECT; + } else { + lostGw= 1; + mqtts_state = MQTTS_STATE_SEARCHING_GW; /* start searching for a new gw */ + mqtts_timer_start_wait_searchgw(); /* delay sending out SEARCHGW */ + } + mqttscb_disconnected(MQTTS_LOST_GATEWAY); +} + +/** + * ack received in state WAITING_ACK */ +void ack_rx(void) { + mqtts_timer_stop_ack(); + mqtts_state=MQTTS_STATE_READY; + if (lostGw == 1) { + mqttscb_connected(); + lostGw= 0; + } +} + +/** + * backup msg for later retransmission */ +void backup_msg(unsigned char *msg) { + unsigned char i; + for (i=0;i<(msg_get_length(msg));i++) { + backupMsg[i]= msg[i]; + } +} +/** + * send backup message */ +void send_backupMsg(void) { + mqtts_timer_start_keep_alive(); + mqtts_timer_start_ack(); + gp_network_msg_send(backupMsg,myGwAddr,myGwAddrLength); +} + +/* Set the MsgId field of the message. The MsgId is simply incremented for any + * message sent + * + * Input: + * msgId pointer to first byte of the MsgId field in the msg + * Returns: + * none + **/ +static void set_msg_id(unsigned char* msgId) { + /* msgId0 is msb, and msgId1 is lsb */ + if (msgId1==255) { + msgId1=0; + if (msgId0==255) msgId0=0; + else msgId0++; + } else msgId1++; + msgId[0]= msgId0; + msgId[1]= msgId1; +} + + +/************************************************************************* + * Handle messages received + * to avoid race conditions and simplify the client implementation + * the client will respond to all gw's requests without making any check! + *************************************************************************/ + +static void handleSEARCHGW(void) { + switch (mqtts_state) { + case MQTTS_STATE_SEARCHING_GW: + /* we are searching for a gw and receive a SEARCHGW before + * we send SEARCHGW => we re-schedule the sending of our SEARCHGW */ + mqtts_timer_start_wait_searchgw(); + break; + /*only reply with GWINFO if we are connected to a gw*/ + case MQTTS_STATE_READY: + /* delay before sending GWINFO to give priority to gw */ + mqtts_timer_start_wait_gwinfo(); + break; + default: + break; + } +} + +static void handleCONNACK(unsigned char* msg) { + switch (mqtts_state) { + /* only accept CNNACK if we are in CONNECTING_TO_GW*/ + case MQTTS_STATE_CONNECTING_TO_GW: + if (gp_byte_get(msg,3) == MQTTS_RET_ACCEPTED) { /* check return code */ + mqtts_timer_stop_ack(); + mqtts_state=MQTTS_STATE_READY; + isConnected= 1; + lostGw= 0; + mqttscb_connected(); + /* set flagCleanSession and flagWill to 0 so that in case we + * lose the gw we can re-connect without requesting the app + * to re-do the subscriptions and the will stuffs */ + //conn_parms->flagCleanSession=0; + /* TODO the new Will feature is not yet supported by the broker */ + //conn_parms->flagWill=0; + } else { + /* conn is rejected => either we look for another gw, or we give + * the rejection reason to the app and let it decide what to do + * current decision: let app decide */ + mqtts_state = MQTTS_STATE_WAITING_CONNECT; + mqtts_timer_stop_ack(); + mqtts_timer_stop_keep_alive(); + mqttscb_disconnected(gp_byte_get(msg,3)); + } + break; + default: + break; + } +} + + +static void handleWILLTOPICREQ(void) { + mqtts_timer_stop_ack(); + mqtts_willtopic(); +} + +static void handleWILLMSGREQ(void) { + mqtts_timer_stop_ack(); + mqtts_willmsg(); +} + +static void handleREGISTER(unsigned char* msg) { + unsigned char i; + unsigned char topic[MQTTS_MAX_MSG_SIZE]; + unsigned char topic_len; + unsigned char msg_len; + + msg_len = gp_byte_get(msg,1); + topic_len = 0; + + /* ignore message if not sent by our gw */ + /* gw = mqtts_conn_get_gw(); + if (memcmp(sender,gw->address, gw->address_len)!=0) return; */ + + /* copy topic */ + for (i=0;i<(msg_len-6);i++) { + topic[i] = gp_byte_get(msg,7+i); + topic_len++; + } + + /* send regack to gw */ + mqtts_regack( + gp_byte_get(msg,3), gp_byte_get(msg,4), /* topic id*/ + gp_byte_get(msg,5), gp_byte_get(msg,6), /* msg id*/ + MQTTS_RET_ACCEPTED); /*return code*/ + /* inform app */ + mqttscb_register_received( + gp_byte_get(msg,3), gp_byte_get(msg,4), + topic, topic_len); +} + +static void handleREGACK(unsigned char* msg) { + + /*gw = mqtts_conn_get_gw();*/ + /*if (memcmp(sender,gw->address, gw->address_len)!=0) return;*/ + + switch (mqtts_state) { + case MQTTS_STATE_WAITING_ACK: + /* check MsgId */ + if ((gp_byte_get(msg,5)!=msgId0) || (gp_byte_get(msg,6)!=msgId1)) return; + + ack_rx(); + mqttscb_regack_received( + gp_byte_get(msg,3), gp_byte_get(msg,4), /* Topic ID */ + gp_byte_get(msg,7)); /* Return code */ + + break; + default: + break; + } +} + +static void handlePUBLISH(unsigned char* msg) { + unsigned char i; + unsigned char data[MQTTS_MAX_MSG_SIZE]; + unsigned char data_len= 0; + unsigned char msg_len= gp_byte_get(msg,1); + + /* ignore message if not sent by our gw */ + /*gw = mqtts_conn_get_gw(); */ + /*if (memcmp(sender,gw->address, gw->address_len)!=0) return;*/ + + /* copy data*/ + for (i=0;i<(msg_len-7);i++) { + data[i]=gp_byte_get(msg,8+i); + data_len++; + } + /* i is now return code from app */ + i= mqttscb_publish_received( + ((gp_byte_get(msg,3) & GET_DUP_FLAG_MASK) >> 7), + ((gp_byte_get(msg,3) & GET_QOS_FLAG_MASK) >> 5), + gp_byte_get(msg,4), gp_byte_get(msg,5), /* Topic id */ + data, data_len); + /* send puback if QoS = 1 or retCode = MQTTS_RET_INVALID_TOPIC_ID */ + if (((gp_byte_get(msg,3) & GET_QOS_FLAG_MASK)>> 5)==1 || + (i == MQTTS_RET_INVALID_TOPIC_ID)) { + mqtts_puback(gp_byte_get(msg,4), gp_byte_get(msg,5), /* Topic ID */ + gp_byte_get(msg,6), gp_byte_get(msg,7), /* Msg Id */ + i); /* return code */ + } + /* TODO QoS=2 not implemented yet */ + +} + +static void handlePUBACK(unsigned char* msg) { + switch (mqtts_state) { + case MQTTS_STATE_WAITING_ACK: + /*gw = mqtts_conn_get_gw();*/ + /*if (memcmp(sender,gw->address, gw->address_len)!=0) return;*/ + + // check msgId + if ((gp_byte_get(msg,5)!=msgId0) || (gp_byte_get(msg,6)!=msgId1)) return; + + ack_rx(); + // inform app + mqttscb_puback_received(gp_byte_get(msg,3), gp_byte_get(msg,4), /* topicId */ + gp_byte_get(msg,7)); /* Return code */ + break; + default: + /*inform app in any case if return code != MQTTS_RET_ACCEPTED*/ + if (gp_byte_get(msg,7)!=MQTTS_RET_ACCEPTED){ + mqttscb_puback_received(gp_byte_get(msg,3), gp_byte_get(msg,4), /* topicId */ + gp_byte_get(msg,7)); /* Return code */ + } + break; + } +} + +static void handleSUBACK(unsigned char* msg) { + + switch (mqtts_state) { + case MQTTS_STATE_WAITING_ACK: + /*gw = mqtts_conn_get_gw();*/ + /*if (memcmp(sender,gw->address, gw->address_len)!=0) return;*/ + if ((gp_byte_get(msg,6)!=msgId0) || (gp_byte_get(msg,7)!=msgId1)) return; + + ack_rx(); + mqttscb_suback_received( + ((gp_byte_get(msg,3) & GET_QOS_FLAG_MASK) >> 5), + gp_byte_get(msg,4), gp_byte_get(msg,5), /* Topic Id */ + gp_byte_get(msg,6)); /* Return code */ + break; + default: + break; + } +} + +static void handleUNSUBACK(unsigned char* msg) { + switch (mqtts_state) { + case MQTTS_STATE_WAITING_ACK: + /*gw = mqtts_conn_get_gw();*/ + /*if (memcmp(sender,gw->address, gw->address_len)!=0) return;*/ + + if ((gp_byte_get(msg,3)!=msgId0) || (gp_byte_get(msg,4)!=msgId1)) return; + + ack_rx(); + mqttscb_unsuback_received(); + break; + default: + break; + } +} + +/* received a PINGREQ from gw; answer it with PINGRESP */ +static void handlePINGREQ(void) { + mqtts_pingresp(); +} + +static void handlePINGRESP(void) { + switch (mqtts_state) { + case MQTTS_STATE_WAITING_ACK: + /*gw = mqtts_conn_get_gw();*/ + /*if (memcmp(sender,gw->address, gw->address_len)!=0) return;*/ + ack_rx(); + break; + default: + break; + } +} + +static void handleDISCONNECT(void) { + mqtts_timer_stop_ack(); + mqtts_timer_stop_keep_alive(); + isConnected= 0; + if (mqtts_state == MQTTS_STATE_DISCONNECTING) { + mqtts_state = MQTTS_STATE_WAITING_CONNECT; + mqttscb_disconnected(MQTTS_OK); + } else { + /* ops , gw does not know me any more, try to re-CONNECT */ + mqtts_state = MQTTS_STATE_CONNECTING_TO_GW; + mqtts_connecting(); + } +} + +static void handlePUBREC(unsigned char* msg) { + switch (mqtts_state) { + case MQTTS_STATE_WAITING_ACK: + /*gw = mqtts_conn_get_gw(); + if (memcmp(sender,gw->address, gw->address_len)!=0) return;*/ + mqtts_timer_stop_ack(); + mqtts_pubrel(gp_byte_get(msg,3), gp_byte_get(msg,4)); /* Msg id */ + /* WARNING: stack is not ready here because pubrel waits for pubcomp */ + break; + default: + break; + } +} + + +static void handlePUBCOMP(void) { + switch (mqtts_state) { + case MQTTS_STATE_WAITING_ACK: + ack_rx(); + mqttscb_pubcomp_received(); + break; + default: + break; + } +} + +static void handleWILLTOPICRESP(void) { + switch (mqtts_state) { + case MQTTS_STATE_WAITING_ACK: + /*gw = mqtts_conn_get_gw();*/ + /*if (memcmp(sender,gw->address, gw->address_len)!=0) return;*/ + ack_rx(); + mqttscb_willtopicresp_received(); + break; + default: + break; + } +} + +static void handleWILLMSGRESP(void) { + switch (mqtts_state) { + case MQTTS_STATE_WAITING_ACK: + /*gw = mqtts_conn_get_gw();*/ + /*if (memcmp(sender,gw->address, gw->address_len)!=0) return;*/ + ack_rx(); + mqttscb_willmsgresp_received(); + break; + default: + break; + } +} + +static void gwFound(void) { + mqtts_timer_stop_wait_searchgw(); + if (isConnected == 0) { + mqtts_state = MQTTS_STATE_CONNECTING_TO_GW; + mqtts_connecting(); + } else { + /* we have lost the gw and find now one again + * we resend the last message without reconnecting */ + mqtts_state = MQTTS_STATE_WAITING_ACK; + send_backupMsg(); + } +} + +static void handleADVERTISE(unsigned char* msg, unsigned char* sender, + unsigned char sender_len) { + + mqtts_timer_stop_wait_gwinfo(); /* cancel send gwinfo */ + switch (mqtts_state) { + case MQTTS_STATE_SEARCHING_GW: +#if MQTTS_DEBUG + gp_debug((unsigned char*)"adv ",4); +#endif + memcpy(myGwAddr,sender,sender_len); + myGwAddrLength= sender_len; + myGwId=gp_byte_get(msg,3); + gwFound(); + break; + default: + /* ignore ADVERTISE if we are not searching for GW */ + break; + } +} + +static void handleGWINFO(unsigned char* msg, unsigned char* sender, + unsigned char sender_len) { + + /* we receive a GWINFO before sending our GWINFO */ + /* => we don't send our GWINFO */ + mqtts_timer_stop_wait_gwinfo(); + + switch (mqtts_state) { + case MQTTS_STATE_SEARCHING_GW: +#if MQTTS_DEBUG + gp_debug((unsigned char*)"gwi ",4); +#endif + gwinfo_received(msg,sender,sender_len); + gwFound(); + break; + default: + /* ignore GWINFO if we are not searching for GW */ + break; + } +} + +/********************************************************************/ + +/*** + * send a SEARCHGW message */ +void mqtts_searchgw(void) { + unsigned char msg[3]= {3,SEARCHGW,MQTTS_SEARCHGW_BROADCAST_RADIUS}; + + /* broadcast message and restart timer */ + mqtts_timer_start_wait_searchgw(); + gp_network_msg_broadcast(msg,MQTTS_SEARCHGW_BROADCAST_RADIUS); +#if MQTTS_DEBUG + gp_debug((unsigned char*)"s_gw ",5); +#endif +} + +/*** + * send a GWINFO message */ +void mqtts_gwinfo(void) { + unsigned char msg[MQTTS_MAX_NETWORK_ADDRESS_LENGTH + HEADER_LENGTH + 1]; + unsigned char i; + + msg_set_type(GWINFO); + msg[HEADER_LENGTH]= myGwId; + msg_set_length (HEADER_LENGTH + 1 + myGwAddrLength); + for (i=0;i<myGwAddrLength;i++) { + msg[HEADER_LENGTH+1+i]=myGwAddr[i]; + } + + /* broadcast message */ + gp_network_msg_broadcast(msg,broadcastRadius); + /* Note: GWINFO is broadcasted with the same radius as received in SEARCHGW + * while SEARCHGW itself is broacasted with radius=MQTTS_SEARCHGW_BROADCAST_RADIUS + */ +} + + + +/*** + * send a PUBACK message */ +static void mqtts_puback(unsigned char topicID_1, unsigned char topicID_2, + unsigned char msgID_1, unsigned char msgID_2, unsigned char retCode ) { + + unsigned char msg[HEADER_LENGTH+5]; + + msg_set_type(PUBACK); + /* (Header size) + 2 (Topic ID) + 2 (Message ID) + 1 (Return code) */ + msg_set_length(HEADER_LENGTH+5); + + /* Fill fixed length parameters */ + msg[HEADER_LENGTH]=topicID_1; + msg[HEADER_LENGTH+1]=topicID_2; + msg[HEADER_LENGTH+2]=msgID_1; + msg[HEADER_LENGTH+3]=msgID_2; + msg[HEADER_LENGTH+4]=retCode; + + /* start keep alive timer and send the message*/ + mqtts_timer_start_keep_alive(); + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); +} + +/*** + * send a REGACK message */ +static void mqtts_regack(unsigned char topicId_1, unsigned char topicId_2, + unsigned char msgId1, unsigned char msgId2, unsigned char retCode ) { + + unsigned char msg[HEADER_LENGTH+5]; + + msg_set_type(REGACK); + /* (Header size) + 2 (topic id) + 2 (Message ID) + 1 (Return code) */ + msg_set_length(HEADER_LENGTH+5); + + /* Fill fixed length parameters */ + msg[HEADER_LENGTH]=topicId_1; + msg[HEADER_LENGTH+1]=topicId_2; + msg[HEADER_LENGTH+2]=msgId1; + msg[HEADER_LENGTH+3]=msgId2; + msg[HEADER_LENGTH+4]=retCode; + + /* start keep alive timer and send the message*/ + mqtts_timer_start_keep_alive(); + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); +} + + +/*** + * send a PUBREL message */ +static void mqtts_pubrel(unsigned char msgID_1, unsigned char msgID_2 ) { + + unsigned char msg[HEADER_LENGTH+2]; + + /* Fill the header */ + msg_set_type(PUBREL); + /*(Header size) + 2 (Message ID) */ + msg_set_length(HEADER_LENGTH+2); + + /* Fill fixed length parameters */ + msg[HEADER_LENGTH]=msgID_1; + msg[HEADER_LENGTH+1]=msgID_2; + + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + + /* we are waiting for PUBCOMP */ + /* TODO What happens if we do not rx a PUBCOMP ? Retransmit PUBREL? */ + /* backup the message for the ack*/ + backup_msg(msg); + mqtts_state=MQTTS_STATE_WAITING_ACK; + + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); +} + + + +/*** + * send an PINGRESP message */ +static void mqtts_pingresp(void) { + unsigned char msg[2]={2,PINGRESP}; + mqtts_timer_start_keep_alive(); + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); +} + +/** + * send an PINGREQ message */ +void mqtts_pingreq(void) { + unsigned char msg[2]={2,PINGREQ}; + /* backup the message for the ack*/ + backup_msg(msg); + /* start timers */ + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + mqtts_state=MQTTS_STATE_WAITING_ACK; + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); +} + +/** + send an WILLMSG message + */ +static void mqtts_willmsg(void) { + unsigned char msg[MQTTS_MAX_MSG_SIZE]; + unsigned char i; + + /* Fill the header */ + msg_set_type(WILLMSG); + + /* Length = Header size + WillMsg */ + msg_set_length(HEADER_LENGTH + conn_parms->vlpWillMsg_length); + + /* then the variable parameters */ + for (i=0; i<conn_parms->vlpWillMsg_length; i++){ + msg[HEADER_LENGTH+i]= conn_parms->vlpWillMsg[i]; + } + + /* backup the message for the ack*/ + backup_msg(msg); + + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); +} + +/** + send an WILLTOPIC message + */ +static void mqtts_willtopic(void) { + + unsigned char msg[MQTTS_MAX_MSG_SIZE]; + unsigned char i; + + /* Fill the header */ + msg_set_type(WILLTOPIC); + /* Length = Header size + Flags + WillTopic */ + msg_set_length(HEADER_LENGTH + 1 + conn_parms->vlpWillTopic_length); + + /* First the flags */ + msg[HEADER_LENGTH] = EMPTY_MASK; + + switch (conn_parms->flagWillQOS) { + case 2: + msg[HEADER_LENGTH] |= QOS2_FLAG_MASK; + break; + + case 1: + msg[HEADER_LENGTH] |= QOS1_FLAG_MASK; + break; + + default: + msg[HEADER_LENGTH] |= QOS0_FLAG_MASK; + break; + } + + switch (conn_parms->flagWillRetain) { + case 1: + msg[HEADER_LENGTH] |= RETAIN_FLAG_MASK; + break; + + default: + break; + } + + /* then the variable parameters */ + for (i=0; i<conn_parms->vlpWillTopic_length; i++) + { + msg[HEADER_LENGTH+1+i]=conn_parms->vlpWillTopic[i]; + } + + /* backup the message for the ack*/ + backup_msg(msg); + + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); +} + +/** + send CONNECT message + */ + +static void mqtts_connecting(void) { + unsigned char msg[MQTTS_MAX_MSG_SIZE]; + unsigned char i; + + /* set the value of the keep_alive timer */ + mqtts_timer_set_keep_alive_time(conn_parms->flpDuration); + + /* Fill the header */ + msg_set_type(CONNECT); + msg_set_length(HEADER_LENGTH + 4 + conn_parms->vlpClientID_length); + + /* Fill the message parameters */ + + /* First the flags */ + msg[HEADER_LENGTH] = EMPTY_MASK; + + switch (conn_parms->flagWill) { + case 1: + msg[HEADER_LENGTH] |= WILL_FLAG_MASK; + break; + + default: + break; + } + + switch (conn_parms->flagCleanSession) + { + case 1: + msg[HEADER_LENGTH] |= CLEANSS_FLAG_MASK; + break; + + default: + break; + } + + /* then the fixed parameters */ + msg[HEADER_LENGTH+1] = conn_parms->flpProtocolID; + msg[HEADER_LENGTH+2] = conn_parms->flpDuration[0]; + msg[HEADER_LENGTH+3] = conn_parms->flpDuration[1]; + + + /* then the variable parameters */ + for (i=0; i<conn_parms->vlpClientID_length; i++) { + msg[HEADER_LENGTH + 4 + i]= conn_parms->vlpClientID[i]; + } + + + /* backup the message for the ack */ + backup_msg(msg); + + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); + /* inform app about CONNECT sent */ + mqttscb_connect_sent(); + return; +} + +/*************************************************/ +/** **/ +/** application API Implementation **/ +/** **/ +/*************************************************/ + + +void mqtts_startStack (void) { + if (mqtts_state != MQTTS_STATE_NOT_ACTIVE) return; + + mqtts_state = MQTTS_STATE_WAITING_CONNECT; + mqtts_timer_init(); + +#if MQTTS_DEBUG + gp_debug((unsigned char *)"MQTT-S client v.",16); + gp_debug((unsigned char *)MQTTS_VERSION,4); + gp_debug((unsigned char *)", ",2); +#endif +} + + +void mqtts_stopStack(void) { + /* reset all stack variables and stop/delete all timers */ + mqtts_state = MQTTS_STATE_NOT_ACTIVE; + isConnected= 0; + lostGw= 0; + msgId0=msgId1= 0; + mqtts_timer_end(); + +} + + +unsigned char mqtts_connect(mqtts_CONNECT_Parms *pParms) { + + if ((mqtts_state != MQTTS_STATE_WAITING_CONNECT)) { + return MQTTS_ERR_STACK_NOT_READY; + } + + /* (Header size) + 4 ( 1 (Flags) + 1 (ProtocolId) + 2 (Duration)) */ + if ( (pParms->vlpClientID_length) > + (MQTTS_MAX_MSG_SIZE - (HEADER_LENGTH + 4)) ) + { + return MQTTS_ERR_DATA_TOO_LONG; + } + + conn_parms = pParms; + + if (myGwAddrLength == 0) { /* no gw available yet */ + mqtts_state = MQTTS_STATE_SEARCHING_GW; + mqtts_timer_start_wait_searchgw(); + } else { + mqtts_state = MQTTS_STATE_CONNECTING_TO_GW; + mqtts_connecting(); + } + + return MQTTS_OK; +} + + +unsigned char mqtts_disconnect(void) { + unsigned char msg[2]={2,DISCONNECT}; + + switch (mqtts_state) { + case MQTTS_STATE_NOT_ACTIVE: + case MQTTS_STATE_WAITING_CONNECT: + return MQTTS_ERR_STACK_NOT_READY; + case MQTTS_STATE_CONNECTING_TO_GW: + case MQTTS_STATE_SEARCHING_GW: + /* no need for sending a DISC since we are not connected */ + mqtts_state = MQTTS_STATE_WAITING_CONNECT; + isConnected= 0; + mqtts_timer_stop_ack(); + mqtts_timer_stop_keep_alive(); + mqtts_timer_stop_wait_gwinfo(); + mqtts_timer_stop_wait_searchgw(); + mqttscb_disconnected(MQTTS_OK); + break; + case MQTTS_STATE_READY: + case MQTTS_STATE_WAITING_ACK: + /* send DISC to gw and wait for DISC */ + backup_msg(msg); + mqtts_timer_start_ack(); + mqtts_timer_stop_keep_alive(); + mqtts_timer_stop_wait_gwinfo(); + mqtts_timer_stop_wait_searchgw(); + mqtts_state = MQTTS_STATE_DISCONNECTING; + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); + break; + default: + break; + } + return MQTTS_OK; +} + + +unsigned char mqtts_register(mqtts_REGISTER_Parms *pParms) { + unsigned char msg[MQTTS_MAX_MSG_SIZE]; + unsigned char i; + + if (mqtts_state != MQTTS_STATE_READY) return MQTTS_ERR_STACK_NOT_READY; + + /* check topic length */ + if ( (pParms->vlpTopic_length) > + (MQTTS_MAX_MSG_SIZE - (HEADER_LENGTH + 2)) ) + { + return MQTTS_ERR_DATA_TOO_LONG; + } + + msg_set_type(REGISTER); + msg_set_length(HEADER_LENGTH + 4 + pParms->vlpTopic_length); + + /* TopicId field */ + msg[HEADER_LENGTH] = 0x00; + msg[HEADER_LENGTH+1] = 0x00; + /* MsgId field */ + set_msg_id(msg+HEADER_LENGTH+2); + /* TopicName Field */ + for (i=0; i<pParms->vlpTopic_length; i++) { + msg[HEADER_LENGTH + 4 + i]=pParms->vlpTopic[i]; + } + + /* backup the message for the ack */ + backup_msg(msg); + /* we waiting for REGACK */ + mqtts_state=MQTTS_STATE_WAITING_ACK; + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); + + return MQTTS_OK; + +} + +unsigned char mqtts_publish(mqtts_PUBLISH_Parms *pParms) { + unsigned char msg[MQTTS_MAX_MSG_SIZE]; + unsigned char i; + + if (mqtts_state != MQTTS_STATE_READY) return MQTTS_ERR_STACK_NOT_READY; + + /* Fill the header */ + msg_set_type(PUBLISH); + + /* Header size + 5 (1 (Flags) + 2 (Message ID) + 2 (Topic ID)) */ + + if ( (pParms->vlpData_length) > (MQTTS_MAX_MSG_SIZE - (HEADER_LENGTH+5)) ) + { + return MQTTS_ERR_DATA_TOO_LONG; + } + + msg_set_length(HEADER_LENGTH + 5 + pParms->vlpData_length); + + + /* Fill the message parameters */ + + /* First the flags */ + msg[HEADER_LENGTH] = EMPTY_MASK; + + switch (pParms->flagQOS) { + case 2: + msg[HEADER_LENGTH] |= QOS2_FLAG_MASK; + set_msg_id(msg+5); + break; + + case 1: + msg[HEADER_LENGTH] |= QOS1_FLAG_MASK; + /* set the message id */ + set_msg_id(msg+5); + break; + + default: + msg[HEADER_LENGTH] |= QOS0_FLAG_MASK; + /* message id = 0x0000 in case of QoS level 0*/ + msg[5]=0x00; + msg[6]=0x00; + break; + } + + switch (pParms->flagRetain) + { + case 1: + msg[HEADER_LENGTH] |= RETAIN_FLAG_MASK; + break; + + default: + break; + } + + switch (pParms->flagTopicIdType) + { + case 2: + msg[HEADER_LENGTH] |= TOPICSHORT_FLAG_MASK; + break; + + case 1: + msg[HEADER_LENGTH] |= TOPICIDPRE_FLAG_MASK; + break; + + default: + break; + } + + /* then the fixed parameters */ + msg[HEADER_LENGTH+1] = pParms->flpTopicID[0]; + msg[HEADER_LENGTH+2] = pParms->flpTopicID[1]; + + /* then the variable parameters */ + for (i=1; i<=pParms->vlpData_length; i++) + { + msg[(msg_get_length(msg)-i)]=pParms->vlpData[pParms->vlpData_length-i]; + } + + if (pParms->flagQOS!=0) + { + /* save the flag and set the DUP flag for the backup */ + i = msg[2]; + msg[2] |= DUP_FLAG_MASK; + /* backup the message for the ack or pubrec*/ + backup_msg(msg); + /* reload the saved flag */ + msg[2] = i; + /* start the ack or pubrec timer */ + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + mqtts_state=MQTTS_STATE_WAITING_ACK; + } + + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); + + return MQTTS_OK; +} + + +unsigned char mqtts_subscribe(mqtts_SUBSCRIBE_Parms *pParms) { + unsigned char msg[MQTTS_MAX_MSG_SIZE]; + unsigned char i; + + if (mqtts_state != MQTTS_STATE_READY) return MQTTS_ERR_STACK_NOT_READY; + + /* Fill the header */ + msg_set_type(SUBSCRIBE); + + /* Header size + 1 (Flags) + 2 (MsgId)*/ + + if ( (pParms->vlpTopic_length) > (MQTTS_MAX_MSG_SIZE - (HEADER_LENGTH+3)) ) + { + return MQTTS_ERR_DATA_TOO_LONG; + } + + msg_set_length((HEADER_LENGTH+3) + pParms->vlpTopic_length); + + /* Fill the message parameters */ + /* First the flags */ + msg[HEADER_LENGTH] = EMPTY_MASK; + + switch (pParms->flagQOS) + { + case 2: + msg[HEADER_LENGTH] |= QOS2_FLAG_MASK; + break; + + case 1: + msg[HEADER_LENGTH] |= QOS1_FLAG_MASK; + break; + + default: + msg[HEADER_LENGTH] |= QOS0_FLAG_MASK; + break; + } + + switch (pParms->flagTopicIdType) + { + case 2: + msg[HEADER_LENGTH] |= TOPICSHORT_FLAG_MASK; + break; + case 1: + msg[HEADER_LENGTH] |= TOPICIDPRE_FLAG_MASK; + break; + default: + break; + } + + /* MsgId field */ + set_msg_id(msg+HEADER_LENGTH+1); + + /* then the variable parameters */ + for (i=0; i<pParms->vlpTopic_length; i++) + { + msg[HEADER_LENGTH + 3 + i]=pParms->vlpTopic[i]; + } + + /* save the flag and set the DUP flag for the backup */ + i = msg[HEADER_LENGTH]; + msg[HEADER_LENGTH] |= DUP_FLAG_MASK; + /* backup the message for the ack */ + backup_msg(msg); + /* reload the saved flag */ + msg[HEADER_LENGTH] = i; + /* we wait for SUBACK */ + mqtts_state=MQTTS_STATE_WAITING_ACK; + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); + + + return MQTTS_OK; +} + + +unsigned char mqtts_unsubscribe(mqtts_UNSUBSCRIBE_Parms *pParms) { + unsigned char msg[MQTTS_MAX_MSG_SIZE]; + unsigned char i; + + if (mqtts_state != MQTTS_STATE_READY) return MQTTS_ERR_STACK_NOT_READY; + + /* Fill the header */ + msg_set_type(UNSUBSCRIBE); + /* Header size + 3 (Flags+MsgId) + (TopicLength))*/ + msg_set_length(HEADER_LENGTH + 3 + pParms->vlpTopic_length); + + /* Fill the message parameters */ + + /* First the flags */ + msg[HEADER_LENGTH] = EMPTY_MASK; + + switch (pParms->flagTopicIdType) + { + case 2: + msg[HEADER_LENGTH] |= TOPICSHORT_FLAG_MASK; + break; + case 1: + msg[HEADER_LENGTH] |= TOPICIDPRE_FLAG_MASK; + break; + default: + break; + } + + /* MsgId field */ + set_msg_id(msg+HEADER_LENGTH+1); + + /* then the variable parameters */ + for (i=0; i<pParms->vlpTopic_length; i++) + { + msg[HEADER_LENGTH + 3 + i]=pParms->vlpTopic[i]; + } + + /* save the flag and set the DUP flag for the backup*/ + i = msg[HEADER_LENGTH]; + msg[HEADER_LENGTH] |= DUP_FLAG_MASK; + /* backup the message for the ack*/ + backup_msg(msg); + /* reload the saved flag */ + msg[HEADER_LENGTH] = i; + /* we wait for UNSUBACK */ + mqtts_state=MQTTS_STATE_WAITING_ACK; + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); + + + return MQTTS_OK; + +} + + +unsigned char mqtts_willtopic_update(mqtts_WILLTOPICUPD_Parms *pParms) { + unsigned char msg[MQTTS_MAX_MSG_SIZE]; + unsigned char i; + + if (mqtts_state != MQTTS_STATE_READY) return MQTTS_ERR_STACK_NOT_READY; + + /* Fill the header */ + msg_set_type(WILLTOPICUPD); + + if (pParms == NULL) { //send an empty WILLTOPICUPD to delete will topic and msg + msg_set_length(HEADER_LENGTH); + } else { + msg_set_length(HEADER_LENGTH + 1 + pParms->vlpWillTopic_length); + msg[HEADER_LENGTH] = EMPTY_MASK; + switch (pParms->flagWillQOS) { + case 2: + msg[HEADER_LENGTH] |= QOS2_FLAG_MASK; + break; + case 1: + msg[HEADER_LENGTH] |= QOS1_FLAG_MASK; + break; + default: + msg[HEADER_LENGTH] |= QOS0_FLAG_MASK; + break; + } + switch (pParms->flagWillRetain) { + case 1: + msg[HEADER_LENGTH] |= RETAIN_FLAG_MASK; + break; + default: + break; + } + for (i=0; i<pParms->vlpWillTopic_length; i++) + { + msg[HEADER_LENGTH+1+i]=pParms->vlpWillTopic[i]; + } + } + + /* backup the message for the ack*/ + backup_msg(msg); + /* we wait for WILLTOPICRESP */ + mqtts_state=MQTTS_STATE_WAITING_ACK; + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); + + return MQTTS_OK; +} + + + +unsigned char mqtts_willmsg_update(mqtts_WILLMSGUPD_Parms *pParms) { + unsigned char msg[MQTTS_MAX_MSG_SIZE]; + unsigned char i; + + if (mqtts_state != MQTTS_STATE_READY) return MQTTS_ERR_STACK_NOT_READY; + + /* Fill the header */ + msg_set_type(WILLMSGUPD); + + /* Length = Header size + WillMsg */ + msg_set_length(HEADER_LENGTH + pParms->vlpWillMsg_length); + + /* then the variable parameters */ + for (i=0; i<pParms->vlpWillMsg_length; i++){ + msg[HEADER_LENGTH+i]= pParms->vlpWillMsg[i]; + } + /* backup the message for the ack*/ + backup_msg(msg); + mqtts_state=MQTTS_STATE_WAITING_ACK; + mqtts_timer_start_ack(); + mqtts_timer_start_keep_alive(); + + gp_network_msg_send(msg,myGwAddr,myGwAddrLength); + + return MQTTS_OK; +} + + + + +static void gwinfo_received(unsigned char *msg, unsigned char *sender, unsigned char sender_len) { + unsigned char i; + unsigned char data[MQTTS_MAX_MSG_SIZE]; + unsigned char data_len; + unsigned char msg_len; + + msg_len = gp_byte_get(msg,1); + data_len = 0; + + switch(msg_len) { + case 3: + /* GWINFO was sent by a gw */ + memcpy(myGwAddr,sender,sender_len); + myGwAddrLength= sender_len; + myGwId= gp_byte_get(msg,3); + break; + default: + /* GWINFO was sent by a client */ + for (i=0;i<(msg_len-3);i++) { + data[i]=gp_byte_get(msg,4+i); + data_len++; + } + memcpy(myGwAddr,data,data_len); + myGwAddrLength= data_len; + myGwId= gp_byte_get(msg,3); + break; + } + +} + + + +void gpcb_network_msg_received(unsigned char *msg,unsigned char *sender,unsigned char sender_len) { + + unsigned char msg_type= gp_byte_get(msg,2); + + if (mqtts_state == MQTTS_STATE_NOT_ACTIVE) { + /* stack not started, all msgs are ignored */ + return; + } + + switch (msg_type) { + case ADVERTISE: + handleADVERTISE(msg, sender, sender_len); + return; + + case GWINFO: + handleGWINFO(msg, sender, sender_len); + return; + + case SEARCHGW: + broadcastRadius= gp_byte_get(msg,3); + handleSEARCHGW(); + return; + + default: + /* ignore msg if not sent by my gw */ + if (memcmp(sender,myGwAddr, myGwAddrLength)!=0) return; + } + + switch (msg_type) { + case CONNACK: + handleCONNACK(msg); + break; + + case WILLTOPICREQ: + handleWILLTOPICREQ(); + break; + + case WILLMSGREQ: + handleWILLMSGREQ(); + break; + + case REGISTER: + handleREGISTER(msg); + break; + + case REGACK: + handleREGACK(msg); + break; + + case PUBLISH: + handlePUBLISH(msg); + break; + + case PUBACK: + handlePUBACK(msg); + break; + + case PUBREC: + handlePUBREC(msg); + break; + + case PUBCOMP: + handlePUBCOMP(); + break; + + case SUBACK: + handleSUBACK(msg); + break; + + case UNSUBACK: + handleUNSUBACK(msg); + break; + + case PINGREQ: + handlePINGREQ(); + break; + + case PINGRESP: + handlePINGRESP(); + break; + + case DISCONNECT: + handleDISCONNECT(); + break; + + case WILLTOPICRESP: + handleWILLTOPICRESP(); + break; + case WILLMSGRESP: + handleWILLMSGRESP(); + break; + + default: + break; + + } /* end switch (msg_type) */ +} + +unsigned char mqtts_get_state(void) { + return (mqtts_state); +} + + +/* END OF FILE */ + + diff --git a/apps/MQTTSN-C-Client/src/mqttsn/mqtts-timer.c b/apps/MQTTSN-C-Client/src/mqttsn/mqtts-timer.c new file mode 100644 index 0000000..2b895e6 --- /dev/null +++ b/apps/MQTTSN-C-Client/src/mqttsn/mqtts-timer.c @@ -0,0 +1,181 @@ +/******************************************************************************* + * Copyright (c) 2008, 2013 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * + * Description : MQTT-SN timer file + * + */ + + +#include <stdlib.h> +#include <string.h> + +#include "mqtts-timer.h" +#include "gp_api.h" +#include "mqtts_api.h" + + +/* Timer ids */ +static unsigned char timer_ack; +static unsigned char timer_keep_alive; +static unsigned char timer_wait_searchgw; +static unsigned char timer_wait_gwinfo; + +static unsigned char gAckMissedCnt=0; + +/* Keep-alive timer value */ +static unsigned char gKeepAliveTime[2]={0,0}; + +/* SEARCHGW procedure */ +/* simplified procedure + * SEARCHGW delay: double after each time-out acc. until it reaches 255 sec + */ +static unsigned char gSearchGwDelay = SEARCHGW_MIN_DELAY; + +/*callback functions when time-out */ +void timeoutcb_ack(void); +void timeoutcb_keep_alive(void); +void timeoutcb_wait_searchgw(void); +void timeoutcb_wait_gwinfo(void); + +/* functions prototypes, see mqtts-core.c */ +void lost_gw(void); +void mqtts_pingreq(void); +void mqtts_searchgw(void); +void mqtts_gwinfo(void); +void send_backupMsg(void); + +/** + * init timer component */ +void mqtts_timer_init(void) { + /* ask gp to create timers */ + timer_ack = gp_timer_new(timeoutcb_ack); + timer_keep_alive = gp_timer_new(timeoutcb_keep_alive); + timer_wait_searchgw = gp_timer_new(timeoutcb_wait_searchgw); + timer_wait_gwinfo = gp_timer_new(timeoutcb_wait_gwinfo); +} + +/** + * set the keep alive timer value, value in sec */ +void mqtts_timer_set_keep_alive_time(unsigned char *time) { + gKeepAliveTime[0] = time[0]; /*Most significant byte*/ + gKeepAliveTime[1] = time[1]; /*Least significant byte */ +} + +/** + * stop and release all timers */ +void mqtts_timer_end(void) { + gp_timer_end(timer_ack); + gp_timer_end(timer_keep_alive); + gp_timer_end(timer_wait_searchgw); + gp_timer_end(timer_wait_gwinfo); + gAckMissedCnt=0; + gSearchGwDelay=SEARCHGW_MIN_DELAY; +} + +/** + * start ACK timer */ +void mqtts_timer_start_ack(void) { + gp_timer_start(timer_ack, 0, ACK_TIME); +} + + +/** + * start keep alive timer */ +void mqtts_timer_start_keep_alive(void) { + gp_timer_start(timer_keep_alive, gKeepAliveTime[0], gKeepAliveTime[1]); +} + + +/** + * start timer for SEARCHGW */ +void mqtts_timer_start_wait_searchgw() { + gp_timer_start(timer_wait_searchgw,0,gSearchGwDelay); + + /* douple time delay for SEARCHGW after every time-out + * until it reaches 255 sec */ + if ((gSearchGwDelay==0x80) || (gSearchGwDelay==0xFF)) { + gSearchGwDelay = 0xFF; + } else { + gSearchGwDelay = gSearchGwDelay << 1; + } +} + +/** + * start timer for GWINFO */ +void mqtts_timer_start_wait_gwinfo() { + gp_timer_start(timer_wait_gwinfo,0,GWINFO_MIN_DELAY); +} + +/** + * stop ack timer and reset gAckMissedCnt */ +void mqtts_timer_stop_ack(void) { + gp_timer_stop(timer_ack); + gAckMissedCnt=0; +} +/** + * stop keep alive timer */ +void mqtts_timer_stop_keep_alive(void) { + gp_timer_stop(timer_keep_alive); +} +/** + * stop wait GWINFO timer */ +void mqtts_timer_stop_wait_gwinfo(void) { + gp_timer_stop(timer_wait_gwinfo); +} +/** + * stop wait SEARCHGW timer */ +void mqtts_timer_stop_wait_searchgw(void) { + gp_timer_stop(timer_wait_searchgw); + gSearchGwDelay = 0x01; +} + +/** + * ACK time-out */ +void timeoutcb_ack(void) { + /* increment counter and stop timer */ + gAckMissedCnt++; + /*no need for stop, timer already stopped when times out*/ + /*gp_timer_stop(timer_ack);*/ + + if (gAckMissedCnt > MAX_ACK_MISSED) { + /* too many ACKs missed */ + lost_gw(); + gAckMissedCnt=0; + } else { + /* send backup message */ + send_backupMsg(); + } +} + +/** + * Keep alive time out: send a PINGREQ to gw */ +void timeoutcb_keep_alive(void) { + mqtts_pingreq(); +} + +/** + * Wait GWINFO time out => send GWINFO */ +void timeoutcb_wait_gwinfo(void) { + mqtts_gwinfo(); +} +/** + * Wait SEARCHGW time out => send SERACHGW */ +void timeoutcb_wait_searchgw(void) { + mqtts_searchgw(); +} + diff --git a/apps/MQTTSN-C-Client/src/mqttsn/mqtts-timer.h b/apps/MQTTSN-C-Client/src/mqttsn/mqtts-timer.h new file mode 100644 index 0000000..78d5931 --- /dev/null +++ b/apps/MQTTSN-C-Client/src/mqttsn/mqtts-timer.h @@ -0,0 +1,60 @@ +/******************************************************************************* + * Copyright (c) 2008, 2013 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * + * + * Description : MQTT-SN timer header file + * + * + */ + + +/** + * Initialize mqtts-timer component */ +void mqtts_timer_init(); +/** + * set value of keep alive timer */ +void mqtts_timer_set_keep_alive_time(unsigned char *time); +/** + * end and delete all timers */ +void mqtts_timer_end(void); +/** + * stack ACK timer */ +void mqtts_timer_start_ack(void); +/** + * start keep alive timer */ +void mqtts_timer_start_keep_alive(void); +/** + * start wait timer before sending SEARCHGW */ +void mqtts_timer_start_wait_searchgw(void); +/** + * start wait timer before sending GWINFO */ +void mqtts_timer_start_wait_gwinfo(void); +/** + * stop ACK timer */ +void mqtts_timer_stop_ack(void); +/** + * stop keep alive timer */ +void mqtts_timer_stop_keep_alive(void); +/** + * stop GWINFO wait timer */ +void mqtts_timer_stop_wait_gwinfo(void); +/** + * stop SEARCHGW wait timer */ +void mqtts_timer_stop_wait_searchgw(void); + + diff --git a/apps/MQTTSN-C-Client/src/mqttsn/mqtts_api.h b/apps/MQTTSN-C-Client/src/mqttsn/mqtts_api.h new file mode 100644 index 0000000..cc6a7cc --- /dev/null +++ b/apps/MQTTSN-C-Client/src/mqttsn/mqtts_api.h @@ -0,0 +1,426 @@ +/******************************************************************************* + * Copyright (c) 2008, 2013 IBM Corp. + * + * All rights reserved. This program and the accompanying materials + * are made available under the terms of the Eclipse Public License v1.0 + * and Eclipse Distribution License v1.0 which accompany this distribution. + * + * The Eclipse Public License is available at + * http://www.eclipse.org/legal/epl-v10.html + * and the Eclipse Distribution License is available at + * http://www.eclipse.org/org/documents/edl-v10.php. + * + * Contributors: + * Ian Craggs - initial API and implementation and/or initial documentation + *******************************************************************************/ + +/** + * + * + * Description : MQTT-S client API header file + * + * + * + * + * + */ + + + +#ifndef _MQTTS_APPLICATION_API_H +#define _MQTTS_APPLICATION_API_H + +/** + * ************** MQTT-S specific parameters **/ +#define MQTTS_VERSION "1.03" +/** + * Maximum length of network address, e.g. 4 for ZigBee, 16 for IPv6, etc. + * Note: it just defines a max value, i.e. 16 can also be used for ZigBee */ +#define MQTTS_MAX_NETWORK_ADDRESS_LENGTH 4 +/** + * Maximum size for a mqtts message */ +#define MQTTS_MAX_MSG_SIZE 60 +/** + * Default value of timers, delay periods, etc. + * All time values are in seconds */ +/** + * max number of ACKs missed before gw is declared to be lost */ +#define MAX_ACK_MISSED 4 +/** + * ack waiting time */ +#define ACK_TIME 10 +/** + * waiting time before sending a SEARCHGW */ +#define SEARCHGW_MIN_DELAY 2 +/** + * waiting time before sending a GWINFO */ +#define GWINFO_MIN_DELAY 2 +/** + * Broadcast radius for SEARCHGW messages */ +#define MQTTS_SEARCHGW_BROADCAST_RADIUS 1 + + + +/** + * ********************* Return codes **************/ +/** + * protocol return codes (received from gw) */ +#define MQTTS_RET_ACCEPTED 0x00 +#define MQTTS_RET_CONGESTION 0x01 +#define MQTTS_RET_INVALID_TOPIC_ID 0x02 + +/* local return codes */ +#define MQTTS_OK 0xF0 +#define MQTTS_ERR_STACK_NOT_READY 0xF1 +#define MQTTS_ERR_DATA_TOO_LONG 0xF2 +#define MQTTS_LOST_GATEWAY 0xF3 + + +/** + ******************** MQTTS client's states ***************/ +/** + * Client not active, needs be started with mqtts_startStack() */ +#define MQTTS_STATE_NOT_ACTIVE 0x00 +/* Client is active, but waits for mqtts_connect() to setup a connection */ +#define MQTTS_STATE_WAITING_CONNECT 0x01 +/* Client is searching for a gateway (or forwarder) */ +#define MQTTS_STATE_SEARCHING_GW 0x02 +/* Client has sent a CONNECT to a gw and is waiting for its response */ +#define MQTTS_STATE_CONNECTING_TO_GW 0x03 +/* Client is ready for sending request to gw */ +#define MQTTS_STATE_READY 0x04 +/* Client has sent a request to the gw and is waiting for an acknowledgment + * Note that client can only have one outstanding request at a time and + * therefore does not accept any further app's request in this state */ +#define MQTTS_STATE_WAITING_ACK 0x05 +/* Client has sent a DISCONNECT to gw and is waiting for its response + * Client will return to state WAITING_CONNECT afterwards */ +#define MQTTS_STATE_DISCONNECTING 0x06 + + +/** + ********************* Definition of structures **************/ +/* CONNECT parameters structure */ +typedef struct { + /* flags */ + unsigned char flagCleanSession; + unsigned char flagWill; + unsigned char flagWillQOS; + unsigned char flagWillRetain; + + /* fixed length parameters */ + unsigned char flpProtocolID; + unsigned char flpDuration[2]; /* Keep Alive timer value */ + + /* variable length parameters */ + unsigned char *vlpClientID; + unsigned char vlpClientID_length; + unsigned char *vlpWillMsg; + unsigned char vlpWillMsg_length; + unsigned char *vlpWillTopic; + unsigned char vlpWillTopic_length; + +} mqtts_CONNECT_Parms; + +/* WILLTOPICUPD parameters structure */ +typedef struct { + /* flags */ + unsigned char flagWillQOS; + unsigned char flagWillRetain; + + /* variable length parameters */ + unsigned char *vlpWillTopic; + unsigned char vlpWillTopic_length; + +} mqtts_WILLTOPICUPD_Parms; + +/* WILLMSGUPD parameters structure */ +typedef struct { + unsigned char *vlpWillMsg; + unsigned char vlpWillMsg_length; +} mqtts_WILLMSGUPD_Parms; + +/* REGISTER parameters structure */ +typedef struct { + /* variable length parameters */ + unsigned char *vlpTopic; + unsigned char vlpTopic_length; +} mqtts_REGISTER_Parms; + +/* PUBLISH parameters structure */ +typedef struct { + /* flags */ + /* DUP is set by mqtts when retransmitting the message */ + unsigned char flagQOS; + unsigned char flagRetain; + unsigned char flagTopicIdType; + /* fixed length parameters */ + unsigned char flpTopicID[2]; + /* variable length parameters */ + unsigned char *vlpData; + unsigned char vlpData_length; +} mqtts_PUBLISH_Parms; + +/* SUBSCRIBE parameters structure */ +typedef struct { + /* flags */ + unsigned char flagQOS; + unsigned char flagTopicIdType; + /* variable length parameters: TopicName or TopicId */ + unsigned char *vlpTopic; + unsigned char vlpTopic_length; +} mqtts_SUBSCRIBE_Parms; + +/* UNSUBSCRIBE parameters structure */ +typedef struct { + /* flags */ + unsigned char flagTopicIdType; + /* variable length parameters: TopicName or TopicId */ + unsigned char *vlpTopic; + unsigned char vlpTopic_length; +} mqtts_UNSUBSCRIBE_Parms; + + +/************************************************************* + * + * Functions provided by the mqtts client + * + * Functions that trigger a request to be sent to the gw/broker and + * required a reply from the gw/broker are non-blocking, i.e. the client + * with return immediately after having sent the request to the gw/broker. + * The gw/broker's response will then be indicated by the corresponding + * call-back function. + * + *************************************************************/ + +/** + start the mqtts client: client will go to state WAITING_CONNECT + and begin to process ADVERTISE and GWINFO, but it will wait for + mqtts_connect() to initialize a connection to a gw + + Parameters : none + + Returns : none + + */ +void mqtts_startStack(void); + + +/** + stop the mqtts client: + client will then ignore all messages and go to state "NOT_ACTIVE"; + app needs to re-issue mqtts_startStack() + + Parameters : none + + Returns : none + + */ +void mqtts_stopStack(void); + + +/** + request client to setup a connection to a gw + client stack has to be already started (with mqtts_startStack() ) + + Parameters : + pParms - pointer to a mqtts_CONNECT_Parms + Returns : + MQTTS_ERR_STACK_NOT_READY + MQTTS_ERR_DATA_TOO_LONG + MQTTS_OK + + */ +unsigned char mqtts_connect(mqtts_CONNECT_Parms *pParms); + + +/** + request client to disconnect + client will wait again for mqtts_connect() to setup a connection + + Parameters : none + + Returns : + MQTTS_ERR_STACK_NOT_READY + MQTTS_OK + + */ +unsigned char mqtts_disconnect(void); + + +/** + request client to send a REGISTER message + only accepted if client state = READY + + Parameters : + pParms - pointer to a REGISTER parameter + + Returns : + MQTTS_ERR_STACK_NOT_READY + MQTTS_OK + * + */ +unsigned char mqtts_register(mqtts_REGISTER_Parms *pParms); + + + +/** + request client to send a PUBLISH message + only accepted if client state = READY + + Parameters : + pParms - pointer to a PUBLISH parameter + + Returns : + MQTTS_ERR_STACK_NOT_READY + MQTTS_OK + + */ +unsigned char mqtts_publish(mqtts_PUBLISH_Parms *pParms); + + +/** + request client to send a SUBSCRIBE message + only accepted if client state = READY + + Parameters : + pParms - pointer to a SUBSCRIBE parameter + + Returns : + MQTTS_ERR_STACK_NOT_READY + MQTTS_OK + + */ +unsigned char mqtts_subscribe(mqtts_SUBSCRIBE_Parms *pParms); + + +/** + request client to send an UNSUBSCRIBE message + only accepted if client state = READY + + Parameters : + pParms - pointer to a UNSUBSCRIBE parameter + + Returns : + MQTTS_ERR_STACK_NOT_READY + MQTTS_OK + + */ +unsigned char mqtts_unsubscribe(mqtts_UNSUBSCRIBE_Parms *pParms); + +/** + * request client to send a WILLTOPICUPD message to update the Will topic + * only accepted if client state = READY + * + * Parameters: + * pParms - pointer to a WILLTOPIC parameter + * NULL to send an empty WILLTOPICUPD message (delete Will) + * + * Returns : + * MQTTS_ERR_STACK_NOT_READY + * MQTTS_OK + * + * + */ +unsigned char mqtts_willtopic_update(mqtts_WILLTOPICUPD_Parms *pParms); + +/** + * request client to send a WILLMSGUPD message to update the Will message + * only accepted if client state = READY + * + * Parameters: + * pParms - pointer to WILLMSGUPD parameter + * + * Returns : + * MQTTS_ERR_STACK_NOT_READY + * MQTTS_OK + * + * + */ +unsigned char mqtts_willmsg_update(mqtts_WILLMSGUPD_Parms *pParms); + + +/** + * Request for client state + * + * Inputs: none + * + * Returns: client state + * + * + */ +unsigned char mqtts_get_state(void); + + + +/*************************************************** + * callback functions (to be implemented by the app) + **************************************************** + */ + +/* CONNECT sent to gw, client is waiting for gw's answer */ +void mqttscb_connect_sent(void); + +/* client is connected to a gw, app can now start publishing, ... */ +void mqttscb_connected(void); + +/* client is disconnected + * reason: reason of disconnection */ +void mqttscb_disconnected(unsigned char reason); + +/* REGACK received, app can now use the indicated topicID for publishing */ +void mqttscb_regack_received( + unsigned char topicID_0, + unsigned char topicID_1, + unsigned char returnCode); + +/* PUBLISH received from gw/broker + * app should return immediately either with + * MQTTS_RET_ACCEPTED or MQTTS_RET_INVALID_TOPIC_ID + * before calling any other mqtts function */ +unsigned char mqttscb_publish_received( + unsigned char dup, + unsigned char qos, + unsigned char topicID_0, /* Topic ID[0] */ + unsigned char topicID_1, /* Topic ID[1] */ + unsigned char *data, + unsigned char data_len); + +/* PUBACK received */ +void mqttscb_puback_received( + unsigned char topicID_0, /* Topic ID[0] */ + unsigned char topicID_1, /* Topic ID[1] */ + unsigned char returnCode); + +/* TODO QoS Level 2 not supported yet */ +/* we only need to inform app with PUBCOMP */ +/* PUBREC received (QoS 2) */ +/* void mqttscb_pubrec_received(void); */ +/* PUBCOMP received (QoS 2) */ +void mqttscb_pubcomp_received(void); + +/* SUBACK received */ +void mqttscb_suback_received( + unsigned char qos, + unsigned char topicID_0, + unsigned char topicID_1, + unsigned char returnCode); + +/* UNSUBACK received */ +void mqttscb_unsuback_received(void); + +/* REGISTER received from gw */ +void mqttscb_register_received( + unsigned char topicID_0, /* Topic ID[0] */ + unsigned char topicID_1, /* Topic ID[1] */ + unsigned char *topic, + unsigned char topic_len); + +/* WILLTOPICRESP received from gw */ +void mqttscb_willtopicresp_received(void); + +/* WILLMSGRESP received from gw */ +void mqttscb_willmsgresp_received(void); + +#endif + |