Duy tran / Mbed OS iot_water_monitor_v2

Dependencies:   easy-connect-v16 Watchdog FP MQTTPacket RecordType-v-16 watersenor_and_temp_code

Files at this revision

API Documentation at this revision

Comitter:
DuyLionTran
Date:
Thu Mar 08 13:13:53 2018 +0000
Parent:
42:3656e1b2f20a
Child:
44:8a99efffeb8e
Commit message:
* version 2.7.5 03-08-2018: DS18B20 & IWDG is being developed

Changed in this revision

Application/CommandExecution.cpp Show annotated file Show diff for this revision Revisions of this file
Application/CommandExecution.h Show annotated file Show diff for this revision Revisions of this file
Application/main.cpp Show annotated file Show diff for this revision Revisions of this file
Flash/flash_programming.cpp Show annotated file Show diff for this revision Revisions of this file
Flash/flash_programming.h Show annotated file Show diff for this revision Revisions of this file
JSON.lib Show diff for this revision Revisions of this file
JSON/Json.cpp Show annotated file Show diff for this revision Revisions of this file
JSON/Json.h Show annotated file Show diff for this revision Revisions of this file
JSON/jsmn.c Show annotated file Show diff for this revision Revisions of this file
JSON/jsmn.h Show annotated file Show diff for this revision Revisions of this file
MQTT.lib Show diff for this revision Revisions of this file
MQTT/FP.lib Show annotated file Show diff for this revision Revisions of this file
MQTT/MQTTAsync.h Show annotated file Show diff for this revision Revisions of this file
MQTT/MQTTClient.h Show annotated file Show diff for this revision Revisions of this file
MQTT/MQTTEthernet.h Show annotated file Show diff for this revision Revisions of this file
MQTT/MQTTLogging.h Show annotated file Show diff for this revision Revisions of this file
MQTT/MQTTPacket.lib Show annotated file Show diff for this revision Revisions of this file
MQTT/MQTTSocket.h Show annotated file Show diff for this revision Revisions of this file
MQTT/MQTTmbed.h Show annotated file Show diff for this revision Revisions of this file
NDefLib.lib Show diff for this revision Revisions of this file
NDefLib/Message.cpp Show annotated file Show diff for this revision Revisions of this file
NDefLib/Message.h Show annotated file Show diff for this revision Revisions of this file
NDefLib/NDefNfcTag.h Show annotated file Show diff for this revision Revisions of this file
NDefLib/Record.h Show annotated file Show diff for this revision Revisions of this file
NDefLib/RecordHeader.h Show annotated file Show diff for this revision Revisions of this file
NDefLib/RecordType.lib Show annotated file Show diff for this revision Revisions of this file
Sensor/ReadSensor.cpp Show annotated file Show diff for this revision Revisions of this file
Sensor/ReadSensor.h Show annotated file Show diff for this revision Revisions of this file
Simple-MQTT/SimpleMQTT.h Show annotated file Show diff for this revision Revisions of this file
jsonString.md Show annotated file Show diff for this revision Revisions of this file
mbed-os.lib Show annotated file Show diff for this revision Revisions of this file
mbed_app.json Show annotated file Show diff for this revision Revisions of this file
--- a/Application/CommandExecution.cpp	Sun Mar 04 07:58:23 2018 +0000
+++ b/Application/CommandExecution.cpp	Thu Mar 08 13:13:53 2018 +0000
@@ -21,4 +21,12 @@
 
 void CE_Calibrate() {
     isCalibrating = true;
+}
+
+void CE_SetAlarmRelays() {
+    
+}
+
+void CE_UpdateImmediately() {
+    
 }
\ No newline at end of file
--- a/Application/CommandExecution.h	Sun Mar 04 07:58:23 2018 +0000
+++ b/Application/CommandExecution.h	Thu Mar 08 13:13:53 2018 +0000
@@ -10,5 +10,7 @@
 void CE_HandleRelays(int Relay1State, int Relat2State, int Relat3State);
 void CE_SetRTCTime(uint32_t CurrentEpochTime);
 void CE_Calibrate();
+void CE_SetAlarmRelays();
+void CE_UpdateImmediately();
 
 #endif /* __COMMANDEXECUTION_H__ */
\ No newline at end of file
--- a/Application/main.cpp	Sun Mar 04 07:58:23 2018 +0000
+++ b/Application/main.cpp	Thu Mar 08 13:13:53 2018 +0000
@@ -8,6 +8,7 @@
   * version 2.6.5 	02-21-2018: developing calibration. Sensor read is completely ok
   * version 2.6.8 	02-27-2018: developing DO calibration. DS18B20 temperature sensor is added
   * version 2.7 	03-04-2018: DO calibration complete. IWDG is added
+  * version 2.7.5 	03-08-2018: DS18B20 & IWDG is being developed
   */
 
 /***************************************************************
@@ -88,20 +89,20 @@
  * Unity function declarations
  ***************************************************************/ 
 void ReadAllFlashValues() {
-	DataStruct.ADC_PHVal = 0;
+	DataStruct.ADC_TEMPVal = 0;
 	DataStruct.ADC_DOVal = 0;
-	DataStruct.SENSOR_PHVal = 0;
+	DataStruct.SENSOR_TEMPVal = 25.0;
 	DataStruct.SENSOR_DOVal = 0;
-	DataStruct.RELAY_State_1			= FP_ReadValue(RELAY1_ADDRESS);
-	DataStruct.RELAY_State_2			= FP_ReadValue(RELAY2_ADDRESS);
-	DataStruct.RELAY_State_3			= FP_ReadValue(RELAY3_ADDRESS);
-	DataStruct.CONFIG_Mode				= FP_ReadValue(MODE_ADDRESS);
-	DataStruct.CONFIG_MinOxi			= FP_ReadValue(MIN_OXI_ADDRESS);
-	DataStruct.CONFIG_MaxOxi			= FP_ReadValue(MAX_OXI_ADDRESS);
-	DataStruct.CONFIG_UploadInterval	= FP_ReadValue(UPLOAD_PERIOD_ADDRESS);
+	DataStruct.RELAY_State_1				= FP_ReadValue(RELAY1_ADDRESS);
+	DataStruct.RELAY_State_2				= FP_ReadValue(RELAY2_ADDRESS);
+	DataStruct.RELAY_State_3				= FP_ReadValue(RELAY3_ADDRESS);
+	DataStruct.CONFIG_Mode					= FP_ReadValue(MODE_ADDRESS);
+	DataStruct.CONFIG_OxyThreshold			= FP_ReadValue(OXY_THRES_ADDRESS);
+	DataStruct.CONFIG_TemperatureThreshold	= FP_ReadValue(TEMP_THRES_ADDRESS);
+	DataStruct.CONFIG_UploadInterval		= FP_ReadValue(UPLOAD_PERIOD_ADDRESS);
 	printf("All values: %d %d %d %d %d %d\r\n", DataStruct.RELAY_State_1, DataStruct.RELAY_State_2,
-	                                            DataStruct.CONFIG_Mode,   DataStruct.CONFIG_MinOxi, 	
-	                                            DataStruct.CONFIG_MaxOxi, DataStruct.CONFIG_UploadInterval);
+	                                            DataStruct.CONFIG_Mode,   DataStruct.CONFIG_OxyThreshold, 	
+	                                            DataStruct.CONFIG_TemperatureThreshold, DataStruct.CONFIG_UploadInterval);
 	CE_HandleRelays(DataStruct.RELAY_State_1, DataStruct.RELAY_State_2, DataStruct.RELAY_State_3);
 }
 
@@ -145,6 +146,7 @@
  ***************************************************************/ 
 int main() {   
    	pc.baud(115200);
+   	printf("PROGRAM STARTS\r\n");
 	enableESP();
    	UploadTimer.start();
    	
@@ -152,7 +154,6 @@
    		ReadAnalogTimer.start();
    	#endif
    	
-   	DisplayDO.attach(&SensorRun, PROCESS_SENSOR_VALUE_S);
    	BinkLEDStart();
    	
    	lastRead = 0;
@@ -174,7 +175,8 @@
 //	    while (true)
 //	    	wait(1.0); // Permanent failures - don't retry
 	}
-
+	
+	DisplayDO.attach(&SensorRun, PROCESS_SENSOR_VALUE_S);
 	ReadAllFlashValues();
 	SENSOR_ReadDoCharacteristicValues();
    	myled = 1;   
--- a/Flash/flash_programming.cpp	Sun Mar 04 07:58:23 2018 +0000
+++ b/Flash/flash_programming.cpp	Thu Mar 08 13:13:53 2018 +0000
@@ -128,7 +128,7 @@
     return PASSED;
 }
 
-int FP_WriteConfigValues(uint8_t Mode, uint8_t MinOxi, uint8_t MaxOxi, uint32_t UploadPeriod) {
+int FP_WriteConfigValues(uint8_t Mode ,uint8_t OxyThres, uint8_t TempThres, uint32_t UploadPeriod) {
     uint8_t  CurrentPage    = FP_GetPage(CONF_BASE_ADDRESS);    
     uint32_t CurrentAddress = MODE_ADDRESS;
     EraseInitStruct.TypeErase   = FLASH_TYPEERASE_PAGES;
@@ -142,8 +142,8 @@
     HAL_FLASH_Unlock();  
     
     if ((FP_ReadValue(MODE_ADDRESS) == Mode) &&
-        (FP_ReadValue(MIN_OXI_ADDRESS) == MinOxi) &&
-        (FP_ReadValue(MAX_OXI_ADDRESS) == MaxOxi) &&
+        (FP_ReadValue(OXY_THRES_ADDRESS) == OxyThres) &&
+        (FP_ReadValue(TEMP_THRES_ADDRESS) == TempThres) &&
         (FP_ReadValue(UPLOAD_PERIOD_ADDRESS) == UploadPeriod)) {
         printf("Configuration values don't change, no need to write\r\n");
         HAL_FLASH_Lock();
@@ -161,18 +161,18 @@
                                     printf("Write Mode failed, error num %d\r\n", HAL_FLASH_GetError());
                                 }
             break;
-            case (MIN_OXI_ADDRESS): if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, CurrentAddress, MinOxi) == HAL_OK) {
-                                        printf("Write MinOxi OK\r\n");
+            case (OXY_THRES_ADDRESS): if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, CurrentAddress, OxyThres) == HAL_OK) {
+                                        printf("Write OxyThres OK\r\n");
                                     }
                                     else {
-                                        printf("Write MinOxi failed, error num %d\r\n", HAL_FLASH_GetError());
+                                        printf("Write OxyThres failed, error num %d\r\n", HAL_FLASH_GetError());
                                     }
             break;
-            case (MAX_OXI_ADDRESS): if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, CurrentAddress, MaxOxi) == HAL_OK) {
-                                        printf("Write MaxOxi OK\r\n");
+            case (TEMP_THRES_ADDRESS): if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, CurrentAddress, TempThres) == HAL_OK) {
+                                        printf("Write TempThres OK\r\n");
                                     }
                                     else {
-                                        printf("Write MaxOxi failed, error num %d\r\n", HAL_FLASH_GetError());
+                                        printf("Write TempThres failed, error num %d\r\n", HAL_FLASH_GetError());
                                     }
             break;
             case (UPLOAD_PERIOD_ADDRESS): if (HAL_FLASH_Program(FLASH_TYPEPROGRAM_DOUBLEWORD, CurrentAddress, UploadPeriod) == HAL_OK) {
@@ -199,20 +199,20 @@
                                     return FAILED;
                                  }
             break;
-            case (MIN_OXI_ADDRESS): if (FP_ReadValue(CurrentAddress) == MinOxi) {
-                                        printf("Read back MinOxi: %d\r\n", FP_ReadValue(CurrentAddress));  
+            case (OXY_THRES_ADDRESS): if (FP_ReadValue(CurrentAddress) == OxyThres) {
+                                        printf("Read back OxyThres: %d\r\n", FP_ReadValue(CurrentAddress));  
                                     }
                                     else {
-                                        printf("Write MinOxi failed, wrong read back value\r\n");
+                                        printf("Write OxyThres failed, wrong read back value\r\n");
                                         HAL_FLASH_Lock();
                                         return FAILED;
                                     }
             break;
-            case (MAX_OXI_ADDRESS): if (FP_ReadValue(CurrentAddress) == MaxOxi) {
-                                        printf("Read back MaxOxi: %d\r\n", FP_ReadValue(CurrentAddress));  
+            case (TEMP_THRES_ADDRESS): if (FP_ReadValue(CurrentAddress) == TempThres) {
+                                        printf("Read back TempThres: %d\r\n", FP_ReadValue(CurrentAddress));  
                                     }
                                     else {
-                                        printf("Write MaxOxi failed, wrong read back value\r\n");
+                                        printf("Write TempThres failed, wrong read back value\r\n");
                                         HAL_FLASH_Lock();
                                         return FAILED;
                                     }
--- a/Flash/flash_programming.h	Sun Mar 04 07:58:23 2018 +0000
+++ b/Flash/flash_programming.h	Thu Mar 08 13:13:53 2018 +0000
@@ -144,9 +144,9 @@
 #define RELAY3_ADDRESS          (RELAY2_ADDRESS     + STEP_ADDRESS)
 
 #define MODE_ADDRESS            (CONF_BASE_ADDRESS + STEP_ADDRESS)
-#define MIN_OXI_ADDRESS         (MODE_ADDRESS      + STEP_ADDRESS)
-#define MAX_OXI_ADDRESS         (MIN_OXI_ADDRESS   + STEP_ADDRESS)
-#define UPLOAD_PERIOD_ADDRESS   (MAX_OXI_ADDRESS   + STEP_ADDRESS)
+#define OXY_THRES_ADDRESS       (MODE_ADDRESS      + STEP_ADDRESS)
+#define TEMP_THRES_ADDRESS      (OXY_THRES_ADDRESS   + STEP_ADDRESS)
+#define UPLOAD_PERIOD_ADDRESS   (TEMP_THRES_ADDRESS   + STEP_ADDRESS)
 
 #define SAT_DO_VOLT_ADDRESS     (CALIB_BASE_ADDRESS + STEP_ADDRESS)
 
@@ -190,12 +190,12 @@
 /**
  * @brief  Write the value to control the switches into the flash memory
  * @param  Mode: Current working mode of the device
- * @param  MinOxi: Current working mode of the device
- * @param  MaxOxi: Current working mode of the device
+ * @param  OxyThres: Current working mode of the device
+ * @param  TempThres: Current working mode of the device
  * @param  UploadPeriod: Current working mode of the device
  * @retval FLASH result
  */
-int FP_WriteConfigValues(uint8_t Mode ,uint8_t MinOxi, uint8_t MaxOxi, uint32_t UploadPeriod);
+int FP_WriteConfigValues(uint8_t Mode ,uint8_t OxyThres, uint8_t TempThres, uint32_t UploadPeriod);
 
 /**
  * @brief  Write the calibration value to the flash memory
--- a/JSON.lib	Sun Mar 04 07:58:23 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-http://os.mbed.com/teams/Night-Crue/code/JSON/#8aa4d0e98eb0
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/JSON/Json.cpp	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,241 @@
+/* Json.cpp */
+/* Original Author: Faheem Inayat
+ * Created by "Night Crue" Team @ TechShop San Jose, CA
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#include "Json.h"
+
+Json::Json ( const char * jsonString, size_t length, unsigned int maxTokens )
+        : maxTokenCount ( maxTokens ), source ( jsonString ), sourceLength ( length )
+{
+    jsmn_parser parser;
+    tokens = new jsmntok_t [ maxTokenCount ];
+
+    jsmn_init ( &parser );
+    tokenCount = jsmn_parse ( &parser, jsonString, length, tokens, maxTokenCount );
+}
+
+Json::Json ( const Json & )
+        : maxTokenCount ( 0 ), source ( NULL ), sourceLength ( 0 )
+{
+    tokenCount = 0;
+    tokens = NULL;
+}
+
+Json::~Json ()
+{
+    delete [] tokens;
+}
+
+int Json::findKeyIndex ( const char * key, const int &startingAt ) const
+{
+    int retVal = -1;
+
+    int i = startingAt + 1;
+    if ( i < 0 ) {
+        i = 0;
+    }
+    
+    for ( ; i < tokenCount; i++ )
+    {
+        jsmntok_t t = tokens [ i ];
+
+        if ( t.type == JSMN_KEY )
+        {
+            size_t keyLength = (size_t) ( t.end - t.start );
+            if ( ( strlen ( key ) == keyLength ) && ( strncmp ( source + t.start, key, keyLength ) == 0 ) )
+            {
+                retVal = i;
+                break;
+            }
+        }
+    }
+
+    return retVal;
+}
+
+int Json::findKeyIndexIn ( const char * key, const int &parentIndex ) const
+{
+    int retVal = -1;
+
+    if ( isValidToken ( parentIndex ) )
+    {
+        for ( int i = parentIndex + 1; i < tokenCount; i++ )
+        {
+            jsmntok_t t = tokens [ i ];
+
+            if ( t.end >= tokens [ parentIndex ].end )
+            {
+                break;
+            }
+
+            if ( ( t.type == JSMN_KEY ) && ( t.parent == parentIndex ) )
+            {
+                size_t keyLength = (size_t) ( t.end - t.start );
+                if ( ( strlen ( key ) == keyLength ) && ( strncmp ( source + t.start, key, keyLength ) == 0 ) )
+                {
+                    retVal = i;
+                    break;
+                }
+            }
+        }
+    }
+
+    return retVal;
+}
+
+int Json::findChildIndexOf ( const int &parentIndex, const int &startingAt ) const
+{
+    int retVal = -1;
+
+    if ( isValidToken ( parentIndex ) )
+    {
+
+        jsmntype_t type = tokens [ parentIndex ].type;
+        if ( ( type == JSMN_KEY ) || ( type == JSMN_OBJECT ) || ( type == JSMN_ARRAY ) )
+        {
+            int i = startingAt + 1;
+            if ( startingAt < 0 )
+            {
+                i = 0;
+            }
+
+            for ( i += parentIndex; i < tokenCount; i++ )
+            {
+                if ( tokens [ i ].parent == parentIndex )
+                {
+                    retVal = i;
+                    break;
+                }
+            }
+        }
+    }
+
+    return retVal;
+}
+
+bool Json::matches ( const int & tokenIndex, const char * value ) const
+{
+    bool retVal = false;
+    
+    if ( isValidToken ( tokenIndex ) )
+    {
+        jsmntok_t token = tokens [ tokenIndex ];
+        retVal = ( strncmp ( source + token.start, value, ( token.end - token.start ) ) == 0 );
+    }
+    
+    return retVal;
+}
+
+int Json::tokenIntegerValue ( const int tokenIndex, int &returnValue ) const
+{
+    int retVal = -1;
+    
+    if ( type ( tokenIndex ) == JSMN_PRIMITIVE )
+    {
+        int len = tokenLength ( tokenIndex );
+        char * tok = new char [ len + 1 ];
+        strncpy ( tok, tokenAddress ( tokenIndex ), len );
+        tok [ len ] = 0;
+        returnValue = atoi ( tok );
+        delete [] tok;
+        retVal = 0;
+    }
+    return retVal;
+}
+
+int Json::tokenNumberValue ( const int tokenIndex, float &returnValue ) const
+{
+    int retVal = -1;
+    
+    if ( type ( tokenIndex ) == JSMN_PRIMITIVE )
+    {
+        int len = tokenLength ( tokenIndex );
+        char * tok = new char [ len + 1 ];
+        strncpy ( tok, tokenAddress ( tokenIndex ), len );
+        tok [ len ] = 0;
+        returnValue = atof ( tok );
+        delete [] tok;
+        retVal = 0;
+    }
+    
+    return retVal;
+}
+
+int Json::tokenBooleanValue ( const int tokenIndex, bool &returnValue ) const
+{
+    int retVal = -1;
+    
+    if ( type ( tokenIndex ) == JSMN_PRIMITIVE )
+    {
+        returnValue = matches ( tokenIndex, "true" );
+        retVal = 0;
+    }
+
+    return retVal;
+}
+
+char * Json::unescape ( char * jsonString )
+{
+    if ( jsonString != NULL )
+    {
+        int stringIndex = 0;
+        int indentLevel = 0;
+        int quoteCount = 0;
+        for ( int i = 0; jsonString [ i ] != 0; i ++ )
+        {
+            switch ( jsonString [ i ] )
+            {
+                case '{':
+                    indentLevel ++;
+                    break;
+                
+                case '}':
+                    indentLevel --;
+                    if ( indentLevel == 0 ) {
+                        // Just close and return the first valid JSON object.  No need to handle complex cases.
+                        jsonString [ stringIndex ++ ] = '}';
+                        jsonString [ stringIndex ] = 0;
+                        return jsonString;
+                    }
+                    break;
+                    
+                case '\\':
+                    i ++;
+                    break;
+                    
+                case '"':
+                    quoteCount ++;
+                    break;
+            }
+            
+            if ( indentLevel > 0 )
+            {
+                if ( quoteCount == 0 ) {
+                    return jsonString; //No need to unescape.  JsonString needs to be already escaped
+                }
+                jsonString [ stringIndex ++ ] = jsonString [ i ];
+            }
+        }
+        jsonString [ stringIndex ] = 0;
+    }
+    
+    return jsonString;
+}
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/JSON/Json.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,500 @@
+/* Json.h */
+/* Original Author: Faheem Inayat
+ * Created by "Night Crue" Team @ TechShop San Jose, CA
+ *
+ * MIT License
+ *
+ * Permission is hereby granted, free of charge, to any person obtaining a copy of this software
+ * and associated documentation files (the "Software"), to deal in the Software without restriction,
+ * including without limitation the rights to use, copy, modify, merge, publish, distribute,
+ * sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is
+ * furnished to do so, subject to the following conditions:
+ *
+ * The above copyright notice and this permission notice shall be included in all copies or
+ * substantial portions of the Software.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING
+ * BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
+ * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
+ * DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+ * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+ */
+
+#ifndef __JSON_LIB_CLASS_H_
+#define __JSON_LIB_CLASS_H_
+
+#include "jsmn.h"
+#include <stdlib.h>
+#include <string.h>
+
+/**
+ * C++ JSON wrapper over JSMN lib (https://github.com/zserge/jsmn).
+ *
+ * This C++ Class is a set of common tools/procedures as a C++ wrapper over JSMN
+ * JSON parser library.  It is intended to provide the boiler-plate code, with
+ * intentions to reduce code clutter, in more of C++ fashion.
+ *
+ * In contrast to original library, Json is intended to work strictly with valid
+ * JSON structures.  Non-standard JSON structures should result in an error.
+ *
+ * This class works explicitly on the indices returned by underlying JSMN
+ * library.  In the scope of this class, its function parameters, return types,
+ * and documentation, the term 'index' will always mean the index of JSMN 
+ * tokens, parsed by the Json constructor, unless and until explicitly mentioned
+ * otherwise.
+ */
+ 
+ /*
+    Example:
+    
+    Let's say we have to parse the samle JSON:
+    
+    {
+        "team": "Night Crue",
+        "company": "TechShop",
+        "city": "San Jose",
+        "state": "California",
+        "country": "USA",
+        "zip": 95113,
+        "active": true,
+        "members":
+        [
+            {
+                "firstName": "John",
+                "lastName": "Smith",
+                "active": false,
+                "hours": 18.5,
+                "age": 21
+            },
+            {
+                "firstName": "Foo",
+                "lastName": "Bar",
+                "active": true,
+                "hours": 25,
+                "age": 21
+            },
+            {
+                "firstName": "Peter",
+                "lastName": "Jones",
+                "active": false
+            }
+        ]
+    }
+    
+    which without the "white spaces" will look like: {"team":"Night Crue","company":"TechShop","city":"San Jose","state":"California","country":"USA","zip":95113,"active":true,"members":[{"firstName":"John","lastName":"Smith","active":false,"hours":18.5,"age":21},{"firstName":"Foo","lastName":"Bar","active":true,"hours":25,"age":21},{"firstName":"Peter","lastName":"Jones","active":false}]}
+    
+    Anyways, this class doesn't care about the formatting of JSON, however, it
+    DOES care about the validity of JSON.  So here's a sample code to parse and
+    extract values from this JSON structure.
+    
+    @code
+    
+    void main ()
+    {
+        // Note that the JSON object is 'escaped'.  One doesn't get escaped JSON
+        // directly from the webservice, if the response type is APPLICATION/JSON
+        // Just a little thing to keep in mind.
+        const char * jsonSource = "{\"team\":\"Night Crue\",\"company\":\"TechShop\",\"city\":\"San Jose\",\"state\":\"California\",\"country\":\"USA\",\"zip\":95113,\"active\":true,\"members\":[{\"firstName\":\"John\",\"lastName\":\"Smith\",\"active\":false,\"hours\":18.5,\"age\":21},{\"firstName\":\"Foo\",\"lastName\":\"Bar\",\"active\":true,\"hours\":25,\"age\":21},{\"firstName\":\"Peter\",\"lastName\":\"Jones\",\"active\":false}]}";
+        
+        Json json ( jsonSource, strlen ( jsonSource ) );
+        
+        if ( !json.isValidJson () )
+        {
+            logError ( "Invalid JSON: %s", jsonSource );
+            return;
+        }
+
+        if ( json.type (0) != JSMN_OBJECT )
+        {
+            logError ( "Invalid JSON.  ROOT element is not Object: %s", jsonSource );
+            return;
+        }
+        
+        // Let's get the value of key "city" in ROOT object, and copy into 
+        // cityValue
+        char cityValue [ 32 ];
+        
+        logInfo ( "Finding \"city\" Key ... " );
+        // ROOT object should have '0' tokenIndex, and -1 parentIndex
+        int cityKeyIndex = json.findKeyIndexIn ( "city", 0 );
+        if ( cityKeyIndex == -1 )
+        {
+            // Error handling part ...
+            logError ( "\"city\" does not exist ... do something!!" );
+        }
+        else
+        {
+            // Find the first child index of key-node "city"
+            int cityValueIndex = json.findChildIndexOf ( cityKeyIndex, -1 );
+            if ( cityValueIndex > 0 )
+            {
+                const char * valueStart  = json.tokenAddress ( cityValueIndex );
+                int          valueLength = json.tokenLength ( cityValueIndex );
+                strncpy ( cityValue, valueStart, valueLength );
+                cityValue [ valueLength ] = 0; // NULL-terminate the string
+                
+                //let's print the value.  It should be "San Jose"
+                logInfo ( "city: %s", cityValue );
+            }
+        }
+        
+        // More on this example to come, later.
+    }
+    
+    @endcode
+ */
+
+class Json
+{
+    private:
+        const unsigned int maxTokenCount;
+        const char * source;
+        const size_t sourceLength;
+        jsmntok_t * tokens;
+        int tokenCount;
+
+        // Copy COntructor is intentionally kept private to enforce the caller
+        // to use pointers/reference, and never pass-by-value
+        Json ( const Json & );
+
+    public:
+        /** The only constructor allowed.
+         As JSON object will create/allocate memory for its working, in favor of
+         small memory footprints, it is not allowed to be passed-by-value.  So
+         there is no copy- or default-constructor
+
+         @param jsonString char string containing JSON data
+         @param length length of the jsonString
+         @param maxTokens optional maximum count of Tokens. Default is 32.
+         */
+        Json ( const char * jsonString, size_t length, unsigned int maxTokens = 32 );
+
+
+        /** Although there is no virtual function to this class, destructor is
+         still made virtual, for just-in-case use.  Destructor will delete the
+         'tokens' array, created in constructor.
+         */
+        virtual ~Json ();
+
+
+        /** findKeyIndex will find and return the token index representing the
+         'Key' in underlying JSON object.  It is a strictly a linear key search
+         and will return the first occurrence, without the JSON node structure
+         semantics.  For search in a specific node, refer to #findKeyIndex
+
+         @param key a char string to find as a 'Key' in JSON structure.
+         @param startingAt the starting token-index for 'key' search.  The
+                search will NOT include this index, but instead will use the
+                next one as the starting point.  In case, a negative value is
+                passed, search will start from '0'.  It's caller's
+                responsibility to make sure what values they're passing.
+                Default value is set to '0', as the zero-th token index in any
+                valid JSON object should always be starting object brace '{'.
+                So default behavior is to always find the very first occurrence
+                of key as represented by 'key' parameter.
+
+         @return a non-zero positive integer, if a key is found in the source
+                 JSON.  If no key is found, -1 will be returned. There should be
+                 no '0' value returned in any valid case.
+         */
+        int findKeyIndex ( const char * key, const int &startingAt = 0 ) const;
+
+
+        /** findKeyIndexIn will find and return the token index representing the
+         'Key' in underlying JSON object node.  It is strictly a single-level
+         key search function, and will NOT look for the key in any child JSON
+         nodes (JSON Object/Array).
+
+         @param key a char string to find as a 'Key' in JSON structure.
+         @param parentIndex the starting token-index for 'key' search.  The
+                search will look for the key, only under the JSON node
+                represented by this parentIndex.  Default value is '0', making
+                the default behavior to look for only root-level keys.  The
+                valid value range is 0 to [parsedTokenCount()-1] both inclusive.
+
+         @return a non-zero positive integer, if a key is found in the source
+                 JSON.  If no key is found, -1 will be returned. There should be
+                 no '0' value returned in any valid case.
+         */
+        int findKeyIndexIn ( const char * key, const int &parentIndex = 0 ) const;
+
+
+        /** findChildIndexOf will find and return the token index representing
+         first child a JSON node represented by parentIndex (that is either a
+         Key, an Object, or an Array), and exists after the startingAt value.
+         This function is particularly handy in iterating over Array Objects, or
+         getting the index for JSON 'Value' of a JSON 'Key'.
+
+         @param parentIndex token index representing the parent node in JSON
+                source.  The valid value range is 0 to [parsedTokenCount()-1]
+                both inclusive.
+         @param startingAt describes the starting index of the nodes to search.
+                In other words, if caller wants to skip some nodes, they can
+                provide this value.  Default value is 0, which means search for
+                all nodes in the parent.
+
+         @return a non-zero positive integer, if the child node is found in 
+                 source JSON.  If no child is found, -1 will be returned. There
+                 should be no '0' value returned in any valid case.
+         */
+        int findChildIndexOf ( const int &parentIndex, const int &startingAt = 0 ) const;
+
+
+        /** matches will tell if the token data (either key or value) matches
+         with the value provided.  This function is particularly handy in
+         iterating over the keys, and finding a specific key, in an object.  The
+         comparison is case-sensitive. i.e. 'Apple' will NOT match with 'apple'.
+
+         @param tokenIndex representing the token to compare.  The valid value
+                range is 0 to [parsedTokenCount()-1] both inclusive.
+         @param value to compare the token data with.
+
+         @return true if the token data matches with value.  false will be
+                 returned either the value doesn't match OR the tokenIndex is
+                 not valid.
+         */
+        bool matches ( const int & tokenIndex, const char * value ) const;
+
+
+        /** parsedTokenCount will tell how many tokens have been parsed by JSMN
+         parser.  It is a utility function, for token validity.
+
+         @return non-negative integer number of tokens parsed by JSMN library.
+                 Negative value may be returned in case of error, but this
+                 behavior is not tested, yet.
+         */
+        inline int parsedTokenCount () const;
+
+
+        /** isValidJson will tell the caller if the parsed JSON was valid,
+         parsed, and accepted to further work on.
+
+         @return true if the JSON is valid, false otherwise.
+         */
+        inline bool isValidJson () const;
+
+
+        /** isValidToken will tell the caller if the tokenIndex is in valid
+         range.  The valid value range is 0 to [parsedTokenCount()-1] both
+         inclusive.
+
+         @param tokenIndex representing the token in the JSON source
+
+         @return true if the JSON is valid, false otherwise.
+         */
+        inline bool isValidToken ( const int tokenIndex ) const;
+
+
+        /** type will return the JSMN type represented by the tokenIndex.
+
+         @param tokenIndex representing the token in the JSON source.  The valid
+                value range is 0 to [parsedTokenCount()-1] both inclusive.
+
+         @return the type represented by tokenIndex.  In case of invalid
+                 tokenIndex, JSMN_UNDEFINED is returned.
+         */
+        inline jsmntype_t type ( const int tokenIndex ) const;
+
+
+        /** parent is a utility function to get the parent index of the
+         tokenIndex passed.
+
+         @param tokenIndex representing the token in the JSON source.  The valid
+                value range is 0 to [parsedTokenCount()-1] both inclusive.
+
+         @return the parentIndex if the node has a parent, and tokenIndex is a
+                 valid index.  In case of no parent, or invalid tokenIndex, -1
+                 is returned.
+         */
+        inline int parent ( const int tokenIndex ) const;
+
+
+        /** childCount returns the number of children sharing the same parent.
+         This utility function is handy for iterating over Arrays or Objects.
+
+         @param tokenIndex representing the token in the JSON source.  The valid
+                value range is 0 to [parsedTokenCount()-1] both inclusive.
+
+         @return non-negative integer representing the number of children
+                 tokenIndex node has.  0 is a valid number, in case the node has
+                 no child nodes.  -1 will be returned if the tokenIndex is not
+                 valid.
+        */
+        inline int childCount ( const int tokenIndex ) const;
+
+
+        /** tokenLength returns the number of characters a node takes up in JSON
+         source string.
+
+         @param tokenIndex representing the token in the JSON source.  The valid
+                value range is 0 to [parsedTokenCount()-1] both inclusive.
+
+         @return positive integer value representing the length of the token
+                 sub-string in the source JSON.  The 0 value is an invalid state
+                 and should never occur.  -1 will be returned in case of invalid
+                 tokenIndex.
+         */
+        inline int tokenLength ( const int tokenIndex ) const;
+
+
+        /** tokenAddress returns the pointer that marks as the start of token
+         in JSON source string.  This is a utility function for character/string
+         manipulation by the caller.
+
+         @param tokenIndex representing the token in the JSON source.  The valid
+                value range is 0 to [parsedTokenCount()-1] both inclusive.
+
+         @return a non-NULL pointer will be returned if tokenIndex is valid, -1
+                 otherwise.
+         */
+        inline const char * tokenAddress ( const int tokenIndex ) const;
+
+
+        /** tokenInterValue will convert the value as int represented by the
+         tokenIndex.  A typical use is that caller has found the Key-index, and
+         then has retrieved the Value-index (by using findChildIndexOf function)
+         , and now they want to read the value of Value-index, as integer value.
+
+         @param tokenIndex representing the "value" in the JSON source.  The
+                valid value range is 0 to [parsedTokenCount()-1] both inclusive.
+
+         @param returnValue is a return-parameter passed by reference to hold up
+                the integer value parsed by this function.  If the converted
+                value would be out of the range of representable values by an
+                int, it causes undefined behavior.  It is caller's
+                responsibility to check for these cases.
+
+         @return 0 if the operation is successful.  -1 if tokenIndex is invalid.
+        */
+        int tokenIntegerValue ( const int tokenIndex, int &returnValue ) const;
+
+
+        /** tokenNumberValue will convert the value as float represented by the
+         tokenIndex.  A typical use is that caller has found the Key-index, and
+         then has retrieved the Value-index (by using findChildIndexOf function)
+         , and now they want to read the value of Value-index, as floating-point
+         value.
+
+         @param tokenIndex representing the "value" in the JSON source.  The
+                valid value range is 0 to [parsedTokenCount()-1] both inclusive.
+
+         @param returnValue is a return-parameter passed by reference to hold up
+                the floating-point value parsed by this function.  If the
+                converted value would be out of the range of representable
+                values by a float, it causes undefined behavior.  It is caller's
+                responsibility to check for these cases.
+
+         @return 0 if the operation is successful.  -1 if tokenIndex is invalid.
+        */
+        int tokenNumberValue ( const int tokenIndex, float &returnValue ) const;
+
+
+        /** tokenBooleanValue will convert the value as bool represented by
+         the tokenIndex.  A typical use is that caller has found the Key-index,
+         and then has retrieved the Value-index (by using findChildIndexOf
+         function), and now they want to read the value of Value-index, as
+         boolean value.
+
+         @param tokenIndex representing the "value" in the JSON source.  The
+                valid value range is 0 to [parsedTokenCount()-1] both inclusive.
+
+         @param returnValue is a return-parameter passed by reference to hold up
+                the bool value parsed by this function.
+
+         @return 0 if the operation is successful.  -1 if tokenIndex is invalid.
+        */
+        int tokenBooleanValue ( const int tokenIndex, bool &returnValue ) const;
+
+
+        /** unescape is a utility function to unescape a JSON string.  This
+         function does not change any state of Json object, and is a pure
+         static utility function.  This function is in-pace unescaping, and WILL
+         modify the source parameter.
+
+         @param jsonString representing an escaped JSON string.  This parameter
+                is also the return parameter as well.  All modifications will be
+                reflected in this parameter.
+
+         @return pointer to unescaped JSON string.  This is exactly the same
+                 pointer as jsonString parameter.
+         */
+        static char * unescape ( char * jsonString );
+};
+
+inline int Json::parsedTokenCount () const
+{
+    return tokenCount;
+}
+
+inline bool Json::isValidJson () const
+{
+    return ( tokenCount >= 1 );
+}
+
+inline bool Json::isValidToken ( const int tokenIndex ) const
+{
+    return ( tokenIndex >= 0 && tokenIndex < tokenCount );
+}
+
+inline jsmntype_t Json::type ( const int tokenIndex ) const
+{
+    jsmntype_t retVal = JSMN_UNDEFINED;
+
+    if ( isValidToken ( tokenIndex ) )
+    {
+        retVal = tokens [ tokenIndex ].type;
+    }
+
+    return retVal;
+}
+
+inline int Json::parent ( const int tokenIndex ) const
+{
+    int retVal = -1;
+
+    if ( isValidToken ( tokenIndex ) )
+    {
+        retVal = tokens [ tokenIndex ].parent;
+    }
+
+    return retVal;
+}
+
+inline int Json::childCount ( const int tokenIndex ) const
+{
+    int retVal = -1;
+
+    if ( isValidToken ( tokenIndex ) )
+    {
+        retVal = tokens [ tokenIndex ].childCount;
+    }
+
+    return retVal;
+}
+
+inline int Json::tokenLength ( const int tokenIndex ) const
+{
+    int retVal = -1;
+
+    if ( isValidToken ( tokenIndex ) )
+    {
+        retVal = tokens [ tokenIndex ].end - tokens [ tokenIndex ].start;
+    }
+
+    return retVal;
+}
+
+inline const char * Json::tokenAddress ( const int tokenIndex ) const
+{
+    char * retVal = NULL;
+
+    if ( isValidToken ( tokenIndex ) )
+    {
+        retVal = (char *) source + tokens [ tokenIndex ].start;
+    }
+
+    return retVal;
+}
+
+#endif
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/JSON/jsmn.c	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,340 @@
+/* Author: Faheem Inayat
+ * Created by "Night Crue" Team @ TechShop San Jose, CA
+ *
+ * --- DISCLAIMER ---
+ * This code is a modified version of original JSMN lirary, written by
+ *    *** Serge A. Zaitsev ***
+ * and hosted at https://github.com/zserge/jsmn
+ * Any modification to the original source is not guaranteed to be included
+ * in this version.  As of writing of this file, the original source is 
+ * licensed under MIT License
+ * (http://www.opensource.org/licenses/mit-license.php).
+ */
+
+#include "jsmn.h"
+
+/**
+ * Allocates a fresh unused token from the token pull.
+ */
+static jsmntok_t *jsmn_alloc_token ( jsmn_parser *parser, jsmntok_t *tokens, size_t num_tokens )
+{
+    jsmntok_t *tok;
+    if ( parser->toknext >= num_tokens )
+    {
+        return NULL ;
+    }
+    tok = &tokens [ parser->toknext++ ];
+    tok->start = tok->end = -1;
+    tok->childCount = 0;
+    tok->parent = -1;
+    return tok;
+}
+
+/**
+ * Fills token type and boundaries.
+ */
+static void jsmn_fill_token ( jsmntok_t *token, jsmntype_t type, int start, int end )
+{
+    token->type = type;
+    token->start = start;
+    token->end = end;
+    token->childCount = 0;
+}
+
+/**
+ * Fills next available token with JSON primitive.
+ */
+static int jsmn_parse_primitive ( jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens )
+{
+    jsmntok_t *token;
+    int start;
+
+    start = parser->pos;
+
+    for ( ; parser->pos < len && js [ parser->pos ] != '\0'; parser->pos++ )
+    {
+        switch ( js [ parser->pos ] )
+        {
+            case '\t':
+            case '\r':
+            case '\n':
+            case ' ':
+            case ',':
+            case ']':
+            case '}':
+                goto found;
+        }
+        if ( js [ parser->pos ] < 32 || js [ parser->pos ] >= 127 )
+        {
+            parser->pos = start;
+            return JSMN_ERROR_INVAL;
+        }
+    }
+    /* primitive must be followed by a comma/object/array */
+    parser->pos = start;
+    return JSMN_ERROR_PART;
+
+    found: if ( tokens == NULL )
+    {
+        parser->pos--;
+        return 0;
+    }
+    token = jsmn_alloc_token ( parser, tokens, num_tokens );
+    if ( token == NULL )
+    {
+        parser->pos = start;
+        return JSMN_ERROR_NOMEM;
+    }
+    jsmn_fill_token ( token, JSMN_PRIMITIVE, start, parser->pos );
+    token->parent = parser->toksuper;
+    parser->pos--;
+    return 0;
+}
+
+/**
+ * Fills next token with JSON string.
+ */
+static int jsmn_parse_string ( jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, size_t num_tokens, unsigned char isKey )
+{
+    jsmntok_t *token;
+
+    int start = parser->pos;
+
+    parser->pos++;
+
+    /* Skip starting quote */
+    for ( ; parser->pos < len && js [ parser->pos ] != '\0'; parser->pos++ )
+    {
+        char c = js [ parser->pos ];
+
+        /* Quote: end of string */
+        if ( c == '\"' )
+        {
+            if ( tokens == NULL )
+            {
+                return 0;
+            }
+            token = jsmn_alloc_token ( parser, tokens, num_tokens );
+            if ( token == NULL )
+            {
+                parser->pos = start;
+                return JSMN_ERROR_NOMEM;
+            }
+            if ( isKey == 1 )
+            {
+                jsmn_fill_token ( token, JSMN_KEY, start + 1, parser->pos );
+            }
+            else
+            {
+                jsmn_fill_token ( token, JSMN_STRING, start + 1, parser->pos );
+            }
+            token->parent = parser->toksuper;
+            return 0;
+        }
+
+        /* Backslash: Quoted symbol expected */
+        if ( c == '\\' && parser->pos + 1 < len )
+        {
+            int i;
+            parser->pos++;
+            switch ( js [ parser->pos ] )
+            {
+                /* Allowed escaped symbols */
+                case '\"':
+                case '/':
+                case '\\':
+                case 'b':
+                case 'f':
+                case 'r':
+                case 'n':
+                case 't':
+                    break;
+                    /* Allows escaped symbol \uXXXX */
+                case 'u':
+                    parser->pos++;
+                    for ( i = 0; i < 4 && parser->pos < len && js [ parser->pos ] != '\0'; i++ )
+                    {
+                        /* If it isn't a hex character we have an error */
+                        if ( ! ( ( js [ parser->pos ] >= 48 && js [ parser->pos ] <= 57 ) || /* 0-9 */
+                        ( js [ parser->pos ] >= 65 && js [ parser->pos ] <= 70 )
+                                 || /* A-F */
+                                 ( js [ parser->pos ] >= 97 && js [ parser->pos ] <= 102 ) ) )
+                        { /* a-f */
+                            parser->pos = start;
+                            return JSMN_ERROR_INVAL;
+                        }
+                        parser->pos++;
+                    }
+                    parser->pos--;
+                    break;
+                    /* Unexpected symbol */
+                default:
+                    parser->pos = start;
+                    return JSMN_ERROR_INVAL;
+            }
+        }
+    }
+    parser->pos = start;
+    return JSMN_ERROR_PART;
+}
+
+/**
+ * Parse JSON string and fill tokens.
+ */
+int jsmn_parse ( jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens )
+{
+    int r;
+    int i;
+    jsmntok_t *token;
+    int count = parser->toknext;
+
+    unsigned char isKey = 1;
+
+    for ( ; parser->pos < len && js [ parser->pos ] != '\0'; parser->pos++ )
+    {
+        char c;
+        jsmntype_t type;
+
+        c = js [ parser->pos ];
+        switch ( c )
+        {
+            case '{':
+            case '[':
+                count++;
+                if ( tokens == NULL )
+                {
+                    break;
+                }
+                token = jsmn_alloc_token ( parser, tokens, num_tokens );
+                if ( token == NULL )
+                    return JSMN_ERROR_NOMEM;
+                if ( parser->toksuper != -1 )
+                {
+                    tokens [ parser->toksuper ].childCount++;
+                    token->parent = parser->toksuper;
+                }
+                token->type = ( c == '{' ? JSMN_OBJECT : JSMN_ARRAY );
+                token->start = parser->pos;
+                parser->toksuper = parser->toknext - 1;
+                if ( token->type == JSMN_OBJECT )
+                {
+                    isKey = 1;
+                }
+                break;
+            case '}':
+            case ']':
+                if ( tokens == NULL )
+                    break;
+                type = ( c == '}' ? JSMN_OBJECT : JSMN_ARRAY );
+                if ( parser->toknext < 1 )
+                {
+                    return JSMN_ERROR_INVAL;
+                }
+                token = &tokens [ parser->toknext - 1 ];
+                for ( ;; )
+                {
+                    if ( token->start != -1 && token->end == -1 )
+                    {
+                        if ( token->type != type )
+                        {
+                            return JSMN_ERROR_INVAL;
+                        }
+                        token->end = parser->pos + 1;
+                        parser->toksuper = token->parent;
+                        break;
+                    }
+                    if ( token->parent == -1 )
+                    {
+                        break;
+                    }
+                    token = &tokens [ token->parent ];
+                }
+                break;
+            case '\"':
+                r = jsmn_parse_string ( parser, js, len, tokens, num_tokens, isKey );
+                if ( r < 0 )
+                    return r;
+                count++;
+                if ( parser->toksuper != -1 && tokens != NULL )
+                    tokens [ parser->toksuper ].childCount++;
+                break;
+            case '\t':
+            case '\r':
+            case '\n':
+            case ' ':
+                break;
+            case ':':
+                isKey = 0;
+                parser->toksuper = parser->toknext - 1;
+                break;
+            case ',':
+                if ( tokens != NULL && parser->toksuper != -1 && tokens [ parser->toksuper ].type != JSMN_ARRAY && tokens [ parser->toksuper ].type != JSMN_OBJECT )
+                {
+                    parser->toksuper = tokens [ parser->toksuper ].parent;
+                }
+                isKey = 1;
+                break;
+                /* In strict mode primitives are: numbers and booleans */
+            case '-':
+            case '0':
+            case '1':
+            case '2':
+            case '3':
+            case '4':
+            case '5':
+            case '6':
+            case '7':
+            case '8':
+            case '9':
+            case 't':
+            case 'f':
+            case 'n':
+                /* And they must not be keys of the object */
+                if ( tokens != NULL && parser->toksuper != -1 )
+                {
+                    jsmntok_t *t = &tokens [ parser->toksuper ];
+                    if ( t->type == JSMN_OBJECT || ( t->type == JSMN_STRING && t->childCount != 0 ) )
+                    {
+                        return JSMN_ERROR_INVAL;
+                    }
+                }
+                r = jsmn_parse_primitive ( parser, js, len, tokens, num_tokens );
+                if ( r < 0 )
+                    return r;
+                count++;
+                if ( parser->toksuper != -1 && tokens != NULL )
+                    tokens [ parser->toksuper ].childCount++;
+                break;
+
+                /* Unexpected char in strict mode */
+            default:
+                return JSMN_ERROR_INVAL;
+        }
+    }
+
+    if ( tokens != NULL )
+    {
+        for ( i = parser->toknext - 1; i >= 0; i-- )
+        {
+            /* Unmatched opened object or array */
+            if ( tokens [ i ].start != -1 && tokens [ i ].end == -1 )
+            {
+                return JSMN_ERROR_PART;
+            }
+        }
+    }
+
+    return count;
+}
+
+/**
+ * Creates a new parser based over a given  buffer with an array of tokens
+ * available.
+ */
+void jsmn_init ( jsmn_parser *parser )
+{
+    parser->pos = 0;
+    parser->toknext = 0;
+    parser->toksuper = -1;
+}
+
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/JSON/jsmn.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,96 @@
+/* Author: Faheem Inayat
+ * Created by "Night Crue" Team @ TechShop San Jose, CA
+ *
+ * --- DISCLAIMER ---
+ * This code is a modified version of original JSMN lirary, written by
+ *    *** Serge A. Zaitsev ***
+ * and hosted at https://github.com/zserge/jsmn
+ * Any modification to the original source is not guaranteed to be included
+ * in this version.  As of writing of this file, the original source is 
+ * licensed under MIT License
+ * (http://www.opensource.org/licenses/mit-license.php).
+ */
+
+#ifndef __JSMN_H_
+#define __JSMN_H_
+
+#include <stddef.h>
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+/*
+  Modified version of original jsmn lib ... added a type "JSMN_KEY" and enabled
+  parent-pointers and strict JSON check.
+*/
+    /**
+     * JSON type identifier. Basic types are:
+     *  o Object
+     *  o Array
+     *  o String
+     *  o Other primitive: number, boolean (true/false) or null
+     */
+    typedef enum
+    {
+        JSMN_UNDEFINED = 0,
+        JSMN_OBJECT = 1,
+        JSMN_ARRAY = 2,
+        JSMN_STRING = 3,
+        JSMN_PRIMITIVE = 4,
+        JSMN_KEY = 5
+    } jsmntype_t;
+
+    enum jsmnerr
+    {
+        /* Not enough tokens were provided */
+        JSMN_ERROR_NOMEM = -1,
+        /* Invalid character inside JSON string */
+        JSMN_ERROR_INVAL = -2,
+        /* The string is not a full JSON packet, more bytes expected */
+        JSMN_ERROR_PART = -3
+    };
+
+    /**
+     * JSON token description.
+     * @param       type    type (object, array, string etc.)
+     * @param       start   start position in JSON data string
+     * @param       end     end position in JSON data string
+     */
+    typedef struct
+    {
+            jsmntype_t type;
+            int start;
+            int end;
+            int parent;
+            int childCount;
+    } jsmntok_t;
+
+    /**
+     * JSON parser. Contains an array of token blocks available. Also stores
+     * the string being parsed now and current position in that string
+     */
+    typedef struct
+    {
+            unsigned int pos; /* offset in the JSON string */
+            unsigned int toknext; /* next token to allocate */
+            int toksuper; /* superior token node, e.g parent object or array */
+    } jsmn_parser;
+
+    /**
+     * Create JSON parser over an array of tokens
+     */
+    void jsmn_init ( jsmn_parser *parser );
+
+    /**
+     * Run JSON parser. It parses a JSON data string into and array of tokens, each describing
+     * a single JSON object.
+     */
+    int jsmn_parse ( jsmn_parser *parser, const char *js, size_t len, jsmntok_t *tokens, unsigned int num_tokens );
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* __JSMN_H_ */
+
--- a/MQTT.lib	Sun Mar 04 07:58:23 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-https://os.mbed.com/users/DuyLionTran/code/MQTT-v-16/#818366b014c9
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/FP.lib	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/users/sam_grove/code/FP/#3c62ba1807ac
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTAsync.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,607 @@
+/*******************************************************************************
+ * Copyright (c) 2014 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *******************************************************************************/
+
+#if !defined(MQTTASYNC_H)
+#define MQTTASYNC_H
+
+#include "FP.h"
+#include "MQTTPacket.h"
+#include "stdio.h"
+
+namespace MQTT
+{
+
+
+enum QoS { QOS0, QOS1, QOS2 };
+
+
+struct Message
+{
+    enum QoS qos;
+    bool retained;
+    bool dup;
+    unsigned short id;
+    void *payload;
+    size_t payloadlen;
+};
+
+
+class PacketId
+{
+public:
+    PacketId();
+    
+    int getNext();
+   
+private:
+    static const int MAX_PACKET_ID = 65535;
+    int next;
+};
+
+typedef void (*messageHandler)(Message*);
+
+typedef struct limits
+{
+	int MAX_MQTT_PACKET_SIZE; // 
+	int MAX_MESSAGE_HANDLERS;  // each subscription requires a message handler
+	int MAX_CONCURRENT_OPERATIONS;  // each command which runs concurrently can have a result handler, when we are in multi-threaded mode
+	int command_timeout_ms;
+		
+	limits()
+	{
+		MAX_MQTT_PACKET_SIZE = 100;
+		MAX_MESSAGE_HANDLERS = 5;
+		MAX_CONCURRENT_OPERATIONS = 1; // 1 indicates single-threaded mode - set to >1 for multithreaded mode
+		command_timeout_ms = 30000;
+	}
+} Limits;
+  
+
+/**
+ * @class Async
+ * @brief non-blocking, threaded MQTT client API
+ * @param Network a network class which supports send, receive
+ * @param Timer a timer class with the methods: 
+ */ 
+template<class Network, class Timer, class Thread, class Mutex> class Async
+{
+    
+public:    
+
+	struct Result
+	{
+    	/* success or failure result data */
+    	Async<Network, Timer, Thread, Mutex>* client;
+		int rc;
+	};
+
+	typedef void (*resultHandler)(Result*);	
+   
+    Async(Network* network, const Limits limits = Limits()); 
+        
+    typedef struct
+    {
+        Async* client;
+        Network* network;
+    } connectionLostInfo;
+    
+    typedef int (*connectionLostHandlers)(connectionLostInfo*);
+    
+    /** Set the connection lost callback - called whenever the connection is lost and we should be connected
+     *  @param clh - pointer to the callback function
+     */
+    void setConnectionLostHandler(connectionLostHandlers clh)
+    {
+        connectionLostHandler.attach(clh);
+    }
+    
+    /** Set the default message handling callback - used for any message which does not match a subscription message handler
+     *  @param mh - pointer to the callback function
+     */
+    void setDefaultMessageHandler(messageHandler mh)
+    {
+        defaultMessageHandler.attach(mh);
+    }
+           
+    int connect(resultHandler fn, MQTTPacket_connectData* options = 0);
+    
+     template<class T>
+    int connect(void(T::*method)(Result *), MQTTPacket_connectData* options = 0, T *item = 0);  // alternative to pass in pointer to member function
+        
+    int publish(resultHandler rh, const char* topic, Message* message);
+    
+    int subscribe(resultHandler rh, const char* topicFilter, enum QoS qos, messageHandler mh);
+    
+    int unsubscribe(resultHandler rh, const char* topicFilter);
+    
+    int disconnect(resultHandler rh);
+    
+private:
+
+    void run(void const *argument);
+    int cycle(int timeout);
+    int waitfor(int packet_type, Timer& atimer);
+	int keepalive();
+	int findFreeOperation();
+
+    int decodePacket(int* value, int timeout);
+    int readPacket(int timeout);
+    int sendPacket(int length, int timeout);
+	int deliverMessage(MQTTString* topic, Message* message);
+    
+    Thread* thread;
+    Network* ipstack;
+    
+    Limits limits;
+    
+    char* buf;  
+    char* readbuf;
+
+    Timer ping_timer, connect_timer;
+    unsigned int keepAliveInterval;
+	bool ping_outstanding;
+    
+    PacketId packetid;
+    
+    typedef FP<void, Result*> resultHandlerFP;    
+    resultHandlerFP connectHandler; 
+    
+    typedef FP<void, Message*> messageHandlerFP;
+    struct MessageHandlers
+    {
+    	const char* topic;
+    	messageHandlerFP fp;
+    } *messageHandlers;      // Message handlers are indexed by subscription topic
+    
+    // how many concurrent operations should we allow?  Each one will require a function pointer
+    struct Operations
+    {
+    	unsigned short id;
+    	resultHandlerFP fp;
+    	const char* topic;         // if this is a publish, store topic name in case republishing is required
+    	Message* message;    // for publish, 
+    	Timer timer;         // to check if the command has timed out
+    } *operations;           // result handlers are indexed by packet ids
+
+	static void threadfn(void* arg);
+	
+	messageHandlerFP defaultMessageHandler;
+    
+    typedef FP<int, connectionLostInfo*> connectionLostFP;
+    
+    connectionLostFP connectionLostHandler;
+    
+};
+
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> void MQTT::Async<Network, Timer, Thread, Mutex>::threadfn(void* arg)
+{
+    ((Async<Network, Timer, Thread, Mutex>*) arg)->run(NULL);
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> MQTT::Async<Network, Timer, Thread, Mutex>::Async(Network* network, Limits limits)  : limits(limits), packetid()
+{
+	this->thread = 0;
+	this->ipstack = network;
+	this->ping_timer = Timer();
+	this->ping_outstanding = 0;
+	   
+	// How to make these memory allocations portable?  I was hoping to avoid the heap
+	buf = new char[limits.MAX_MQTT_PACKET_SIZE];
+	readbuf = new char[limits.MAX_MQTT_PACKET_SIZE];
+	this->operations = new struct Operations[limits.MAX_CONCURRENT_OPERATIONS];
+	for (int i = 0; i < limits.MAX_CONCURRENT_OPERATIONS; ++i)
+		operations[i].id = 0;
+	this->messageHandlers = new struct MessageHandlers[limits.MAX_MESSAGE_HANDLERS];
+	for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i)
+		messageHandlers[i].topic = 0;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::sendPacket(int length, int timeout)
+{
+    int sent = 0;
+    
+    while (sent < length)
+        sent += ipstack->write(&buf[sent], length, timeout);
+	if (sent == length)
+	    ping_timer.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet    
+    return sent;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::decodePacket(int* value, int timeout)
+{
+    char c;
+    int multiplier = 1;
+    int len = 0;
+	const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4;
+
+    *value = 0;
+    do
+    {
+        int rc = MQTTPACKET_READ_ERROR;
+
+        if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
+        {
+            rc = MQTTPACKET_READ_ERROR; /* bad data */
+            goto exit;
+        }
+        rc = ipstack->read(&c, 1, timeout);
+        if (rc != 1)
+            goto exit;
+        *value += (c & 127) * multiplier;
+        multiplier *= 128;
+    } while ((c & 128) != 0);
+exit:
+    return len;
+}
+
+
+/**
+ * If any read fails in this method, then we should disconnect from the network, as on reconnect
+ * the packets can be retried. 
+ * @param timeout the max time to wait for the packet read to complete, in milliseconds
+ * @return the MQTT packet type, or -1 if none
+ */
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::readPacket(int timeout) 
+{
+    int rc = -1;
+    MQTTHeader header = {0};
+    int len = 0;
+    int rem_len = 0;
+
+    /* 1. read the header byte.  This has the packet type in it */
+    if (ipstack->read(readbuf, 1, timeout) != 1)
+        goto exit;
+
+    len = 1;
+    /* 2. read the remaining length.  This is variable in itself */
+    decodePacket(&rem_len, timeout);
+    len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length back into the buffer */
+
+    /* 3. read the rest of the buffer using a callback to supply the rest of the data */
+    if (ipstack->read(readbuf + len, rem_len, timeout) != rem_len)
+        goto exit;
+
+    header.byte = readbuf[0];
+    rc = header.bits.type;
+exit:
+    return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::deliverMessage(MQTTString* topic, Message* message)
+{
+	int rc = -1;
+
+	// we have to find the right message handler - indexed by topic
+	for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i)
+	{
+		if (messageHandlers[i].topic != 0 && MQTTPacket_equals(topic, (char*)messageHandlers[i].topic))
+		{
+			messageHandlers[i].fp(message);
+			rc = 0;
+			break;
+		}
+	}
+	
+	return rc;
+}
+
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::cycle(int timeout)
+{
+    /* get one piece of work off the wire and one pass through */
+
+    // read the socket, see what work is due
+    int packet_type = readPacket(timeout);
+    
+	int len, rc;
+    switch (packet_type)
+    {
+        case CONNACK:
+			if (this->thread)
+			{
+				Result res = {this, 0};
+            	if (MQTTDeserialize_connack(&res.rc, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+                	;
+				connectHandler(&res);
+				connectHandler.detach(); // only invoke the callback once
+			}
+			break;
+        case PUBACK:
+        	if (this->thread)
+        		; //call resultHandler
+        case SUBACK:
+            break;
+        case PUBLISH:
+			MQTTString topicName;
+			Message msg;
+			rc = MQTTDeserialize_publish((int*)&msg.dup, (int*)&msg.qos, (int*)&msg.retained, (int*)&msg.id, &topicName,
+								 (char**)&msg.payload, (int*)&msg.payloadlen, readbuf, limits.MAX_MQTT_PACKET_SIZE);;
+			if (msg.qos == QOS0)
+				deliverMessage(&topicName, &msg);
+            break;
+        case PUBREC:
+   	        int type, dup, mypacketid;
+   	        if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+   	            ; 
+   	        // must lock this access against the application thread, if we are multi-threaded
+			len = MQTTSerialize_ack(buf, limits.MAX_MQTT_PACKET_SIZE, PUBREL, 0, mypacketid);
+		    rc = sendPacket(len, timeout); // send the PUBREL packet
+			if (rc != len) 
+				goto exit; // there was a problem
+
+            break;
+        case PUBCOMP:
+            break;
+        case PINGRESP:
+			ping_outstanding = false;
+            break;
+    }
+	keepalive();
+exit:
+    return packet_type;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::keepalive()
+{
+	int rc = 0;
+
+	if (keepAliveInterval == 0)
+		goto exit;
+
+	if (ping_timer.expired())
+	{
+		if (ping_outstanding)
+			rc = -1;
+		else
+		{
+			int len = MQTTSerialize_pingreq(buf, limits.MAX_MQTT_PACKET_SIZE);
+			rc = sendPacket(len, 1000); // send the ping packet
+			if (rc != len) 
+				rc = -1; // indicate there's a problem
+			else
+				ping_outstanding = true;
+		}
+	}
+
+exit:
+	return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> void MQTT::Async<Network, Timer, Thread, Mutex>::run(void const *argument)
+{
+	while (true)
+		cycle(ping_timer.left_ms());
+}
+
+
+// only used in single-threaded mode where one command at a time is in process
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::waitfor(int packet_type, Timer& atimer)
+{
+	int rc = -1;
+	
+	do
+    {
+		if (atimer.expired()) 
+			break; // we timed out
+	}
+	while ((rc = cycle(atimer.left_ms())) != packet_type);	
+	
+	return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::connect(resultHandler resultHandler, MQTTPacket_connectData* options)
+{
+	connect_timer.countdown(limits.command_timeout_ms);
+
+    MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;
+    if (options == 0)
+        options = &default_options; // set default options if none were supplied
+    
+    this->keepAliveInterval = options->keepAliveInterval;
+	ping_timer.countdown(this->keepAliveInterval);
+    int len = MQTTSerialize_connect(buf, limits.MAX_MQTT_PACKET_SIZE, options);
+    int rc = sendPacket(len, connect_timer.left_ms()); // send the connect packet
+	if (rc != len) 
+		goto exit; // there was a problem
+    
+    if (resultHandler == 0)     // wait until the connack is received 
+    {
+        // this will be a blocking call, wait for the connack
+		if (waitfor(CONNACK, connect_timer) == CONNACK)
+		{
+        	int connack_rc = -1;
+        	if (MQTTDeserialize_connack(&connack_rc, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+	        	rc = connack_rc;
+	    }
+    }
+    else
+    {
+        // set connect response callback function
+        connectHandler.attach(resultHandler);
+        
+        // start background thread            
+        this->thread = new Thread((void (*)(void const *argument))&MQTT::Async<Network, Timer, Thread, Mutex>::threadfn, (void*)this);
+    }
+    
+exit:
+    return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::findFreeOperation()
+{
+	int found = -1;
+	for (int i = 0; i < limits.MAX_CONCURRENT_OPERATIONS; ++i)
+	{
+		if (operations[i].id == 0)
+		{
+			found = i;
+			break;
+		}
+	}
+	return found;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::subscribe(resultHandler resultHandler, const char* topicFilter, enum QoS qos, messageHandler messageHandler)
+{
+	int index = 0;
+	if (this->thread)
+		index = findFreeOperation();	
+	Timer& atimer = operations[index].timer;
+	
+	atimer.countdown(limits.command_timeout_ms);
+    MQTTString topic = {(char*)topicFilter, 0, 0};
+    
+    int len = MQTTSerialize_subscribe(buf, limits.MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic, (int*)&qos);
+    int rc = sendPacket(len, atimer.left_ms()); // send the subscribe packet
+	if (rc != len) 
+		goto exit; // there was a problem
+    
+    /* wait for suback */
+    if (resultHandler == 0)
+    {
+        // this will block
+        if (waitfor(SUBACK, atimer) == SUBACK)
+        {
+            int count = 0, grantedQoS = -1, mypacketid;
+            if (MQTTDeserialize_suback(&mypacketid, 1, &count, &grantedQoS, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+                rc = grantedQoS; // 0, 1, 2 or 0x80 
+            if (rc != 0x80)
+            {
+            	for (int i = 0; i < limits.MAX_MESSAGE_HANDLERS; ++i)
+				{
+					if (messageHandlers[i].topic == 0)
+					{
+						messageHandlers[i].topic = topicFilter;
+						messageHandlers[i].fp.attach(messageHandler);
+						rc = 0;
+						break;
+					}
+				}
+            }
+        }
+    }
+    else
+    {
+        // set subscribe response callback function
+        
+    }
+    
+exit:
+    return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::unsubscribe(resultHandler resultHandler, const char* topicFilter)
+{
+	int index = 0;
+	if (this->thread)
+		index = findFreeOperation();	
+	Timer& atimer = operations[index].timer;
+
+	atimer.countdown(limits.command_timeout_ms);
+    MQTTString topic = {(char*)topicFilter, 0, 0};
+    
+    int len = MQTTSerialize_unsubscribe(buf, limits.MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic);
+    int rc = sendPacket(len, atimer.left_ms()); // send the subscribe packet
+	if (rc != len) 
+		goto exit; // there was a problem
+    
+    // set unsubscribe response callback function
+        
+    
+exit:
+    return rc;
+}
+
+
+   
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::publish(resultHandler resultHandler, const char* topicName, Message* message)
+{
+	int index = 0;
+	if (this->thread)
+		index = findFreeOperation();	
+	Timer& atimer = operations[index].timer;
+
+	atimer.countdown(limits.command_timeout_ms);
+    MQTTString topic = {(char*)topicName, 0, 0};
+
+	if (message->qos == QOS1 || message->qos == QOS2)
+		message->id = packetid.getNext();
+    
+	int len = MQTTSerialize_publish(buf, limits.MAX_MQTT_PACKET_SIZE, 0, message->qos, message->retained, message->id, topic, (char*)message->payload, message->payloadlen);
+    int rc = sendPacket(len, atimer.left_ms()); // send the subscribe packet
+	if (rc != len) 
+		goto exit; // there was a problem
+    
+    /* wait for acks */
+    if (resultHandler == 0)
+    {
+ 		if (message->qos == QOS1)
+		{
+	        if (waitfor(PUBACK, atimer) == PUBACK)
+    	    {
+    	        int type, dup, mypacketid;
+    	        if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+    	            rc = 0; 
+    	    }
+		}
+		else if (message->qos == QOS2)
+		{
+	        if (waitfor(PUBCOMP, atimer) == PUBCOMP)
+	   	    {
+	   	    	int type, dup, mypacketid;
+            	if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, limits.MAX_MQTT_PACKET_SIZE) == 1)
+    	           	rc = 0; 
+			}
+
+		}
+    }
+    else
+    {
+        // set publish response callback function
+        
+    }
+    
+exit:
+    return rc;
+}
+
+
+template<class Network, class Timer, class Thread, class Mutex> int MQTT::Async<Network, Timer, Thread, Mutex>::disconnect(resultHandler resultHandler)
+{  
+    Timer timer = Timer(limits.command_timeout_ms);     // we might wait for incomplete incoming publishes to complete
+    int len = MQTTSerialize_disconnect(buf, limits.MAX_MQTT_PACKET_SIZE);
+    int rc = sendPacket(len, timer.left_ms());   // send the disconnect packet
+    
+    return (rc == len) ? 0 : -1;
+}
+
+
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTClient.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,1052 @@
+/*******************************************************************************
+ * Copyright (c) 2014, 2017 IBM Corp.
+ *
+ * All rights reserved. This program and the accompanying materials
+ * are made available under the terms of the Eclipse Public License v1.0
+ * and Eclipse Distribution License v1.0 which accompany this distribution.
+ *
+ * The Eclipse Public License is available at
+ *    http://www.eclipse.org/legal/epl-v10.html
+ * and the Eclipse Distribution License is available at
+ *   http://www.eclipse.org/org/documents/edl-v10.php.
+ *
+ * Contributors:
+ *    Ian Craggs - initial API and implementation and/or initial documentation
+ *    Ian Craggs - fix for bug 458512 - QoS 2 messages
+ *    Ian Craggs - fix for bug 460389 - send loop uses wrong length
+ *    Ian Craggs - fix for bug 464169 - clearing subscriptions
+ *    Ian Craggs - fix for bug 464551 - enums and ints can be different size
+ *    Mark Sonnentag - fix for bug 475204 - inefficient instantiation of Timer
+ *    Ian Craggs - fix for bug 475749 - packetid modified twice
+ *    Ian Craggs - add ability to set message handler separately #6
+ *******************************************************************************/
+
+#if !defined(MQTTCLIENT_H)
+#define MQTTCLIENT_H
+
+#include "FP.h"
+#include "MQTTPacket.h"
+#include <stdio.h>
+#include "MQTTLogging.h"
+
+#if !defined(MQTTCLIENT_QOS1)
+    #define MQTTCLIENT_QOS1 1
+#endif
+#if !defined(MQTTCLIENT_QOS2)
+    #define MQTTCLIENT_QOS2 0
+#endif
+
+namespace MQTT
+{
+
+
+enum QoS { QOS0, QOS1, QOS2 };
+
+// all failure return codes must be negative
+enum returnCode { BUFFER_OVERFLOW = -2, FAILURE = -1, SUCCESS = 0 };
+
+
+struct Message
+{
+    enum QoS qos;
+    bool retained;
+    bool dup;
+    unsigned short id;
+    void *payload;
+    size_t payloadlen;
+};
+
+
+struct MessageData
+{
+    MessageData(MQTTString &aTopicName, struct Message &aMessage)  : message(aMessage), topicName(aTopicName)
+    { }
+
+    struct Message &message;
+    MQTTString &topicName;
+};
+
+
+struct connackData
+{
+    int rc;
+    bool sessionPresent;
+};
+
+
+struct subackData
+{
+    int grantedQoS;
+};
+
+
+class PacketId
+{
+public:
+    PacketId()
+    {
+        next = 0;
+    }
+
+    int getNext()
+    {
+        return next = (next == MAX_PACKET_ID) ? 1 : next + 1;
+    }
+
+private:
+    static const int MAX_PACKET_ID = 65535;
+    int next;
+};
+
+
+/**
+ * @class Client
+ * @brief blocking, non-threaded MQTT client API
+ *
+ * This version of the API blocks on all method calls, until they are complete.  This means that only one
+ * MQTT request can be in process at any one time.
+ * @param Network a network class which supports send, receive
+ * @param Timer a timer class with the methods:
+ */
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE = 100, int MAX_MESSAGE_HANDLERS = 5>
+class Client
+{
+
+public:
+
+    typedef void (*messageHandler)(MessageData&);
+
+    /** Construct the client
+     *  @param network - pointer to an instance of the Network class - must be connected to the endpoint
+     *      before calling MQTT connect
+     *  @param limits an instance of the Limit class - to alter limits as required
+     */
+    Client(Network& network, unsigned int command_timeout_ms = 30000);
+
+    /** Set the default message handling callback - used for any message which does not match a subscription message handler
+     *  @param mh - pointer to the callback function.  Set to 0 to remove.
+     */
+    void setDefaultMessageHandler(messageHandler mh)
+    {
+        if (mh != 0)
+            defaultMessageHandler.attach(mh);
+        else
+            defaultMessageHandler.detach();
+    }
+
+    /** Set a message handling callback.  This can be used outside of the the subscribe method.
+     *  @param topicFilter - a topic pattern which can include wildcards
+     *  @param mh - pointer to the callback function. If 0, removes the callback if any
+     */
+    int setMessageHandler(const char* topicFilter, messageHandler mh);
+
+    /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack
+     *  The nework object must be connected to the network endpoint before calling this
+     *  Default connect options are used
+     *  @return success code -
+     */
+    int connect();
+
+    /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack
+     *  The nework object must be connected to the network endpoint before calling this
+     *  @param options - connect options
+     *  @return success code -
+     */
+    int connect(MQTTPacket_connectData& options);
+
+    /** MQTT Connect - send an MQTT connect packet down the network and wait for a Connack
+     *  The nework object must be connected to the network endpoint before calling this
+     *  @param options - connect options
+     *  @param connackData - connack data to be returned
+     *  @return success code -
+     */
+    int connect(MQTTPacket_connectData& options, connackData& data);
+
+    /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs
+     *  @param topic - the topic to publish to
+     *  @param message - the message to send
+     *  @return success code -
+     */
+    int publish(const char* topicName, Message& message);
+
+    /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs
+     *  @param topic - the topic to publish to
+     *  @param payload - the data to send
+     *  @param payloadlen - the length of the data
+     *  @param qos - the QoS to send the publish at
+     *  @param retained - whether the message should be retained
+     *  @return success code -
+     */
+    int publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos = QOS0, bool retained = false);
+
+    /** MQTT Publish - send an MQTT publish packet and wait for all acks to complete for all QoSs
+     *  @param topic - the topic to publish to
+     *  @param payload - the data to send
+     *  @param payloadlen - the length of the data
+     *  @param id - the packet id used - returned
+     *  @param qos - the QoS to send the publish at
+     *  @param retained - whether the message should be retained
+     *  @return success code -
+     */
+    int publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos = QOS1, bool retained = false);
+
+    /** MQTT Subscribe - send an MQTT subscribe packet and wait for the suback
+     *  @param topicFilter - a topic pattern which can include wildcards
+     *  @param qos - the MQTT QoS to subscribe at
+     *  @param mh - the callback function to be invoked when a message is received for this subscription
+     *  @return success code -
+     */
+    int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh);
+
+    /** MQTT Subscribe - send an MQTT subscribe packet and wait for the suback
+     *  @param topicFilter - a topic pattern which can include wildcards
+     *  @param qos - the MQTT QoS to subscribe at©
+     *  @param mh - the callback function to be invoked when a message is received for this subscription
+     *  @param
+     *  @return success code -
+     */
+    int subscribe(const char* topicFilter, enum QoS qos, messageHandler mh, subackData &data);
+
+    /** MQTT Unsubscribe - send an MQTT unsubscribe packet and wait for the unsuback
+     *  @param topicFilter - a topic pattern which can include wildcards
+     *  @return success code -
+     */
+    int unsubscribe(const char* topicFilter);
+
+    /** MQTT Disconnect - send an MQTT disconnect packet, and clean up any state
+     *  @return success code -
+     */
+    int disconnect();
+
+    /** A call to this API must be made within the keepAlive interval to keep the MQTT connection alive
+     *  yield can be called if no other MQTT operation is needed.  This will also allow messages to be
+     *  received.
+     *  @param timeout_ms the time to wait, in milliseconds
+     *  @return success code - on failure, this means the client has disconnected
+     */
+    int yield(unsigned long timeout_ms = 1000L);
+
+    /** Is the client connected?
+     *  @return flag - is the client connected or not?
+     */
+    bool isConnected()
+    {
+        return isconnected;
+    }
+
+private:
+
+    void closeSession();
+    void cleanSession();
+    int cycle(Timer& timer);
+    int waitfor(int packet_type, Timer& timer);
+    int keepalive();
+    int publish(int len, Timer& timer, enum QoS qos);
+
+    int decodePacket(int* value, int timeout);
+    int readPacket(Timer& timer);
+    int sendPacket(int length, Timer& timer);
+    int deliverMessage(MQTTString& topicName, Message& message);
+    bool isTopicMatched(char* topicFilter, MQTTString& topicName);
+
+    Network& ipstack;
+    unsigned long command_timeout_ms;
+
+    unsigned char sendbuf[MAX_MQTT_PACKET_SIZE];
+    unsigned char readbuf[MAX_MQTT_PACKET_SIZE];
+
+    Timer last_sent, last_received;
+    unsigned int keepAliveInterval;
+    bool ping_outstanding;
+    bool cleansession;
+
+    PacketId packetid;
+
+    struct MessageHandlers
+    {
+        const char* topicFilter;
+        FP<void, MessageData&> fp;
+    } messageHandlers[MAX_MESSAGE_HANDLERS];      // Message handlers are indexed by subscription topic
+
+    FP<void, MessageData&> defaultMessageHandler;
+
+    bool isconnected;
+
+#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
+    unsigned char pubbuf[MAX_MQTT_PACKET_SIZE];  // store the last publish for sending on reconnect
+    int inflightLen;
+    unsigned short inflightMsgid;
+    enum QoS inflightQoS;
+#endif
+
+#if MQTTCLIENT_QOS2
+    bool pubrel;
+    #if !defined(MAX_INCOMING_QOS2_MESSAGES)
+        #define MAX_INCOMING_QOS2_MESSAGES 10
+    #endif
+    unsigned short incomingQoS2messages[MAX_INCOMING_QOS2_MESSAGES];
+    bool isQoS2msgidFree(unsigned short id);
+    bool useQoS2msgid(unsigned short id);
+    void freeQoS2msgid(unsigned short id);
+#endif
+
+};
+
+}
+
+
+template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
+void MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::cleanSession()
+{
+    for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+        messageHandlers[i].topicFilter = 0;
+
+#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
+    inflightMsgid = 0;
+    inflightQoS = QOS0;
+#endif
+
+#if MQTTCLIENT_QOS2
+    pubrel = false;
+    for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
+        incomingQoS2messages[i] = 0;
+#endif
+}
+
+
+template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
+void MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::closeSession()
+{
+    ping_outstanding = false;
+    isconnected = false;
+    if (cleansession)
+        cleanSession();
+}
+
+
+template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
+MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::Client(Network& network, unsigned int command_timeout_ms)  : ipstack(network), packetid()
+{
+    this->command_timeout_ms = command_timeout_ms;
+    cleansession = true;
+      closeSession();
+}
+
+
+#if MQTTCLIENT_QOS2
+template<class Network, class Timer, int a, int b>
+bool MQTT::Client<Network, Timer, a, b>::isQoS2msgidFree(unsigned short id)
+{
+    for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
+    {
+        if (incomingQoS2messages[i] == id)
+            return false;
+    }
+    return true;
+}
+
+
+template<class Network, class Timer, int a, int b>
+bool MQTT::Client<Network, Timer, a, b>::useQoS2msgid(unsigned short id)
+{
+    for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
+    {
+        if (incomingQoS2messages[i] == 0)
+        {
+            incomingQoS2messages[i] = id;
+            return true;
+        }
+    }
+    return false;
+}
+
+
+template<class Network, class Timer, int a, int b>
+void MQTT::Client<Network, Timer, a, b>::freeQoS2msgid(unsigned short id)
+{
+    for (int i = 0; i < MAX_INCOMING_QOS2_MESSAGES; ++i)
+    {
+        if (incomingQoS2messages[i] == id)
+        {
+            incomingQoS2messages[i] = 0;
+            return;
+        }
+    }
+}
+#endif
+
+
+template<class Network, class Timer, int a, int b>
+int MQTT::Client<Network, Timer, a, b>::sendPacket(int length, Timer& timer)
+{
+    int rc = FAILURE,
+        sent = 0;
+
+    while (sent < length)
+    {
+        rc = ipstack.write(&sendbuf[sent], length - sent, timer.left_ms());
+        if (rc < 0)  // there was an error writing the data
+            break;
+        sent += rc;
+        if (timer.expired()) // only check expiry after at least one attempt to write
+            break;
+    }
+    if (sent == length)
+    {
+        if (this->keepAliveInterval > 0)
+            last_sent.countdown(this->keepAliveInterval); // record the fact that we have successfully sent the packet
+        rc = SUCCESS;
+    }
+    else
+        rc = FAILURE;
+
+#if defined(MQTT_DEBUG)
+    char printbuf[150];
+    DEBUG("Rc %d from sending packet %s\r\n", rc, 
+        MQTTFormat_toServerString(printbuf, sizeof(printbuf), sendbuf, length));
+#endif
+    return rc;
+}
+
+
+template<class Network, class Timer, int a, int b>
+int MQTT::Client<Network, Timer, a, b>::decodePacket(int* value, int timeout)
+{
+    unsigned char c;
+    int multiplier = 1;
+    int len = 0;
+    const int MAX_NO_OF_REMAINING_LENGTH_BYTES = 4;
+
+    *value = 0;
+    do
+    {
+        int rc = MQTTPACKET_READ_ERROR;
+
+        if (++len > MAX_NO_OF_REMAINING_LENGTH_BYTES)
+        {
+            rc = MQTTPACKET_READ_ERROR; /* bad data */
+            goto exit;
+        }
+        rc = ipstack.read(&c, 1, timeout);
+        if (rc != 1)
+            goto exit;
+        *value += (c & 127) * multiplier;
+        multiplier *= 128;
+    } while ((c & 128) != 0);
+exit:
+    return len;
+}
+
+
+/**
+ * If any read fails in this method, then we should disconnect from the network, as on reconnect
+ * the packets can be retried.
+ * @param timeout the max time to wait for the packet read to complete, in milliseconds
+ * @return the MQTT packet type, 0 if none, -1 if error
+ */
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::readPacket(Timer& timer)
+{
+    int rc = FAILURE;
+    MQTTHeader header = {0};
+    int len = 0;
+    int rem_len = 0;
+
+    /* 1. read the header byte.  This has the packet type in it */
+    rc = ipstack.read(readbuf, 1, timer.left_ms());
+    if (rc != 1)
+        goto exit;
+
+    len = 1;
+    /* 2. read the remaining length.  This is variable in itself */
+    decodePacket(&rem_len, timer.left_ms());
+    len += MQTTPacket_encode(readbuf + 1, rem_len); /* put the original remaining length into the buffer */
+
+    if (rem_len > (MAX_MQTT_PACKET_SIZE - len))
+    {
+        rc = BUFFER_OVERFLOW;
+        goto exit;
+    }
+
+    /* 3. read the rest of the buffer using a callback to supply the rest of the data */
+    if (rem_len > 0 && (ipstack.read(readbuf + len, rem_len, timer.left_ms()) != rem_len))
+        goto exit;
+
+    header.byte = readbuf[0];
+    rc = header.bits.type;
+    if (this->keepAliveInterval > 0)
+        last_received.countdown(this->keepAliveInterval); // record the fact that we have successfully received a packet
+exit:
+
+#if defined(MQTT_DEBUG)
+    if (rc >= 0)
+    {
+        char printbuf[50];
+        DEBUG("Rc %d receiving packet %s\r\n", rc, 
+            MQTTFormat_toClientString(printbuf, sizeof(printbuf), readbuf, len));
+    }
+#endif
+    return rc;
+}
+
+
+// assume topic filter and name is in correct format
+// # can only be at end
+// + and # can only be next to separator
+template<class Network, class Timer, int a, int b>
+bool MQTT::Client<Network, Timer, a, b>::isTopicMatched(char* topicFilter, MQTTString& topicName)
+{
+    char* curf = topicFilter;
+    char* curn = topicName.lenstring.data;
+    char* curn_end = curn + topicName.lenstring.len;
+
+    while (*curf && curn < curn_end)
+    {
+        if (*curn == '/' && *curf != '/')
+            break;
+        if (*curf != '+' && *curf != '#' && *curf != *curn)
+            break;
+        if (*curf == '+')
+        {   // skip until we meet the next separator, or end of string
+            char* nextpos = curn + 1;
+            while (nextpos < curn_end && *nextpos != '/')
+                nextpos = ++curn + 1;
+        }
+        else if (*curf == '#')
+            curn = curn_end - 1;    // skip until end of string
+        curf++;
+        curn++;
+    };
+
+    return (curn == curn_end) && (*curf == '\0');
+}
+
+
+
+template<class Network, class Timer, int a, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, a, MAX_MESSAGE_HANDLERS>::deliverMessage(MQTTString& topicName, Message& message)
+{
+    int rc = FAILURE;
+
+    // we have to find the right message handler - indexed by topic
+    for (int i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+    {
+        if (messageHandlers[i].topicFilter != 0 && (MQTTPacket_equals(&topicName, (char*)messageHandlers[i].topicFilter) ||
+                isTopicMatched((char*)messageHandlers[i].topicFilter, topicName)))
+        {
+            if (messageHandlers[i].fp.attached())
+            {
+                MessageData md(topicName, message);
+                messageHandlers[i].fp(md);
+                rc = SUCCESS;
+            }
+        }
+    }
+
+    if (rc == FAILURE && defaultMessageHandler.attached())
+    {
+        MessageData md(topicName, message);
+        defaultMessageHandler(md);
+        rc = SUCCESS;
+    }
+
+    return rc;
+}
+
+
+
+template<class Network, class Timer, int a, int b>
+int MQTT::Client<Network, Timer, a, b>::yield(unsigned long timeout_ms)
+{
+    int rc = SUCCESS;
+    Timer timer;
+
+    timer.countdown_ms(timeout_ms);
+    while (!timer.expired())
+    {
+        if (cycle(timer) < 0)
+        {
+            rc = FAILURE;
+            break;
+        }
+    }
+
+    return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::cycle(Timer& timer)
+{
+    // get one piece of work off the wire and one pass through
+    int len = 0,
+        rc = SUCCESS;
+
+    int packet_type = readPacket(timer);    // read the socket, see what work is due
+
+    switch (packet_type)
+    {
+        default:
+            // no more data to read, unrecoverable. Or read packet fails due to unexpected network error
+            rc = packet_type;
+            goto exit;
+        case NSAPI_ERROR_WOULD_BLOCK:
+        case NSAPI_ERROR_OK: // timed out reading packet            
+            break;
+        case CONNACK:
+        case PUBACK:
+        case SUBACK:
+            break;
+        case PUBLISH:
+        {
+            MQTTString topicName = MQTTString_initializer;
+            Message msg;
+            int intQoS;
+            msg.payloadlen = 0; /* this is a size_t, but deserialize publish sets this as int */
+            if (MQTTDeserialize_publish((unsigned char*)&msg.dup, &intQoS, (unsigned char*)&msg.retained, (unsigned short*)&msg.id, &topicName,
+                                 (unsigned char**)&msg.payload, (int*)&msg.payloadlen, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
+                goto exit;
+            msg.qos = (enum QoS)intQoS;
+#if MQTTCLIENT_QOS2
+            if (msg.qos != QOS2)
+#endif
+                deliverMessage(topicName, msg);
+#if MQTTCLIENT_QOS2
+            else if (isQoS2msgidFree(msg.id))
+            {
+                if (useQoS2msgid(msg.id))
+                    deliverMessage(topicName, msg);
+                else
+                    WARN("Maximum number of incoming QoS2 messages exceeded");
+            }
+#endif
+#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
+            if (msg.qos != QOS0)
+            {
+                if (msg.qos == QOS1)
+                    len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBACK, 0, msg.id);
+                else if (msg.qos == QOS2)
+                    len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREC, 0, msg.id);
+                if (len <= 0)
+                    rc = FAILURE;
+                else
+                    rc = sendPacket(len, timer);
+                if (rc == FAILURE)
+                    goto exit; // there was a problem
+            }
+            break;
+#endif
+        }
+#if MQTTCLIENT_QOS2
+        case PUBREC:
+        case PUBREL:
+            unsigned short mypacketid;
+            unsigned char dup, type;
+            if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
+                rc = FAILURE;
+            else if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE,
+                                 (packet_type == PUBREC) ? PUBREL : PUBCOMP, 0, mypacketid)) <= 0)
+                rc = FAILURE;
+            else if ((rc = sendPacket(len, timer)) != SUCCESS) // send the PUBREL packet
+                rc = FAILURE; // there was a problem
+            if (rc == FAILURE)
+                goto exit; // there was a problem
+            if (packet_type == PUBREL)
+                freeQoS2msgid(mypacketid);
+            break;
+
+        case PUBCOMP:
+            break;
+#endif
+        case PINGRESP:
+            ping_outstanding = false;
+            break;
+    }
+
+    if (keepalive() != SUCCESS)
+        //check only keepalive FAILURE status so that previous FAILURE status can be considered as FAULT
+        rc = FAILURE;
+
+exit:
+    if (rc == SUCCESS)
+        rc = packet_type;
+    else if (isconnected)
+        closeSession();
+    return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::keepalive()
+{
+    int rc = SUCCESS;
+    static Timer ping_sent;
+
+    if (keepAliveInterval == 0)
+        goto exit;
+    
+    if (ping_outstanding)
+    {
+        if (ping_sent.expired())
+        {
+            rc = FAILURE; // session failure
+            #if defined(MQTT_DEBUG)
+                DEBUG("PINGRESP not received in keepalive interval\r\n");
+            #endif
+        }
+    }
+    else if (last_sent.expired() || last_received.expired())
+    {
+        Timer timer(1000);
+        int len = MQTTSerialize_pingreq(sendbuf, MAX_MQTT_PACKET_SIZE);
+        if (len > 0 && (rc = sendPacket(len, timer)) == SUCCESS) // send the ping packet
+        {
+            ping_outstanding = true;
+            ping_sent.countdown(this->keepAliveInterval);
+        }
+    }
+exit:
+    return rc;
+}
+
+
+// only used in single-threaded mode where one command at a time is in process
+template<class Network, class Timer, int a, int b>
+int MQTT::Client<Network, Timer, a, b>::waitfor(int packet_type, Timer& timer)
+{
+    int rc = FAILURE;
+
+    do
+    {
+        if (timer.expired())
+            break; // we timed out
+        rc = cycle(timer);
+    }
+    while (rc != packet_type && rc >= 0);
+
+    return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect(MQTTPacket_connectData& options, connackData& data)
+{
+    Timer connect_timer(command_timeout_ms);
+    int rc = FAILURE;
+    int len = 0;
+
+    if (isconnected) // don't send connect packet again if we are already connected
+        goto exit;
+
+    this->keepAliveInterval = options.keepAliveInterval;
+    this->cleansession = options.cleansession;
+    if ((len = MQTTSerialize_connect(sendbuf, MAX_MQTT_PACKET_SIZE, &options)) <= 0)
+        goto exit;
+    if ((rc = sendPacket(len, connect_timer)) != SUCCESS)  // send the connect packet
+        goto exit; // there was a problem
+
+    if (this->keepAliveInterval > 0)
+        last_received.countdown(this->keepAliveInterval);
+    // this will be a blocking call, wait for the connack
+    if (waitfor(CONNACK, connect_timer) == CONNACK)
+    {
+        data.rc = 0;
+        data.sessionPresent = false;
+        if (MQTTDeserialize_connack((unsigned char*)&data.sessionPresent,
+                            (unsigned char*)&data.rc, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
+            rc = data.rc;
+        else
+            rc = FAILURE;
+    }
+    else
+        rc = FAILURE;
+
+#if MQTTCLIENT_QOS2
+    // resend any inflight publish
+    if (inflightMsgid > 0 && inflightQoS == QOS2 && pubrel)
+    {
+        if ((len = MQTTSerialize_ack(sendbuf, MAX_MQTT_PACKET_SIZE, PUBREL, 0, inflightMsgid)) <= 0)
+            rc = FAILURE;
+        else
+            rc = publish(len, connect_timer, inflightQoS);
+    }
+    else
+#endif
+#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
+    if (inflightMsgid > 0)
+    {
+        memcpy(sendbuf, pubbuf, MAX_MQTT_PACKET_SIZE);
+        rc = publish(inflightLen, connect_timer, inflightQoS);
+    }
+#endif
+
+exit:
+    if (rc == SUCCESS)
+    {
+        isconnected = true;
+        ping_outstanding = false;
+    }
+    return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect(MQTTPacket_connectData& options)
+{
+    connackData data;
+    return connect(options, data);
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::connect()
+{
+    MQTTPacket_connectData default_options = MQTTPacket_connectData_initializer;
+    return connect(default_options);
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::setMessageHandler(const char* topicFilter, messageHandler messageHandler)
+{
+    int rc = FAILURE;
+    int i = -1;
+
+    // first check for an existing matching slot
+    for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+    {
+        if (messageHandlers[i].topicFilter != 0 && strcmp(messageHandlers[i].topicFilter, topicFilter) == 0)
+        {
+            if (messageHandler == 0) // remove existing
+            {
+                messageHandlers[i].topicFilter = 0;
+                messageHandlers[i].fp.detach();
+            }
+            rc = SUCCESS; // return i when adding new subscription
+            break;
+        }
+    }
+    // if no existing, look for empty slot (unless we are removing)
+    if (messageHandler != 0) {
+        if (rc == FAILURE)
+        {
+            for (i = 0; i < MAX_MESSAGE_HANDLERS; ++i)
+            {
+                if (messageHandlers[i].topicFilter == 0)
+                {
+                    rc = SUCCESS;
+                    break;
+                }
+            }
+        }
+        if (i < MAX_MESSAGE_HANDLERS)
+        {
+            messageHandlers[i].topicFilter = topicFilter;
+            messageHandlers[i].fp.attach(messageHandler);
+        }
+    }
+    return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::subscribe(const char* topicFilter,
+     enum QoS qos, messageHandler messageHandler, subackData& data)
+{
+    int rc = FAILURE;
+    Timer timer(command_timeout_ms);
+    int len = 0;
+    MQTTString topic = {(char*)topicFilter, {0, 0}};
+
+    if (!isconnected)
+        goto exit;
+
+    len = MQTTSerialize_subscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic, (int*)&qos);
+    if (len <= 0)
+        goto exit;
+    if ((rc = sendPacket(len, timer)) != SUCCESS) // send the subscribe packet
+        goto exit;             // there was a problem
+
+    if (waitfor(SUBACK, timer) == SUBACK)      // wait for suback
+    {
+        int count = 0;
+        unsigned short mypacketid;
+        data.grantedQoS = 0;
+        if (MQTTDeserialize_suback(&mypacketid, 1, &count, &data.grantedQoS, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
+        {
+            if (data.grantedQoS != 0x80)
+                rc = setMessageHandler(topicFilter, messageHandler);
+        }
+    }
+    else
+        rc = FAILURE;
+
+exit:
+    if (rc == FAILURE)
+        closeSession();
+    return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::subscribe(const char* topicFilter, enum QoS qos, messageHandler messageHandler)
+{
+    subackData data;
+    return subscribe(topicFilter, qos, messageHandler, data);
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int MAX_MESSAGE_HANDLERS>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, MAX_MESSAGE_HANDLERS>::unsubscribe(const char* topicFilter)
+{
+    int rc = FAILURE;
+    Timer timer(command_timeout_ms);
+    MQTTString topic = {(char*)topicFilter, {0, 0}};
+    int len = 0;
+
+    if (!isconnected)
+        goto exit;
+
+    if ((len = MQTTSerialize_unsubscribe(sendbuf, MAX_MQTT_PACKET_SIZE, 0, packetid.getNext(), 1, &topic)) <= 0)
+        goto exit;
+    if ((rc = sendPacket(len, timer)) != SUCCESS) // send the unsubscribe packet
+        goto exit; // there was a problem
+
+    if (waitfor(UNSUBACK, timer) == UNSUBACK)
+    {
+        unsigned short mypacketid;  // should be the same as the packetid above
+        if (MQTTDeserialize_unsuback(&mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) == 1)
+        {
+            // remove the subscription message handler associated with this topic, if there is one
+            setMessageHandler(topicFilter, 0);
+        }
+    }
+    else
+        rc = FAILURE;
+
+exit:
+    if (rc != SUCCESS)
+        closeSession();
+    return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(int len, Timer& timer, enum QoS qos)
+{
+    int rc;
+
+    if ((rc = sendPacket(len, timer)) != SUCCESS) // send the publish packet
+        goto exit; // there was a problem
+
+#if MQTTCLIENT_QOS1
+    if (qos == QOS1)
+    {
+        if (waitfor(PUBACK, timer) == PUBACK)
+        {
+            unsigned short mypacketid;
+            unsigned char dup, type;
+            if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
+                rc = FAILURE;
+            else if (inflightMsgid == mypacketid)
+                inflightMsgid = 0;
+        }
+        else
+            rc = FAILURE;
+    }
+#endif
+#if MQTTCLIENT_QOS2
+    else if (qos == QOS2)
+    {
+        if (waitfor(PUBCOMP, timer) == PUBCOMP)
+        {
+            unsigned short mypacketid;
+            unsigned char dup, type;
+            if (MQTTDeserialize_ack(&type, &dup, &mypacketid, readbuf, MAX_MQTT_PACKET_SIZE) != 1)
+                rc = FAILURE;
+            else if (inflightMsgid == mypacketid)
+                inflightMsgid = 0;
+        }
+        else
+            rc = FAILURE;
+    }
+#endif
+
+exit:
+    if (rc != SUCCESS)
+        closeSession();
+    return rc;
+}
+
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, void* payload, size_t payloadlen, unsigned short& id, enum QoS qos, bool retained)
+{
+    int rc = FAILURE;
+    Timer timer(command_timeout_ms);
+    MQTTString topicString = MQTTString_initializer;
+    int len = 0;
+
+    if (!isconnected)
+        goto exit;
+
+    topicString.cstring = (char*)topicName;
+
+#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
+    if (qos == QOS1 || qos == QOS2)
+        id = packetid.getNext();
+#endif
+
+    len = MQTTSerialize_publish(sendbuf, MAX_MQTT_PACKET_SIZE, 0, qos, retained, id,
+              topicString, (unsigned char*)payload, payloadlen);
+    if (len <= 0)
+        goto exit;
+
+#if MQTTCLIENT_QOS1 || MQTTCLIENT_QOS2
+    if (!cleansession)
+    {
+        memcpy(pubbuf, sendbuf, len);
+        inflightMsgid = id;
+        inflightLen = len;
+        inflightQoS = qos;
+#if MQTTCLIENT_QOS2
+        pubrel = false;
+#endif
+    }
+#endif
+
+    rc = publish(len, timer, qos);
+exit:
+    return rc;
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, void* payload, size_t payloadlen, enum QoS qos, bool retained)
+{
+    unsigned short id = 0;  // dummy - not used for anything
+    return publish(topicName, payload, payloadlen, id, qos, retained);
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::publish(const char* topicName, Message& message)
+{
+    return publish(topicName, message.payload, message.payloadlen, message.qos, message.retained);
+}
+
+
+template<class Network, class Timer, int MAX_MQTT_PACKET_SIZE, int b>
+int MQTT::Client<Network, Timer, MAX_MQTT_PACKET_SIZE, b>::disconnect()
+{
+    int rc = FAILURE;
+    Timer timer(command_timeout_ms);     // we might wait for incomplete incoming publishes to complete
+    int len = MQTTSerialize_disconnect(sendbuf, MAX_MQTT_PACKET_SIZE);
+    if (len > 0)
+        rc = sendPacket(len, timer);            // send the disconnect packet
+    closeSession();
+    return rc;
+}
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTEthernet.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,29 @@
+
+#if !defined(MQTTETHERNET_H)
+#define MQTTETHERNET_H
+
+#include "MQTTmbed.h"
+#include "EthernetInterface.h"
+#include "MQTTSocket.h"
+
+class MQTTEthernet : public MQTTSocket
+{
+public:    
+    MQTTEthernet() : MQTTSocket(&eth)
+    {
+        eth.connect();
+    }
+    
+    EthernetInterface& getEth()
+    {
+        return eth;
+    }
+    
+private:
+
+    EthernetInterface eth;
+    
+};
+
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTLogging.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,39 @@
+#if !defined(MQTT_LOGGING_H)
+#define MQTT_LOGGING_H
+
+#define STREAM      stdout
+#if !defined(DEBUG)
+#define DEBUG(...)    \
+    {\
+    fprintf(STREAM, "DEBUG:   %s L#%d ", __PRETTY_FUNCTION__, __LINE__);  \
+    fprintf(STREAM, ##__VA_ARGS__); \
+    fflush(STREAM); \
+    }
+#endif
+#if !defined(LOG)
+#define LOG(...)    \
+    {\
+    fprintf(STREAM, "LOG:   %s L#%d ", __PRETTY_FUNCTION__, __LINE__);  \
+    fprintf(STREAM, ##__VA_ARGS__); \
+    fflush(STREAM); \
+    }
+#endif
+#if !defined(WARN)
+#define WARN(...)   \
+    { \
+    fprintf(STREAM, "WARN:  %s L#%d ", __PRETTY_FUNCTION__, __LINE__);  \
+    fprintf(STREAM, ##__VA_ARGS__); \
+    fflush(STREAM); \
+    }
+#endif 
+#if !defined(ERROR)
+#define ERROR(...)  \
+    { \
+    fprintf(STREAM, "ERROR: %s L#%d ", __PRETTY_FUNCTION__, __LINE__); \
+    fprintf(STREAM, ##__VA_ARGS__); \
+    fflush(STREAM); \
+    exit(1); \
+    }
+#endif
+
+#endif
\ No newline at end of file
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTPacket.lib	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,1 @@
+http://mbed.org/teams/mqtt/code/MQTTPacket/#aedcaf7984d5
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTSocket.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,97 @@
+#if !defined(MQTTSOCKET_H)
+#define MQTTSOCKET_H
+
+#include "MQTTmbed.h"
+#include <EthernetInterface.h>
+#include <Timer.h>
+
+class MQTTSocket
+{
+public:
+    MQTTSocket(EthernetInterface *anet)
+    {
+        net = anet;
+        open = false;
+    }
+    
+    int connect(char* hostname, int port, int timeout=1000)
+    {
+        if (open)
+            disconnect();
+        nsapi_error_t rc = mysock.open(net);
+        open = true;
+        mysock.set_blocking(true);
+        mysock.set_timeout((unsigned int)timeout);  
+        rc = mysock.connect(hostname, port);
+        mysock.set_blocking(false);  // blocking timeouts seem not to work
+        return rc;
+    }
+
+    // common read/write routine, avoiding blocking timeouts
+    int common(unsigned char* buffer, int len, int timeout, bool read)
+    {
+        timer.start();
+        mysock.set_blocking(false); // blocking timeouts seem not to work
+        int bytes = 0;
+        bool first = true;
+        do 
+        {
+            if (first)
+                first = false;
+            else
+                wait_ms(timeout < 100 ? timeout : 100);
+            int rc;
+            if (read)
+                rc = mysock.recv((char*)buffer, len);
+            else
+                rc = mysock.send((char*)buffer, len);
+            if (rc < 0)
+            {
+                if (rc != NSAPI_ERROR_WOULD_BLOCK)
+                {
+                    bytes = -1;
+                    break;
+                }
+            } 
+            else
+                bytes += rc;
+        }
+        while (bytes < len && timer.read_ms() < timeout);
+        timer.stop();
+        return bytes;
+    }
+
+    /* returns the number of bytes read, which could be 0.
+       -1 if there was an error on the socket
+    */
+    int read(unsigned char* buffer, int len, int timeout)
+    {
+        return common(buffer, len, timeout, true);
+    }
+
+    int write(unsigned char* buffer, int len, int timeout)
+    {
+        return common(buffer, len, timeout, false);
+    }
+
+    int disconnect()
+    {
+        open = false;
+        return mysock.close();
+    }
+
+    /*bool is_connected()
+    {
+        return mysock.is_connected();
+    }*/
+
+private:
+
+    bool open;
+    TCPSocket mysock;
+    EthernetInterface *net;
+    Timer timer;
+
+};
+
+#endif
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/MQTT/MQTTmbed.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,48 @@
+#if !defined(MQTT_MBED_H)
+#define MQTT_MBED_H
+
+#include "mbed.h"
+
+class Countdown
+{
+public:
+    Countdown() : t()
+    {
+  
+    }
+    
+    Countdown(int ms) : t()
+    {
+        countdown_ms(ms);   
+    }
+    
+    
+    bool expired()
+    {
+        return t.read_ms() >= interval_end_ms;
+    }
+    
+    void countdown_ms(unsigned long ms)  
+    {
+        t.stop();
+        interval_end_ms = ms;
+        t.reset();
+        t.start();
+    }
+    
+    void countdown(int seconds)
+    {
+        countdown_ms((unsigned long)seconds * 1000L);
+    }
+    
+    int left_ms()
+    {
+        return interval_end_ms - t.read_ms();
+    }
+    
+private:
+    Timer t;
+    unsigned long interval_end_ms; 
+};
+
+#endif
\ No newline at end of file
--- a/NDefLib.lib	Sun Mar 04 07:58:23 2018 +0000
+++ /dev/null	Thu Jan 01 00:00:00 1970 +0000
@@ -1,1 +0,0 @@
-https://os.mbed.com/users/DuyLionTran/code/NDefLib-v-16/#872e54e3c364
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/Message.cpp	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,132 @@
+/**
+ ******************************************************************************
+ * @file    Message.cpp
+ * @author  ST / Central Labs
+ * @version V2.0.0
+ * @date    28 Apr 2017
+ * @brief   NDef Message class implementation
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *   1. Redistributions of source code must retain the above copyright notice,
+ *      this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright notice,
+ *      this list of conditions and the following disclaimer in the documentation
+ *      and/or other materials provided with the distribution.
+ *   3. Neither the name of STMicroelectronics nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#include <cstdlib>
+#include "NDefLib/Message.h"
+#include "RecordType/EmptyRecord.h"
+#include "RecordType/RecordText.h"
+#include "RecordType/RecordAAR.h"
+#include "RecordType/RecordMimeType.h"
+#include "RecordType/RecordURI.h"
+
+namespace NDefLib {
+
+uint16_t Message::get_byte_length() const {
+	uint16_t lenght = 2; //length size
+
+	if (mRecords.size() == 0) {
+		return lenght + EmptyRecord().get_byte_length();
+	}
+
+	std::vector<Record*>::const_iterator it = mRecords.begin();
+	const std::vector<Record*>::const_iterator end = mRecords.end();
+
+	for (; it != end; ++it) {
+		lenght += (*it)->get_byte_length();
+	} //for
+
+	return lenght;
+} //getByteLenght
+
+uint16_t Message::write(uint8_t *buffer) const {
+
+	const uint16_t length = get_byte_length() - 2;
+	uint16_t offset = 0;
+	buffer[offset++] = (uint8_t) ((length & 0xFF00) >> 8);
+	buffer[offset++] = (uint8_t) ((length & 0x00FF));
+
+	const uint32_t nRecord = mRecords.size();
+
+	if (mRecords.size() == 0) {
+		offset += EmptyRecord().write(buffer + offset);
+		return offset;
+	} //else
+
+	for (uint32_t i = 0; i < nRecord; i++) {
+		Record *r = mRecords[i];
+
+		r->set_as_middle_record();
+		if (i == 0) {
+			r->set_as_first_record();
+		}
+		if (i == nRecord - 1) {
+			r->set_as_last_record();
+		}
+
+		offset += r->write(buffer + offset);
+	} //for
+
+	return offset;
+} //write
+
+void Message::parse_message(const uint8_t * const rawNdefFile, const uint16_t length, Message *msg) {
+	uint16_t offset = 0;
+	Record *r;
+
+	RecordHeader header;
+	do {
+		const uint8_t headerLenght = header.load_header(rawNdefFile + offset);
+		r = RecordText::parse(header, rawNdefFile + offset + headerLenght);
+		if (r == NULL) {
+			r = RecordAAR::parse(header, rawNdefFile + offset + headerLenght);
+		}
+		if (r == NULL) {
+			r = RecordMimeType::parse(header,
+			rawNdefFile + offset + headerLenght);
+		}
+		if (r == NULL) {
+			r = RecordURI::parse(header, rawNdefFile + offset + headerLenght);
+		}
+
+		offset += header.get_record_length();
+		msg->add_record(r);
+	} while (offset < length);
+}
+
+void Message::remove_and_delete_all_record(Message &msg){
+    const uint32_t nRecords =msg.get_N_records();
+	for (uint32_t i =0 ;i<nRecords ;i++) {
+        NDefLib::Record *r = msg[i];
+        delete r;
+    }//for
+    msg.mRecords.clear();
+}//removeAndDeleteAllRecord
+
+} /* namespace NDefLib */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/Message.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,142 @@
+/**
+ ******************************************************************************
+ * @file    Message.h
+ * @author  ST / Central Labs
+ * @version V2.0.0
+ * @date    28 Apr 2017
+ * @brief   NDef Message class
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *   1. Redistributions of source code must retain the above copyright notice,
+ *      this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright notice,
+ *      this list of conditions and the following disclaimer in the documentation
+ *      and/or other materials provided with the distribution.
+ *   3. Neither the name of STMicroelectronics nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#ifndef NDEFLIB_MESSAGE_H_
+#define NDEFLIB_MESSAGE_H_
+
+#include <algorithm>
+#include <vector>
+
+#include "Record.h"
+
+namespace NDefLib {
+
+/**
+ * Class containing a list of {@link Record}
+ */
+class Message {
+public:
+
+	/**
+	 * Add a ndef record to this message.
+	 * @param r Record to add
+	 */
+	void add_record(Record *r) {
+		mRecords.push_back(r);
+	}
+
+	/**
+	 * Remove a ndef record to this message
+	 * @param r record to remove 
+	 */
+	void remove_record(Record *r){
+		mRecords.erase( std::remove( mRecords.begin(), mRecords.end(), r ), mRecords.end() ); 
+	}
+	
+
+	/**
+	 * Add all the records in the list to this message.
+	 * @param addList List of records to add.
+	 */
+	void add_records(const std::vector<Record*> &addList) {
+		mRecords.insert(mRecords.end(), addList.begin(), addList.end());
+	}
+
+	/**
+	 * Get the specific record contained by this message, NULL if not a valid index.
+	 * @param index Record index.
+	 * @return a Record object if present, otherwise NULL
+	 */
+	Record* operator[](const uint32_t index)const{
+		if (index >= mRecords.size()) {
+			return NULL;
+		}
+
+		return mRecords[index];
+	}
+
+	/**
+	 * Get the number of records in this message.
+	 * @return number of records in this message
+	 */
+	uint32_t get_N_records() const {
+		return mRecords.size();
+	}
+
+	/**
+	 * Length in bytes needed to write this message.
+	 * @return number of bytes needed to write this message
+	 */
+	uint16_t get_byte_length() const;
+
+	/**
+	 * Write message in the provided buffer
+	 * @par The first 2 bytes contain the NDEF message length.
+	 * @param[out] buffer Buffer the message must be written into.
+	 * @return number of bytes written
+	 */
+	uint16_t write(uint8_t *buffer) const;
+
+	/**
+	 * Create a set of records from a raw buffer adding them to a message object.
+	 * @par Message buffer must NOT contain the buffer length in the first two bytes.
+	 * @param buffer Buffer containing the message record.
+	 * @param bufferLength Buffer length.
+	 * @param[in,out] Message message that will contain the new records.
+	 */
+	static void parse_message(const uint8_t * const buffer,
+		const uint16_t bufferLength, Message *message);
+
+	/**
+	 * Remove all the recrods from the mesasge and delete it 
+	 * @param msg Message with the records to delete
+	 */
+	static void remove_and_delete_all_record(Message &msg);
+
+	virtual ~Message() {
+	}
+
+private:
+	/**
+	 * List of records contained by this message.
+	 */
+	std::vector<Record*> mRecords;
+};
+
+} /* namespace NDefLib */
+
+#endif /* NDEFLIB_MESSAGE_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/NDefNfcTag.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,329 @@
+/**
+ ******************************************************************************
+ * @file    NDefNfcTag.h
+ * @author  ST / Central Labs
+ * @version V2.0.0
+ * @date    28 Apr 2017
+ * @brief   Generic interface that a device must implement to use the NDefLib
+ * with async communication
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *   1. Redistributions of source code must retain the above copyright notice,
+ *      this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright notice,
+ *      this list of conditions and the following disclaimer in the documentation
+ *      and/or other materials provided with the distribution.
+ *   3. Neither the name of STMicroelectronics nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#ifndef NDEFLIB_NDEFNFCTAG_H_
+#define NDEFLIB_NDEFNFCTAG_H_
+
+#include "NDefLib/Message.h"
+
+namespace NDefLib {
+
+/**
+ * Abstract class used to write/read NDef messages to/from a nfc tag.
+ * This class is made to handle also asynchronous communication with the nfc component.
+ * All the functions in this class will return immediately, when a command ends a proper callback function will
+ * be called.
+ */
+class NDefNfcTag {
+
+public:
+
+	/**
+	 * Class that contains all the function called when a command finished.
+	 * The default implementation is an empty function.
+	 */
+	class Callbacks {
+		public:
+
+			/**
+			 * Called when a session is open.
+			 * @param tag Tag where the session is open.
+			 * @param success True if the operation has success.
+			 */
+			virtual void on_session_open(NDefNfcTag *tag,bool success){
+				(void)tag;(void)success;
+			};
+
+			/**
+			 * Called when a message is written.
+			 * @param tag Tag where the message is written.
+			 * @param success True if the operation has success.
+			 */
+			virtual void on_message_write(NDefNfcTag *tag,bool success){
+				(void)tag;(void)success;
+
+			};
+
+			/**
+			 * Called when a message is read.
+			 * @param tag Tag where the message is read.
+			 * @param success True if the operation has success.
+			 */
+			virtual void on_message_read(NDefNfcTag *tag,bool success,
+					const Message *msg){
+				(void)tag;(void)success; (void)msg;
+			};
+
+
+			/**
+			 * Called when a session is closed.
+			 * @param tag Tag where the session is closed.
+			 * @param success True if the operation has success.
+			 */
+			virtual void on_session_close(NDefNfcTag *tag,bool success){
+				(void)tag;(void)success;
+			};
+
+			virtual ~Callbacks(){};
+	};
+
+private:
+
+	/**
+	 * Data used to store the callback status during a read/write operation.
+	 */
+	struct CallbackStatus{
+		
+		/** Object that triggers the callback */
+		NDefNfcTag *callOwner;
+		
+		/** Message that the callback is writing/reading */
+		Message *msg;
+	};
+
+public:
+
+	NDefNfcTag():mCallBack(&mDefaultCallBack){}
+
+	/**
+	 * Set the callback object.
+	 * @param c Object containing the callback.
+	 */
+	void set_callback(Callbacks *c){
+		if (c!=NULL) {
+			mCallBack=c;
+		} else {
+			mCallBack=&mDefaultCallBack;
+		}
+	}//setCallBack
+
+
+	/**
+	 * Open the communication session with the nfc tag.
+	 * @param force Force to open a communication.
+	 * @return true if success
+	 */
+	virtual bool open_session(bool force = false)=0;
+
+	/**
+	 * Close the communication with the nfc tag.
+	 * @return true if success
+	 */
+	virtual bool close_session()=0;
+
+	/**
+	 * Returns true if a communication with the nfc tag is open.
+	 * @return true if a communication with the nfc tag is open
+	 */
+	virtual bool is_session_open()=0;
+
+	/**
+	 * Write a message in the nfc tag.
+	 * @par This call will delete the previous message.
+	 * @param msg Message to write.
+	 * @return true if success
+	 */
+	virtual bool write(Message &msg) {
+		if (!is_session_open()) {
+			mCallBack->on_message_write(this,false);
+			return false;
+		}
+
+		const uint16_t length = msg.get_byte_length();
+		uint8_t *buffer = new uint8_t[length];
+		if (buffer==NULL){ //impossible to allocate the buffer
+			mCallBack->on_message_write(this,false);
+			return false;
+		}
+
+		msg.write(buffer);
+
+		mCallBackStatus.callOwner=this;
+		mCallBackStatus.msg=NULL;
+
+		return writeByte(buffer, length,0,NDefNfcTag::onWriteMessageCallback,&mCallBackStatus);
+	}
+
+	/**
+	 * Read a message from the tag.
+	 * @param[in,out] msg Message object the read records are added to.
+	 * @return true if success
+	 */
+	virtual bool read(Message *msg) {
+		if (!is_session_open()) {
+			mCallBack->on_message_read(this,false,msg);
+			return false;
+		}
+
+		uint8_t *buffer = new uint8_t[2];
+		if (buffer==NULL) {
+			mCallBack->on_message_read(this,false,msg);
+			return false;
+		}
+
+		mCallBackStatus.callOwner=this;
+		mCallBackStatus.msg=msg;
+		return readByte(0,2,buffer,NDefNfcTag::onReadMessageLength,&mCallBackStatus);
+	}
+
+	virtual ~NDefNfcTag() {}
+
+protected:
+
+	typedef struct CallbackStatus CallbackStatus_t;
+
+	/**
+	 * Function that the component will call when a read/write operation is completed
+	 * @param internalState Callback internal state data.
+	 * @param status True if the operation succeed.
+	 * @param buffer Buffer to read/write.
+	 * @param length Number of byte read/write.
+	 * @return true if the operation had success
+	 */
+	typedef bool(*byteOperationCallback_t)(CallbackStatus_t *internalState,
+		bool status,const uint8_t *buffer, uint16_t length);
+
+	/**
+	 * Write a sequence of bytes to the NDEF file.
+	 * @param buffer Buffer to write.
+	 * @param length Number of bytes to write.
+	 * @param offset Offset where start to write.
+	 * @param callback Function to call when the operation ended.
+	 * @param callbackStatus Parameter to pass to the callback function.
+	 * @return true if the operation has success
+	 */
+	virtual bool writeByte(const uint8_t *buffer, uint16_t length,uint16_t offset,
+		byteOperationCallback_t callback,CallbackStatus_t *callbackStatus)=0;
+
+	/**
+	 * Read a sequence of bytes from the NDEF file.
+	 * @param byteOffset Read offset in bytes.
+	 * @param byteLength Number of bytes to read.
+	 * @param[out] buffer Buffer to store the read data into.
+	 * @param callback Function to call when the operation ended.
+	 * @param callbackStatus Parameter to pass to the callback function.
+	 * @return true if the operation has success
+	 */
+	virtual bool readByte(const uint16_t byteOffset, const uint16_t byteLength,
+		uint8_t *buffer, byteOperationCallback_t callback,CallbackStatus_t *callbackStatus)=0;
+
+
+	/** object with the user callback */
+	Callbacks *mCallBack;
+private:
+
+	/** object with the current callback status */
+	CallbackStatus_t mCallBackStatus;
+	/** default callback object, all the functions are empty */
+	Callbacks mDefaultCallBack;
+
+	/**
+	 * Function called when a write operation completes, it will invoke on_message_write
+	 * @param internalState Object that invokes the write operation.
+	 * @param status True if the operation had success.
+	 * @param buffer Buffer written.
+	 * @param length Number of bytes written.
+	 * @return true if the write had success
+	 */
+	static bool onWriteMessageCallback(CallbackStatus_t *internalState,
+		bool status,const uint8_t *buffer, uint16_t ){
+		delete [] buffer;
+
+		internalState->callOwner->mCallBack->on_message_write(internalState->callOwner,status);
+
+		return status;
+	}
+
+	/**
+	 * Function called when a read operation completes.
+	 * In this case we read the message length, this function will read all the message
+	 * @param internalState Object that invokes the write operation.
+	 * @param status true If the operation had success.
+	 * @param buffer Buffer read.
+	 * @param length Number of bytes read.
+	 * @return true if the read had success
+	 */
+	static bool onReadMessageLength(CallbackStatus_t *internalState,
+		bool status,const uint8_t *buffer, uint16_t length){
+
+		if (!status || length!=2) {
+			internalState->callOwner->mCallBack->on_message_read(internalState->callOwner,false,internalState->msg);
+			return false;
+		}//if
+
+		length = (((uint16_t) buffer[0]) << 8 | buffer[1]);
+		delete [] buffer;
+
+		uint8_t *readBuffer = new uint8_t[length];
+		if (readBuffer==NULL) {
+			internalState->callOwner->mCallBack->on_message_read(internalState->callOwner,false,internalState->msg);
+			return false;
+		}//readBuffer
+
+		internalState->callOwner->readByte(2,length,readBuffer, &NDefNfcTag::onReadMessageCallback,internalState);
+		return status;
+	}
+
+	/**
+	 * Function called when all messages have been read
+	 * @param internalState Object that invokes the write operation.
+	 * @param status True if the operation had success.
+	 * @param buffer Buffer read.
+	 * @param length Number of bytes read.
+	 * @return true if the read had success
+	 */
+	static bool onReadMessageCallback(CallbackStatus_t *internalState,
+		bool status,const uint8_t *buffer, uint16_t length){
+		if(!status) {
+			internalState->callOwner->mCallBack->on_message_read(internalState->callOwner,false,internalState->msg);
+			return false;
+		}
+		Message::parse_message(buffer, length, internalState->msg);
+		delete [] buffer;
+		internalState->callOwner->mCallBack-> on_message_read(internalState->callOwner,true,internalState->msg);
+		return status ;
+	}
+
+}; //class NDefNfcTagASync
+
+}// namespace NDefLib
+
+#endif /* NDEFLIB_NDEFNFCTAG_H_ */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/Record.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,161 @@
+/**
+ ******************************************************************************
+ * @file    Record.h
+ * @author  ST / Central Labs
+ * @version V2.0.0
+ * @date    28 Apr 2017
+ * @brief   Generic Record class 
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *   1. Redistributions of source code must retain the above copyright notice,
+ *      this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright notice,
+ *      this list of conditions and the following disclaimer in the documentation
+ *      and/or other materials provided with the distribution.
+ *   3. Neither the name of STMicroelectronics nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#ifndef NDEFLIB_RECORD_H_
+#define NDEFLIB_RECORD_H_
+#include <stdint.h>
+
+#include "RecordHeader.h"
+
+namespace NDefLib {
+
+/**
+ * Base class for a NDefRecord
+ * @see NFC Data Exchange Format (NDEF) Technical Specification NDEF 1.0
+ */
+class Record {
+public:
+
+	/**
+	 * Enum used to identify the record type.
+	 */
+	typedef enum {
+		TYPE_UNKNOWN,        //!< UNKNOWN record
+		TYPE_TEXT,           //!< TEXT
+		TYPE_AAR,            //!< Android Archive record
+		TYPE_MIME,           //!< generic MIME type
+		TYPE_URI,            //!< generic URI
+		TYPE_URI_MAIL,       //!< Email URI record
+		TYPE_URI_SMS,        //!< SMS URI record
+		TYPE_URI_GEOLOCATION,//!< position URI record
+		TYPE_MIME_VCARD,     //!< VCard record
+		TYPE_WIFI_CONF 		 //!< Wifi configuration
+	} RecordType_t;
+
+	Record() {
+	}
+
+	/**
+	 * Set the record as the first record in the message.
+	 */
+	void set_as_first_record() {
+		mRecordHeader.set_MB(true);
+	}
+
+	/**
+	 * Set the record as the last record in the message.
+	 */
+	void set_as_last_record() {
+		mRecordHeader.set_ME(true);
+	}
+
+	/**
+	 * Check if it is the last record in the message.
+	 * @return true if it is the last record in the message
+	 */
+	bool is_last_record() const {
+		return mRecordHeader.get_ME();
+	}
+
+	/**
+	 * Check if it is the first record in the message.
+	 * @return true if it is the fist record in the message
+	 */
+	bool is_first_record() const {
+		return mRecordHeader.get_MB();
+	}
+
+	/**
+	 * Set the record as generic (not the first one and not the last one)
+	 */
+	void set_as_middle_record() {
+		mRecordHeader.set_MB(false);
+		mRecordHeader.set_ME(false);
+	}
+
+	/**
+	 * Check if the record is in the middle of a chain.
+	 * @return true if is not the fist or the last one
+	 */
+	bool is_middle_record() const{
+		return ! (mRecordHeader.get_MB() || mRecordHeader.get_ME());
+	}
+
+	/**
+	 * Get tag type.
+	 * @par This method should be overridden to return a valid type.
+	 * @return tag type 
+	 */
+	virtual RecordType_t get_type() const {
+		return TYPE_UNKNOWN;
+	} //getType
+
+
+	/**
+	 * Get the record header.
+	 * @return record header 
+	 */
+	const RecordHeader& get_header() const{
+		return mRecordHeader;
+	}
+
+	/**
+	 * Number of bytes needed to store this record.
+	 * @return size of the header + size of the record content
+	 */
+	virtual uint16_t get_byte_length() {
+		return mRecordHeader.get_record_length();
+	}
+
+	/**
+	 * Write the record content into a buffer.
+	 * @param[out] buffer buffer to write the record content into, the buffer size
+	 *  must be almost {@link Record#getByteLength} bytes.
+	 * @return number of written bytes
+	 */
+	virtual uint16_t write(uint8_t *buffer)=0;
+
+	virtual ~Record() {
+	};
+
+protected:
+	RecordHeader mRecordHeader;
+};
+
+} /* namespace NDefLib */
+
+#endif /* NDEFLIB_RECORD_H_ */
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/RecordHeader.h	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,340 @@
+/**
+ ******************************************************************************
+ * @file    RecordHeader.h
+ * @author  ST / Central Labs
+ * @version V2.0.0
+ * @date    28 Apr 2017
+ * @brief   Record header class 
+ ******************************************************************************
+ * @attention
+ *
+ * <h2><center>&copy; COPYRIGHT(c) 2015 STMicroelectronics</center></h2>
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *   1. Redistributions of source code must retain the above copyright notice,
+ *      this list of conditions and the following disclaimer.
+ *   2. Redistributions in binary form must reproduce the above copyright notice,
+ *      this list of conditions and the following disclaimer in the documentation
+ *      and/or other materials provided with the distribution.
+ *   3. Neither the name of STMicroelectronics nor the names of its contributors
+ *      may be used to endorse or promote products derived from this software
+ *      without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
+ * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
+ * CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
+ * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ ******************************************************************************
+ */
+
+#ifndef NDEFLIB_RECORDHEADER_H_
+#define NDEFLIB_RECORDHEADER_H_
+
+#include <stdint.h>
+
+namespace NDefLib {
+
+/**
+ * Record header class.
+ * @see NFC Data Exchange Format (NDEF) Technical Specification NDEF 1.0
+ */
+class RecordHeader {
+
+public:
+
+	/**
+	 * Record type format
+	 */
+	typedef enum TypeNameFormat {
+		Empty = 0x00,          //!< Empty
+		NFC_well_known = 0x01, //!< NFC_well_known
+		Mime_media_type = 0x02,//!< Mime_media_type
+		Absolute_URI = 0x03,   //!< Absolute_URI
+		NFC_external = 0x04,   //!< NFC_external
+		Unknown = 0x05,        //!< Unknown
+		Unchanged = 0x06,      //!< Unchanged
+		Reserved = 0x07        //!< Reserved
+	} TypeNameFormat_t;
+
+
+	RecordHeader() :
+			headerFlags(0), typeLength(0), payloadLength(0) {
+		set_SR(true);
+	}
+
+	/**
+	 * Set the 'message begin' flag.
+	 * @param value True if the record is the first within the message.
+	 */
+	void set_MB(bool value) {
+		if (value) {
+			headerFlags |= 0x80;
+		} else {
+			headerFlags &= ~0x80;
+		}
+	}//set_MB
+
+	/**
+	 * Check the 'message begin' flag.
+	 * @return true if it is the first record in the message
+	 */
+	bool get_MB() const {
+		return (headerFlags & 0x80) != 0;
+	}//get_MB
+
+	/**
+	 * Set the 'message end' flag.
+	 * @param value True if it is the last record in the message.
+	 */
+	void set_ME(bool value) {
+		if (value) {
+			headerFlags |= 0x40;
+		} else {
+			headerFlags &= ~0x40;
+		}
+	}//set_ME
+
+	/**
+	 * Check the 'message end' flag.
+	 * @return true if it is the last record in the message
+	 */
+	bool get_ME() const {
+		return (headerFlags & 0x40) != 0;
+	}//get_ME
+
+	/**
+	 * Set the 'Chunk' flag.
+	 * @param value True if the record is in the first record chunk or in a middle record
+	 * chunk of a chunked payload.
+	 */
+	void set_CF(bool value) {
+		if (value) {
+			headerFlags |= 0x20;
+		} else {
+			headerFlags &= ~0x20;
+		}
+	}//set_CF
+
+	/**
+	 * Check the 'Chunk' flag value.
+	 * @return true if the record is in the first record chunk or in a middle record
+	 * chunk of a chunked payload
+	 */
+	bool get_CF() const {
+		return (headerFlags & 0x20) != 0;
+	}//get_CF
+
+	/**
+	 * Set the 'Short record' flag value.
+	 * @param value True if the record size can be encoded with 8 bits.
+	 */
+	void set_SR(bool value) {
+		if (value) {
+			headerFlags |= 0x10;
+		} else {
+			headerFlags &= ~0x10;
+		}
+	}//setSR
+
+	/**
+	 * Check the 'Short record' flag.
+	 * @return true if the short range header format is set
+	 */
+	bool get_SR() const {
+		return (headerFlags & 0x10) != 0;
+	}//getSR
+
+	/**
+	 * Set the 'ID length' flag.
+	 * @param value True if the 'ID length' value is used.
+	 */
+	void set_IL(bool value) {
+		if (value) {
+			headerFlags |= 0x08;
+		} else {
+			headerFlags &= ~0x08;
+		}
+	}//setIL
+
+	/**
+	 * Check the 'ID length' flag.
+	 * @param value True if 'ID length' is set.
+	 */
+	bool get_IL() const {
+		return (headerFlags & 0x08) != 0;
+	}//getIL
+
+	/**
+	 * Set the type name format field.
+	 * @param value Record type name format.
+	 */
+	void set_FNT(const TypeNameFormat_t value) {
+		uint8_t temp = (uint8_t) value;
+		temp &= 0x07; //keep the first 3 bits
+		headerFlags &= 0xF8; //clean the fist 3 bits
+		headerFlags |= temp; //set the fist 3 bits
+	}//setFNT
+
+	/**
+	 * Get the record type name.
+	 * @return type name format of the record
+	 */
+	TypeNameFormat_t get_FNT() const {
+		return (TypeNameFormat_t) (headerFlags & 0x07);
+	}
+
+	/**
+	 * Set the record payload length.
+	 * @par This function will update the SR field as needed.
+	 * @param length payload length
+	 */
+	void set_payload_length(uint32_t length) {
+		payloadLength = length;
+		set_SR(payloadLength <= 255);
+	}
+
+	/**
+	 * Get the payload length.
+	 * @return payload length
+	 */
+	uint32_t get_payload_length() const {
+		return payloadLength;
+	}
+
+	/**
+	 * Set the type length.
+	 * @param size.
+	 */
+	void set_type_length(uint8_t size) {
+		typeLength = size;
+	}
+
+	/**
+	 * Get the type length.
+	 * @return type length
+	 */
+	uint8_t get_type_length() const {
+		return typeLength;
+	}
+
+	/**
+	 * Set the id length.
+	 * @param size.
+	 */
+	void set_id_length(uint8_t size) {
+		if (size!=0) {
+			idLength = size;
+			set_IL(true);
+		} else {
+			set_IL(false);
+		}
+	}
+
+	/**
+	 * Get the id length.
+	 * @return id length
+	 */
+	uint8_t get_id_length() const {
+		return idLength;
+	}
+
+	/**
+	 * Get the number of bytes needed to store this record.
+	 * @return 3 or 6
+	 */
+	uint16_t get_record_length() const {
+		return (get_SR() ? 3 : 6) + (get_IL() ? idLength : 0)+typeLength + payloadLength;
+	}
+
+	/**
+	 * Store the header information in the buffer.
+	 * @param[out] outBuffer Buffer to write the header into.
+	 * @return number of write bytes
+	 */
+	uint8_t write_header(uint8_t *outBuffer) const {
+
+		uint32_t index = 0;
+
+		outBuffer[index++] = headerFlags;
+		outBuffer[index++] = typeLength;
+		if (get_SR()) {
+			outBuffer[index++] = (uint8_t) payloadLength;
+		} else {
+			outBuffer[index++] = (uint8_t) ((payloadLength & 0xFF000000)
+					>> 24);
+			outBuffer[index++] = (uint8_t) ((payloadLength & 0x00FF0000)
+					>> 16);
+			outBuffer[index++] = (uint8_t) ((payloadLength & 0x0000FF00)
+					>> 8);
+			outBuffer[index++] = (uint8_t) (payloadLength & 0x000000FF);
+		} //if-else
+		if (get_IL()) {
+			outBuffer[index++] =idLength;
+		}
+
+		return index;
+	} //writeHeader
+
+	/**
+	 * Load an header from a buffer.
+	 * @param buffer Buffer to load the header from.
+	 * @return number of read bytes
+	 */
+	uint16_t load_header(const uint8_t * const buffer) {
+		uint32_t index = 0;
+		headerFlags = buffer[index++];
+		typeLength = buffer[index++];
+		if (get_SR()) {
+			payloadLength = buffer[index++];
+		} else {
+			payloadLength = (((uint32_t) buffer[index + 0]) << 24)
+				| (((uint32_t) buffer[index + 1]) << 16)
+				| (((uint32_t) buffer[index + 2]) << 8)
+				| ((uint32_t) buffer[index + 3]);
+			index += 4;
+		} //if-else
+		if (get_IL()) {
+			idLength = buffer[index++];
+		} else { 
+			idLength = 0;
+		}
+
+		return index;
+	} //loadHeader
+
+	/**
+	 * Equal operator.
+	 * @param other Other object to compare with.
+	 * @return true if the two record headers are equal
+	 */
+	bool operator==(const RecordHeader &other) const{
+		return (headerFlags==other.headerFlags) &&
+		(typeLength==other.typeLength) &&
+		(payloadLength==other.payloadLength);
+	}
+
+	bool operator!=(const RecordHeader &other) const{
+		return !(*this==other);
+	}
+
+private:
+	uint8_t idLength;
+	uint8_t headerFlags;
+	uint8_t typeLength;
+	uint32_t payloadLength;
+};
+
+} /* namespace NDefLib */
+
+#endif /* NDEFLIB_RECORDHEADER_H_ */
+
+
+/************************ (C) COPYRIGHT STMicroelectronics *****END OF FILE****/ 
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/NDefLib/RecordType.lib	Thu Mar 08 13:13:53 2018 +0000
@@ -0,0 +1,1 @@
+https://os.mbed.com/users/DuyLionTran/code/RecordType-v-16/#89e2c8a57572
--- a/Sensor/ReadSensor.cpp	Sun Mar 04 07:58:23 2018 +0000
+++ b/Sensor/ReadSensor.cpp	Thu Mar 08 13:13:53 2018 +0000
@@ -71,7 +71,7 @@
     float voltageRaw    = analogReadRaw*3.3;
     analogBuffer[analogBufferIndex] = (int)((voltageRaw * 1023.0)/5.0);
     
-    printf("Analog read %d %d\r\n", analogBufferIndex, analogBuffer[analogBufferIndex]);
+//    printf("Analog read %d %d\r\n", analogBufferIndex, analogBuffer[analogBufferIndex]);
     analogBufferIndex++;
     if(analogBufferIndex == SCOUNT) {
         analogBufferIndex = 0;
@@ -95,7 +95,7 @@
     SaturationDoTemperature = temperature;
     doValue =  (SaturationValueTab[(int)(SaturationDoTemperature + 0.5)] * averageVoltage) / (float)(SaturationDoVoltage);  
     float a = 1;
-    printf("SaturationDoVoltage %.2f; DO Value %.2f mg/L\r\n", SaturationDoVoltage, doValue/a);
+    printf("SaturationDoTemperature %.2f; SaturationDoVoltage %.2f; DO Value %.2f mg/L\r\n", SaturationDoTemperature, SaturationDoVoltage, doValue/a);
 }
 
 void SENSOR_DoCalibration(uint8_t CurrentCalibrationMode) {
--- a/Sensor/ReadSensor.h	Sun Mar 04 07:58:23 2018 +0000
+++ b/Sensor/ReadSensor.h	Thu Mar 08 13:13:53 2018 +0000
@@ -2,7 +2,7 @@
 #define __READSENSOR_H__
 
 #define SENSOR_1_PIN  (A5)
-#define SENSOR_2_PIN  (A4)
+#define SENSOR_2_PIN  (A3)
 #define SENSOR_3_PIN  (A3)
 
 #define VREF           5000
--- a/Simple-MQTT/SimpleMQTT.h	Sun Mar 04 07:58:23 2018 +0000
+++ b/Simple-MQTT/SimpleMQTT.h	Thu Mar 08 13:13:53 2018 +0000
@@ -27,8 +27,8 @@
 #define TYPE                DEFAULT_TYPE_NAME       // For a registered connection, replace with your type
 #define IBM_IOT_PORT        MQTT_PORT
 
-#define MQTT_MAX_PACKET_SIZE    400   
-#define MQTT_MAX_PAYLOAD_SIZE   300 
+#define MQTT_MAX_PACKET_SIZE    450   
+#define MQTT_MAX_PAYLOAD_SIZE   350 
 
 /***************************************************************
  * Variables
@@ -47,10 +47,10 @@
 } CommandType;
  
 struct UploadValue {
-    float ADC_PHVal;
+    float ADC_TEMPVal;
     float ADC_DOVal;
     
-    float SENSOR_PHVal;
+    float SENSOR_TEMPVal;
     float SENSOR_DOVal;
     
     uint8_t   RELAY_State_1;
@@ -60,8 +60,8 @@
     uint32_t CONFIG_Time;
 	
 	uint8_t  CONFIG_Mode;
-	uint8_t  CONFIG_MinOxi;
-	uint8_t  CONFIG_MaxOxi;
+	uint8_t  CONFIG_OxyThreshold;
+	uint8_t  CONFIG_TemperatureThreshold;
 	uint16_t CONFIG_UploadInterval;
 } UploadValue; 
  
@@ -132,7 +132,7 @@
  *  param[in]   pHVal         The pHVal value to be sent
  *  retral      returnCode from MQTTClient.h 
  */
-int MQTT_PublishSensorVal(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, time_t inputTime, float DOVal);
+int MQTT_PublishSensorVal(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, time_t inputTime, float TempVal, float DOVal);
 
 /** brief       Publish relay states to the server
  *  param[in]   client        MQTT client 
@@ -152,7 +152,7 @@
 // *  param[in]   uploadInterval	Interval between upload turns
  *  retral      returnCode from MQTTClient.h 
  */
-int MQTT_PublishConfigValue(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, time_t inputTime, uint8_t mode, uint8_t minOxi, uint8_t maxOxi);
+int MQTT_PublishConfigValue(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, time_t inputTime, uint8_t mode, uint8_t OxyThreshold, uint8_t TemperatureThreshold, uint16_t UploadInterval);
 
 /** brief       Upload all the data to the MQTT server
  *  param[in]   client          MQTT client 
@@ -191,10 +191,12 @@
             printf("Command Type: %d, error %d\r\n", CommandType, ret);
             
             switch (CommandType) {
-            	case CONTROL_CMD: CE_Calibrate();
+            	case 0: CE_Calibrate();
             	break;
             	
-            	case 3: int relayState1, relayState2, relayState3;
+            	case 1:
+            	
+            	case 2: int relayState1, relayState2, relayState3;
             			KeyIndex      = json.findKeyIndexIn("cmdID", 0);
             			KeyValueIndex = json.findChildIndexOf(KeyIndex, 0);
             			ret = json.tokenIntegerValue(KeyValueIndex, receiveCmdID); 
@@ -219,7 +221,7 @@
             			CE_HandleRelays(relayState1, relayState2, relayState3);
             	break;
             	
-            	case 4: int mode, minOxiVal, maxOxiVal, uploadInterval, setRTCTime;
+            	case 3: int mode, OxiThres, TempThres, uploadInterval, setRTCTime;
             	
             			KeyIndex      = json.findKeyIndexIn("cmdID", 0);
             			KeyValueIndex = json.findChildIndexOf(KeyIndex, 0);
@@ -229,13 +231,13 @@
             			KeyValueIndex = json.findChildIndexOf(KeyIndex, 0);
             			ret = json.tokenIntegerValue(KeyValueIndex, mode);     
             			        			
-            			KeyIndex      = json.findKeyIndexIn("minOxygenVal", 0);
+            			KeyIndex      = json.findKeyIndexIn("OxygenThreshold", 0);
             			KeyValueIndex = json.findChildIndexOf(KeyIndex, 0);
-            			ret = json.tokenIntegerValue(KeyValueIndex, minOxiVal); 
+            			ret = json.tokenIntegerValue(KeyValueIndex, OxiThres); 
             			
-            			KeyIndex      = json.findKeyIndexIn("maxOxygenVal", 0);
+            			KeyIndex      = json.findKeyIndexIn("TemperatureThreshold", 0);
             			KeyValueIndex = json.findChildIndexOf(KeyIndex, 0);
-            			ret = json.tokenIntegerValue(KeyValueIndex, maxOxiVal);     
+            			ret = json.tokenIntegerValue(KeyValueIndex, TempThres);     
             		
             			KeyIndex      = json.findKeyIndexIn("uploadInterval", 0);
             			KeyValueIndex = json.findChildIndexOf(KeyIndex, 0);
@@ -245,14 +247,17 @@
             			KeyValueIndex = json.findChildIndexOf(KeyIndex, 0);
             			ret = json.tokenIntegerValue(KeyValueIndex, setRTCTime);      
             			
-            			DataStruct.CONFIG_Mode   		 = mode;   
-            			DataStruct.CONFIG_MinOxi 		 = minOxiVal;   
-            			DataStruct.CONFIG_MaxOxi 		 = maxOxiVal;   
-            			DataStruct.CONFIG_UploadInterval = uploadInterval;			
-            			FP_WriteConfigValues(DataStruct.CONFIG_Mode, DataStruct.CONFIG_MinOxi, DataStruct.CONFIG_MaxOxi, DataStruct.CONFIG_UploadInterval);  	
+            			DataStruct.CONFIG_Mode   		 		= mode;   
+            			DataStruct.CONFIG_OxyThreshold 		 	= OxiThres;   
+            			DataStruct.CONFIG_TemperatureThreshold	= TempThres;   
+            			DataStruct.CONFIG_UploadInterval 		= uploadInterval;			
+            			FP_WriteConfigValues(DataStruct.CONFIG_Mode, DataStruct.CONFIG_OxyThreshold, DataStruct.CONFIG_TemperatureThreshold, DataStruct.CONFIG_UploadInterval);  	
             			CE_SetRTCTime(setRTCTime);		
             	break;
             	
+            	case 4: CE_UpdateImmediately();
+            	break;
+            	
             	default: break;
             }
         }    	
@@ -376,7 +381,7 @@
     return client->publish(pubTopic, message);    
 }
 
-int MQTT_PublishSensorVal(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, time_t inputTime, float DOVal) {
+int MQTT_PublishSensorVal(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, time_t inputTime, float TempVal, float DOVal) {
     MQTT::Message message;
     const char* pubTopic = MQTT_EVENT_TOPIC;
             
@@ -388,8 +393,8 @@
     }
     
     strftime(timeBuf, 50, "%Y/%m/%d %H:%M:%S", localtime(&inputTime));
-    sprintf(buf, "{\"type\":2,\"deviceId\":\"PROEVN\",\"time\":\"%s\",\"cmdId\":%d,\"DO(ppm)\":%.2f}",
-                timeBuf, commandID, DOVal);
+    sprintf(buf, "{\"type\":2,\"deviceId\":\"PROEVN\",\"time\":\"%s\",\"cmdId\":%d,\"temperature0\":%.2f,\"oxygen0\":%.2f}",
+                timeBuf, commandID, TempVal ,DOVal);
     message.qos        = MQTT::QOS0;
     message.retained   = false;
     message.dup        = false;
@@ -429,7 +434,8 @@
     return client->publish(pubTopic, message);       
 }
 
-int MQTT_PublishConfigValue(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, time_t inputTime, uint8_t mode, uint8_t minOxi, uint8_t maxOxi) {
+int MQTT_PublishConfigValue(MQTT::Client<MQTTNetwork, Countdown, MQTT_MAX_PACKET_SIZE>* client, time_t inputTime, uint8_t mode, 
+                                                    uint8_t OxyThreshold, uint8_t TemperatureThreshold, uint16_t UploadInterval) {
     MQTT::Message message;
     const char* pubTopic = MQTT_EVENT_TOPIC;         
     char buf[MQTT_MAX_PAYLOAD_SIZE];
@@ -440,8 +446,8 @@
         return MQTT::FAILURE; 
     }
     strftime(timeBuf, 50, "%Y/%m/%d %H:%M:%S", localtime(&inputTime));
-    sprintf(buf, "{\"type\":4,\"deviceId\":\"PROEVN\",\"time\":\"%s\",\"cmdId\":%d,\"mode\":%d,\"minOxygenVal\":%d,\"maxOxygenVal\":%d}",
-                timeBuf, commandID, mode, minOxi, maxOxi);
+    sprintf(buf, "{\"type\":4,\"deviceId\":\"PROEVN\",\"time\":\"%s\",\"cmdId\":%d,\"mode\":%d,\"OxygenThreshold\":%d,\"TemperatureThreshold\":%d,\"uploadInterval\":%d}",
+                timeBuf, commandID, mode, OxyThreshold, TemperatureThreshold, UploadInterval);
     message.qos        = MQTT::QOS0;
     message.retained   = false;
     message.dup        = false;
@@ -460,11 +466,11 @@
     switch (uploadType) {
         case (ADC_VALUE): 		retVal = MQTT_PublishADC(client, inputTime, uploadStruct.ADC_DOVal);
             break;
-        case (SENSOR_VALUE): 	retVal =  MQTT_PublishSensorVal(client, inputTime, uploadStruct.SENSOR_DOVal);
+        case (SENSOR_VALUE): 	retVal =  MQTT_PublishSensorVal(client, inputTime, uploadStruct.SENSOR_TEMPVal ,uploadStruct.SENSOR_DOVal);
             break;
         case (RELAY_STATE):		retVal = MQTT_PublishRelayState(client, inputTime, uploadStruct.RELAY_State_1, uploadStruct.RELAY_State_2, uploadStruct.RELAY_State_3);
             break;
-        case (CONFIG_VALUE): 	retVal = MQTT_PublishConfigValue(client, inputTime, uploadStruct.CONFIG_Mode, uploadStruct.CONFIG_MinOxi, uploadStruct.CONFIG_MaxOxi);
+        case (CONFIG_VALUE): 	retVal = MQTT_PublishConfigValue(client, inputTime, uploadStruct.CONFIG_Mode, uploadStruct.CONFIG_OxyThreshold, uploadStruct.CONFIG_TemperatureThreshold, uploadStruct.CONFIG_UploadInterval);
         	break;
         default: break;
     }
--- a/jsonString.md	Sun Mar 04 07:58:23 2018 +0000
+++ b/jsonString.md	Thu Mar 08 13:13:53 2018 +0000
@@ -1,1 +1,44 @@
-# PACKET JSON DETAILS
\ No newline at end of file
+# PACKET JSON DETAILS
+
+1/ SYSTEM TO SERVER
+
+{"type":1, "deviceId": "string", "cmdID":uint16_t,"adc0":uint16_t,"adc1":uint16_t,"adc2":uint16_t,"adc3":uint16_t} //no need to display this one
+
+{"type":2,"deviceId": "string", "time":epoch, "cmdID":uint16_t, "temperature0":float, "oxygen0":float}  //sensor values 
+                                                                 //map voi cai hien    //map voi cai hien thi
+                                                                  thi nhiet do           DO
+
+{"type":3,"deviceId": "string", "time":epoch, "cmdID":uint16_t,"relayState1":int 0 or 1, "relayState2":int 0 or 1} //relay state values
+                                 //thoi gian thiet bi             //Ngo ra 1                 //Ngo ra 2
+
+{"type":4, "deviceId": "string", "time":epoch, "cmdID":uint16_t,"mode":int 0 or 1,"OxygenThreshold":int ,"TemperatureThreshold":int, "uploadInterval":int} //config values
+                                       0 for automatic         //che do hoat dong   //nguong tac dong DO     //nguong tac dong nhiet do   //tan suat lay du lieu
+                                       1 for manual
+
+cmdID should increase 1 if all 4 types are sent successfully
+
+
+
+2/ SERVER TO SYSTEM
+//send one command when pressing one button. For example when there 4 buttons with 4 different commands, pressing button A will send command 0, not sending 
+all 4 commands 0 1 2 3
+(y em la moi nut nhan chi gui 1 command thoi dc ko anh? gui het 4 comman 1 luc de bi loi lam a)
+
+{"type":0, "deviceId": "string", "cmdID":uint16_t}   //Calib button
+                      
+{"type":1, "deviceId": "string", "cmdID":uint16_t, "AlarmTime":epoch, "SetRelayState1":int 0 or 1, "SetRelayState2":int 0 or 1} //Set time to turn on//off the relay
+                                                                                                                  //Cái này là tính nang hen gio bat tat relay,
+                                                                                                                    and Danh thêm vô giúp em dc ko ??
+
+{"type":2, "deviceId": "string", "cmdID":uint16_t,"relayState1":int 0 or 1, "relayState2":int 0 or 1} //Ði?u khi?n relay nhu cu
+
+{"type":3, "deviceId": "string", "cmdID":"uint16_t","mode":int 0 or 1,"OxygenThreshold":int,"TemperatureThreshold":int, "uploadInterval":int, "setRTCTime":epoch} 
+                                       0 for automatic                                                                             //field cuoi là cài dat gio cho
+                                       1 for manual                                                                                //thiet bi
+
+{"type":4, "deviceId": "string", "cmdID":uint16_t} //Cái này là nút synchronize, nh?n vô phát là c?p nh?t h?t giá tr? c?m bi?n
+
+    uint8_t  CONFIG_Mode;
+    uint8_t  CONFIG_MinOxi;
+    uint8_t  CONFIG_MaxOxi;
+    uint16_t CONFIG_UploadInterval;
\ No newline at end of file
--- a/mbed-os.lib	Sun Mar 04 07:58:23 2018 +0000
+++ b/mbed-os.lib	Thu Mar 08 13:13:53 2018 +0000
@@ -1,1 +1,1 @@
-https://github.com/ARMmbed/mbed-os/#569159b784f70feaa32ce226aaca896fb83452f7
+https://github.com/ARMmbed/mbed-os/#91e6db1ea251ffcc973001ed90477f42fdca5751
--- a/mbed_app.json	Sun Mar 04 07:58:23 2018 +0000
+++ b/mbed_app.json	Thu Mar 08 13:13:53 2018 +0000
@@ -17,10 +17,10 @@
             "value": "D0"
         },
         "esp8266-ssid": {
-            "value": "\"KH\""
+            "value": "\"GreenLantern\""
         },
         "esp8266-password": {
-            "value": "\"kh123456\""
+            "value": "\"deAlembert\""
         },
         "esp8266-debug": {
             "value": true