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.

Revision:
5:ea9f483e0294
Parent:
3:3d91bf839b49
--- a/DeviceClient.cpp	Mon Mar 20 10:07:49 2017 +0000
+++ b/DeviceClient.cpp	Tue May 30 06:24:06 2017 +0000
@@ -13,6 +13,8 @@
  * Contributors:
  *    Sathisumar Palaniappan - initial implementation
  *    Sathisumar Palaniappan - added reconnect logic and isConnected() method
+ *    Lokesh K Haralakatta - Port to mbed OS 5 support
+ *    Lokesh K Haralakatta - Added SSL/TLS Support
  *******************************************************************************/
 #include "MQTTClient.h"
 #include "DeviceClient.h"
@@ -42,109 +44,126 @@
  * This class allows device to publish events and receive commands to/from IBM IoT Foundation wtih simple function calls.
  */
 DeviceClient::DeviceClient():org(NULL),deviceType(NULL),deviceId(NULL),
-              authMethod(NULL),authToken(NULL),mqttClient(ipstack),connected(false)
+        authMethod(NULL),authToken(NULL),mqttNetwork(NULL),mqttClient(NULL),connected(false),port(0)
 {
-    LOG("Constructor#1 called::\n");
+    LOG("Constructor#1 called::\r\n");
 }
 
-DeviceClient::DeviceClient(char *orgId, char *typeId, char *id)
-             :org(orgId),deviceType(typeId),deviceId(id),authMethod(NULL),
-             authToken(NULL),mqttClient(ipstack),connected(false)
+DeviceClient::DeviceClient(char *orgId, char *typeId, char *id, int port):org(orgId),deviceType(typeId),
+        deviceId(id),authMethod(NULL),authToken(NULL),connected(false), port(port)
 {
-    LOG("Constructor#2 called:: org=%s, type=%s, id=%s", (org==NULL)?"NULL":org,
+    LOG("Constructor#2 called:: org=%s, type=%s, id=%s\r\n", (org==NULL)?"NULL":org,
                     (deviceType==NULL)?"NULL":deviceType, (deviceId==NULL)?"NULL":deviceId);
- 
+
     if(strcmp(this->org, QUICKSTART) != 0) {
-        WARN("Registered flow must provide valid token");
+        WARN("Registered flow must provide valid token\r\n");
     }
+
+    mqttNetwork = new MQTTNetwork();
+    mqttClient = new MQTT::Client<MQTTNetwork, Countdown>(*mqttNetwork);
+
 }
 
-DeviceClient::DeviceClient(char *orgId, char *typeId, 
-            char *id, char *method, char *token)
-            :org(orgId),deviceType(typeId),deviceId(id),authMethod(method),
-            authToken(token),mqttClient(ipstack),connected(false)
+DeviceClient::DeviceClient(char *orgId, char *typeId,char *id, char *method, char *token, int port):org(orgId),
+        deviceType(typeId),deviceId(id),authMethod(method),authToken(token),connected(false), port(port)
 {
     // Don't print token for security reasons
-    LOG("Constructor#3 called:: org=%s, type=%s, id=%s", (org==NULL)?"NULL":org,
+    LOG("Constructor#3 called:: org=%s, type=%s, id=%s\r\n", (org==NULL)?"NULL":org,
                     (deviceType==NULL)?"NULL":deviceType, (deviceId==NULL)?"NULL":deviceId);
+
+    mqttNetwork = new MQTTNetwork();
+    mqttClient = new MQTT::Client<MQTTNetwork, Countdown>(*mqttNetwork);
 }
 
 /**
  * Connect to the IBM Internet of Things Foundation
- */ 
+ */
 bool DeviceClient::connect()
-{   
+{
     char *organizationName, *typeId, *id;
+    bool rc = false;
     // Check if any organization is set
-    if(this->org == NULL || (strcmp("", this->org) == 0)) 
+    if(this->org == NULL || (strcmp("", this->org) == 0))
     {
         organizationName = QUICKSTART;
     } else {
         organizationName = this->org;
     }
-    
+
     // Check if device type is already mentioned
-    if(this->deviceType == NULL || (strcmp("", this->deviceType) == 0)) 
+    if(this->deviceType == NULL || (strcmp("", this->deviceType) == 0))
     {
         typeId = "iotsample-mbed";
     } else {
         typeId = this->deviceType;
     }
-    
+
     char hostname[strlen(organizationName) + strlen(IBM_IOT_MESSAGING) + 1];
     sprintf(hostname, "%s%s", organizationName, IBM_IOT_MESSAGING);
-    
-    EthernetInterface& eth = ipstack.getEth();
+
+    NetworkInterface* net = mqttNetwork->getEth();
+    const char* ip = net->get_ip_address();
 
     // Get devices MAC address if deviceId is not set already
-    if(this->deviceId == NULL || (strcmp("", this->deviceId) == 0)) 
+    if(this->deviceId == NULL || (strcmp("", this->deviceId) == 0))
     {
         char tmpBuf[50];
         id = getMac(tmpBuf, sizeof(tmpBuf));
     } else {
         id = this->deviceId;
     }
-    
+
     // Construct clientId - d:org:type:id
     char clientId[strlen(organizationName) + strlen(typeId) + strlen(id) + 5];
     sprintf(clientId, "d:%s:%s:%s", organizationName, typeId, id);
-    
-    logData(eth, hostname, clientId);
-    
+
     // Initialize MQTT Connect
     MQTTPacket_connectData data = MQTTPacket_connectData_initializer;
     data.MQTTVersion = 4;
     data.clientID.cstring = clientId;
-    
+
     int quickstartMode = (strcmp(organizationName, QUICKSTART) == 0);
-    
-    if (!quickstartMode) 
-    {        
+
+    if (!quickstartMode)
+    {
         data.username.cstring = "use-token-auth";
         data.password.cstring = this->authToken;
-    } 
-    bool rc = tryConnect(hostname, data);
-    // By default subscribe to commands if we are in registered flow
-    if(rc == true && !quickstartMode) 
-    {
-        subscribeToCommands();
+
+        //Check and initialize appropriate port
+        if(port == 1883)
+            port = MQTT_TLS_PORT;
     }
-    if(rc == true) 
-    {
-        connected = true;
+
+    logData(net, hostname, clientId);
+
+    if(ip){
+       rc = tryConnect(hostname, data);
+       // By default subscribe to commands if we are in registered flow
+       if(rc == true && !quickstartMode)
+       {
+           subscribeToCommands();
+       }
+       if(rc == true)
+       {
+          connected = true;
+          LOG("Device Client Connected to %s:%d\r\n",hostname,port);
+        }
     }
+    else
+       LOG("No IP Assigned to Network Interface...\r\n");
+
     return rc;
 }
 
 /**
  * Reconnect when the connection is lost. This method disconnects the active connection if any
- * and tries to initiate a fresh connection. 
+ * and tries to initiate a fresh connection.
  * This method uses the Ethernet Link status wherever applicable while reconnecting. i.e, tries to
  * initiate the connection only when the Ethernet cable is plugged in.
  */
-bool DeviceClient::reConnect() 
+bool DeviceClient::reConnect()
 {
-    LOG("DeviceClient::reConnect() entry and connected = %s\n",(connected == true)?"true":"false");
+    LOG("DeviceClient::reConnect() entry and connected = %s\r\n",(connected == true)?"true":"false");
     if(connected == true)
     {
         disconnect();
@@ -152,13 +171,13 @@
 
     if(linkStatus())
     {
-        EthernetInterface& eth = ipstack.getEth();
-        if(eth.connect() == 0) 
+        NetworkInterface* net = mqttNetwork->getEth();
+        if(net->connect() == 0)
         {
             bool status = connect();
-            if(status == false) 
+            if(status == false)
             {
-                eth.disconnect();
+                net->disconnect();
             }
             return status;
         }
@@ -166,31 +185,31 @@
     return false;
 }
 
-bool DeviceClient::tryConnect(char *hostname, MQTTPacket_connectData &data) 
+bool DeviceClient::tryConnect(char *hostname, MQTTPacket_connectData &data)
 {
     int rc = -1;
     int retryAttempt = 0;
-    do {    
-        rc = ipstack.connect(hostname, IBM_IOT_PORT, CONNECT_TIMEOUT);
-        if (rc != 0) 
+    do {
+        rc = mqttNetwork->connect(hostname, port);
+        if (rc != 0)
         {
-            WARN("IP Stack connect returned: %d\n", rc);    
+            WARN("mqttNetwork connect returned: %d\r\n", rc);
         }
 
-        // MQTT connect        
-        if (rc == 0 && (rc = mqttClient.connect(data)) != 0) 
+        // MQTT connect
+        if (rc == 0 && (rc = mqttClient->connect(data)) != 0)
         {
-            WARN("MQTT connect returned %d\n", rc);
+            WARN("MQTT connect returned %d\r\n", rc);
             if (rc == MQTT_NOT_AUTHORIZED || rc == MQTT_BAD_USERNAME_OR_PASSWORD)
                 return false; // don't reattempt to connect if credentials are wrong
         } else if (rc == MQTT_CONNECTION_ACCEPTED) {
             return true;
         }
-   
+
         int timeout = getConnTimeout(++retryAttempt);
-        
-        WARN("Retry attempt number %d waiting %d\n", retryAttempt, timeout);
-        
+
+        WARN("Retry attempt number %d waiting %d\r\n", retryAttempt, timeout);
+
         // enough retry is done - return to application
         if (retryAttempt == 5)
             return false;
@@ -199,18 +218,19 @@
     } while(true);
 }
 
-void DeviceClient::logData(EthernetInterface& eth, char *hostname, char *clientId) 
+void DeviceClient::logData(NetworkInterface* net, char *hostname, char *clientId)
 {
-    // Network debug statements 
-    LOG("=====================================\n");
-    LOG("Connecting Ethernet.\n");
-    LOG("IP ADDRESS: %s\n", eth.getIPAddress());
-    LOG("MAC ADDRESS: %s\n", eth.getMACAddress());
-    LOG("Gateway: %s\n", eth.getGateway());
-    LOG("Network Mask: %s\n", eth.getNetworkMask());
-    LOG("Server Hostname: %s\n", hostname);
-    LOG("Client ID: %s\n", clientId);
-    LOG("=====================================\n");
+    // Network debug statements
+    LOG("=====================================\r\n");
+    LOG("Connection Config Details:\r\n");
+    LOG("IP ADDRESS: %s\r\n", net->get_ip_address());
+    LOG("MAC ADDRESS: %s\r\n", net->get_mac_address());
+    LOG("Gateway: %s\r\n", net->get_gateway());
+    LOG("Network Mask: %s\r\n", net->get_netmask());
+    LOG("Server Hostname: %s\r\n", hostname);
+    LOG("Server Port: %d\r\n", port);
+    LOG("Client ID: %s\r\n", clientId);
+    LOG("=====================================\r\n");
 }
 
 int DeviceClient::getConnTimeout(int attemptNumber)
@@ -223,22 +243,22 @@
  * Returns the connection status, connected or disconnected.
  */
 bool DeviceClient::isConnected() {
-    return mqttClient.isConnected();
+    return mqttClient->isConnected();
 }
 
 /**
- * Publish data to the IBM Internet of Things Foundation. Note that data is published 
- * by default at Quality of Service (QoS) 0, which means that a successful send 
+ * Publish data to the IBM Internet of Things Foundation. Note that data is published
+ * by default at Quality of Service (QoS) 0, which means that a successful send
  * does not guarantee receipt even if the publish has been successful.
  */
 bool DeviceClient::publishEvent(char *eventName, char *data, MQTT::QoS qos)
 {
-    if(!mqttClient.isConnected()) 
+    if(!mqttClient->isConnected())
     {
-        WARN("Client is not connected \n");
+        WARN("Client is not connected \r\n");
         return false;
     }
-    
+
     MQTT::Message message;
     /* Topic format must be iot-2/evt/<eventName>/fmt/json (let us stick to json format for now)
      *
@@ -247,22 +267,22 @@
      * /fmt/json = 9
      * NULL char = 1
      */
-    
+
     char topic[10 + strlen(eventName) + 9 + 1];
     sprintf(topic, "%s%s%s", "iot-2/evt/", eventName, "/fmt/json");
-            
+
     message.qos = qos;
     message.retained = false;
     message.dup = false;
     message.payload = (void*)data;
     message.payloadlen = strlen(data);
-    
-    LOG("Publishing %s\n", data);
-    int rc = mqttClient.publish(topic, message);
+
+    LOG("Publishing %s\r\n", data);
+    int rc = mqttClient->publish(topic, message);
     return rc == 0;
 }
 
-void DeviceClient::setCommandCallback(CommandHandler callbackFunc) 
+void DeviceClient::setCommandCallback(CommandHandler callbackFunc)
 {
     handler = callbackFunc;
 }
@@ -270,51 +290,51 @@
  * Subscribe to commands from the application. This will be executed only for
  * registered flow (quickstart flow does not support command publish)
  */
-int DeviceClient::subscribeToCommands() 
+int DeviceClient::subscribeToCommands()
 {
     int rc = 0;
     // iot-2/cmd/+/fmt/+
-    if ((rc = mqttClient.subscribe("iot-2/cmd/+/fmt/+", MQTT::QOS2, msgArrived)) != 0)
-        WARN("rc from MQTT subscribe is %d\n", rc); 
+    if ((rc = mqttClient->subscribe("iot-2/cmd/+/fmt/+", MQTT::QOS2, msgArrived)) != 0)
+        WARN("rc from MQTT subscribe is %d\r\n", rc);
     return rc;
 }
 
 /**
- * Callback method to be registered with MQTT::Client. MQTT::Client calls whenever 
+ * Callback method to be registered with MQTT::Client. MQTT::Client calls whenever
  * any command is published to the topic subscribed earlier.
  */
 void msgArrived(MQTT::MessageData& md)
 {
     // check whether callback is registered by the client code
-    if(handler == NULL) 
+    if(handler == NULL)
     {
         return;
     }
-    
+
     MQTT::Message &message = md.message;
     char topic[md.topicName.lenstring.len + 1];
-    
+
     sprintf(topic, "%.*s", md.topicName.lenstring.len, md.topicName.lenstring.data);
-    
-    LOG("Message arrived on topic %s: %.*s\n",  topic, message.payloadlen, message.payload);
-          
+
+    LOG("Message arrived on topic %s: %.*s\r\n",  topic, message.payloadlen, message.payload);
+
     // Command topic: iot-2/cmd/blink/fmt/json - cmd is the string between cmd/ and /fmt/
     char* start = strstr(topic, "/cmd/") + 5;
     int len = strstr(topic, "/fmt/") - start;
 
     char name[len + 1];
-    
+
     memcpy(name, start, len);
     name[len] = NULL;
-    
+
     start = strstr(topic, "/fmt/") + 5;
-      
+
     char format[20];  // ToDO: need to find the length of the format
     strcpy(format, start);
-    
+
     char payload[message.payloadlen + 1];
     sprintf(payload, "%.*s", message.payloadlen, (char*)message.payload);
-    
+
     IoTF::Command cmd(name, format, payload);
     (*handler)(cmd);
 }
@@ -322,34 +342,34 @@
 /**
  * Disconnects the connection in order.
  */
-bool DeviceClient::disconnect() 
+bool DeviceClient::disconnect()
 {
     int rc = 0;
-    if(mqttClient.isConnected())
+    if(mqttClient->isConnected())
     {
-        rc = mqttClient.disconnect();
+        rc = mqttClient->disconnect();
     }
-    
-    EthernetInterface& eth = ipstack.getEth();
-    ipstack.disconnect();
-    eth.disconnect();
+
+    NetworkInterface* net = mqttNetwork->getEth();
+    mqttNetwork->disconnect();
+    net->disconnect();
     connected = false;
     return rc == 0;
 }
 
 // Yield to allow MQTT client to process the command
-void DeviceClient::yield(int ms) 
+void DeviceClient::yield(int ms)
 {
-    if(mqttClient.isConnected()) 
+    if(mqttClient->isConnected())
     {
-        mqttClient.yield(ms);
+        mqttClient->yield(ms);
     }
 }
 
 // Obtain DeviceId address
-char* DeviceClient::getDeviceId(char* buf, int buflen) 
-{   
-    if(this->deviceId == NULL || (strcmp("", this->deviceId) == 0)) 
+char* DeviceClient::getDeviceId(char* buf, int buflen)
+{
+    if(this->deviceId == NULL || (strcmp("", this->deviceId) == 0))
     {
         return getMac(buf, buflen);
     } else {
@@ -357,13 +377,13 @@
     }
 }
 // Obtain MAC address
-char* DeviceClient::getMac(char* buf, int buflen)    
-{   
-    EthernetInterface& eth = ipstack.getEth();
-    strncpy(buf, eth.getMACAddress(), buflen);
+char* DeviceClient::getMac(char* buf, int buflen)
+{
+    NetworkInterface* net = mqttNetwork->getEth();
+    strncpy(buf, net->get_mac_address(), buflen);
 
     char* pos;                                                 // Remove colons from mac address
     while ((pos = strchr(buf, ':')) != NULL)
         memmove(pos, pos + 1, strlen(pos) + 1);
     return buf;
-}
\ No newline at end of file
+}