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:
Wed Oct 26 16:33:46 2016 -0600
Revision:
8:aec9cfdd4c8e
Parent:
6:82e142a864ad
Child:
17:75cc2d58e796
Updated credential ordering to match dashboard. Added init function to set the credentials. Added setDefaultMessageHandler override so member functions can be used for message handling. Updated namesspace name to be more specific.

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 0:09ef59d2d0f7 65 template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE = 128, 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] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 325 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 326 * @param[in] channel The channel to send response to
jburhenn 0:09ef59d2d0f7 327 * @param[in] id ID of message the response is for
jburhenn 0:09ef59d2d0f7 328 * @param[in] error Optional error message, NULL for success
jburhenn 0:09ef59d2d0f7 329 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 330 * @return success code
jburhenn 0:09ef59d2d0f7 331 */
jburhenn 0:09ef59d2d0f7 332 int publishResponse(unsigned int channel, const char* id, const char* error, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 333 char buffer[MAX_MQTT_PACKET_SIZE + 1] = { 0 };
jburhenn 0:09ef59d2d0f7 334 int result = CayenneBuildTopic(buffer, sizeof(buffer), _username, clientID ? clientID : _clientID, RESPONSE_TOPIC, channel);
jburhenn 0:09ef59d2d0f7 335 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 336 size_t size = strlen(buffer);
jburhenn 0:09ef59d2d0f7 337 char* payload = &buffer[size + 1];
jburhenn 0:09ef59d2d0f7 338 size = sizeof(buffer) - (size + 1);
jburhenn 0:09ef59d2d0f7 339 result = CayenneBuildResponsePayload(payload, &size, id, error);
jburhenn 0:09ef59d2d0f7 340 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 341 result = Base::publish(buffer, payload, size, MQTT::QOS1, true);
jburhenn 0:09ef59d2d0f7 342 }
jburhenn 0:09ef59d2d0f7 343 }
jburhenn 0:09ef59d2d0f7 344 return result;
jburhenn 0:09ef59d2d0f7 345 }
jburhenn 0:09ef59d2d0f7 346
jburhenn 0:09ef59d2d0f7 347 /**
jburhenn 0:09ef59d2d0f7 348 * Subscribe to a topic.
jburhenn 0:09ef59d2d0f7 349 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 350 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 351 * @param[in] handler The message handler, NULL to use default handler
jburhenn 0:09ef59d2d0f7 352 * @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 353 * @return success code
jburhenn 0:09ef59d2d0f7 354 */
jburhenn 0:09ef59d2d0f7 355 int subscribe(CayenneTopic topic, unsigned int channel, CayenneMessageHandler handler = NULL, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 356 char topicName[MAX_MQTT_PACKET_SIZE] = { 0 };
jburhenn 0:09ef59d2d0f7 357 int result = CayenneBuildTopic(topicName, sizeof(topicName), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 358 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 359 result = Base::subscribe(topicName, MQTT::QOS0, NULL);
jburhenn 0:09ef59d2d0f7 360 if (handler && result == MQTT::QOS0) {
jburhenn 0:09ef59d2d0f7 361 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 362 if (!_messageHandlers[i].fp.attached()) {
jburhenn 0:09ef59d2d0f7 363 _messageHandlers[i].clientID = clientID ? clientID : _clientID;
jburhenn 0:09ef59d2d0f7 364 _messageHandlers[i].topic = topic;
jburhenn 0:09ef59d2d0f7 365 _messageHandlers[i].channel = channel;
jburhenn 0:09ef59d2d0f7 366 _messageHandlers[i].fp.attach(handler);
jburhenn 0:09ef59d2d0f7 367 break;
jburhenn 0:09ef59d2d0f7 368 }
jburhenn 0:09ef59d2d0f7 369 }
jburhenn 0:09ef59d2d0f7 370 }
jburhenn 0:09ef59d2d0f7 371 }
jburhenn 0:09ef59d2d0f7 372 return result;
jburhenn 0:09ef59d2d0f7 373 };
jburhenn 0:09ef59d2d0f7 374
jburhenn 0:09ef59d2d0f7 375 /**
jburhenn 0:09ef59d2d0f7 376 * Unsubscribe from a topic.
jburhenn 0:09ef59d2d0f7 377 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 378 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 379 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 380 * @return success code
jburhenn 0:09ef59d2d0f7 381 */
jburhenn 0:09ef59d2d0f7 382 int unsubscribe(CayenneTopic topic, unsigned int channel, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 383 char topicName[MAX_MQTT_PACKET_SIZE] = { 0 };
jburhenn 0:09ef59d2d0f7 384 int result = CayenneBuildTopic(topicName, sizeof(topicName), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 385 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 386 result = Base::unsubscribe(topicName);
jburhenn 0:09ef59d2d0f7 387 if (result == MQTT::SUCCESS) {
jburhenn 0:09ef59d2d0f7 388 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 389 if ((_messageHandlers[i].topic == topic && _messageHandlers[i].channel == channel) &&
jburhenn 0:09ef59d2d0f7 390 (strcmp(clientID ? clientID : _clientID, _messageHandlers[i].clientID) == 0)) {
jburhenn 0:09ef59d2d0f7 391 _messageHandlers[i].clientID = NULL;
jburhenn 0:09ef59d2d0f7 392 _messageHandlers[i].topic = UNDEFINED_TOPIC;
jburhenn 0:09ef59d2d0f7 393 _messageHandlers[i].channel = CAYENNE_NO_CHANNEL;
jburhenn 0:09ef59d2d0f7 394 _messageHandlers[i].fp.detach();
jburhenn 0:09ef59d2d0f7 395 }
jburhenn 0:09ef59d2d0f7 396 }
jburhenn 0:09ef59d2d0f7 397 }
jburhenn 0:09ef59d2d0f7 398 }
jburhenn 0:09ef59d2d0f7 399 return result;
jburhenn 0:09ef59d2d0f7 400 }
jburhenn 0:09ef59d2d0f7 401
jburhenn 0:09ef59d2d0f7 402 /**
jburhenn 0:09ef59d2d0f7 403 * Handler for incoming MQTT::Client messages.
jburhenn 0:09ef59d2d0f7 404 * @param[in] md Message data
jburhenn 0:09ef59d2d0f7 405 */
jburhenn 0:09ef59d2d0f7 406 void mqttMessageArrived(MQTT::MessageData& md)
jburhenn 0:09ef59d2d0f7 407 {
jburhenn 0:09ef59d2d0f7 408 int result = MQTT::FAILURE;
jburhenn 0:09ef59d2d0f7 409 MessageData message;
jburhenn 0:09ef59d2d0f7 410
jburhenn 0:09ef59d2d0f7 411 result = CayenneParseTopic(&message.topic, &message.channel, &message.clientID, _username, md.topicName.lenstring.data, md.topicName.lenstring.len);
jburhenn 0:09ef59d2d0f7 412 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 413 return;
jburhenn 0:09ef59d2d0f7 414 //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 415 (static_cast<char*>(md.message.payload))[md.message.payloadlen] = '\0';
jburhenn 0:09ef59d2d0f7 416 message.valueCount = CAYENNE_MAX_MESSAGE_VALUES;
jburhenn 0:09ef59d2d0f7 417 result = CayenneParsePayload(message.values, &message.valueCount, &message.type, &message.id, message.topic, static_cast<char*>(md.message.payload));
jburhenn 0:09ef59d2d0f7 418 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 419 return;
jburhenn 0:09ef59d2d0f7 420
jburhenn 0:09ef59d2d0f7 421 result = MQTT::FAILURE;
jburhenn 0:09ef59d2d0f7 422 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 423 if (_messageHandlers[i].fp.attached() && _messageHandlers[i].topic == message.topic &&
jburhenn 0:09ef59d2d0f7 424 (_messageHandlers[i].channel == message.channel || _messageHandlers[i].channel == CAYENNE_ALL_CHANNELS) &&
jburhenn 0:09ef59d2d0f7 425 (strcmp(_messageHandlers[i].clientID, message.clientID) == 0))
jburhenn 0:09ef59d2d0f7 426 {
jburhenn 0:09ef59d2d0f7 427 _messageHandlers[i].fp(message);
jburhenn 0:09ef59d2d0f7 428 result = MQTT::SUCCESS;
jburhenn 0:09ef59d2d0f7 429 }
jburhenn 0:09ef59d2d0f7 430 }
jburhenn 0:09ef59d2d0f7 431
jburhenn 0:09ef59d2d0f7 432 if (result == MQTT::FAILURE && _defaultMessageHandler.attached()) {
jburhenn 0:09ef59d2d0f7 433 _defaultMessageHandler(message);
jburhenn 0:09ef59d2d0f7 434 }
jburhenn 0:09ef59d2d0f7 435 }
jburhenn 0:09ef59d2d0f7 436
jburhenn 0:09ef59d2d0f7 437 private:
jburhenn 6:82e142a864ad 438 const char* _username;
jburhenn 8:aec9cfdd4c8e 439 const char* _password;
jburhenn 6:82e142a864ad 440 const char* _clientID;
jburhenn 0:09ef59d2d0f7 441 struct CayenneMessageHandlers
jburhenn 0:09ef59d2d0f7 442 {
jburhenn 0:09ef59d2d0f7 443 const char* clientID;
jburhenn 0:09ef59d2d0f7 444 CayenneTopic topic;
jburhenn 0:09ef59d2d0f7 445 unsigned int channel;
jburhenn 0:09ef59d2d0f7 446 FP<void, MessageData&> fp;
jburhenn 0:09ef59d2d0f7 447 } _messageHandlers[MAX_MESSAGE_HANDLERS]; /* Message handlers are indexed by subscription topic */
jburhenn 0:09ef59d2d0f7 448 FP<void, MessageData&> _defaultMessageHandler;
jburhenn 0:09ef59d2d0f7 449 };
jburhenn 0:09ef59d2d0f7 450
jburhenn 0:09ef59d2d0f7 451 }
jburhenn 0:09ef59d2d0f7 452
jburhenn 0:09ef59d2d0f7 453 #endif