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

Fork of Cayenne-MQTT-mbed by myDevicesIoT

Committer:
jburhenn
Date:
Fri Oct 07 22:24:51 2016 +0000
Revision:
1:90dccf306268
Parent:
0:09ef59d2d0f7
Child:
2:c0419dbecfbd
Added 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 0:09ef59d2d0f7 29 typedef struct MessageData
jburhenn 0:09ef59d2d0f7 30 {
jburhenn 0:09ef59d2d0f7 31 const char* clientID;
jburhenn 0:09ef59d2d0f7 32 CayenneTopic topic;
jburhenn 0:09ef59d2d0f7 33 unsigned int channel;
jburhenn 0:09ef59d2d0f7 34 const char* id;
jburhenn 0:09ef59d2d0f7 35 const char* type;
jburhenn 0:09ef59d2d0f7 36 CayenneValuePair values[CAYENNE_MAX_MESSAGE_VALUES];
jburhenn 0:09ef59d2d0f7 37 size_t valueCount;
jburhenn 0:09ef59d2d0f7 38 } MessageData;
jburhenn 1:90dccf306268 39
jburhenn 1:90dccf306268 40 /**
jburhenn 1:90dccf306268 41 * Client class for connecting to Cayenne via MQTT.
jburhenn 1:90dccf306268 42 * @class MQTTClient
jburhenn 1:90dccf306268 43 * @param Network A network class with the methods: read, write. See NetworkInterface.h for function definitions.
jburhenn 1:90dccf306268 44 * @param Timer A timer class with the methods: countdown_ms, countdown, left_ms, expired. See TimerInterface.h for function definitions.
jburhenn 1:90dccf306268 45 * @param MAX_MQTT_PACKET_SIZE Maximum size of an MQTT message, in bytes.
jburhenn 1:90dccf306268 46 * @param MAX_MESSAGE_HANDLERS Maximum number of message handlers.
jburhenn 1:90dccf306268 47 */
jburhenn 0:09ef59d2d0f7 48 template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE = 128, int MAX_MESSAGE_HANDLERS = 5>
jburhenn 0:09ef59d2d0f7 49 class MQTTClient : private MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, 0>
jburhenn 0:09ef59d2d0f7 50 {
jburhenn 0:09ef59d2d0f7 51 public:
jburhenn 0:09ef59d2d0f7 52 typedef MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, 0> Base;
jburhenn 0:09ef59d2d0f7 53 typedef void(*CayenneMessageHandler)(MessageData&);
jburhenn 0:09ef59d2d0f7 54
jburhenn 0:09ef59d2d0f7 55 /**
jburhenn 0:09ef59d2d0f7 56 * Create an Cayenne MQTT client object.
jburhenn 0:09ef59d2d0f7 57 * @param[in] network Pointer to an instance of the Network class. Must be connected to the endpoint before calling MQTTClient connect.
jburhenn 0:09ef59d2d0f7 58 * @param[in] command_timeout_ms Timeout for commands in milliseconds.
jburhenn 0:09ef59d2d0f7 59 */
jburhenn 0:09ef59d2d0f7 60 MQTTClient(Network& network, unsigned int command_timeout_ms = 30000) : Base(network, command_timeout_ms), _username(NULL), _clientID(NULL)
jburhenn 0:09ef59d2d0f7 61 {
jburhenn 0:09ef59d2d0f7 62 Base::setDefaultMessageHandler(this, &MQTTClient::mqttMessageArrived);
jburhenn 0:09ef59d2d0f7 63 };
jburhenn 0:09ef59d2d0f7 64
jburhenn 0:09ef59d2d0f7 65 /**
jburhenn 0:09ef59d2d0f7 66 * Set default handler function called when a message is received.
jburhenn 0:09ef59d2d0f7 67 * @param[in] handler Function called when message is received, if no other handlers exist for the topic.
jburhenn 0:09ef59d2d0f7 68 */
jburhenn 0:09ef59d2d0f7 69 void setDefaultMessageHandler(CayenneMessageHandler handler)
jburhenn 0:09ef59d2d0f7 70 {
jburhenn 0:09ef59d2d0f7 71 _defaultMessageHandler.attach(handler);
jburhenn 0:09ef59d2d0f7 72 };
jburhenn 0:09ef59d2d0f7 73
jburhenn 0:09ef59d2d0f7 74 /**
jburhenn 0:09ef59d2d0f7 75 * Connect to the Cayenne server.
jburhenn 0:09ef59d2d0f7 76 * @param[in] username Cayenne username
jburhenn 0:09ef59d2d0f7 77 * @param[in] clientID Cayennne client ID
jburhenn 0:09ef59d2d0f7 78 * @param[in] password Password
jburhenn 0:09ef59d2d0f7 79 * @return success code
jburhenn 0:09ef59d2d0f7 80 */
jburhenn 0:09ef59d2d0f7 81 int connect(char* username, char* clientID, char* password) {
jburhenn 0:09ef59d2d0f7 82 MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
jburhenn 0:09ef59d2d0f7 83 data.MQTTVersion = 3;
jburhenn 0:09ef59d2d0f7 84 data.clientID.cstring = _clientID = clientID;
jburhenn 0:09ef59d2d0f7 85 data.username.cstring = _username = username;
jburhenn 0:09ef59d2d0f7 86 data.password.cstring = password;
jburhenn 0:09ef59d2d0f7 87 return Base::connect(data);
jburhenn 0:09ef59d2d0f7 88 };
jburhenn 0:09ef59d2d0f7 89
jburhenn 0:09ef59d2d0f7 90 /**
jburhenn 0:09ef59d2d0f7 91 * Yield to allow MQTT message processing.
jburhenn 0:09ef59d2d0f7 92 * @param[in] timeout_ms The time in milliseconds to yield for
jburhenn 0:09ef59d2d0f7 93 * @return success code
jburhenn 0:09ef59d2d0f7 94 */
jburhenn 0:09ef59d2d0f7 95 int yield(unsigned long timeout_ms = 1000L) {
jburhenn 0:09ef59d2d0f7 96 return Base::yield(timeout_ms);
jburhenn 0:09ef59d2d0f7 97 };
jburhenn 0:09ef59d2d0f7 98
jburhenn 0:09ef59d2d0f7 99 /**
jburhenn 0:09ef59d2d0f7 100 * Check if the client is connected to the Cayenne server.
jburhenn 0:09ef59d2d0f7 101 * @return true if connected, false if not connected
jburhenn 0:09ef59d2d0f7 102 */
jburhenn 0:09ef59d2d0f7 103 bool connected() {
jburhenn 0:09ef59d2d0f7 104 return Base::isConnected();
jburhenn 0:09ef59d2d0f7 105 };
jburhenn 0:09ef59d2d0f7 106
jburhenn 0:09ef59d2d0f7 107 /**
jburhenn 0:09ef59d2d0f7 108 * Disconnect from the Cayenne server.
jburhenn 0:09ef59d2d0f7 109 * @return success code
jburhenn 0:09ef59d2d0f7 110 */
jburhenn 0:09ef59d2d0f7 111 int disconnect() {
jburhenn 0:09ef59d2d0f7 112 return Base::disconnect();
jburhenn 0:09ef59d2d0f7 113 };
jburhenn 0:09ef59d2d0f7 114
jburhenn 0:09ef59d2d0f7 115 /**
jburhenn 0:09ef59d2d0f7 116 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 117 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 118 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 119 * @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 120 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 121 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 122 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 123 * @return success code
jburhenn 0:09ef59d2d0f7 124 */
jburhenn 0:09ef59d2d0f7 125 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, const char* value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 126 CayenneValuePair valuePair[1];
jburhenn 0:09ef59d2d0f7 127 valuePair[0].value = value;
jburhenn 0:09ef59d2d0f7 128 valuePair[0].unit = unit;
jburhenn 0:09ef59d2d0f7 129 return publishData(topic, channel, type, valuePair, 1, clientID);
jburhenn 0:09ef59d2d0f7 130 };
jburhenn 0:09ef59d2d0f7 131
jburhenn 0:09ef59d2d0f7 132 /**
jburhenn 0:09ef59d2d0f7 133 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 134 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 135 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 136 * @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 137 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 138 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 139 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 140 * @return success code
jburhenn 0:09ef59d2d0f7 141 */
jburhenn 0:09ef59d2d0f7 142 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, int value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 143 char str[2 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 144 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 145 itoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 146 #else
jburhenn 0:09ef59d2d0f7 147 snprintf(str, sizeof(str), "%d", value);
jburhenn 0:09ef59d2d0f7 148 #endif
jburhenn 0:09ef59d2d0f7 149 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 150 };
jburhenn 0:09ef59d2d0f7 151
jburhenn 0:09ef59d2d0f7 152 /**
jburhenn 0:09ef59d2d0f7 153 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 154 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 155 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 156 * @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 157 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 158 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 159 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 160 * @return success code
jburhenn 0:09ef59d2d0f7 161 */
jburhenn 0:09ef59d2d0f7 162 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, unsigned int value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 163 char str[1 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 164 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 165 utoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 166 #else
jburhenn 0:09ef59d2d0f7 167 snprintf(str, sizeof(str), "%u", value);
jburhenn 0:09ef59d2d0f7 168 #endif
jburhenn 0:09ef59d2d0f7 169 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 170 };
jburhenn 0:09ef59d2d0f7 171
jburhenn 0:09ef59d2d0f7 172 /**
jburhenn 0:09ef59d2d0f7 173 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 174 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 175 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 176 * @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 177 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 178 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 179 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 180 * @return success code
jburhenn 0:09ef59d2d0f7 181 */
jburhenn 0:09ef59d2d0f7 182 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, long value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 183 char str[2 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 184 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 185 ltoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 186 #else
jburhenn 0:09ef59d2d0f7 187 snprintf(str, sizeof(str), "%ld", value);
jburhenn 0:09ef59d2d0f7 188 #endif
jburhenn 0:09ef59d2d0f7 189 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 190 };
jburhenn 0:09ef59d2d0f7 191
jburhenn 0:09ef59d2d0f7 192 /**
jburhenn 0:09ef59d2d0f7 193 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 194 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 195 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 196 * @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 197 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 198 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 199 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 200 * @return success code
jburhenn 0:09ef59d2d0f7 201 */
jburhenn 0:09ef59d2d0f7 202 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, unsigned long value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 203 char str[1 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 204 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 205 ultoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 206 #else
jburhenn 0:09ef59d2d0f7 207 snprintf(str, sizeof(str), "%lu", value);
jburhenn 0:09ef59d2d0f7 208 #endif
jburhenn 0:09ef59d2d0f7 209 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 210 };
jburhenn 0:09ef59d2d0f7 211
jburhenn 0:09ef59d2d0f7 212 /**
jburhenn 0:09ef59d2d0f7 213 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 214 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 215 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 216 * @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 217 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 218 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 219 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 220 * @return success code
jburhenn 0:09ef59d2d0f7 221 */
jburhenn 0:09ef59d2d0f7 222 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, double value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 223 char str[33];
jburhenn 0:09ef59d2d0f7 224 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 225 dtostrf(value, 5, 3, str);
jburhenn 0:09ef59d2d0f7 226 #else
jburhenn 0:09ef59d2d0f7 227 snprintf(str, 33, "%2.3f", value);
jburhenn 0:09ef59d2d0f7 228 #endif
jburhenn 0:09ef59d2d0f7 229 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 230 };
jburhenn 0:09ef59d2d0f7 231
jburhenn 0:09ef59d2d0f7 232 /**
jburhenn 0:09ef59d2d0f7 233 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 234 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 235 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 236 * @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 237 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 238 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 239 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 240 * @return success code
jburhenn 0:09ef59d2d0f7 241 */
jburhenn 0:09ef59d2d0f7 242 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, float value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 243 char str[33];
jburhenn 0:09ef59d2d0f7 244 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 245 dtostrf(value, 5, 3, str);
jburhenn 0:09ef59d2d0f7 246 #else
jburhenn 0:09ef59d2d0f7 247 snprintf(str, 33, "%2.3f", value);
jburhenn 0:09ef59d2d0f7 248 #endif
jburhenn 0:09ef59d2d0f7 249 return publishData(topic, channel, type, unit, str, clientID);
jburhenn 0:09ef59d2d0f7 250 };
jburhenn 0:09ef59d2d0f7 251
jburhenn 0:09ef59d2d0f7 252 /**
jburhenn 0:09ef59d2d0f7 253 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 254 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 255 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 256 * @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 257 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 258 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 259 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 260 * @return success code
jburhenn 0:09ef59d2d0f7 261 */
jburhenn 0:09ef59d2d0f7 262 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const CayenneValuePair* values, size_t valueCount, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 263 char buffer[MAX_MQTT_PACKET_SIZE + 1] = { 0 };
jburhenn 0:09ef59d2d0f7 264 int result = CayenneBuildTopic(buffer, sizeof(buffer), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 265 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 266 size_t size = strlen(buffer);
jburhenn 0:09ef59d2d0f7 267 char* payload = &buffer[size + 1];
jburhenn 0:09ef59d2d0f7 268 size = sizeof(buffer) - (size + 1);
jburhenn 0:09ef59d2d0f7 269 result = CayenneBuildDataPayload(payload, &size, type, values, valueCount);
jburhenn 0:09ef59d2d0f7 270 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 271 result = Base::publish(buffer, payload, size, MQTT::QOS0, true);
jburhenn 0:09ef59d2d0f7 272 }
jburhenn 0:09ef59d2d0f7 273 }
jburhenn 0:09ef59d2d0f7 274 return result;
jburhenn 0:09ef59d2d0f7 275 };
jburhenn 0:09ef59d2d0f7 276
jburhenn 0:09ef59d2d0f7 277 /**
jburhenn 0:09ef59d2d0f7 278 * Send a response to a channel.
jburhenn 0:09ef59d2d0f7 279 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 280 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 281 * @param[in] channel The channel to send response to
jburhenn 0:09ef59d2d0f7 282 * @param[in] id ID of message the response is for
jburhenn 0:09ef59d2d0f7 283 * @param[in] error Optional error message, NULL for success
jburhenn 0:09ef59d2d0f7 284 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 285 * @return success code
jburhenn 0:09ef59d2d0f7 286 */
jburhenn 0:09ef59d2d0f7 287 int publishResponse(unsigned int channel, const char* id, const char* error, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 288 char buffer[MAX_MQTT_PACKET_SIZE + 1] = { 0 };
jburhenn 0:09ef59d2d0f7 289 int result = CayenneBuildTopic(buffer, sizeof(buffer), _username, clientID ? clientID : _clientID, RESPONSE_TOPIC, channel);
jburhenn 0:09ef59d2d0f7 290 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 291 size_t size = strlen(buffer);
jburhenn 0:09ef59d2d0f7 292 char* payload = &buffer[size + 1];
jburhenn 0:09ef59d2d0f7 293 size = sizeof(buffer) - (size + 1);
jburhenn 0:09ef59d2d0f7 294 result = CayenneBuildResponsePayload(payload, &size, id, error);
jburhenn 0:09ef59d2d0f7 295 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 296 result = Base::publish(buffer, payload, size, MQTT::QOS1, true);
jburhenn 0:09ef59d2d0f7 297 }
jburhenn 0:09ef59d2d0f7 298 }
jburhenn 0:09ef59d2d0f7 299 return result;
jburhenn 0:09ef59d2d0f7 300 }
jburhenn 0:09ef59d2d0f7 301
jburhenn 0:09ef59d2d0f7 302 /**
jburhenn 0:09ef59d2d0f7 303 * Subscribe to a topic.
jburhenn 0:09ef59d2d0f7 304 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 305 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 306 * @param[in] handler The message handler, NULL to use default handler
jburhenn 0:09ef59d2d0f7 307 * @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 308 * @return success code
jburhenn 0:09ef59d2d0f7 309 */
jburhenn 0:09ef59d2d0f7 310 int subscribe(CayenneTopic topic, unsigned int channel, CayenneMessageHandler handler = NULL, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 311 char topicName[MAX_MQTT_PACKET_SIZE] = { 0 };
jburhenn 0:09ef59d2d0f7 312 int result = CayenneBuildTopic(topicName, sizeof(topicName), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 313 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 314 result = Base::subscribe(topicName, MQTT::QOS0, NULL);
jburhenn 0:09ef59d2d0f7 315 if (handler && result == MQTT::QOS0) {
jburhenn 0:09ef59d2d0f7 316 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 317 if (!_messageHandlers[i].fp.attached()) {
jburhenn 0:09ef59d2d0f7 318 _messageHandlers[i].clientID = clientID ? clientID : _clientID;
jburhenn 0:09ef59d2d0f7 319 _messageHandlers[i].topic = topic;
jburhenn 0:09ef59d2d0f7 320 _messageHandlers[i].channel = channel;
jburhenn 0:09ef59d2d0f7 321 _messageHandlers[i].fp.attach(handler);
jburhenn 0:09ef59d2d0f7 322 break;
jburhenn 0:09ef59d2d0f7 323 }
jburhenn 0:09ef59d2d0f7 324 }
jburhenn 0:09ef59d2d0f7 325 }
jburhenn 0:09ef59d2d0f7 326 }
jburhenn 0:09ef59d2d0f7 327 return result;
jburhenn 0:09ef59d2d0f7 328 };
jburhenn 0:09ef59d2d0f7 329
jburhenn 0:09ef59d2d0f7 330 /**
jburhenn 0:09ef59d2d0f7 331 * Unsubscribe from a topic.
jburhenn 0:09ef59d2d0f7 332 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 333 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 334 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 335 * @return success code
jburhenn 0:09ef59d2d0f7 336 */
jburhenn 0:09ef59d2d0f7 337 int unsubscribe(CayenneTopic topic, unsigned int channel, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 338 char topicName[MAX_MQTT_PACKET_SIZE] = { 0 };
jburhenn 0:09ef59d2d0f7 339 int result = CayenneBuildTopic(topicName, sizeof(topicName), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 340 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 341 result = Base::unsubscribe(topicName);
jburhenn 0:09ef59d2d0f7 342 if (result == MQTT::SUCCESS) {
jburhenn 0:09ef59d2d0f7 343 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 344 if ((_messageHandlers[i].topic == topic && _messageHandlers[i].channel == channel) &&
jburhenn 0:09ef59d2d0f7 345 (strcmp(clientID ? clientID : _clientID, _messageHandlers[i].clientID) == 0)) {
jburhenn 0:09ef59d2d0f7 346 _messageHandlers[i].clientID = NULL;
jburhenn 0:09ef59d2d0f7 347 _messageHandlers[i].topic = UNDEFINED_TOPIC;
jburhenn 0:09ef59d2d0f7 348 _messageHandlers[i].channel = CAYENNE_NO_CHANNEL;
jburhenn 0:09ef59d2d0f7 349 _messageHandlers[i].fp.detach();
jburhenn 0:09ef59d2d0f7 350 }
jburhenn 0:09ef59d2d0f7 351 }
jburhenn 0:09ef59d2d0f7 352 }
jburhenn 0:09ef59d2d0f7 353 }
jburhenn 0:09ef59d2d0f7 354 return result;
jburhenn 0:09ef59d2d0f7 355 }
jburhenn 0:09ef59d2d0f7 356
jburhenn 0:09ef59d2d0f7 357 /**
jburhenn 0:09ef59d2d0f7 358 * Handler for incoming MQTT::Client messages.
jburhenn 0:09ef59d2d0f7 359 * @param[in] md Message data
jburhenn 0:09ef59d2d0f7 360 */
jburhenn 0:09ef59d2d0f7 361 void mqttMessageArrived(MQTT::MessageData& md)
jburhenn 0:09ef59d2d0f7 362 {
jburhenn 0:09ef59d2d0f7 363 int result = MQTT::FAILURE;
jburhenn 0:09ef59d2d0f7 364 MessageData message;
jburhenn 0:09ef59d2d0f7 365
jburhenn 0:09ef59d2d0f7 366 result = CayenneParseTopic(&message.topic, &message.channel, &message.clientID, _username, md.topicName.lenstring.data, md.topicName.lenstring.len);
jburhenn 0:09ef59d2d0f7 367 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 368 return;
jburhenn 0:09ef59d2d0f7 369 //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 370 (static_cast<char*>(md.message.payload))[md.message.payloadlen] = '\0';
jburhenn 0:09ef59d2d0f7 371 message.valueCount = CAYENNE_MAX_MESSAGE_VALUES;
jburhenn 0:09ef59d2d0f7 372 result = CayenneParsePayload(message.values, &message.valueCount, &message.type, &message.id, message.topic, static_cast<char*>(md.message.payload));
jburhenn 0:09ef59d2d0f7 373 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 374 return;
jburhenn 0:09ef59d2d0f7 375
jburhenn 0:09ef59d2d0f7 376 result = MQTT::FAILURE;
jburhenn 0:09ef59d2d0f7 377 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 378 if (_messageHandlers[i].fp.attached() && _messageHandlers[i].topic == message.topic &&
jburhenn 0:09ef59d2d0f7 379 (_messageHandlers[i].channel == message.channel || _messageHandlers[i].channel == CAYENNE_ALL_CHANNELS) &&
jburhenn 0:09ef59d2d0f7 380 (strcmp(_messageHandlers[i].clientID, message.clientID) == 0))
jburhenn 0:09ef59d2d0f7 381 {
jburhenn 0:09ef59d2d0f7 382 _messageHandlers[i].fp(message);
jburhenn 0:09ef59d2d0f7 383 result = MQTT::SUCCESS;
jburhenn 0:09ef59d2d0f7 384 }
jburhenn 0:09ef59d2d0f7 385 }
jburhenn 0:09ef59d2d0f7 386
jburhenn 0:09ef59d2d0f7 387 if (result == MQTT::FAILURE && _defaultMessageHandler.attached()) {
jburhenn 0:09ef59d2d0f7 388 _defaultMessageHandler(message);
jburhenn 0:09ef59d2d0f7 389 }
jburhenn 0:09ef59d2d0f7 390 }
jburhenn 0:09ef59d2d0f7 391
jburhenn 0:09ef59d2d0f7 392 private:
jburhenn 0:09ef59d2d0f7 393 char* _username;
jburhenn 0:09ef59d2d0f7 394 char* _clientID;
jburhenn 0:09ef59d2d0f7 395 struct CayenneMessageHandlers
jburhenn 0:09ef59d2d0f7 396 {
jburhenn 0:09ef59d2d0f7 397 const char* clientID;
jburhenn 0:09ef59d2d0f7 398 CayenneTopic topic;
jburhenn 0:09ef59d2d0f7 399 unsigned int channel;
jburhenn 0:09ef59d2d0f7 400 FP<void, MessageData&> fp;
jburhenn 0:09ef59d2d0f7 401 } _messageHandlers[MAX_MESSAGE_HANDLERS]; /* Message handlers are indexed by subscription topic */
jburhenn 0:09ef59d2d0f7 402 FP<void, MessageData&> _defaultMessageHandler;
jburhenn 0:09ef59d2d0f7 403 };
jburhenn 0:09ef59d2d0f7 404
jburhenn 0:09ef59d2d0f7 405 }
jburhenn 0:09ef59d2d0f7 406
jburhenn 0:09ef59d2d0f7 407 #endif