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:
Tue Nov 08 18:48:37 2016 -0700
Revision:
21:e46b1f93c011
Parent:
17:75cc2d58e796
Child:
22:0dbabcc6e7b2
Use CAYENNE_MAX_MESSAGE_SIZE for max packet size.

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