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 Jan 25 11:10:39 2017 -0700
Branch:
feature/multivalue
Revision:
23:1a9aed5e77c9
Parent:
22:0dbabcc6e7b2
CayenneDataArray updates for multivalue support.

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
jburhenn 8:aec9cfdd4c8e 26 namespace CayenneMQTT
jburhenn 0:09ef59d2d0f7 27 {
jburhenn 2:c0419dbecfbd 28 /**
jburhenn 2:c0419dbecfbd 29 * Cayenne message data passed to message handler functions.
jburhenn 2:c0419dbecfbd 30 */
jburhenn 0:09ef59d2d0f7 31 typedef struct MessageData
jburhenn 0:09ef59d2d0f7 32 {
jburhenn 4:421366004b5d 33 const char* clientID; /**< The client ID of the message. */
jburhenn 4:421366004b5d 34 CayenneTopic topic; /**< The topic the message was received on. */
jburhenn 4:421366004b5d 35 unsigned int channel; /**< The channel the message was received on. */
jburhenn 4:421366004b5d 36 const char* id; /**< The message ID, if it is a command message, otherwise NULL. */
jburhenn 4:421366004b5d 37 const char* type; /**< The type of data in the message, if it exists, otherwise NULL. */
jburhenn 22:0dbabcc6e7b2 38 const char* unit; /**< The unit of data in the message, if it exists, otherwise NULL. */
jburhenn 22:0dbabcc6e7b2 39 const char* value; /**< The value of data in the message, if it exists, otherwise NULL. */
jburhenn 8:aec9cfdd4c8e 40
jburhenn 8:aec9cfdd4c8e 41 /**
jburhenn 22:0dbabcc6e7b2 42 * Get message value.
jburhenn 22:0dbabcc6e7b2 43 * @return Message value, can be NULL.
jburhenn 8:aec9cfdd4c8e 44 */
jburhenn 22:0dbabcc6e7b2 45 const char* getValue() const { return value; }
jburhenn 8:aec9cfdd4c8e 46
jburhenn 8:aec9cfdd4c8e 47 /**
jburhenn 22:0dbabcc6e7b2 48 * Get message unit.
jburhenn 22:0dbabcc6e7b2 49 * @return Message unit, can be NULL.
jburhenn 8:aec9cfdd4c8e 50 */
jburhenn 22:0dbabcc6e7b2 51 const char* getUnit() const { return unit; }
jburhenn 0:09ef59d2d0f7 52 } MessageData;
jburhenn 1:90dccf306268 53
jburhenn 1:90dccf306268 54 /**
jburhenn 1:90dccf306268 55 * Client class for connecting to Cayenne via MQTT.
jburhenn 1:90dccf306268 56 * @class MQTTClient
jburhenn 1:90dccf306268 57 * @param Network A network class with the methods: read, write. See NetworkInterface.h for function definitions.
jburhenn 1:90dccf306268 58 * @param Timer A timer class with the methods: countdown_ms, countdown, left_ms, expired. See TimerInterface.h for function definitions.
jburhenn 1:90dccf306268 59 * @param MAX_MQTT_PACKET_SIZE Maximum size of an MQTT message, in bytes.
jburhenn 1:90dccf306268 60 * @param MAX_MESSAGE_HANDLERS Maximum number of message handlers.
jburhenn 1:90dccf306268 61 */
jburhenn 21:e46b1f93c011 62 template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE = CAYENNE_MAX_MESSAGE_SIZE, int MAX_MESSAGE_HANDLERS = 5>
jburhenn 22:0dbabcc6e7b2 63 class MQTTClient : private MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, 1>
jburhenn 0:09ef59d2d0f7 64 {
jburhenn 0:09ef59d2d0f7 65 public:
jburhenn 22:0dbabcc6e7b2 66 typedef MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, 1> Base;
jburhenn 0:09ef59d2d0f7 67 typedef void(*CayenneMessageHandler)(MessageData&);
jburhenn 0:09ef59d2d0f7 68
jburhenn 0:09ef59d2d0f7 69 /**
jburhenn 0:09ef59d2d0f7 70 * Create an Cayenne MQTT client object.
jburhenn 0:09ef59d2d0f7 71 * @param[in] network Pointer to an instance of the Network class. Must be connected to the endpoint before calling MQTTClient connect.
jburhenn 8:aec9cfdd4c8e 72 * @param[in] username Cayenne username
jburhenn 8:aec9cfdd4c8e 73 * @param[in] password Cayenne password
jburhenn 8:aec9cfdd4c8e 74 * @param[in] clientID Cayennne client ID
jburhenn 0:09ef59d2d0f7 75 * @param[in] command_timeout_ms Timeout for commands in milliseconds.
jburhenn 0:09ef59d2d0f7 76 */
jburhenn 8:aec9cfdd4c8e 77 MQTTClient(Network& network, const char* username = NULL, const char* password = NULL, const char* clientID = NULL, unsigned int command_timeout_ms = 30000) :
jburhenn 8:aec9cfdd4c8e 78 Base(network, command_timeout_ms), _username(username), _password(password), _clientID(clientID)
jburhenn 0:09ef59d2d0f7 79 {
jburhenn 0:09ef59d2d0f7 80 Base::setDefaultMessageHandler(this, &MQTTClient::mqttMessageArrived);
jburhenn 0:09ef59d2d0f7 81 };
jburhenn 0:09ef59d2d0f7 82
jburhenn 0:09ef59d2d0f7 83 /**
jburhenn 8:aec9cfdd4c8e 84 * Initialize connection credentials.
jburhenn 8:aec9cfdd4c8e 85 * @param[in] username Cayenne username
jburhenn 8:aec9cfdd4c8e 86 * @param[in] password Cayenne password
jburhenn 8:aec9cfdd4c8e 87 * @param[in] clientID Cayennne client ID
jburhenn 8:aec9cfdd4c8e 88 */
jburhenn 8:aec9cfdd4c8e 89 void init(const char* username, const char* password, const char* clientID)
jburhenn 8:aec9cfdd4c8e 90 {
jburhenn 8:aec9cfdd4c8e 91 _username = username;
jburhenn 8:aec9cfdd4c8e 92 _password = password;
jburhenn 8:aec9cfdd4c8e 93 _clientID = clientID;
jburhenn 8:aec9cfdd4c8e 94 };
jburhenn 8:aec9cfdd4c8e 95
jburhenn 8:aec9cfdd4c8e 96 /**
jburhenn 0:09ef59d2d0f7 97 * Set default handler function called when a message is received.
jburhenn 0:09ef59d2d0f7 98 * @param[in] handler Function called when message is received, if no other handlers exist for the topic.
jburhenn 0:09ef59d2d0f7 99 */
jburhenn 0:09ef59d2d0f7 100 void setDefaultMessageHandler(CayenneMessageHandler handler)
jburhenn 0:09ef59d2d0f7 101 {
jburhenn 0:09ef59d2d0f7 102 _defaultMessageHandler.attach(handler);
jburhenn 0:09ef59d2d0f7 103 };
jburhenn 0:09ef59d2d0f7 104
jburhenn 0:09ef59d2d0f7 105 /**
jburhenn 8:aec9cfdd4c8e 106 * Set default handler function called when a message is received.
jburhenn 8:aec9cfdd4c8e 107 * @param item Address of initialized object
jburhenn 8:aec9cfdd4c8e 108 * @param handler Function called when message is received, if no other handlers exist for the topic.
jburhenn 8:aec9cfdd4c8e 109 */
jburhenn 8:aec9cfdd4c8e 110 template<class T>
jburhenn 8:aec9cfdd4c8e 111 void setDefaultMessageHandler(T *item, void (T::*handler)(MessageData&))
jburhenn 8:aec9cfdd4c8e 112 {
jburhenn 8:aec9cfdd4c8e 113 _defaultMessageHandler.attach(item, handler);
jburhenn 8:aec9cfdd4c8e 114 }
jburhenn 8:aec9cfdd4c8e 115
jburhenn 8:aec9cfdd4c8e 116 /**
jburhenn 8:aec9cfdd4c8e 117 * Connect to the Cayenne server. Connection credentials must be initialized before calling this function.
jburhenn 0:09ef59d2d0f7 118 * @param[in] username Cayenne username
jburhenn 8:aec9cfdd4c8e 119 * @param[in] password Cayenne password
jburhenn 0:09ef59d2d0f7 120 * @param[in] clientID Cayennne client ID
jburhenn 0:09ef59d2d0f7 121 * @return success code
jburhenn 0:09ef59d2d0f7 122 */
jburhenn 8:aec9cfdd4c8e 123 int connect() {
jburhenn 0:09ef59d2d0f7 124 MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
jburhenn 0:09ef59d2d0f7 125 data.MQTTVersion = 3;
jburhenn 8:aec9cfdd4c8e 126 data.username.cstring = const_cast<char*>(_username);
jburhenn 8:aec9cfdd4c8e 127 data.password.cstring = const_cast<char*>(_password);
jburhenn 6:82e142a864ad 128 data.clientID.cstring = const_cast<char*>(_clientID);
jburhenn 0:09ef59d2d0f7 129 return Base::connect(data);
jburhenn 0:09ef59d2d0f7 130 };
jburhenn 0:09ef59d2d0f7 131
jburhenn 0:09ef59d2d0f7 132 /**
jburhenn 0:09ef59d2d0f7 133 * Yield to allow MQTT message processing.
jburhenn 0:09ef59d2d0f7 134 * @param[in] timeout_ms The time in milliseconds to yield for
jburhenn 0:09ef59d2d0f7 135 * @return success code
jburhenn 0:09ef59d2d0f7 136 */
jburhenn 0:09ef59d2d0f7 137 int yield(unsigned long timeout_ms = 1000L) {
jburhenn 0:09ef59d2d0f7 138 return Base::yield(timeout_ms);
jburhenn 0:09ef59d2d0f7 139 };
jburhenn 0:09ef59d2d0f7 140
jburhenn 0:09ef59d2d0f7 141 /**
jburhenn 0:09ef59d2d0f7 142 * Check if the client is connected to the Cayenne server.
jburhenn 0:09ef59d2d0f7 143 * @return true if connected, false if not connected
jburhenn 0:09ef59d2d0f7 144 */
jburhenn 0:09ef59d2d0f7 145 bool connected() {
jburhenn 0:09ef59d2d0f7 146 return Base::isConnected();
jburhenn 0:09ef59d2d0f7 147 };
jburhenn 0:09ef59d2d0f7 148
jburhenn 0:09ef59d2d0f7 149 /**
jburhenn 0:09ef59d2d0f7 150 * Disconnect from the Cayenne server.
jburhenn 0:09ef59d2d0f7 151 * @return success code
jburhenn 0:09ef59d2d0f7 152 */
jburhenn 0:09ef59d2d0f7 153 int disconnect() {
jburhenn 0:09ef59d2d0f7 154 return Base::disconnect();
jburhenn 0:09ef59d2d0f7 155 };
jburhenn 0:09ef59d2d0f7 156
jburhenn 0:09ef59d2d0f7 157 /**
jburhenn 0:09ef59d2d0f7 158 * Send data to Cayenne.
jburhenn 0:09ef59d2d0f7 159 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 160 * @param[in] channel The channel to send data to, or CAYENNE_NO_CHANNEL if there is none
jburhenn 0:09ef59d2d0f7 161 * @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 162 * @param[in] unit Optional unit to use for a type,unit=value payload, can be NULL
jburhenn 0:09ef59d2d0f7 163 * @param[in] value Data value
jburhenn 0:09ef59d2d0f7 164 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 165 * @return success code
jburhenn 0:09ef59d2d0f7 166 */
jburhenn 0:09ef59d2d0f7 167 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, int value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 168 char str[2 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 169 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 170 itoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 171 #else
jburhenn 0:09ef59d2d0f7 172 snprintf(str, sizeof(str), "%d", value);
jburhenn 0:09ef59d2d0f7 173 #endif
jburhenn 0:09ef59d2d0f7 174 return publishData(topic, channel, type, unit, str, 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, unsigned int value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 188 char str[1 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 189 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 190 utoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 191 #else
jburhenn 0:09ef59d2d0f7 192 snprintf(str, sizeof(str), "%u", 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, long value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 208 char str[2 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 209 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 210 ltoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 211 #else
jburhenn 0:09ef59d2d0f7 212 snprintf(str, sizeof(str), "%ld", 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, unsigned long value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 228 char str[1 + 8 * sizeof(value)];
jburhenn 0:09ef59d2d0f7 229 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 230 ultoa(value, str, 10);
jburhenn 0:09ef59d2d0f7 231 #else
jburhenn 0:09ef59d2d0f7 232 snprintf(str, sizeof(str), "%lu", 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, double value, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 248 char str[33];
jburhenn 0:09ef59d2d0f7 249 #if defined(__AVR__) || defined (ARDUINO_ARCH_ARC32)
jburhenn 0:09ef59d2d0f7 250 dtostrf(value, 5, 3, str);
jburhenn 0:09ef59d2d0f7 251 #else
jburhenn 0:09ef59d2d0f7 252 snprintf(str, 33, "%2.3f", 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, float 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 22:0dbabcc6e7b2 287 int publishData(CayenneTopic topic, unsigned int channel, const char* type, const char* unit, const char* value, 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, 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 22:0dbabcc6e7b2 294 result = CayenneBuildDataPayload(payload, &size, type, unit, value);
jburhenn 0:09ef59d2d0f7 295 if (result == CAYENNE_SUCCESS) {
jburhenn 22:0dbabcc6e7b2 296 result = Base::publish(buffer, payload, size, MQTT::QOS0, (topic != COMMAND_TOPIC) ? true : false);
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 * Send a response to a channel.
jburhenn 0:09ef59d2d0f7 304 * @param[in] id ID of message the response is for
jburhenn 0:09ef59d2d0f7 305 * @param[in] error Optional error message, NULL for success
jburhenn 0:09ef59d2d0f7 306 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 307 * @return success code
jburhenn 0:09ef59d2d0f7 308 */
jburhenn 17:75cc2d58e796 309 int publishResponse(const char* id, const char* error, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 310 char buffer[MAX_MQTT_PACKET_SIZE + 1] = { 0 };
jburhenn 17:75cc2d58e796 311 int result = CayenneBuildTopic(buffer, sizeof(buffer), _username, clientID ? clientID : _clientID, RESPONSE_TOPIC, CAYENNE_NO_CHANNEL);
jburhenn 0:09ef59d2d0f7 312 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 313 size_t size = strlen(buffer);
jburhenn 0:09ef59d2d0f7 314 char* payload = &buffer[size + 1];
jburhenn 0:09ef59d2d0f7 315 size = sizeof(buffer) - (size + 1);
jburhenn 0:09ef59d2d0f7 316 result = CayenneBuildResponsePayload(payload, &size, id, error);
jburhenn 0:09ef59d2d0f7 317 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 318 result = Base::publish(buffer, payload, size, MQTT::QOS1, true);
jburhenn 0:09ef59d2d0f7 319 }
jburhenn 0:09ef59d2d0f7 320 }
jburhenn 0:09ef59d2d0f7 321 return result;
jburhenn 0:09ef59d2d0f7 322 }
jburhenn 0:09ef59d2d0f7 323
jburhenn 0:09ef59d2d0f7 324 /**
jburhenn 0:09ef59d2d0f7 325 * Subscribe to a topic.
jburhenn 0:09ef59d2d0f7 326 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 327 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 328 * @param[in] handler The message handler, NULL to use default handler
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. This string is not copied, so it must remain available for the life of the subscription.
jburhenn 0:09ef59d2d0f7 330 * @return success code
jburhenn 0:09ef59d2d0f7 331 */
jburhenn 0:09ef59d2d0f7 332 int subscribe(CayenneTopic topic, unsigned int channel, CayenneMessageHandler handler = NULL, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 333 char topicName[MAX_MQTT_PACKET_SIZE] = { 0 };
jburhenn 0:09ef59d2d0f7 334 int result = CayenneBuildTopic(topicName, sizeof(topicName), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 335 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 336 result = Base::subscribe(topicName, MQTT::QOS0, NULL);
jburhenn 0:09ef59d2d0f7 337 if (handler && result == MQTT::QOS0) {
jburhenn 0:09ef59d2d0f7 338 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 339 if (!_messageHandlers[i].fp.attached()) {
jburhenn 0:09ef59d2d0f7 340 _messageHandlers[i].clientID = clientID ? clientID : _clientID;
jburhenn 0:09ef59d2d0f7 341 _messageHandlers[i].topic = topic;
jburhenn 0:09ef59d2d0f7 342 _messageHandlers[i].channel = channel;
jburhenn 0:09ef59d2d0f7 343 _messageHandlers[i].fp.attach(handler);
jburhenn 0:09ef59d2d0f7 344 break;
jburhenn 0:09ef59d2d0f7 345 }
jburhenn 0:09ef59d2d0f7 346 }
jburhenn 0:09ef59d2d0f7 347 }
jburhenn 0:09ef59d2d0f7 348 }
jburhenn 0:09ef59d2d0f7 349 return result;
jburhenn 0:09ef59d2d0f7 350 };
jburhenn 0:09ef59d2d0f7 351
jburhenn 0:09ef59d2d0f7 352 /**
jburhenn 0:09ef59d2d0f7 353 * Unsubscribe from a topic.
jburhenn 0:09ef59d2d0f7 354 * @param[in] topic Cayenne topic
jburhenn 0:09ef59d2d0f7 355 * @param[in] channel The topic channel, CAYENNE_NO_CHANNEL for none, CAYENNE_ALL_CHANNELS for all
jburhenn 0:09ef59d2d0f7 356 * @param[in] clientID The client ID to use in the topic, NULL to use the clientID the client was initialized with
jburhenn 0:09ef59d2d0f7 357 * @return success code
jburhenn 0:09ef59d2d0f7 358 */
jburhenn 0:09ef59d2d0f7 359 int unsubscribe(CayenneTopic topic, unsigned int channel, const char* clientID = NULL) {
jburhenn 0:09ef59d2d0f7 360 char topicName[MAX_MQTT_PACKET_SIZE] = { 0 };
jburhenn 0:09ef59d2d0f7 361 int result = CayenneBuildTopic(topicName, sizeof(topicName), _username, clientID ? clientID : _clientID, topic, channel);
jburhenn 0:09ef59d2d0f7 362 if (result == CAYENNE_SUCCESS) {
jburhenn 0:09ef59d2d0f7 363 result = Base::unsubscribe(topicName);
jburhenn 0:09ef59d2d0f7 364 if (result == MQTT::SUCCESS) {
jburhenn 0:09ef59d2d0f7 365 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 366 if ((_messageHandlers[i].topic == topic && _messageHandlers[i].channel == channel) &&
jburhenn 0:09ef59d2d0f7 367 (strcmp(clientID ? clientID : _clientID, _messageHandlers[i].clientID) == 0)) {
jburhenn 0:09ef59d2d0f7 368 _messageHandlers[i].clientID = NULL;
jburhenn 0:09ef59d2d0f7 369 _messageHandlers[i].topic = UNDEFINED_TOPIC;
jburhenn 0:09ef59d2d0f7 370 _messageHandlers[i].channel = CAYENNE_NO_CHANNEL;
jburhenn 0:09ef59d2d0f7 371 _messageHandlers[i].fp.detach();
jburhenn 0:09ef59d2d0f7 372 }
jburhenn 0:09ef59d2d0f7 373 }
jburhenn 0:09ef59d2d0f7 374 }
jburhenn 0:09ef59d2d0f7 375 }
jburhenn 0:09ef59d2d0f7 376 return result;
jburhenn 0:09ef59d2d0f7 377 }
jburhenn 0:09ef59d2d0f7 378
jburhenn 0:09ef59d2d0f7 379 /**
jburhenn 0:09ef59d2d0f7 380 * Handler for incoming MQTT::Client messages.
jburhenn 0:09ef59d2d0f7 381 * @param[in] md Message data
jburhenn 0:09ef59d2d0f7 382 */
jburhenn 0:09ef59d2d0f7 383 void mqttMessageArrived(MQTT::MessageData& md)
jburhenn 0:09ef59d2d0f7 384 {
jburhenn 0:09ef59d2d0f7 385 int result = MQTT::FAILURE;
jburhenn 0:09ef59d2d0f7 386 MessageData message;
jburhenn 0:09ef59d2d0f7 387
jburhenn 0:09ef59d2d0f7 388 result = CayenneParseTopic(&message.topic, &message.channel, &message.clientID, _username, md.topicName.lenstring.data, md.topicName.lenstring.len);
jburhenn 0:09ef59d2d0f7 389 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 390 return;
jburhenn 0:09ef59d2d0f7 391 //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 392 (static_cast<char*>(md.message.payload))[md.message.payloadlen] = '\0';
jburhenn 22:0dbabcc6e7b2 393 result = CayenneParsePayload(&message.type, &message.unit, &message.value, &message.id, message.topic, static_cast<char*>(md.message.payload));
jburhenn 0:09ef59d2d0f7 394 if (result != CAYENNE_SUCCESS)
jburhenn 0:09ef59d2d0f7 395 return;
jburhenn 0:09ef59d2d0f7 396
jburhenn 0:09ef59d2d0f7 397 result = MQTT::FAILURE;
jburhenn 0:09ef59d2d0f7 398 for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i) {
jburhenn 0:09ef59d2d0f7 399 if (_messageHandlers[i].fp.attached() && _messageHandlers[i].topic == message.topic &&
jburhenn 0:09ef59d2d0f7 400 (_messageHandlers[i].channel == message.channel || _messageHandlers[i].channel == CAYENNE_ALL_CHANNELS) &&
jburhenn 0:09ef59d2d0f7 401 (strcmp(_messageHandlers[i].clientID, message.clientID) == 0))
jburhenn 0:09ef59d2d0f7 402 {
jburhenn 0:09ef59d2d0f7 403 _messageHandlers[i].fp(message);
jburhenn 0:09ef59d2d0f7 404 result = MQTT::SUCCESS;
jburhenn 0:09ef59d2d0f7 405 }
jburhenn 0:09ef59d2d0f7 406 }
jburhenn 0:09ef59d2d0f7 407
jburhenn 0:09ef59d2d0f7 408 if (result == MQTT::FAILURE && _defaultMessageHandler.attached()) {
jburhenn 0:09ef59d2d0f7 409 _defaultMessageHandler(message);
jburhenn 0:09ef59d2d0f7 410 }
jburhenn 0:09ef59d2d0f7 411 }
jburhenn 0:09ef59d2d0f7 412
jburhenn 0:09ef59d2d0f7 413 private:
jburhenn 6:82e142a864ad 414 const char* _username;
jburhenn 8:aec9cfdd4c8e 415 const char* _password;
jburhenn 6:82e142a864ad 416 const char* _clientID;
jburhenn 0:09ef59d2d0f7 417 struct CayenneMessageHandlers
jburhenn 0:09ef59d2d0f7 418 {
jburhenn 0:09ef59d2d0f7 419 const char* clientID;
jburhenn 0:09ef59d2d0f7 420 CayenneTopic topic;
jburhenn 0:09ef59d2d0f7 421 unsigned int channel;
jburhenn 0:09ef59d2d0f7 422 FP<void, MessageData&> fp;
jburhenn 0:09ef59d2d0f7 423 } _messageHandlers[MAX_MESSAGE_HANDLERS]; /* Message handlers are indexed by subscription topic */
jburhenn 0:09ef59d2d0f7 424 FP<void, MessageData&> _defaultMessageHandler;
jburhenn 0:09ef59d2d0f7 425 };
jburhenn 0:09ef59d2d0f7 426
jburhenn 0:09ef59d2d0f7 427 }
jburhenn 0:09ef59d2d0f7 428
jburhenn 0:09ef59d2d0f7 429 #endif