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

Dependencies:   MQTT

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