demo project

Dependencies:   AX-12A Dynamixel mbed iothub_client EthernetInterface NTPClient ConfigFile SDFileSystem iothub_amqp_transport mbed-rtos proton-c-mbed wolfSSL

Files at this revision

API Documentation at this revision

Comitter:
henryrawas
Date:
Tue Jan 26 20:19:43 2016 +0000
Parent:
20:891b5270845a
Child:
22:f9c13c5f75c1
Commit message:
Use private modified iothub client and eliminate extra thread.

Changed in this revision

IothubRobotArm.cpp Show annotated file Show diff for this revision Revisions of this file
IothubRobotArm.h Show annotated file Show diff for this revision Revisions of this file
Utils/MeasureBuf.h Show annotated file Show diff for this revision Revisions of this file
Utils/iothub_mod_client.c Show annotated file Show diff for this revision Revisions of this file
Utils/iothub_mod_client.h Show annotated file Show diff for this revision Revisions of this file
--- a/IothubRobotArm.cpp	Tue Jan 26 17:34:51 2016 +0000
+++ b/IothubRobotArm.cpp	Tue Jan 26 20:19:43 2016 +0000
@@ -9,7 +9,7 @@
 #include "mbed/mbedtime.h"
 #include <NTPClient.h>
 
-#include "iothub_client.h"
+#include "iothub_mod_client.h"
 #include "iothub_message.h"
 #include "threadapi.h"
 #include "crt_abstractions.h"
@@ -107,7 +107,7 @@
 
 // a larger MESSAGE_COUNT results in being able to send as data is available
 // but requires more heap space to hold buffers
-#define MESSAGE_COUNT       2
+#define MESSAGE_COUNT       4
 EVENT_INSTANCE messages[MESSAGE_COUNT];
 
 // sending thread timeout
@@ -125,6 +125,9 @@
 #define SEND_CONFIRM_TO  30000
 RtosTimer* confirmTimer;
 
+// object for IoTHub interface
+IothubRobotArm iotRobot;
+
 
 // pass received commands to device
 extern void ControlArmCommands(const char* cmd);
@@ -171,6 +174,12 @@
     IoTHubMessage_Destroy(eventInstance->messageHandle);
 }
 
+
+static void SendCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, void* userContextCallback)
+{
+    iotRobot.SendMessage(iotHubClientHandle, userContextCallback);
+}
+
 // communication timeout
 void CommunicationTO(void const * tid)
 {
@@ -178,59 +187,26 @@
     ShowLedColor(2);
 }
 
-
-// IoT Hub thread
-static THREAD_HANDLE IotThread;
-static bool IotThreadClose;
-
-// entry point for ITHub sending thread
-int IothubThread(void *args)
-{
-    (void)printf("Iothub thread start\r\n");
-    IotThreadClose = false;
-    IothubRobotArm iotRobot;
- 
-    confirmTimer = new RtosTimer(CommunicationTO, osTimerOnce, (void *)osThreadGetId());
-  
-    iotRobot.Init();
-    // wait for connection establishment for SSL
-    ThreadAPI_Sleep(15000);
-    
-    while (1)
-    {
-        if (IotThreadClose)
-        {
-            (void)printf("Iothub thread close\r\n");
-            iotRobot.Terminate();
-            break;
-        }
-        else
-        {
-            iotRobot.SendMessage();
-        }
-        ThreadAPI_Sleep(SEND_POLL_MS);        
-    }
-    
-    return 0;
-}
-
-
+// entry point to start IoTHub connection
 bool StartIothubThread()
 {
     InitEthernet();
     
-    ThreadAPI_Create(&IotThread, IothubThread, NULL);
+    confirmTimer = new RtosTimer(CommunicationTO, osTimerOnce, (void *)osThreadGetId());
+  
+    iotRobot.Init();
  
     return true;
 }
 
-
+// stop IoTHub connection
 void EndIothubThread()
 {
-    IotThreadClose = true;
+    iotRobot.Terminate();
 }
 
-
+// IoTHub connection for RobotArm
+// sends and receives messages between RobotArm and IoTHub
 IothubRobotArm::IothubRobotArm()
 {
     iotHubClientHandle = NULL;
@@ -244,7 +220,7 @@
     
     (void)printf("Starting the IoTHub RobotArm sample AMQP...\r\n");
 
-    if ((iotHubClientHandle = IoTHubClient_CreateFromConnectionString(connectionString, AMQP_Protocol)) == NULL)
+    if ((iotHubClientHandle = IoTHubClient_Mod_CreateFromConnectionString(connectionString, AMQP_Protocol)) == NULL)
     {
         (void)printf("ERROR: iotHubClientHandle is NULL!\r\n");
         return false;
@@ -254,16 +230,15 @@
 #ifdef MBED_BUILD_TIMESTAMP
         (void)printf("INFO: IoTHubClient_SetOption\r\n");
         // For mbed add the certificate information
-        if (IoTHubClient_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK)
+        if (IoTHubClient_Mod_SetOption(iotHubClientHandle, "TrustedCerts", certificates) != IOTHUB_CLIENT_OK)
         {
             printf("failure to set option \"TrustedCerts\"\r\n");
             return false;
         }
 #endif // MBED_BUILD_TIMESTAMP
 
-        (void)printf("INFO: IoTHubClient_SetMessageCallback\r\n");
         /* Setting Message call back, so we can receive Commands. */
-        if (IoTHubClient_SetMessageCallback(iotHubClientHandle, ReceiveMessageCallback, &receiveContext) != IOTHUB_CLIENT_OK)
+        if (IoTHubClient_Mod_SetMessageCallback(iotHubClientHandle, ReceiveMessageCallback, &receiveContext) != IOTHUB_CLIENT_OK)
         {
             (void)printf("ERROR: IoTHubClient_SetMessageCallback..........FAILED!\r\n");
             return false;
@@ -272,6 +247,17 @@
         {
             (void)printf("IoTHubClient_SetMessageCallback...successful.\r\n");
         }
+
+        /* Setting Send call back, so we can send from worker thread. */
+        if (IoTHubClient_Mod_SetSendCallback(iotHubClientHandle, SendCallback, &receiveContext) != IOTHUB_CLIENT_OK)
+        {
+            (void)printf("ERROR: IoTHubClient_SetSendCallback..........FAILED!\r\n");
+            return false;
+        }
+        else
+        {
+            (void)printf("IoTHubClient_SetSendCallback...successful.\r\n");
+        }
     }
     return true;
 }
@@ -280,13 +266,13 @@
 {
     if (iotHubClientHandle != NULL)
     {
-        IoTHubClient_Destroy(iotHubClientHandle);
+        IoTHubClient_Mod_Destroy(iotHubClientHandle);
         iotHubClientHandle = NULL;
     }
 }
 
-
-void IothubRobotArm::SendMessage(void)
+// Invoked from callback
+void IothubRobotArm::SendMessage(IOTHUB_CLIENT_HANDLE iotHubClient, void* userContextCallback)
 {
     // send until circular buf empty or no sending buffers avail
     // may drop message if confirmations are slow
@@ -313,9 +299,9 @@
             else
             {
                 messages[i].messageTrackingId = msgNumber;
-                
+
                 confirmTimer->stop();
-                if (IoTHubClient_SendEventAsync(iotHubClientHandle, messages[i].messageHandle, SendConfirmationCallback, &messages[i]) != IOTHUB_CLIENT_OK)
+                if (IoTHubClient_Mod_SendEventAsync(iotHubClient, messages[i].messageHandle, SendConfirmationCallback, &messages[i]) != IOTHUB_CLIENT_OK)
                 {
                     (void)printf("ERROR: IoTHubClient_SendEventAsync..........FAILED!\r\n");
                 }
--- a/IothubRobotArm.h	Tue Jan 26 17:34:51 2016 +0000
+++ b/IothubRobotArm.h	Tue Jan 26 20:19:43 2016 +0000
@@ -4,7 +4,7 @@
 #ifndef IOTHUB_ROBOTARM_H
 #define IOTHUB_ROBOTARM_H
 
-#include "iothub_client.h"
+#include "iothub_mod_client.h"
 
 #include "IothubSerial.h"
 
@@ -17,7 +17,7 @@
     
     void Terminate();
     
-    void SendMessage(void);
+    void SendMessage(IOTHUB_CLIENT_HANDLE iotHubClient, void* userContextCallback);
 
 private:
 
--- a/Utils/MeasureBuf.h	Tue Jan 26 17:34:51 2016 +0000
+++ b/Utils/MeasureBuf.h	Tue Jan 26 20:19:43 2016 +0000
@@ -9,7 +9,7 @@
 #include "RobotArmCfg.h"
 
 
-#define MeasureBufSize  4
+#define MeasureBufSize  8
 
 
 class MeasureGroup
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Utils/iothub_mod_client.c	Tue Jan 26 20:19:43 2016 +0000
@@ -0,0 +1,415 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// iothub_mod_client.c : modified version of iothub_client.c
+
+#include <stdlib.h> 
+#ifdef _CRTDBG_MAP_ALLOC
+#include <crtdbg.h>
+#endif
+#include "gballoc.h"
+
+#include <stdlib.h>
+#include <signal.h>
+#include "crt_abstractions.h"
+#include "iothub_mod_client.h"
+#include "iothub_client_ll.h"
+#include "threadapi.h"
+#include "lock.h"
+#include "iot_logging.h"
+
+typedef struct IOTHUB_CLIENT_INSTANCE_TAG
+{
+    IOTHUB_CLIENT_LL_HANDLE IoTHubClientLLHandle;
+    THREAD_HANDLE ThreadHandle;
+    LOCK_HANDLE LockHandle;
+    sig_atomic_t StopThread;
+    IOTHUB_CLIENT_SEND_CALLBACK SendCallback;
+    void* SendContext;
+} IOTHUB_CLIENT_INSTANCE;
+
+static int ScheduleWork_Mod_Thread(void* threadArgument)
+{
+    IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)threadArgument;
+    
+    /* Codes_SRS_IOTHUBCLIENT_01_038: [The thread shall exit when IoTHubClient_Destroy is called.] */
+    while (!iotHubClientInstance->StopThread)
+    {
+        /* Codes_SRS_IOTHUBCLIENT_01_039: [All calls to IoTHubClient_LL_DoWork shall be protected by the lock created in IotHubClient_Create.] */
+        /* Codes_SRS_IOTHUBCLIENT_01_040: [If acquiring the lock fails, IoTHubClient_LL_DoWork shall not be called.] */
+        if (Lock(iotHubClientInstance->LockHandle) == LOCK_OK)
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_037: [The thread created by IoTHubClient_SendEvent or IoTHubClient_SetMessageCallback shall call IoTHubClient_LL_DoWork every 1 ms.] */
+            IoTHubClient_LL_DoWork(iotHubClientInstance->IoTHubClientLLHandle);
+            /* Codes_SRS_IOTHUBCLIENT_01_039: [All calls to IoTHubClient_LL_DoWork shall be protected by the lock created in IotHubClient_Create.] */
+            Unlock(iotHubClientInstance->LockHandle);
+        }
+        if (iotHubClientInstance->SendCallback != NULL)
+        {
+            iotHubClientInstance->SendCallback(iotHubClientInstance, iotHubClientInstance->SendContext);
+        }
+        ThreadAPI_Sleep(1);
+    }
+
+    return 0;
+}
+
+static void StartWorkerThreadIfNeeded_Mod(IOTHUB_CLIENT_INSTANCE* iotHubClientInstance)
+{
+    if (iotHubClientInstance->ThreadHandle == NULL)
+    {
+        iotHubClientInstance->StopThread = 0;
+        if (ThreadAPI_Create(&iotHubClientInstance->ThreadHandle, ScheduleWork_Mod_Thread, iotHubClientInstance) != THREADAPI_OK)
+        {
+            iotHubClientInstance->ThreadHandle = NULL;
+        }
+    }
+}
+
+IOTHUB_CLIENT_HANDLE IoTHubClient_Mod_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol)
+{
+    IOTHUB_CLIENT_INSTANCE* result = NULL;
+
+    /* Codes_SRS_IOTHUBCLIENT_12_003: [IoTHubClient_CreateFromConnectionString shall verify the input parameter and if it is NULL then return NULL] */
+    if (connectionString == NULL)
+    {
+        LogError("Input parameter is NULL: connectionString\r\n");
+    }
+    else if (protocol == NULL)
+    {
+        LogError("Input parameter is NULL: protocol\r\n");
+    }
+    else
+    {
+        /* Codes_SRS_IOTHUBCLIENT_12_004: [IoTHubClient_CreateFromConnectionString shall allocate a new IoTHubClient instance.] */
+        result = malloc(sizeof(IOTHUB_CLIENT_INSTANCE));
+
+        /* Codes_SRS_IOTHUBCLIENT_12_011: [If the allocation failed, IoTHubClient_CreateFromConnectionString returns NULL] */
+        if (result == NULL)
+        {
+            LogError("Malloc failed\r\n");
+        }
+        else
+        {
+            /* Codes_SRS_IOTHUBCLIENT_12_005: [IoTHubClient_CreateFromConnectionString shall create a lock object to be used later for serializing IoTHubClient calls] */
+            result->LockHandle = Lock_Init();
+            if (result->LockHandle == NULL)
+            {
+                /* Codes_SRS_IOTHUBCLIENT_12_009: [If lock creation failed, IoTHubClient_CreateFromConnectionString shall do clean up and return NULL] */
+                free(result);
+                result = NULL;
+                LogError("Lock_Init failed\r\n");
+            }
+            else
+            {
+                /* Codes_SRS_IOTHUBCLIENT_12_006: [IoTHubClient_CreateFromConnectionString shall instantiate a new IoTHubClient_LL instance by calling IoTHubClient_LL_CreateFromConnectionString and passing the connectionString] */
+                result->IoTHubClientLLHandle = IoTHubClient_LL_CreateFromConnectionString(connectionString, protocol);
+                if (result->IoTHubClientLLHandle == NULL)
+                {
+                    /* Codes_SRS_IOTHUBCLIENT_12_010: [If IoTHubClient_LL_CreateFromConnectionString fails then IoTHubClient_CreateFromConnectionString shall do clean - up and return NULL] */
+                    Lock_Deinit(result->LockHandle);
+                    free(result);
+                    result = NULL;
+                    LogError("IoTHubClient_LL_CreateFromConnectionString failed\r\n");
+                }
+                else
+                { 
+                    result->ThreadHandle = NULL;
+                    result->SendCallback = NULL;
+                }
+            }
+        }
+    }
+    return result;
+}
+
+
+IOTHUB_CLIENT_HANDLE IoTHubClient_Mod_Create(const IOTHUB_CLIENT_CONFIG* config)
+{
+    /* Codes_SRS_IOTHUBCLIENT_01_001: [IoTHubClient_Create shall allocate a new IoTHubClient instance and return a non-NULL handle to it.] */
+    IOTHUB_CLIENT_INSTANCE* result = (IOTHUB_CLIENT_INSTANCE*)malloc(sizeof(IOTHUB_CLIENT_INSTANCE));
+
+    /* Codes_SRS_IOTHUBCLIENT_01_004: [If allocating memory for the new IoTHubClient instance fails, then IoTHubClient_Create shall return NULL.] */
+    if (result != NULL)
+    {
+        result->ThreadHandle = NULL;
+        result->SendCallback = NULL;
+
+        /* Codes_SRS_IOTHUBCLIENT_01_029: [IoTHubClient_Create shall create a lock object to be used later for serializing IoTHubClient calls.] */
+        result->LockHandle = Lock_Init();
+        if (result->LockHandle == NULL)
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_030: [If creating the lock fails, then IoTHubClient_Create shall return NULL.] */
+            /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */
+            free(result);
+            result = NULL;
+        }
+        else
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_002: [IoTHubClient_Create shall instantiate a new IoTHubClient_LL instance by calling IoTHubClient_LL_Create and passing the config argument.] */
+            result->IoTHubClientLLHandle = IoTHubClient_LL_Create(config);
+            if (result->IoTHubClientLLHandle == NULL)
+            {
+                /* Codes_SRS_IOTHUBCLIENT_01_003: [If IoTHubClient_LL_Create fails, then IoTHubClient_Create shall return NULL.] */
+                /* Codes_SRS_IOTHUBCLIENT_01_031: [If IoTHubClient_Create fails, all resources allocated by it shall be freed.] */
+                Lock_Deinit(result->LockHandle);
+                free(result);
+                result = NULL;
+            }
+        }
+    }
+
+    return result;
+}
+
+/* Codes_SRS_IOTHUBCLIENT_01_005: [IoTHubClient_Destroy shall free all resources associated with the iotHubClientHandle instance.] */
+void IoTHubClient_Mod_Destroy(IOTHUB_CLIENT_HANDLE iotHubClientHandle)
+{
+    /* Codes_SRS_IOTHUBCLIENT_01_008: [IoTHubClient_Destroy shall do nothing if parameter iotHubClientHandle is NULL.] */
+    if (iotHubClientHandle != NULL)
+    {
+        IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle;
+
+        /* Codes_SRS_IOTHUBCLIENT_01_007: [The thread created as part of executing IoTHubClient_SendEventAsync or IoTHubClient_SetMessageCallback shall be joined.] */
+        if (iotHubClientInstance->ThreadHandle != NULL)
+        {
+            int res;
+            iotHubClientInstance->StopThread = 1;
+            if (ThreadAPI_Join(iotHubClientInstance->ThreadHandle, &res) != THREADAPI_OK)
+            {
+                LogError("ThreadAPI_Join failed\r\n");
+            }
+        }
+
+        /* Codes_SRS_IOTHUBCLIENT_01_006: [That includes destroying the IoTHubClient_LL instance by calling IoTHubClient_LL_Destroy.] */
+        IoTHubClient_LL_Destroy(iotHubClientInstance->IoTHubClientLLHandle);
+
+        /* Codes_SRS_IOTHUBCLIENT_01_032: [The lock allocated in IoTHubClient_Create shall be also freed.] */
+        Lock_Deinit(iotHubClientInstance->LockHandle);
+        free(iotHubClientInstance);
+    }
+}
+
+IOTHUB_CLIENT_RESULT IoTHubClient_Mod_SendEventAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback)
+{
+    IOTHUB_CLIENT_RESULT result;
+
+    if (iotHubClientHandle == NULL)
+    {
+        /* Codes_SRS_IOTHUBCLIENT_01_011: [If iotHubClientHandle is NULL, IoTHubClient_SendEventAsync shall return IOTHUB_CLIENT_INVALID_ARG.] */
+        result = IOTHUB_CLIENT_INVALID_ARG;
+        LogError("NULL iothubClientHandle\r\n");
+    }
+    else
+    {
+        IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle;
+
+        /* Codes_SRS_IOTHUBCLIENT_01_025: [IoTHubClient_SendEventAsync shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+        if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK)
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_026: [If acquiring the lock fails, IoTHubClient_SendEventAsync shall return IOTHUB_CLIENT_ERROR.] */
+            result = IOTHUB_CLIENT_ERROR;
+            LogError("Could not acquire lock\r\n");
+        }
+        else
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_009: [IoTHubClient_SendEventAsync shall start the worker thread if it was not previously started.] */
+            StartWorkerThreadIfNeeded_Mod(iotHubClientInstance);
+
+            if (iotHubClientInstance->ThreadHandle == NULL)
+            {
+                /* Codes_SRS_IOTHUBCLIENT_01_010: [If starting the thread fails, IoTHubClient_SendEventAsync shall return IOTHUB_CLIENT_ERROR.] */
+                result = IOTHUB_CLIENT_ERROR;
+                LogError("Could not start worker thread\r\n");
+            }
+            else
+            {
+                /* Codes_SRS_IOTHUBCLIENT_01_012: [IoTHubClient_SendEventAsync shall call IoTHubClient_LL_SendEventAsync, while passing the IoTHubClient_LL handle created by IoTHubClient_Create and the parameters eventMessageHandle, eventConfirmationCallback and userContextCallback.] */
+                /* Codes_SRS_IOTHUBCLIENT_01_013: [When IoTHubClient_LL_SendEventAsync is called, IoTHubClient_SendEventAsync shall return the result of IoTHubClient_LL_SendEventAsync.] */
+                result = IoTHubClient_LL_SendEventAsync(iotHubClientInstance->IoTHubClientLLHandle, eventMessageHandle, eventConfirmationCallback, userContextCallback);
+            }
+
+            /* Codes_SRS_IOTHUBCLIENT_01_025: [IoTHubClient_SendEventAsync shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+            (void)Unlock(iotHubClientInstance->LockHandle);
+        }
+    }
+
+    return result;
+}
+
+IOTHUB_CLIENT_RESULT IoTHubClient_Mod_GetSendStatus(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus)
+{
+    IOTHUB_CLIENT_RESULT result;
+
+    if (iotHubClientHandle == NULL)
+    {
+        /* Codes_SRS_IOTHUBCLIENT_01_023: [If iotHubClientHandle is NULL, IoTHubClient_ GetSendStatus shall return IOTHUB_CLIENT_INVALID_ARG.] */
+        result = IOTHUB_CLIENT_INVALID_ARG;
+        LogError("NULL iothubClientHandle\r\n");
+    }
+    else
+    {
+        IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle;
+
+        /* Codes_SRS_IOTHUBCLIENT_01_033: [IoTHubClient_GetSendStatus shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+        if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK)
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_034: [If acquiring the lock fails, IoTHubClient_GetSendStatus shall return IOTHUB_CLIENT_ERROR.] */
+            result = IOTHUB_CLIENT_ERROR;
+            LogError("Could not acquire lock\r\n");
+        }
+        else
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_022: [IoTHubClient_GetSendStatus shall call IoTHubClient_LL_GetSendStatus, while passing the IoTHubClient_LL handle created by IoTHubClient_Create and the parameter iotHubClientStatus.] */
+            /* Codes_SRS_IOTHUBCLIENT_01_024: [Otherwise, IoTHubClient_GetSendStatus shall return the result of IoTHubClient_LL_GetSendStatus.] */
+            result = IoTHubClient_LL_GetSendStatus(iotHubClientInstance->IoTHubClientLLHandle, iotHubClientStatus);
+
+            /* Codes_SRS_IOTHUBCLIENT_01_033: [IoTHubClient_GetSendStatus shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+            (void)Unlock(iotHubClientInstance->LockHandle);
+        }
+    }
+
+    return result;
+}
+
+IOTHUB_CLIENT_RESULT IoTHubClient_Mod_SetMessageCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback)
+{
+    IOTHUB_CLIENT_RESULT result;
+
+    if (iotHubClientHandle == NULL)
+    {
+        /* Codes_SRS_IOTHUBCLIENT_01_016: [If iotHubClientHandle is NULL, IoTHubClient_SetMessageCallback shall return IOTHUB_CLIENT_INVALID_ARG.] */
+        result = IOTHUB_CLIENT_INVALID_ARG;
+        LogError("NULL iothubClientHandle\r\n");
+    }
+    else
+    {
+        IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle;
+
+        /* Codes_SRS_IOTHUBCLIENT_01_027: [IoTHubClient_SetMessageCallback shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+        if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK)
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_028: [If acquiring the lock fails, IoTHubClient_SetMessageCallback shall return IOTHUB_CLIENT_ERROR.] */
+            result = IOTHUB_CLIENT_ERROR;
+            LogError("Could not acquire lock\r\n");
+        }
+        else
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_014: [IoTHubClient_SetMessageCallback shall start the worker thread if it was not previously started.] */
+            StartWorkerThreadIfNeeded_Mod(iotHubClientInstance);
+
+            if (iotHubClientInstance->ThreadHandle == NULL)
+            {
+                /* Codes_SRS_IOTHUBCLIENT_01_015: [If starting the thread fails, IoTHubClient_SetMessageCallback shall return IOTHUB_CLIENT_ERROR.] */
+                result = IOTHUB_CLIENT_ERROR;
+                LogError("Could not start worker thread\r\n");
+            }
+            else
+            {
+                /* Codes_SRS_IOTHUBCLIENT_01_017: [IoTHubClient_SetMessageCallback shall call IoTHubClient_LL_SetMessageCallback, while passing the IoTHubClient_LL handle created by IoTHubClient_Create and the parameters messageCallback and userContextCallback.] */
+                result = IoTHubClient_LL_SetMessageCallback(iotHubClientInstance->IoTHubClientLLHandle, messageCallback, userContextCallback);
+            }
+
+            /* Codes_SRS_IOTHUBCLIENT_01_027: [IoTHubClient_SetMessageCallback shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+            Unlock(iotHubClientInstance->LockHandle);
+        }
+    }
+
+    return result;
+}
+
+IOTHUB_CLIENT_RESULT IoTHubClient_Mod_SetSendCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_SEND_CALLBACK sendCallback, void* userContextCallback)
+{
+    IOTHUB_CLIENT_RESULT result;
+
+    if (iotHubClientHandle == NULL)
+    {
+        /* Codes_SRS_IOTHUBCLIENT_01_016: [If iotHubClientHandle is NULL, IoTHubClient_SetMessageCallback shall return IOTHUB_CLIENT_INVALID_ARG.] */
+        result = IOTHUB_CLIENT_INVALID_ARG;
+        LogError("NULL iothubClientHandle\r\n");
+    }
+    else
+    {
+        IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle;
+
+        /* Codes_SRS_IOTHUBCLIENT_01_027: [IoTHubClient_SetMessageCallback shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+        if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK)
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_028: [If acquiring the lock fails, IoTHubClient_SetMessageCallback shall return IOTHUB_CLIENT_ERROR.] */
+            result = IOTHUB_CLIENT_ERROR;
+            LogError("Could not acquire lock\r\n");
+        }
+        else
+        {
+            iotHubClientInstance->SendCallback = sendCallback;
+            iotHubClientInstance->SendContext = userContextCallback;
+
+            /* Codes_SRS_IOTHUBCLIENT_01_027: [IoTHubClient_SetMessageCallback shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+            Unlock(iotHubClientInstance->LockHandle);
+        }
+    }
+
+    return result;
+}
+
+IOTHUB_CLIENT_RESULT IoTHubClient_Mod_GetLastMessageReceiveTime(IOTHUB_CLIENT_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime)
+{
+    IOTHUB_CLIENT_RESULT result;
+
+    if (iotHubClientHandle == NULL)
+    {
+        /* Codes_SRS_IOTHUBCLIENT_01_020: [If iotHubClientHandle is NULL, IoTHubClient_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_INVALID_ARG.] */
+        result = IOTHUB_CLIENT_INVALID_ARG;
+        LogError("NULL iothubClientHandle\r\n");
+    }
+    else
+    {
+        IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle;
+
+        /* Codes_SRS_IOTHUBCLIENT_01_035: [IoTHubClient_GetLastMessageReceiveTime shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+        if (Lock(iotHubClientInstance->LockHandle) != LOCK_OK)
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_036: [If acquiring the lock fails, IoTHubClient_GetLastMessageReceiveTime shall return IOTHUB_CLIENT_ERROR.] */
+            result = IOTHUB_CLIENT_ERROR;
+            LogError("Could not acquire lock\r\n");
+        }
+        else
+        {
+            /* Codes_SRS_IOTHUBCLIENT_01_019: [IoTHubClient_GetLastMessageReceiveTime shall call IoTHubClient_LL_GetLastMessageReceiveTime, while passing the IoTHubClient_LL handle created by IoTHubClient_Create and the parameter lastMessageReceiveTime.] */
+            /* Codes_SRS_IOTHUBCLIENT_01_021: [Otherwise, IoTHubClient_GetLastMessageReceiveTime shall return the result of IoTHubClient_LL_GetLastMessageReceiveTime.] */
+            result = IoTHubClient_LL_GetLastMessageReceiveTime(iotHubClientInstance->IoTHubClientLLHandle, lastMessageReceiveTime);
+
+            /* Codes_SRS_IOTHUBCLIENT_01_035: [IoTHubClient_GetLastMessageReceiveTime shall be made thread-safe by using the lock created in IoTHubClient_Create.] */
+            Unlock(iotHubClientInstance->LockHandle);
+        }
+    }
+
+    return result;
+}
+
+IOTHUB_CLIENT_RESULT IoTHubClient_Mod_SetOption(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* optionName, const void* value)
+{
+    IOTHUB_CLIENT_RESULT result;
+    /*Codes_SRS_IOTHUBCLIENT_02_034: [If parameter iotHubClientHandle is NULL then IoTHubClient_SetOption shall return IOTHUB_CLIENT_INVALID_ARG.] */
+    if (
+        (iotHubClientHandle == NULL) ||
+        (optionName == NULL) ||
+        (value == NULL)
+        )
+    {
+        result = IOTHUB_CLIENT_INVALID_ARG;
+        LogError("invalid arg (NULL)r\n");
+    }
+    else
+    {
+        IOTHUB_CLIENT_INSTANCE* iotHubClientInstance = (IOTHUB_CLIENT_INSTANCE*)iotHubClientHandle;
+        /*Codes_SRS_IOTHUBCLIENT_02_038: [If optionName doesn't match one of the options handled by this module then IoTHubClient_SetOption shall call IoTHubClient_LL_SetOption passing the same parameters and return what IoTHubClient_LL_SetOption returns.] */
+        result = IoTHubClient_LL_SetOption(iotHubClientInstance->IoTHubClientLLHandle, optionName, value);
+
+        if (result != IOTHUB_CLIENT_OK)
+        {
+            LogError("IoTHubClient_LL_SetOption failed\r\n");
+        }
+    }
+    return result;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/Utils/iothub_mod_client.h	Tue Jan 26 20:19:43 2016 +0000
@@ -0,0 +1,195 @@
+// Copyright (c) Microsoft. All rights reserved.
+// Licensed under the MIT license. See LICENSE file in the project root for full license information.
+
+// iothub_mod_client.h : modified version of iothub_client.h
+
+/** @file iothub_client.h
+*   @brief Extends the IoTHubCLient_LL module with additional features.
+*
+*   @details IoTHubClient is a module that extends the IoTHubCLient_LL
+*            module with 2 features:
+*               - scheduling the work for the IoTHubCLient from a  
+*                 thread, so that the user does not need to create their
+*                 own thread
+*               - thread-safe APIs
+*/
+
+#ifndef IOTHUB_MOD_CLIENT_H
+
+#include "iothub_client_ll.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+    typedef void* IOTHUB_CLIENT_HANDLE;
+
+    typedef void (*IOTHUB_CLIENT_SEND_CALLBACK)(IOTHUB_CLIENT_HANDLE iotHubClientHandle, void* userContextCallback);
+
+    /**
+    * @brief    Creates a IoT Hub client for communication with an existing
+    *           IoT Hub using the specified connection string parameter.
+    *
+    * @param    connectionString    Pointer to a character string
+    * @param    protocol            Function pointer for protocol implementation
+    *
+    *           Sample connection string:
+    *               <blockquote>
+    *                   <pre>HostName=[IoT Hub name goes here].[IoT Hub suffix goes here, e.g., private.azure-devices-int.net];DeviceId=[Device ID goes here];SharedAccessKey=[Device key goes here];</pre>
+    *               </blockquote>
+    *
+    * @return   A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when
+    *           invoking other functions for IoT Hub client and @c NULL on failure.
+    */
+    extern IOTHUB_CLIENT_HANDLE IoTHubClient_Mod_CreateFromConnectionString(const char* connectionString, IOTHUB_CLIENT_TRANSPORT_PROVIDER protocol);
+
+    /**
+    * @brief    Creates a IoT Hub client for communication with an existing IoT
+    *           Hub using the specified parameters.
+    *
+    * @param    config  Pointer to an @c IOTHUB_CLIENT_CONFIG structure
+    *
+    *           The API does not allow sharing of a connection across multiple
+    *           devices. This is a blocking call.
+    *
+    * @return   A non-NULL @c IOTHUB_CLIENT_HANDLE value that is used when
+    *           invoking other functions for IoT Hub client and @c NULL on failure.
+    */
+    extern IOTHUB_CLIENT_HANDLE IoTHubClient_Mod_Create(const IOTHUB_CLIENT_CONFIG* config);
+
+    /**
+    * @brief    Disposes of resources allocated by the IoT Hub client. This is a
+    *           blocking call.
+    *
+    * @param    iotHubClientHandle  The handle created by a call to the create function.
+    */
+    extern void IoTHubClient_Mod_Destroy(IOTHUB_CLIENT_HANDLE iotHubClientHandle);
+
+    /**
+    * @brief    Asynchronous call to send the message specified by @p eventMessageHandle.
+    *
+    * @param    iotHubClientHandle          The handle created by a call to the create function.
+    * @param    eventMessageHandle          The handle to an IoT Hub message.
+    * @param    eventConfirmationCallback   The callback specified by the device for receiving
+    *                                       confirmation of the delivery of the IoT Hub message.
+    *                                       This callback can be expected to invoke the
+    *                                       ::IoTHubClient_SendEventAsync function for the
+    *                                       same message in an attempt to retry sending a failing
+    *                                       message. The user can specify a @c NULL value here to
+    *                                       indicate that no callback is required.
+    * @param    userContextCallback         User specified context that will be provided to the
+    *                                       callback. This can be @c NULL.
+    *
+    *           @b NOTE: The application behavior is undefined if the user calls
+    *           the ::IoTHubClient_Destroy function from within any callback.
+    *
+    * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    */
+    extern IOTHUB_CLIENT_RESULT IoTHubClient_Mod_SendEventAsync(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_MESSAGE_HANDLE eventMessageHandle, IOTHUB_CLIENT_EVENT_CONFIRMATION_CALLBACK eventConfirmationCallback, void* userContextCallback);
+
+    /**
+    * @brief    This function returns the current sending status for IoTHubClient.
+    *
+    * @param    iotHubClientHandle      The handle created by a call to the create function.
+    * @param    iotHubClientStatus      The sending state is populated at the address pointed
+    *                                   at by this parameter. The value will be set to
+    *                                   @c IOTHUBCLIENT_SENDSTATUS_IDLE if there is currently
+    *                                   no item to be sent and @c IOTHUBCLIENT_SENDSTATUS_BUSY
+    *                                   if there are.
+    *
+    * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    */
+    extern IOTHUB_CLIENT_RESULT IoTHubClient_Mod_GetSendStatus(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_STATUS *iotHubClientStatus);
+
+    /**
+    * @brief    Sets up the message callback to be invoked when IoT Hub issues a
+    *           message to the device. This is a blocking call.
+    *
+    * @param    iotHubClientHandle          The handle created by a call to the create function.
+    * @param    messageCallback             The callback specified by the device for receiving
+    *                                       messages from IoT Hub.
+    * @param    userContextCallback         User specified context that will be provided to the
+    *                                       callback. This can be @c NULL.
+    *
+    *           @b NOTE: The application behavior is undefined if the user calls
+    *           the ::IoTHubClient_Destroy function from within any callback.
+    *
+    * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    */
+    extern IOTHUB_CLIENT_RESULT IoTHubClient_Mod_SetMessageCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_MESSAGE_CALLBACK_ASYNC messageCallback, void* userContextCallback);
+
+    /**
+    * @brief    Sets up the  callback to be invoked from IoT Hub worker thread.
+    *           Can call SendAsync from here without blocking on lock. 
+    *
+    * @param    iotHubClientHandle          The handle created by a call to the create function.
+    * @param    sendCallback                The callback specified by the device for sending
+    *                                       messages to IoT Hub.
+    * @param    userContextCallback         User specified context that will be provided to the
+    *                                       callback. This can be @c NULL.
+    *
+    *           @b NOTE: The application behavior is undefined if the user calls
+    *           the ::IoTHubClient_Destroy function from within any callback.
+    *
+    * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    */
+    extern IOTHUB_CLIENT_RESULT IoTHubClient_Mod_SetSendCallback(IOTHUB_CLIENT_HANDLE iotHubClientHandle, IOTHUB_CLIENT_SEND_CALLBACK messageCallback, void* userContextCallback);
+
+    /**
+    * @brief    This function returns in the out parameter @p lastMessageReceiveTime
+    *           what was the value of the @c time function when the last message was
+    *           received at the client.
+    *
+    * @param    iotHubClientHandle              The handle created by a call to the create function.
+    * @param    lastMessageReceiveTime          Out parameter containing the value of @c time function
+    *                                           when the last message was received.
+    *
+    * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    */
+    extern IOTHUB_CLIENT_RESULT IoTHubClient_Mod_GetLastMessageReceiveTime(IOTHUB_CLIENT_HANDLE iotHubClientHandle, time_t* lastMessageReceiveTime);
+
+    /**
+    * @brief    This API sets a runtime option identified by parameter @p optionName
+    *           to a value pointed to by @p value. @p optionName and the data type
+    *           @p value is pointing to are specific for every option.
+    *
+    * @param    iotHubClientHandle  The handle created by a call to the create function.
+    * @param    optionName          Name of the option.
+    * @param    value               The value.
+    *
+    *           The options that can be set via this API are:
+    *               - @b timeout - the maximum time in milliseconds a communication is
+    *                 allowed to use. @p value is a pointer to an @c unsigned @c int with
+    *                 the timeout value in milliseconds. This is only supported for the HTTP
+    *                 protocol as of now. When the HTTP protocol uses CURL, the meaning of
+    *                 the parameter is <em>total request time</em>. When the HTTP protocol uses
+    *                 winhttp, the meaning is the same as the @c dwSendTimeout and
+    *                 @c dwReceiveTimeout parameters of the
+    *                 <a href="https://msdn.microsoft.com/en-us/library/windows/desktop/aa384116(v=vs.85).aspx">
+    *                 WinHttpSetTimeouts</a> API.
+    *               - @b CURLOPT_LOW_SPEED_LIMIT - only available for HTTP protocol and only
+    *                 when CURL is used. It has the same meaning as CURL's option with the same
+    *                 name. @p value is pointer to a long.
+    *               - @b CURLOPT_LOW_SPEED_TIME - only available for HTTP protocol and only
+    *                 when CURL is used. It has the same meaning as CURL's option with the same
+    *                 name. @p value is pointer to a long.
+    *               - @b CURLOPT_FORBID_REUSE - only available for HTTP protocol and only
+    *                 when CURL is used. It has the same meaning as CURL's option with the same
+    *                 name. @p value is pointer to a long.
+    *               - @b CURLOPT_FRESH_CONNECT - only available for HTTP protocol and only
+    *                 when CURL is used. It has the same meaning as CURL's option with the same
+    *                 name. @p value is pointer to a long.
+    *               - @b CURLOPT_VERBOSE - only available for HTTP protocol and only
+    *                 when CURL is used. It has the same meaning as CURL's option with the same
+    *                 name. @p value is pointer to a long.
+    *
+    * @return   IOTHUB_CLIENT_OK upon success or an error code upon failure.
+    */
+    extern IOTHUB_CLIENT_RESULT IoTHubClient_Mod_SetOption(IOTHUB_CLIENT_HANDLE iotHubClientHandle, const char* optionName, const void* value);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* IOTHUB_CLIENT_H */