The Cayenne MQTT mbed Library provides functions to easily connect to the Cayenne IoT project builder.

Dependents:   Cayenne-ESP8266Interface Cayenne-WIZnet_Library Cayenne-WIZnetInterface Cayenne-X-NUCLEO-IDW01M1 ... more

Committer:
jburhenn
Date:
Fri Oct 07 22:41:27 2016 +0000
Revision:
3:b5f895caeeb0
Parent:
2:c0419dbecfbd
Child:
4:421366004b5d
Updated documentation comments.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
jburhenn 0:09ef59d2d0f7 1 /*
jburhenn 0:09ef59d2d0f7 2 The MIT License(MIT)
jburhenn 0:09ef59d2d0f7 3
jburhenn 0:09ef59d2d0f7 4 Cayenne MQTT Client Library
jburhenn 0:09ef59d2d0f7 5 Copyright (c) 2016 myDevices
jburhenn 0:09ef59d2d0f7 6
jburhenn 0:09ef59d2d0f7 7 Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated
jburhenn 0:09ef59d2d0f7 8 documentation files(the "Software"), to deal in the Software without restriction, including without limitation
jburhenn 0:09ef59d2d0f7 9 the rights to use, copy, modify, merge, publish, distribute, sublicense, and / or sell copies of the Software,
jburhenn 0:09ef59d2d0f7 10 and to permit persons to whom the Software is furnished to do so, subject to the following conditions :
jburhenn 0:09ef59d2d0f7 11 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
jburhenn 0:09ef59d2d0f7 12 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE
jburhenn 0:09ef59d2d0f7 13 WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.IN NO EVENT SHALL THE AUTHORS OR
jburhenn 0:09ef59d2d0f7 14 COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
jburhenn 0:09ef59d2d0f7 15 ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
jburhenn 0:09ef59d2d0f7 16 */
jburhenn 0:09ef59d2d0f7 17
jburhenn 0:09ef59d2d0f7 18 #ifndef _CAYENNEMQTTCLIENT_h
jburhenn 0:09ef59d2d0f7 19 #define _CAYENNEMQTTCLIENT_h
jburhenn 0:09ef59d2d0f7 20
jburhenn 0:09ef59d2d0f7 21 #include <string.h>
jburhenn 0:09ef59d2d0f7 22 #include "MQTTClient.h"
jburhenn 0:09ef59d2d0f7 23 #include "../CayenneUtils/CayenneDefines.h"
jburhenn 0:09ef59d2d0f7 24 #include "../CayenneUtils/CayenneUtils.h"
jburhenn 0:09ef59d2d0f7 25 #include "../CayenneUtils/CayenneDataArray.h"
jburhenn 0:09ef59d2d0f7 26
jburhenn 0:09ef59d2d0f7 27 namespace Cayenne
jburhenn 0:09ef59d2d0f7 28 {
jburhenn 2:c0419dbecfbd 29 /**
jburhenn 2:c0419dbecfbd 30 * Cayenne message data passed to message handler functions.
jburhenn 2:c0419dbecfbd 31 */
jburhenn 0:09ef59d2d0f7 32 typedef struct MessageData
jburhenn 0:09ef59d2d0f7 33 {
jburhenn 3:b5f895caeeb0 34 const char* clientID; //**< The client ID of the message. */
jburhenn 3:b5f895caeeb0 35 CayenneTopic topic; //**< The topic the message was received on. */
jburhenn 3:b5f895caeeb0 36 unsigned int channel; //**< The channel the message was received on. */
jburhenn 3:b5f895caeeb0 37 const char* id; //**< The message ID, if it is a command message, otherwise NULL. */
jburhenn 3:b5f895caeeb0 38 const char* type; //**< The type of data in the message, if it exists, otherwise NULL. */
jburhenn 3:b5f895caeeb0 39 CayenneValuePair values[CAYENNE_MAX_MESSAGE_VALUES]; //**< The unit/value data pairs in the message. The units and values can be NULL. */
jburhenn 3:b5f895caeeb0 40 size_t valueCount; //**< The count of items in the values array. */
jburhenn 0:09ef59d2d0f7 41 } MessageData;
jburhenn 1:90dccf306268 42
jburhenn 1:90dccf306268 43 /**
jburhenn 1:90dccf306268 44 * Client class for connecting to Cayenne via MQTT.
jburhenn 1:90dccf306268 45 * @class MQTTClient
jburhenn 1:90dccf306268 46 * @param Network A network class with the methods: read, write. See NetworkInterface.h for function definitions.
jburhenn 1:90dccf306268 47 * @param Timer A timer class with the methods: countdown_ms, countdown, left_ms, expired. See TimerInterface.h for function definitions.
jburhenn 1:90dccf306268 48 * @param MAX_MQTT_PACKET_SIZE Maximum size of an MQTT message, in bytes.
jburhenn 1:90dccf306268 49 * @param MAX_MESSAGE_HANDLERS Maximum number of message handlers.
jburhenn 1:90dccf306268 50 */
jburhenn 0:09ef59d2d0f7 51 template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE = 128, int MAX_MESSAGE_HANDLERS = 5>
jburhenn 0:09ef59d2d0f7 52 class MQTTClient : private MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, 0>
jburhenn 0:09ef59d2d0f7 53 {
jburhenn 0:09ef59d2d0f7 54 public:
jburhenn 0:09ef59d2d0f7 55 typedef MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, 0> Base;
jburhenn 0:09ef59d2d0f7 56 typedef void(*CayenneMessageHandler)(MessageData&);
jburhenn 0:09ef59d2d0f7 57
jburhenn 0:09ef59d2d0f7 58 /**
jburhenn 0:09ef59d2d0f7 59 * Create an Cayenne MQTT client object.
jburhenn 0:09ef59d2d0f7 60 * @param[in] network Pointer to an instance of the Network class. Must be connected to the endpoint before calling MQTTClient connect.
jburhenn 0:09ef59d2d0f7 61 * @param[in] command_timeout_ms Timeout for commands in milliseconds.
jburhenn 0:09ef59d2d0f7 62 */
jburhenn 0:09ef59d2d0f7 63 MQTTClient(Network& network, unsigned int command_timeout_ms = 30000) : Base(network, command_timeout_ms), _username(NULL), _clientID(NULL)
jburhenn 0:09ef59d2d0f7 64 {
jburhenn 0:09ef59d2d0f7 65 Base::setDefaultMessageHandler(this, &MQTTClient::mqttMessageArrived);
jburhenn 0:09ef59d2d0f7 66 };
jburhenn 0:09ef59d2d0f7 67
jburhenn 0:09ef59d2d0f7 68 /**
jburhenn 0:09ef59d2d0f7 69 * Set default handler function called when a message is received.
jburhenn 0:09ef59d2d0f7 70 * @param[in] handler Function called when message is received, if no other handlers exist for the topic.
jburhenn 0:09ef59d2d0f7 71 */
jburhenn 0:09ef59d2d0f7 72 void setDefaultMessageHandler(CayenneMessageHandler handler)
jburhenn 0:09ef59d2d0f7 73 {
jburhenn 0:09ef59d2d0f7 74 _defaultMessageHandler.attach(handler);
jburhenn 0:09ef59d2d0f7 75 };
jburhenn 0:09ef59d2d0f7 76
jburhenn 0:09ef59d2d0f7 77 /**
jburhenn 0:09ef59d2d0f7 78 * Connect to the Cayenne server.
jburhenn 0:09ef59d2d0f7 79 * @param[in] username Cayenne username
jburhenn 0:09ef59d2d0f7 80 * @param[in] clientID Cayennne client ID
jburhenn 0:09ef59d2d0f7 81 * @param[in] password Password
jburhenn 0:09ef59d2d0f7 82 * @return success code
jburhenn 0:09ef59d2d0f7 83 */
jburhenn 0:09ef59d2d0f7 84 int connect(char* username, char* clientID, char* password) {
jburhenn 0:09ef59d2d0f7 85 MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
jburhenn 0:09ef59d2d0f7 86 data.MQTTVersion = 3;
jburhenn 0:09ef59d2d0f7 87 data.clientID.cstring = _clientID = clientID;
jburhenn 0:09ef59d2d0f7 88 data.username.cstring = _username = username;
jburhenn 0:09ef59d2d0f7 89 data.password.cstring = password;
jburhenn 0:09ef59d2d0f7 90 return Base::connect(data);
jburhenn 0:09ef59d2d0f7 91 };
jburhenn 0:09ef59d2d0f7 92
jburhenn 0:09ef59d2d0f7 93 /**
jburhenn 0:09ef59d2d0f7 94 * Yield to allow MQTT message processing.
jburhenn 0:09ef59d2d0f7 95 * @param[in] timeout_ms The time in milliseconds to yield for
jburhenn 0:09ef59d2d0f7 96 * @return success code
jburhenn 0:09ef59d2d0f7 97 */
jburhenn 0:09ef59d2d0f7 98 int yield(unsigned long timeout_ms = 1000L) {
jburhenn 0:09ef59d2d0f7 99 return Base::yield(timeout_ms);
jburhenn 0:09ef59d2d0f7 100 };
jburhenn 0:09ef59d2d0f7 101
jburhenn 0:09ef59d2d0f7 102 /**
jburhenn 0:09ef59d2d0f7 103 * Check if the client is connected to the Cayenne server.
jburhenn 0:09ef59d2d0f7 104 * @return true if connected, false if not connected
jburhenn 0:09ef59d2d0f7 105 */
jburhenn 0:09ef59d2d0f7 106 bool connected() {
jburhenn 0:09ef59d2d0f7 107 return Base::isConnected();
jburhenn 0:09ef59d2d0f7 108 };
jburhenn 0:09ef59d2d0f7 109
jburhenn 0:09ef59d2d0f7 110 /**
jburhenn 0:09ef59d2d0f7 111 * Disconnect from the Cayenne server.
jburhenn 0:09ef59d2d0f7 112 * @return success code
jburhenn 0:09ef59d2d0f7 113 */
jburhenn 0:09ef59d2d0f7 114 int disconnect() {
jburhenn 0:09ef59d2d0f7 115 return Base::disconnect();
jburhenn 0:09ef59d2d0f7 116 };
jburhenn 0:09ef59d2d0f7 117
jburhenn 0:09ef59d2d0f7 118 /**
jburhenn 0:09ef59d2d0f7 119 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 120 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 121 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 122 * @param[in] type Type to use for a type=value pair, can be NULL if sending to a topic that doesn't require type
jburhenn 0:09ef59d2d0f7 123 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 124 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 125 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 126 * @return success code
jburhenn 0:09ef59d2d0f7 127 */
jburhenn 0:09ef59d2d0f7 128 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, const char* value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 129 CayenneValuePair valuePair[1];
jburhenn 0:09ef59d2d0f7 130 valuePair[0].value = value;
jburhenn 0:09ef59d2d0f7 131 valuePair[0].unit = unit;
jburhenn 0:09ef59d2d0f7 132 return publishData(topic, channel, type, valuePair, 1, clientID);
jburhenn 0:09ef59d2d0f7 133 };
jburhenn 0:09ef59d2d0f7 134
jburhenn 0:09ef59d2d0f7 135 /**
jburhenn 0:09ef59d2d0f7 136 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 137 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 138 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 139 * @param[in] type Type to use for a type=value pair, can be NULL if sending to a topic that doesn't require type
jburhenn 0:09ef59d2d0f7 140 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 141 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 142 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 143 * @return success code
jburhenn 0:09ef59d2d0f7 144 */
jburhenn 0:09ef59d2d0f7 145 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, int value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 146 char str[2 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 147 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 148 itoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 149 #else
jburhenn 0:09ef59d2d0f7 150 snprintf(str, sizeof(str), "%d", value);
jburhenn 0:09ef59d2d0f7 151 #endif
jburhenn 0:09ef59d2d0f7 152 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 153 };
jburhenn 0:09ef59d2d0f7 154
jburhenn 0:09ef59d2d0f7 155 /**
jburhenn 0:09ef59d2d0f7 156 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 157 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 158 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 159 * @param[in] type Type to use for a type=value pair, can be NULL if sending to a topic that doesn't require type
jburhenn 0:09ef59d2d0f7 160 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 161 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 162 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 163 * @return success code
jburhenn 0:09ef59d2d0f7 164 */
jburhenn 0:09ef59d2d0f7 165 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, unsigned int value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 166 char str[1 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 167 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 168 utoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 169 #else
jburhenn 0:09ef59d2d0f7 170 snprintf(str, sizeof(str), "%u", value);
jburhenn 0:09ef59d2d0f7 171 #endif
jburhenn 0:09ef59d2d0f7 172 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 173 };
jburhenn 0:09ef59d2d0f7 174
jburhenn 0:09ef59d2d0f7 175 /**
jburhenn 0:09ef59d2d0f7 176 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 177 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 178 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 179 * @param[in] type Type to use for a type=value pair, can be NULL if sending to a topic that doesn't require type
jburhenn 0:09ef59d2d0f7 180 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 181 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 182 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 183 * @return success code
jburhenn 0:09ef59d2d0f7 184 */
jburhenn 0:09ef59d2d0f7 185 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, long value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 186 char str[2 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 187 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 188 ltoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 189 #else
jburhenn 0:09ef59d2d0f7 190 snprintf(str, sizeof(str), "%ld", value);
jburhenn 0:09ef59d2d0f7 191 #endif
jburhenn 0:09ef59d2d0f7 192 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 193 };
jburhenn 0:09ef59d2d0f7 194
jburhenn 0:09ef59d2d0f7 195 /**
jburhenn 0:09ef59d2d0f7 196 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 197 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 198 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 199 * @param[in] type Type to use for a type=value pair, can be NULL if sending to a topic that doesn't require type
jburhenn 0:09ef59d2d0f7 200 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 201 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 202 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 203 * @return success code
jburhenn 0:09ef59d2d0f7 204 */
jburhenn 0:09ef59d2d0f7 205 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, unsigned long value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 206 char str[1 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 207 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 208 ultoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 209 #else
jburhenn 0:09ef59d2d0f7 210 snprintf(str, sizeof(str), "%lu", value);
jburhenn 0:09ef59d2d0f7 211 #endif
jburhenn 0:09ef59d2d0f7 212 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 213 };
jburhenn 0:09ef59d2d0f7 214
jburhenn 0:09ef59d2d0f7 215 /**
jburhenn 0:09ef59d2d0f7 216 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 217 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 218 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 219 * @param[in] type Type to use for a type=value pair, can be NULL if sending to a topic that doesn't require type
jburhenn 0:09ef59d2d0f7 220 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 221 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 222 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 223 * @return success code
jburhenn 0:09ef59d2d0f7 224 */
jburhenn 0:09ef59d2d0f7 225 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, double value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 226 char str[33];
jburhenn 0:09ef59d2d0f7 227 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 228 dtostrf(value, 5, 3, str);
jburhenn 0:09ef59d2d0f7 229 #else
jburhenn 0:09ef59d2d0f7 230 snprintf(str, 33, "%2.3f", value);
jburhenn 0:09ef59d2d0f7 231 #endif
jburhenn 0:09ef59d2d0f7 232 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 233 };
jburhenn 0:09ef59d2d0f7 234
jburhenn 0:09ef59d2d0f7 235 /**
jburhenn 0:09ef59d2d0f7 236 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 237 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 238 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 239 * @param[in] type Type to use for a type=value pair, can be NULL if sending to a topic that doesn't require type
jburhenn 0:09ef59d2d0f7 240 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 241 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 242 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 243 * @return success code
jburhenn 0:09ef59d2d0f7 244 */
jburhenn 0:09ef59d2d0f7 245 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, float value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 246 char str[33];
jburhenn 0:09ef59d2d0f7 247 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 248 dtostrf(value, 5, 3, str);
jburhenn 0:09ef59d2d0f7 249 #else
jburhenn 0:09ef59d2d0f7 250 snprintf(str, 33, "%2.3f", value);
jburhenn 0:09ef59d2d0f7 251 #endif
jburhenn 0:09ef59d2d0f7 252 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 253 };
jburhenn 0:09ef59d2d0f7 254
jburhenn 0:09ef59d2d0f7 255 /**
jburhenn 0:09ef59d2d0f7 256 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 257 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 258 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 259 * @param[in] type Type to use for a type=value pair, can be NULL if sending to a topic that doesn't require type
jburhenn 0:09ef59d2d0f7 260 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 261 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 262 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 263 * @return success code
jburhenn 0:09ef59d2d0f7 264 */
jburhenn 0:09ef59d2d0f7 265 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const CayenneValuePair* values, size_t valueCount, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 266 char buffer[MAX_MQTT_PACKET_SIZE + 1] = { 0 };
jburhenn 0:09ef59d2d0f7 267 int result = CayenneBuildTopic(buffer, sizeof(buffer), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 268 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 269 size_t size = strlen(buffer);
jburhenn 0:09ef59d2d0f7 270 char* payload = &buffer[size + 1];
jburhenn 0:09ef59d2d0f7 271 size = sizeof(buffer) - (size + 1);
jburhenn 0:09ef59d2d0f7 272 result = CayenneBuildDataPayload(payload, &size, type, values, valueCount);
jburhenn 0:09ef59d2d0f7 273 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 274 result = Base::publish(buffer, payload, size, MQTT::QOS0, true);
jburhenn 0:09ef59d2d0f7 275 }
jburhenn 0:09ef59d2d0f7 276 }
jburhenn 0:09ef59d2d0f7 277 return result;
jburhenn 0:09ef59d2d0f7 278 };
jburhenn 0:09ef59d2d0f7 279
jburhenn 0:09ef59d2d0f7 280 /**
jburhenn 0:09ef59d2d0f7 281 * Send a response to a channel.
jburhenn 0:09ef59d2d0f7 282 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 283 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 284 * @param[in] channel The channel to send response to
jburhenn 0:09ef59d2d0f7 285 * @param[in] id ID of message the response is for
jburhenn 0:09ef59d2d0f7 286 * @param[in] error Optional error message, NULL for success
jburhenn 0:09ef59d2d0f7 287 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 288 * @return success code
jburhenn 0:09ef59d2d0f7 289 */
jburhenn 0:09ef59d2d0f7 290 int publishResponse(unsigned int channel, const char* id, const char* error, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 291 char buffer[MAX_MQTT_PACKET_SIZE + 1] = { 0 };
jburhenn 0:09ef59d2d0f7 292 int result = CayenneBuildTopic(buffer, sizeof(buffer), _username, clientID ? clientID : _clientID, RESPONSE_TOPIC, channel);
jburhenn 0:09ef59d2d0f7 293 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 294 size_t size = strlen(buffer);
jburhenn 0:09ef59d2d0f7 295 char* payload = &buffer[size + 1];
jburhenn 0:09ef59d2d0f7 296 size = sizeof(buffer) - (size + 1);
jburhenn 0:09ef59d2d0f7 297 result = CayenneBuildResponsePayload(payload, &size, id, error);
jburhenn 0:09ef59d2d0f7 298 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 299 result = Base::publish(buffer, payload, size, MQTT::QOS1, true);
jburhenn 0:09ef59d2d0f7 300 }
jburhenn 0:09ef59d2d0f7 301 }
jburhenn 0:09ef59d2d0f7 302 return result;
jburhenn 0:09ef59d2d0f7 303 }
jburhenn 0:09ef59d2d0f7 304
jburhenn 0:09ef59d2d0f7 305 /**
jburhenn 0:09ef59d2d0f7 306 * Subscribe to a topic.
jburhenn 0:09ef59d2d0f7 307 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 308 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 309 * @param[in] handler The message handler, NULL to use default handler
jburhenn 0:09ef59d2d0f7 310 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with. This string is not copied, so it must remain available for the life of the subscription.
jburhenn 0:09ef59d2d0f7 311 * @return success code
jburhenn 0:09ef59d2d0f7 312 */
jburhenn 0:09ef59d2d0f7 313 int subscribe(CayenneTopic topic, unsigned int channel, CayenneMessageHandler handler = NULL, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 314 char topicName[MAX_MQTT_PACKET_SIZE] = { 0 };
jburhenn 0:09ef59d2d0f7 315 int result = CayenneBuildTopic(topicName, sizeof(topicName), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 316 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 317 result = Base::subscribe(topicName, MQTT::QOS0, NULL);
jburhenn 0:09ef59d2d0f7 318 if (handler && result == MQTT::QOS0) {
jburhenn 0:09ef59d2d0f7 319 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 320 if (!_messageHandlers[i].fp.attached()) {
jburhenn 0:09ef59d2d0f7 321 _messageHandlers[i].clientID = clientID ? clientID : _clientID;
jburhenn 0:09ef59d2d0f7 322 _messageHandlers[i].topic = topic;
jburhenn 0:09ef59d2d0f7 323 _messageHandlers[i].channel = channel;
jburhenn 0:09ef59d2d0f7 324 _messageHandlers[i].fp.attach(handler);
jburhenn 0:09ef59d2d0f7 325 break;
jburhenn 0:09ef59d2d0f7 326 }
jburhenn 0:09ef59d2d0f7 327 }
jburhenn 0:09ef59d2d0f7 328 }
jburhenn 0:09ef59d2d0f7 329 }
jburhenn 0:09ef59d2d0f7 330 return result;
jburhenn 0:09ef59d2d0f7 331 };
jburhenn 0:09ef59d2d0f7 332
jburhenn 0:09ef59d2d0f7 333 /**
jburhenn 0:09ef59d2d0f7 334 * Unsubscribe from a topic.
jburhenn 0:09ef59d2d0f7 335 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 336 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 337 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 338 * @return success code
jburhenn 0:09ef59d2d0f7 339 */
jburhenn 0:09ef59d2d0f7 340 int unsubscribe(CayenneTopic topic, unsigned int channel, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 341 char topicName[MAX_MQTT_PACKET_SIZE] = { 0 };
jburhenn 0:09ef59d2d0f7 342 int result = CayenneBuildTopic(topicName, sizeof(topicName), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 343 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 344 result = Base::unsubscribe(topicName);
jburhenn 0:09ef59d2d0f7 345 if (result == MQTT::SUCCESS) {
jburhenn 0:09ef59d2d0f7 346 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 347 if ((_messageHandlers[i].topic == topic && _messageHandlers[i].channel == channel) &&
jburhenn 0:09ef59d2d0f7 348 (strcmp(clientID ? clientID : _clientID, _messageHandlers[i].clientID) == 0)) {
jburhenn 0:09ef59d2d0f7 349 _messageHandlers[i].clientID = NULL;
jburhenn 0:09ef59d2d0f7 350 _messageHandlers[i].topic = UNDEFINED_TOPIC;
jburhenn 0:09ef59d2d0f7 351 _messageHandlers[i].channel = CAYENNE_NO_CHANNEL;
jburhenn 0:09ef59d2d0f7 352 _messageHandlers[i].fp.detach();
jburhenn 0:09ef59d2d0f7 353 }
jburhenn 0:09ef59d2d0f7 354 }
jburhenn 0:09ef59d2d0f7 355 }
jburhenn 0:09ef59d2d0f7 356 }
jburhenn 0:09ef59d2d0f7 357 return result;
jburhenn 0:09ef59d2d0f7 358 }
jburhenn 0:09ef59d2d0f7 359
jburhenn 0:09ef59d2d0f7 360 /**
jburhenn 0:09ef59d2d0f7 361 * Handler for incoming MQTT::Client messages.
jburhenn 0:09ef59d2d0f7 362 * @param[in] md Message data
jburhenn 0:09ef59d2d0f7 363 */
jburhenn 0:09ef59d2d0f7 364 void mqttMessageArrived(MQTT::MessageData& md)
jburhenn 0:09ef59d2d0f7 365 {
jburhenn 0:09ef59d2d0f7 366 int result = MQTT::FAILURE;
jburhenn 0:09ef59d2d0f7 367 MessageData message;
jburhenn 0:09ef59d2d0f7 368
jburhenn 0:09ef59d2d0f7 369 result = CayenneParseTopic(&message.topic, &message.channel, &message.clientID, _username, md.topicName.lenstring.data, md.topicName.lenstring.len);
jburhenn 0:09ef59d2d0f7 370 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 371 return;
jburhenn 0:09ef59d2d0f7 372 //Null terminate the string since that is required by CayenneParsePayload. The readbuf is set to CAYENNE_MAX_MESSAGE_SIZE+1 to allow for appending a null.
jburhenn 0:09ef59d2d0f7 373 (static_cast<char*>(md.message.payload))[md.message.payloadlen] = '\0';
jburhenn 0:09ef59d2d0f7 374 message.valueCount = CAYENNE_MAX_MESSAGE_VALUES;
jburhenn 0:09ef59d2d0f7 375 result = CayenneParsePayload(message.values, &message.valueCount, &message.type, &message.id, message.topic, static_cast<char*>(md.message.payload));
jburhenn 0:09ef59d2d0f7 376 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 377 return;
jburhenn 0:09ef59d2d0f7 378
jburhenn 0:09ef59d2d0f7 379 result = MQTT::FAILURE;
jburhenn 0:09ef59d2d0f7 380 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 381 if (_messageHandlers[i].fp.attached() && _messageHandlers[i].topic == message.topic &&
jburhenn 0:09ef59d2d0f7 382 (_messageHandlers[i].channel == message.channel || _messageHandlers[i].channel == CAYENNE_ALL_CHANNELS) &&
jburhenn 0:09ef59d2d0f7 383 (strcmp(_messageHandlers[i].clientID, message.clientID) == 0))
jburhenn 0:09ef59d2d0f7 384 {
jburhenn 0:09ef59d2d0f7 385 _messageHandlers[i].fp(message);
jburhenn 0:09ef59d2d0f7 386 result = MQTT::SUCCESS;
jburhenn 0:09ef59d2d0f7 387 }
jburhenn 0:09ef59d2d0f7 388 }
jburhenn 0:09ef59d2d0f7 389
jburhenn 0:09ef59d2d0f7 390 if (result == MQTT::FAILURE && _defaultMessageHandler.attached()) {
jburhenn 0:09ef59d2d0f7 391 _defaultMessageHandler(message);
jburhenn 0:09ef59d2d0f7 392 }
jburhenn 0:09ef59d2d0f7 393 }
jburhenn 0:09ef59d2d0f7 394
jburhenn 0:09ef59d2d0f7 395 private:
jburhenn 0:09ef59d2d0f7 396 char* _username;
jburhenn 0:09ef59d2d0f7 397 char* _clientID;
jburhenn 0:09ef59d2d0f7 398 struct CayenneMessageHandlers
jburhenn 0:09ef59d2d0f7 399 {
jburhenn 0:09ef59d2d0f7 400 const char* clientID;
jburhenn 0:09ef59d2d0f7 401 CayenneTopic topic;
jburhenn 0:09ef59d2d0f7 402 unsigned int channel;
jburhenn 0:09ef59d2d0f7 403 FP<void, MessageData&> fp;
jburhenn 0:09ef59d2d0f7 404 } _messageHandlers[MAX_MESSAGE_HANDLERS]; /* Message handlers are indexed by subscription topic */
jburhenn 0:09ef59d2d0f7 405 FP<void, MessageData&> _defaultMessageHandler;
jburhenn 0:09ef59d2d0f7 406 };
jburhenn 0:09ef59d2d0f7 407
jburhenn 0:09ef59d2d0f7 408 }
jburhenn 0:09ef59d2d0f7 409
jburhenn 0:09ef59d2d0f7 410 #endif