A library that simplifies interactions with the IBM Internet of Things Foundation

Dependencies:   MQTT

Dependents:   IBMIoTClientLibrarySamplefinal IBMWIoTPSecureClientSample IBMIoTClientLibrarySample

Introduction

Use the mBed C++ client library to easily connect mBed devices, such as LPC1768 or FRDM-K64F, to the IBM Watson™ IoT Platform service.

Although the library uses C++, it still avoids dynamic memory allocations and the use of STL functions, because the mBed devices sometimes have idiosyncratic memory models that make porting difficult. In any case, the library allows you to make memory use as predictable as possible.

Dependencies

DependencyDescription
Eclipse Paho MQTT libraryProvides an MQTT client library for mBed devices. For more information, see Embedded MQTT C/C++ client libraries
mbed-osARM mbed OS

How to use the library

Use the mBed compiler to create your applications when you use the mBed C++ IBMIoTF client library. The mBed compiler provides a lightweight online C/C++ IDE that is configured for writing, compiling, and downloading programs to run on your mBed microcontroller.

Note: You don't have to install or set up anything to get running with mBed.

For information on how to connect an ARM mBed NXP LPC 1768 microcontroller to the Watson IoT Platform, see the mBed C++ client library for IBM Watson IoT Platform recipe.

Supported Features

FeatureSupported?
Device connectivityYes
Event publishYes
Command receiptYes
SSL/TLSYes
Device ManagementNo

License

This library is shipped under License Eclipse Public License - v 1.0 and Eclipse Distribution License v1.0

For more information about the Eclipse Public License - v 1.0, refer this page. Also, for more information about the Eclipse Distribution License v1.0, refer this page.

Contributor License Agreement

Version 1.0.0 January 14, 2014

In order for You (as defined below) to make intellectual property Contributions (as defined below) now or in the future to IBM repositories, You must agree to this Contributor License Agreement ("CLA").

Please read this CLA carefully before accepting its terms. By accepting the CLA, You are agreeing to be bound by its terms. If You submit a Pull Request against this repository You must include in the Pull Request a statement of Your acceptance of this CLA.

As used in this CLA: (i) "You" (or "Your") shall mean the entity that is making this Agreement with IBM; (ii)"Contribution" shall mean any original work of authorship, including any modifications or additions to an existing work, that is submitted by You to IBM for inclusion in, or documentation of, any of the IBM repositories; (iii) "Submit" (or "Submitted") means any form of communication sent to IBM (e.g. the content You post in the Issue or submit as part of a Pull Request).

This agreement applies to all Contributions You Submit.

This CLA, and the license(s) associated with the particular IBM repositories You are contributing to, provides a license to Your Contributions to IBM and downstream consumers, but You still own Your Contributions, and except for the licenses provided for in this CLA, You reserve all right, title and interest in Your Contributions.

IBM requires that each Contribution You Submit now or in the future comply with the following four commitments.

1) You will only Submit Contributions where You have authored 100% of the content. 2) You will only Submit Contributions to which You have the necessary rights. This means that if You are employed You have received the necessary permissions from Your employer to make the Contributions. 3) Whatever content You Contribute will be provided under the license(s) associated with the particular IBM repository You are contributing to. 4) You understand and agree that IBM repositories and Your contributions are public, and that a record of the contribution (including all personal information You submit with it) is maintained indefinitely and may be redistributed consistent with the license(s) involved. You will promptly notify the Eclipse Foundation if You become aware of any facts or circumstances that would make these commitments inaccurate in any way. To do so, please create an Issue in the appropriate repository.

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 }