Had to fork with a different name, because of some incompatibility issues.

Dependencies:   MQTT

Committer:
sathipal
Date:
Fri Nov 06 14:56:34 2015 +0000
Revision:
2:199ddea804cd
Parent:
0:f86732d81998
Child:
3:3d91bf839b49
A library that simplifies interactions with IBM Internet of Things Foundation.

Who changed what in which revision?

UserRevisionLine numberNew contents of line
sathipal 0:f86732d81998 1 /*******************************************************************************
sathipal 0:f86732d81998 2 * Copyright (c) 2015 IBM Corp.
sathipal 0:f86732d81998 3 *
sathipal 0:f86732d81998 4 * All rights reserved. This program and the accompanying materials
sathipal 0:f86732d81998 5 * are made available under the terms of the Eclipse Public License v1.0
sathipal 0:f86732d81998 6 * and Eclipse Distribution License v1.0 which accompany this distribution.
sathipal 0:f86732d81998 7 *
sathipal 0:f86732d81998 8 * The Eclipse Public License is available at
sathipal 0:f86732d81998 9 * http://www.eclipse.org/legal/epl-v10.html
sathipal 0:f86732d81998 10 * and the Eclipse Distribution License is available at
sathipal 0:f86732d81998 11 * http://www.eclipse.org/org/documents/edl-v10.php.
sathipal 0:f86732d81998 12 *
sathipal 0:f86732d81998 13 * Contributors:
sathipal 0:f86732d81998 14 * Sathisumar Palaniappan - initial implementation
sathipal 0:f86732d81998 15 *******************************************************************************/
sathipal 0:f86732d81998 16 #include "MQTTClient.h"
sathipal 0:f86732d81998 17 #include "DeviceClient.h"
sathipal 0:f86732d81998 18
sathipal 0:f86732d81998 19 using namespace IoTF;
sathipal 0:f86732d81998 20
sathipal 0:f86732d81998 21 CommandHandler handler = NULL;
sathipal 0:f86732d81998 22 void msgArrived(MQTT::MessageData& md);
sathipal 0:f86732d81998 23
sathipal 0:f86732d81998 24 /**
sathipal 0:f86732d81998 25 * A client, used by device, that handles connections with the IBM Internet of Things Foundation.
sathipal 0:f86732d81998 26 * This class allows device to publish events and receive commands to/from IBM IoT Foundation wtih simple function calls.
sathipal 0:f86732d81998 27 */
sathipal 2:199ddea804cd 28 DeviceClient::DeviceClient():org(NULL),deviceType(NULL),deviceId(NULL),
sathipal 2:199ddea804cd 29 authMethod(NULL),authToken(NULL),mqttClient(ipstack)
sathipal 2:199ddea804cd 30 {
sathipal 0:f86732d81998 31 LOG("Constructor#1 called::\n");
sathipal 0:f86732d81998 32 }
sathipal 0:f86732d81998 33
sathipal 2:199ddea804cd 34 DeviceClient::DeviceClient(char *orgId, char *typeId, char *id)
sathipal 2:199ddea804cd 35 :org(orgId),deviceType(typeId),deviceId(id),authMethod(NULL),authToken(NULL),mqttClient(ipstack)
sathipal 2:199ddea804cd 36 {
sathipal 0:f86732d81998 37 LOG("Constructor#2 called:: org=%s, type=%s, id=%s", (org==NULL)?"NULL":org,
sathipal 0:f86732d81998 38 (deviceType==NULL)?"NULL":deviceType, (deviceId==NULL)?"NULL":deviceId);
sathipal 0:f86732d81998 39
sathipal 0:f86732d81998 40 if(strcmp(this->org, QUICKSTART) != 0) {
sathipal 0:f86732d81998 41 WARN("Registered flow must provide valid token");
sathipal 0:f86732d81998 42 }
sathipal 0:f86732d81998 43 }
sathipal 0:f86732d81998 44
sathipal 2:199ddea804cd 45 DeviceClient::DeviceClient(char *orgId, char *typeId,
sathipal 2:199ddea804cd 46 char *id, char *method, char *token)
sathipal 2:199ddea804cd 47 :org(orgId),deviceType(typeId),deviceId(id),authMethod(method),authToken(token),mqttClient(ipstack)
sathipal 2:199ddea804cd 48 {
sathipal 0:f86732d81998 49 // Don't print token for security reasons
sathipal 0:f86732d81998 50 LOG("Constructor#3 called:: org=%s, type=%s, id=%s", (org==NULL)?"NULL":org,
sathipal 0:f86732d81998 51 (deviceType==NULL)?"NULL":deviceType, (deviceId==NULL)?"NULL":deviceId);
sathipal 0:f86732d81998 52 }
sathipal 0:f86732d81998 53
sathipal 0:f86732d81998 54 /**
sathipal 0:f86732d81998 55 * Connect to the IBM Internet of Things Foundation
sathipal 0:f86732d81998 56 */
sathipal 0:f86732d81998 57 bool DeviceClient::connect()
sathipal 0:f86732d81998 58 {
sathipal 0:f86732d81998 59 char *organizationName, *typeId, *id;
sathipal 0:f86732d81998 60 // Check if any organization is set
sathipal 2:199ddea804cd 61 if(this->org == NULL || (strcmp("", this->org) == 0)) {
sathipal 0:f86732d81998 62 organizationName = QUICKSTART;
sathipal 0:f86732d81998 63 } else {
sathipal 0:f86732d81998 64 organizationName = this->org;
sathipal 0:f86732d81998 65 }
sathipal 0:f86732d81998 66
sathipal 0:f86732d81998 67 // Check if device type is already mentioned
sathipal 2:199ddea804cd 68 if(this->deviceType == NULL || (strcmp("", this->deviceType) == 0)) {
sathipal 0:f86732d81998 69 typeId = "iotsample-mbed";
sathipal 0:f86732d81998 70 } else {
sathipal 0:f86732d81998 71 typeId = this->deviceType;
sathipal 0:f86732d81998 72 }
sathipal 0:f86732d81998 73
sathipal 0:f86732d81998 74 char hostname[strlen(organizationName) + strlen(IBM_IOT_MESSAGING) + 1];
sathipal 0:f86732d81998 75 sprintf(hostname, "%s%s", organizationName, IBM_IOT_MESSAGING);
sathipal 0:f86732d81998 76
sathipal 0:f86732d81998 77 EthernetInterface& eth = ipstack.getEth();
sathipal 0:f86732d81998 78
sathipal 0:f86732d81998 79 // Get devices MAC address if deviceId is not set already
sathipal 0:f86732d81998 80 if(this->deviceId == NULL || (strcmp("", this->deviceId) == 0)) {
sathipal 0:f86732d81998 81 char tmpBuf[50];
sathipal 0:f86732d81998 82 id = getMac(tmpBuf, sizeof(tmpBuf));
sathipal 0:f86732d81998 83 } else {
sathipal 0:f86732d81998 84 id = this->deviceId;
sathipal 0:f86732d81998 85 }
sathipal 0:f86732d81998 86
sathipal 0:f86732d81998 87 // Construct clientId - d:org:type:id
sathipal 0:f86732d81998 88 char clientId[strlen(organizationName) + strlen(typeId) + strlen(id) + 5];
sathipal 0:f86732d81998 89 sprintf(clientId, "d:%s:%s:%s", organizationName, typeId, id);
sathipal 0:f86732d81998 90
sathipal 0:f86732d81998 91 logData(eth, hostname, clientId);
sathipal 0:f86732d81998 92
sathipal 0:f86732d81998 93 // Initialize MQTT Connect
sathipal 0:f86732d81998 94 MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
sathipal 0:f86732d81998 95 data.MQTTVersion = 4;
sathipal 0:f86732d81998 96 data.clientID.cstring = clientId;
sathipal 0:f86732d81998 97
sathipal 0:f86732d81998 98 int quickstartMode = (strcmp(organizationName, QUICKSTART) == 0);
sathipal 0:f86732d81998 99
sathipal 0:f86732d81998 100 if (!quickstartMode) {
sathipal 0:f86732d81998 101 data.username.cstring = "use-token-auth";
sathipal 0:f86732d81998 102 data.password.cstring = this->authToken;
sathipal 0:f86732d81998 103 }
sathipal 0:f86732d81998 104 bool rc = tryConnect(hostname, data);
sathipal 0:f86732d81998 105 // By default subscribe to commands if we are in registered flow
sathipal 0:f86732d81998 106 if(rc == true && !quickstartMode) {
sathipal 0:f86732d81998 107 subscribeToCommands();
sathipal 0:f86732d81998 108 }
sathipal 0:f86732d81998 109 return rc;
sathipal 0:f86732d81998 110 }
sathipal 0:f86732d81998 111
sathipal 2:199ddea804cd 112 bool DeviceClient::tryConnect(char *hostname, MQTTPacket_connectData &data)
sathipal 2:199ddea804cd 113 {
sathipal 0:f86732d81998 114 int rc = -1;
sathipal 0:f86732d81998 115 int retryAttempt = 0;
sathipal 0:f86732d81998 116 do {
sathipal 0:f86732d81998 117 rc = ipstack.connect(hostname, IBM_IOT_PORT, CONNECT_TIMEOUT);
sathipal 0:f86732d81998 118 if (rc != 0) {
sathipal 0:f86732d81998 119 WARN("IP Stack connect returned: %d\n", rc);
sathipal 0:f86732d81998 120 }
sathipal 0:f86732d81998 121
sathipal 0:f86732d81998 122 // MQTT connect
sathipal 0:f86732d81998 123 if (rc == 0 && (rc = mqttClient.connect(data)) != 0) {
sathipal 0:f86732d81998 124 WARN("MQTT connect returned %d\n", rc);
sathipal 0:f86732d81998 125 if (rc == MQTT_NOT_AUTHORIZED || rc == MQTT_BAD_USERNAME_OR_PASSWORD)
sathipal 0:f86732d81998 126 return false; // don't reattempt to connect if credentials are wrong
sathipal 0:f86732d81998 127 } else if (rc == MQTT_CONNECTION_ACCEPTED) {
sathipal 0:f86732d81998 128 return true;
sathipal 0:f86732d81998 129 }
sathipal 0:f86732d81998 130
sathipal 0:f86732d81998 131 int timeout = getConnTimeout(++retryAttempt);
sathipal 0:f86732d81998 132
sathipal 0:f86732d81998 133 WARN("Retry attempt number %d waiting %d\n", retryAttempt, timeout);
sathipal 0:f86732d81998 134
sathipal 2:199ddea804cd 135 // enough retry is done - return to application
sathipal 0:f86732d81998 136 if (retryAttempt == 5)
sathipal 2:199ddea804cd 137 return false;
sathipal 0:f86732d81998 138 else
sathipal 0:f86732d81998 139 wait(timeout);
sathipal 0:f86732d81998 140 } while(true);
sathipal 0:f86732d81998 141 }
sathipal 0:f86732d81998 142
sathipal 2:199ddea804cd 143 void DeviceClient::logData(EthernetInterface& eth, char *hostname, char *clientId)
sathipal 2:199ddea804cd 144 {
sathipal 0:f86732d81998 145 // Network debug statements
sathipal 0:f86732d81998 146 LOG("=====================================\n");
sathipal 0:f86732d81998 147 LOG("Connecting Ethernet.\n");
sathipal 0:f86732d81998 148 LOG("IP ADDRESS: %s\n", eth.getIPAddress());
sathipal 0:f86732d81998 149 LOG("MAC ADDRESS: %s\n", eth.getMACAddress());
sathipal 0:f86732d81998 150 LOG("Gateway: %s\n", eth.getGateway());
sathipal 0:f86732d81998 151 LOG("Network Mask: %s\n", eth.getNetworkMask());
sathipal 0:f86732d81998 152 LOG("Server Hostname: %s\n", hostname);
sathipal 0:f86732d81998 153 LOG("Client ID: %s\n", clientId);
sathipal 0:f86732d81998 154 LOG("=====================================\n");
sathipal 0:f86732d81998 155 }
sathipal 0:f86732d81998 156
sathipal 0:f86732d81998 157 int DeviceClient::getConnTimeout(int attemptNumber)
sathipal 0:f86732d81998 158 {
sathipal 0:f86732d81998 159 // Try to increase the timeout every time
sathipal 0:f86732d81998 160 return (attemptNumber * attemptNumber * 5);
sathipal 0:f86732d81998 161 }
sathipal 0:f86732d81998 162
sathipal 0:f86732d81998 163
sathipal 0:f86732d81998 164 /**
sathipal 0:f86732d81998 165 * Publish data to the IBM Internet of Things Foundation. Note that data is published
sathipal 0:f86732d81998 166 * by default at Quality of Service (QoS) 0, which means that a successful send
sathipal 0:f86732d81998 167 * does not guarantee receipt even if the publish has been successful.
sathipal 0:f86732d81998 168 */
sathipal 0:f86732d81998 169 bool DeviceClient::publishEvent(char *eventName, char *data, MQTT::QoS qos)
sathipal 0:f86732d81998 170 {
sathipal 0:f86732d81998 171 if(!mqttClient.isConnected()) {
sathipal 0:f86732d81998 172 WARN("Client is not connected \n");
sathipal 0:f86732d81998 173 return false;
sathipal 0:f86732d81998 174 }
sathipal 0:f86732d81998 175
sathipal 0:f86732d81998 176 MQTT::Message message;
sathipal 0:f86732d81998 177 /* Topic format must be iot-2/evt/<eventName>/fmt/json (let us stick to json format for now)
sathipal 0:f86732d81998 178 *
sathipal 0:f86732d81998 179 * So length must be 10 + strlen(eventName) + 9 + 1
sathipal 0:f86732d81998 180 * iot-2/evt/ = 10
sathipal 0:f86732d81998 181 * /fmt/json = 9
sathipal 0:f86732d81998 182 * NULL char = 1
sathipal 0:f86732d81998 183 */
sathipal 0:f86732d81998 184
sathipal 0:f86732d81998 185 char topic[10 + strlen(eventName) + 9 + 1];
sathipal 0:f86732d81998 186 sprintf(topic, "%s%s%s", "iot-2/evt/", eventName, "/fmt/json");
sathipal 0:f86732d81998 187
sathipal 0:f86732d81998 188 message.qos = qos;
sathipal 0:f86732d81998 189 message.retained = false;
sathipal 0:f86732d81998 190 message.dup = false;
sathipal 0:f86732d81998 191 message.payload = (void*)data;
sathipal 0:f86732d81998 192 message.payloadlen = strlen(data);
sathipal 0:f86732d81998 193
sathipal 0:f86732d81998 194 LOG("Publishing %s\n", data);
sathipal 0:f86732d81998 195 int rc = mqttClient.publish(topic, message);
sathipal 2:199ddea804cd 196 mqttClient.yield(5);
sathipal 0:f86732d81998 197 return rc == 0;
sathipal 0:f86732d81998 198 }
sathipal 0:f86732d81998 199
sathipal 2:199ddea804cd 200 void DeviceClient::setCommandCallback(CommandHandler callbackFunc)
sathipal 2:199ddea804cd 201 {
sathipal 0:f86732d81998 202 handler = callbackFunc;
sathipal 0:f86732d81998 203 }
sathipal 0:f86732d81998 204 /**
sathipal 0:f86732d81998 205 * Subscribe to commands from the application. This will be executed only for
sathipal 0:f86732d81998 206 * registered flow (quickstart flow does not support command publish)
sathipal 0:f86732d81998 207 */
sathipal 2:199ddea804cd 208 int DeviceClient::subscribeToCommands()
sathipal 2:199ddea804cd 209 {
sathipal 0:f86732d81998 210 int rc = 0;
sathipal 0:f86732d81998 211 // iot-2/cmd/+/fmt/+
sathipal 0:f86732d81998 212 if ((rc = mqttClient.subscribe("iot-2/cmd/+/fmt/+", MQTT::QOS2, msgArrived)) != 0)
sathipal 0:f86732d81998 213 WARN("rc from MQTT subscribe is %d\n", rc);
sathipal 0:f86732d81998 214 return rc;
sathipal 0:f86732d81998 215 }
sathipal 0:f86732d81998 216
sathipal 0:f86732d81998 217 /**
sathipal 0:f86732d81998 218 * Callback method to be registered with MQTT::Client. MQTT::Client calls whenever
sathipal 0:f86732d81998 219 * any command is published to the topic subscribed earlier.
sathipal 0:f86732d81998 220 */
sathipal 0:f86732d81998 221 void msgArrived(MQTT::MessageData& md)
sathipal 0:f86732d81998 222 {
sathipal 0:f86732d81998 223 // check whether callback is registered by the client code
sathipal 0:f86732d81998 224 if(handler == NULL) {
sathipal 0:f86732d81998 225 return;
sathipal 0:f86732d81998 226 }
sathipal 0:f86732d81998 227
sathipal 0:f86732d81998 228 MQTT::Message &message = md.message;
sathipal 0:f86732d81998 229 char topic[md.topicName.lenstring.len + 1];
sathipal 0:f86732d81998 230
sathipal 0:f86732d81998 231 sprintf(topic, "%.*s", md.topicName.lenstring.len, md.topicName.lenstring.data);
sathipal 0:f86732d81998 232
sathipal 0:f86732d81998 233 LOG("Message arrived on topic %s: %.*s\n", topic, message.payloadlen, message.payload);
sathipal 0:f86732d81998 234
sathipal 0:f86732d81998 235 // Command topic: iot-2/cmd/blink/fmt/json - cmd is the string between cmd/ and /fmt/
sathipal 0:f86732d81998 236 char* start = strstr(topic, "/cmd/") + 5;
sathipal 0:f86732d81998 237 int len = strstr(topic, "/fmt/") - start;
sathipal 0:f86732d81998 238
sathipal 0:f86732d81998 239 char name[len + 1];
sathipal 0:f86732d81998 240
sathipal 0:f86732d81998 241 memcpy(name, start, len);
sathipal 0:f86732d81998 242 name[len] = NULL;
sathipal 0:f86732d81998 243
sathipal 0:f86732d81998 244 start = strstr(topic, "/fmt/") + 5;
sathipal 0:f86732d81998 245
sathipal 0:f86732d81998 246 char format[20]; // ToDO: need to find the length of the format
sathipal 0:f86732d81998 247 strcpy(format, start);
sathipal 0:f86732d81998 248
sathipal 0:f86732d81998 249 char payload[message.payloadlen + 1];
sathipal 0:f86732d81998 250 sprintf(payload, "%.*s", message.payloadlen, (char*)message.payload);
sathipal 0:f86732d81998 251
sathipal 0:f86732d81998 252 IoTF::Command cmd(name, format, payload);
sathipal 0:f86732d81998 253 (*handler)(cmd);
sathipal 0:f86732d81998 254 }
sathipal 0:f86732d81998 255
sathipal 2:199ddea804cd 256 bool DeviceClient::disconnect()
sathipal 2:199ddea804cd 257 {
sathipal 0:f86732d81998 258 if(mqttClient.isConnected()) {
sathipal 0:f86732d81998 259 int rc = mqttClient.disconnect();
sathipal 0:f86732d81998 260 return rc == 0;
sathipal 0:f86732d81998 261 }
sathipal 0:f86732d81998 262 return false;
sathipal 0:f86732d81998 263 }
sathipal 0:f86732d81998 264
sathipal 0:f86732d81998 265 // Yield to allow MQTT client to process the command
sathipal 2:199ddea804cd 266 void DeviceClient::yield(int ms)
sathipal 2:199ddea804cd 267 {
sathipal 0:f86732d81998 268 if(mqttClient.isConnected()) {
sathipal 0:f86732d81998 269 mqttClient.yield(ms);
sathipal 0:f86732d81998 270 }
sathipal 0:f86732d81998 271 }
sathipal 0:f86732d81998 272
sathipal 2:199ddea804cd 273 // Obtain DeviceId address
sathipal 2:199ddea804cd 274 char* DeviceClient::getDeviceId(char* buf, int buflen)
sathipal 2:199ddea804cd 275 {
sathipal 2:199ddea804cd 276 if(this->deviceId == NULL || (strcmp("", this->deviceId) == 0)) {
sathipal 2:199ddea804cd 277 return getMac(buf, buflen);
sathipal 2:199ddea804cd 278 } else {
sathipal 2:199ddea804cd 279 return strncpy(buf, this->deviceId, buflen);
sathipal 2:199ddea804cd 280 }
sathipal 2:199ddea804cd 281 }
sathipal 0:f86732d81998 282 // Obtain MAC address
sathipal 0:f86732d81998 283 char* DeviceClient::getMac(char* buf, int buflen)
sathipal 0:f86732d81998 284 {
sathipal 0:f86732d81998 285 EthernetInterface& eth = ipstack.getEth();
sathipal 0:f86732d81998 286 strncpy(buf, eth.getMACAddress(), buflen);
sathipal 0:f86732d81998 287
sathipal 0:f86732d81998 288 char* pos; // Remove colons from mac address
sathipal 0:f86732d81998 289 while ((pos = strchr(buf, ':')) != NULL)
sathipal 0:f86732d81998 290 memmove(pos, pos + 1, strlen(pos) + 1);
sathipal 0:f86732d81998 291 return buf;
sathipal 0:f86732d81998 292 }