From d6b4a750258b34c79e3c643595a0ae1cb0e18bed Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Sat, 26 Aug 2023 15:59:55 +0800
Subject: [PATCH] Add modules and MQTT project

---
 project/mosquitto/etc/mqttd.conf                      |   69 
 modules/makefile                                      |   34 
 project/coreMQTT/coreMQTT/core_mqtt_state.h           |  310 
 project/coreMQTT/coreMQTT/core_mqtt_config_defaults.h |  204 
 project/openlibs/openssl/build.sh                     |  182 
 project/modules/pwm.h                                 |   44 
 project/coreMQTT/etc/mqttd.conf                       |   69 
 project/coreMQTT/coreMQTT/core_mqtt_state.c           | 1214 ++
 project/gpsd/gpsd.c                                   |  245 
 project/modules/leds.h                                |   58 
 project/booster/atcmd.h                               |   93 
 project/modules/modules.h                             |   24 
 project/openlibs/libgpiod/build.sh                    |  185 
 project/booster/atcmd.c                               |  300 
 project/coreMQTT/coreMQTT/transport_interface.h       |  316 
 project/booster/iniparser.h                           |  359 
 project/booster/list.h                                |  723 +
 project/coreMQTT/conf.c                               |  200 
 project/coreMQTT/coreMQTT/core_mqtt.c                 | 3314 +++++++
 project/modules/sht20.c                               |  244 
 project/booster/util_proc.c                           |  432 
 project/modules/makefile                              |   38 
 project/coreMQTT/coreJSON/core_json.h                 |  339 
 modules/infrared.c                                    |  274 
 project/coreMQTT/coreMQTT/core_mqtt.h                 | 1015 ++
 project/booster/iniparser.c                           |  837 +
 project/booster/util_proc.h                           |   89 
 project/mosquitto/.gitignore                          |    9 
 project/modules/sht20.h                               |   34 
 project/coreMQTT/coreSNTP/core_sntp_client.c          |  959 ++
 project/modules/relay.c                               |  156 
 project/booster/makefile                              |   35 
 project/coreMQTT/coreJSON/core_json.c                 | 1818 +++
 modules/sht20.c                                       |  331 
 project/coreMQTT/coreSNTP/core_sntp_client.h          |  655 +
 project/modules/relay.h                               |   56 
 project/modules/ds18b20.c                             |  119 
 project/modules/tsl2561.c                             |  209 
 project/booster/at-esp32.h                            |  140 
 project/coreMQTT/conf.h                               |   71 
 project/booster/at-esp32.c                            |  388 
 project/modules/tsl2561.h                             |   35 
 project/modules/ds18b20.h                             |   31 
 modules/relay.c                                       |  286 
 project/mosquitto/conf.c                              |  200 
 .gitignore                                            |   11 
 project/booster/comport.h                             |   74 
 project/openlibs/libevent/build.sh                    |  182 
 project/booster/comport.c                             |  520 +
 project/coreMQTT/coreSNTP/README.md                   |   28 
 project/booster/README.md                             |    4 
 project/booster/dictionary.h                          |  174 
 project/coreMQTT/makefile                             |   69 
 project/mosquitto/conf.h                              |   71 
 project/booster/dictionary.c                          |  381 
 project/coreMQTT/coreSNTP/core_sntp_config_defaults.h |  144 
 project/coreMQTT/coreMQTT/core_mqtt_serializer.h      | 1306 ++
 project/booster/logger.h                              |   68 
 project/coreMQTT/main.c                               |  458 
 project/mosquitto/main.c                              |  458 
 project/coreMQTT/coreMQTT/core_mqtt_serializer.c      | 2684 +++++
 project/booster/logger.c                              |  279 
 project/booster/ringbuf.c                             |  107 
 project/coreMQTT/coreMQTT/makefile                    |   35 
 project/coreMQTT/coreSNTP/core_sntp_serializer.h      |  535 +
 project/booster/esp32.h                               |   35 
 project/mosquitto/makefile                            |   68 
 project/booster/ringbuf.h                             |   57 
 project/gpsd/makefile                                 |   62 
 modules/pwm.c                                         |  311 
 project/coreMQTT/coreMQTT/README.md                   |   35 
 project/coreMQTT/coreSNTP/core_sntp_serializer.c      |  853 +
 project/coreMQTT/coreSNTP/makefile                    |   35 
 project/openlibs/cjson/build.sh                       |  182 
 project/booster/esp32.c                               |  166 
 project/openlibs/mosquitto/build.sh                   |  188 
 modules/leds.c                                        |  260 
 modules/tsl2561.c                                     |  337 
 project/modules/leds.c                                |  162 
 modules/ds18b20.c                                     |  134 
 project/openlibs/makefile                             |   10 
 project/coreMQTT/coreJSON/README.md                   |   65 
 project/coreMQTT/coreMQTT/core_mqtt_default_logging.h |  132 
 project/modules/pwm.c                                 |  213 
 project/coreMQTT/coreJSON/makefile                    |   35 
 85 files changed, 27,671 insertions(+), 0 deletions(-)

diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..18623c2
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,11 @@
+# git ignore files/folders in the list
+
+# ignore folders
+install/
+
+# ignore files
+*.so*
+*.o
+*.a
+cscope.*
+tags
diff --git a/modules/ds18b20.c b/modules/ds18b20.c
new file mode 100644
index 0000000..cb895a4
--- /dev/null
+++ b/modules/ds18b20.c
@@ -0,0 +1,134 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  ds18b20.c
+ *    Description:  This file is temperature sensor DS18B20 code
+ *
+ *        Version:  1.0.0(2023/8/10)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2023/8/10 12:13:26"
+ *
+ * Pin connection:
+ *
+ *               DS18B20 Module          Raspberry Pi Board
+ *                   VCC      <----->      #Pin1(3.3V)
+ *                   DQ       <----->      #Pin7(BCM GPIO4)
+ *                   GND      <----->      GND
+ *
+ * /boot/config.txt:
+ *
+ *          dtoverlay=w1-gpio-pullup,gpiopin=4
+ *
+ ********************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+int ds18b20_get_temperature(float *temp);
+
+int main(int argc, char *argv[])
+{
+    float       temp;
+
+    if( ds18b20_get_temperature(&temp) < 0 )
+    {
+        printf("ERROR: ds18b20 get temprature failure\n");
+        return 1;
+    }
+
+    printf("DS18B20 get temperature: %f ℃\n", temp);
+    return 0;
+}
+
+
+/* File Content:
+   pi@raspberrypi:~/guowenxue $ cat /sys/bus/w1/devices/28-041731f7c0ff/w1_slave
+   3a 01 4b 46 7f ff 0c 10 a5 : crc=a5 YES
+   3a 01 4b 46 7f ff 0c 10 a5 t=19625
+   */
+
+int ds18b20_get_temperature(float *temp)
+{
+    char            w1_path[50] = "/sys/bus/w1/devices/";
+    char            chip[20];
+    char            buf[128];
+    DIR            *dirp;
+    struct dirent  *direntp;
+    int             fd =-1;
+    char           *ptr;
+    float           value;
+    int             found = 0;
+
+    if( !temp )
+    {
+        return -1;
+    }
+
+    /*+-------------------------------------------------------------------+
+     *|  open dierectory /sys/bus/w1/devices to get chipset Serial Number |
+     *+-------------------------------------------------------------------+*/
+    if((dirp = opendir(w1_path)) == NULL)
+    {
+        printf("opendir error: %s\n", strerror(errno));
+        return -2;
+    }
+
+    while((direntp = readdir(dirp)) != NULL)
+    {
+        if(strstr(direntp->d_name,"28-"))
+        {
+            /* find and get the chipset SN filename */
+            strcpy(chip,direntp->d_name);
+            found = 1;
+            break;
+        }
+    }
+    closedir(dirp);
+
+    if( !found )
+    {
+        printf("Can not find ds18b20 in %s\n", w1_path);
+        return -3;
+    }
+
+    /* get DS18B20 sample file full path: /sys/bus/w1/devices/28-xxxx/w1_slave */
+    strncat(w1_path, chip, sizeof(w1_path)-strlen(w1_path));
+    strncat(w1_path, "/w1_slave", sizeof(w1_path)-strlen(w1_path));
+
+    /* open file /sys/bus/w1/devices/28-xxxx/w1_slave to get temperature */
+    if( (fd=open(w1_path, O_RDONLY)) < 0 )
+    {
+        printf("open %s error: %s\n", w1_path, strerror(errno));
+        return -4;
+    }
+
+    if(read(fd, buf, sizeof(buf)) < 0)
+    {
+        printf("read %s error: %s\n", w1_path, strerror(errno));
+        return -5;
+    }
+
+    ptr = strstr(buf, "t=");
+    if( !ptr )
+    {
+        printf("ERROR: Can not get temperature\n");
+        return -6;
+    }
+
+    ptr+=2;
+
+    /* convert string value to float value */
+    *temp = atof(ptr)/1000;
+
+    close(fd);
+
+    return 0;
+}
diff --git a/modules/infrared.c b/modules/infrared.c
new file mode 100644
index 0000000..dd39d97
--- /dev/null
+++ b/modules/infrared.c
@@ -0,0 +1,274 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  led.c
+ *    Description:  This file is HC-SR501 infrared sensor code
+ *
+ *
+ * Pin connection:
+ *               HC-SR501 Module           Raspberry Pi Board
+ *                  VCC       <----->      5V
+ *                  I/O       <----->      #Pin18(BCM GPIO24)
+ *                  GND       <----->      GND
+ *
+ * System install:
+ *                  sudo apt install -y libgpiod-dev gpiod
+ *
+ *
+ ********************************************************************************/
+
+
+#include <stdio.h>
+#include <unistd.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <string.h>
+#include <signal.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include <gpiod.h>
+
+/* infrared code */
+enum
+{
+    IR1 = 0,
+    IR_CNT,
+};
+
+/* infrared hardware information */
+typedef struct ir_info_s
+{
+    const char         *name;  /* infrared name  */
+    int                 gpio;  /* infrared BCM pin number */
+    int                 active;/* infrared active GPIO level: 0->low 1->high */
+    struct gpiod_line  *line;  /* libgpiod line */
+} ir_info_t;
+
+static ir_info_t    ir_info[IR_CNT] =
+{
+    {"IR1", 23, 1, NULL },
+};
+
+/* infrared API context */
+typedef struct ir_ctx_s
+{
+    struct gpiod_chip   *chip;
+    ir_info_t           *ir;
+    int                  count;
+} ir_ctx_t;
+
+int init_infrared(ir_ctx_t *ctx);
+int term_infrared(ir_ctx_t *ctx);
+int detect_infrared(ir_ctx_t *ctx, int which);
+static inline void msleep(unsigned long ms);
+
+static inline void banner(const char *progname)
+{
+    printf("%s program Version v1.0.0\n", progname);
+    printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
+}
+
+static void program_usage(const char *progname)
+{
+
+    printf("Usage: %s [OPTION]...\n", progname);
+    printf(" This is infrared detect program. \n");
+
+    printf(" -d[device  ]  Specify infrared device, such as 0\n");
+    printf(" -h[help    ]  Display this help information\n");
+    printf(" -v[version ]  Display the program version\n");
+
+    printf("\n");
+    banner(progname);
+    return;
+}
+
+int g_stop = 0;
+void sig_handler(int signum)
+{
+    switch( signum )
+    {
+        case SIGINT:
+        case SIGTERM:
+            g_stop = 1;
+
+        default:
+            break;
+    }
+
+    return ;
+}
+
+int main(int argc, char **argv)
+{
+    int             i, rv;
+    char           *progname=NULL;
+
+    ir_ctx_t        ir_ctx =
+    {
+        .chip  = NULL,
+        .ir    = ir_info,
+        .count = IR_CNT,
+    };
+
+    struct option long_options[] = {
+        {"version", no_argument, NULL, 'v'},
+        {"help", no_argument, NULL, 'h'},
+        {NULL, 0, NULL, 0}
+    };
+
+    progname = basename(argv[0]);
+
+    /* Parser the command line parameters */
+    while ((rv = getopt_long(argc, argv, "vh", long_options, NULL)) != -1)
+    {
+        switch (rv)
+        {
+            case 'v':  /* Get software version */
+                banner(progname);
+                return EXIT_SUCCESS;
+
+            case 'h':  /* Get help information */
+                program_usage(progname);
+                return 0;
+
+            default:
+                break;
+        }
+    }
+
+    if( (rv=init_infrared(&ir_ctx)) < 0 )
+    {
+        printf("initial infrared gpio failure, rv=%d\n", rv);
+        return 1;
+    }
+
+    signal(SIGINT,  sig_handler);
+    signal(SIGTERM, sig_handler);
+
+    while( !g_stop )
+    {
+		for( i=0; i<ir_ctx.count; i++ )
+		{
+			rv = detect_infrared(&ir_ctx, i);
+			printf("Infrared[%d] monitor: %s\n", i, rv ? "Someone is closing!":"No one nearby!");
+		}
+        sleep(1);
+    }
+
+    term_infrared(&ir_ctx);
+    return 0;
+}
+
+int term_infrared(ir_ctx_t *ctx)
+{
+    int            i;
+    ir_info_t     *ir;
+
+    if( !ctx )
+    {
+        printf("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( !ctx->chip )
+        return 0;
+
+    for(i=0; i<ctx->count; i++)
+    {
+        ir = &ctx->ir[i];
+
+        if( ir->line )
+            gpiod_line_release(ir->line);
+    }
+
+    gpiod_chip_close(ctx->chip);
+    return 0;
+}
+
+int init_infrared(ir_ctx_t *ctx)
+{
+    int            i, rv;
+    ir_info_t     *ir;
+
+    if( !ctx )
+    {
+        printf("Invalid input arguments\n");
+        return -1;
+    }
+
+    ctx->chip = gpiod_chip_open_by_name("gpiochip0");
+    if( !ctx->chip )
+    {
+        printf("open gpiochip failure, maybe you need running as root\n");
+        return -2;
+    }
+
+
+    for(i=0; i<ctx->count; i++)
+    {
+        ir = &ctx->ir[i];
+
+        ir ->line = gpiod_chip_get_line(ctx->chip, ir->gpio);
+        if( !ir->line )
+        {
+            printf("open gpioline for %s[%d] failed\n", ir->name, ir->gpio);
+            rv = -3;
+            goto failed;
+        }
+
+        rv  = gpiod_line_request_input(ir->line, ir->name);
+        if( rv )
+        {
+            printf("request gpio input for %s[%d] failed\n", ir->name, ir->gpio);
+            rv = -4;
+            goto failed;
+        }
+    }
+
+    return 0;
+
+failed:
+    term_infrared(ctx);
+    return rv;
+}
+
+int detect_infrared(ir_ctx_t *ctx, int which)
+{
+    int            rv = 0;
+    ir_info_t     *ir;
+
+    if( !ctx || which<0 || which>=ctx->count )
+    {
+        printf("Invalid input arguments\n");
+        return 0;
+    }
+
+    ir = &ctx->ir[which];
+
+	return gpiod_line_get_value(ir->line)==ir->active ? 1 : 0;
+}
+
+
+static inline void msleep(unsigned long ms)
+{
+    struct timespec cSleep;
+    unsigned long ulTmp;
+
+    cSleep.tv_sec = ms / 1000;
+    if (cSleep.tv_sec == 0)
+    {
+        ulTmp = ms * 10000;
+        cSleep.tv_nsec = ulTmp * 100;
+    }
+    else
+    {
+        cSleep.tv_nsec = 0;
+    }
+
+    nanosleep(&cSleep, 0);
+
+    return ;
+}
diff --git a/modules/leds.c b/modules/leds.c
new file mode 100644
index 0000000..7ea158b
--- /dev/null
+++ b/modules/leds.c
@@ -0,0 +1,260 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  led.c
+ *    Description:  This file is used to control RGB 3-colors LED
+ *
+ *
+ * Pin connection:
+ *               RGB Led Module           Raspberry Pi Board
+ *                   R        <----->      #Pin33(BCM GPIO13)
+ *                   G        <----->      #Pin35(BCM GPIO19)
+ *                   B        <----->      #Pin37(BCM GPIO26)
+ *                  GND       <----->      GND
+ *
+ * System install:
+ *                  sudo apt install -y libgpiod-dev gpiod
+ *
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <gpiod.h>
+
+#define DELAY     500
+
+#define ON        1
+#define OFF       0
+
+/* Three LEDs code */
+enum
+{
+    LED_R = 0,
+    LED_G,
+    LED_B,
+    LEDCNT,
+};
+
+/* Three LEDs hardware information */
+typedef struct led_info_s
+{
+    const char         *name;  /* RGB 3-color LED name  */
+    int                 gpio;  /* RGB 3-color LED BCM pin number */
+    int                 active;/* RGB 3-color LED active GPIO level: 0->low 1->high */
+    struct gpiod_line  *line;  /* libgpiod line */
+} led_info_t;
+
+static led_info_t    leds_info[LEDCNT] =
+{
+    {"red",   13, 1, NULL },
+    {"green", 19, 1, NULL },
+    {"blue",  26, 1, NULL },
+};
+
+/* Three LEDs API context */
+typedef struct led_ctx_s
+{
+    struct gpiod_chip   *chip;
+    led_info_t          *leds;
+    int                  count;
+} led_ctx_t;
+
+int init_led(led_ctx_t *ctx);
+int term_led(led_ctx_t *ctx);
+int turn_led(led_ctx_t *ctx, int which, int cmd);
+static inline void msleep(unsigned long ms);
+
+
+int g_stop = 0;
+
+void sig_handler(int signum)
+{
+    switch( signum )
+    {
+        case SIGINT:
+        case SIGTERM:
+            g_stop = 1;
+
+        default:
+            break;
+    }
+
+    return ;
+}
+
+int main(int argc, char *argv[])
+{
+    int                 rv;
+    led_ctx_t           led_ctx =
+    {
+        .chip  = NULL,
+        .leds  = leds_info,
+        .count = LEDCNT,
+    };
+
+    if( (rv=init_led(&led_ctx)) < 0 )
+    {
+        printf("initial leds gpio failure, rv=%d\n", rv);
+        return 1;
+    }
+    printf("initial RGB Led gpios okay\n", rv);
+
+    signal(SIGINT,  sig_handler);
+    signal(SIGTERM, sig_handler);
+
+    while( !g_stop )
+    {
+        turn_led(&led_ctx, LED_R, ON);
+        msleep(DELAY);
+        turn_led(&led_ctx, LED_R, OFF);
+        msleep(DELAY);
+
+        turn_led(&led_ctx, LED_G, ON);
+        msleep(DELAY);
+        turn_led(&led_ctx, LED_G, OFF);
+        msleep(DELAY);
+
+        turn_led(&led_ctx, LED_B, ON);
+        msleep(DELAY);
+        turn_led(&led_ctx, LED_B, OFF);
+        msleep(DELAY);
+    }
+
+    term_led(&led_ctx);
+    return 0;
+}
+
+int term_led(led_ctx_t *ctx)
+{
+    int            i;
+    led_info_t    *led;
+
+    printf("terminate RGB Led gpios\n");
+
+    if( !ctx )
+    {
+        printf("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( !ctx->chip )
+        return 0;
+
+    for(i=0; i<ctx->count; i++)
+    {
+        led = &ctx->leds[i];
+
+        if( led->line )
+            gpiod_line_release(led->line);
+    }
+
+    gpiod_chip_close(ctx->chip);
+    return 0;
+}
+
+
+int init_led(led_ctx_t *ctx)
+{
+    int            i, rv;
+    led_info_t    *led;
+
+    if( !ctx )
+    {
+        printf("Invalid input arguments\n");
+        return -1;
+    }
+
+    ctx->chip = gpiod_chip_open_by_name("gpiochip0");
+    if( !ctx->chip )
+    {
+        printf("open gpiochip failure, maybe you need running as root\n");
+        return -2;
+    }
+
+
+    for(i=0; i<ctx->count; i++)
+    {
+        led = &ctx->leds[i];
+
+        led->line = gpiod_chip_get_line(ctx->chip, led->gpio);
+        if( !led->line )
+        {
+            printf("open gpioline for %s[%d] failed\n", led->name, led->gpio);
+            rv = -3;
+            goto failed;
+        }
+
+        rv  = gpiod_line_request_output(led->line, led->name, !led->active);
+        if( rv )
+        {
+            printf("request gpio output for %5s[%d] failed\n", led->name, led->gpio);
+            rv = -4;
+            goto failed;
+        }
+
+        //printf("request %5s led[%d] for gpio output okay\n", led->name, led->gpio);
+    }
+
+    return 0;
+
+failed:
+    term_led(ctx);
+    return rv;
+}
+
+int turn_led(led_ctx_t *ctx, int which, int cmd)
+{
+    int            rv = 0;
+    led_info_t    *led;
+
+    if( !ctx || which<0 || which>=ctx->count )
+    {
+        printf("Invalid input arguments\n");
+        return -1;
+    }
+
+    led = &ctx->leds[which];
+
+    if( OFF == cmd )
+    {
+        gpiod_line_set_value(led->line, !led->active);
+    }
+    else
+    {
+        gpiod_line_set_value(led->line, led->active);
+    }
+
+    return 0;
+}
+
+static inline void msleep(unsigned long ms)
+{
+    struct timespec cSleep;
+    unsigned long ulTmp;
+
+    cSleep.tv_sec = ms / 1000;
+    if (cSleep.tv_sec == 0)
+    {
+        ulTmp = ms * 10000;
+        cSleep.tv_nsec = ulTmp * 100;
+    }
+    else
+    {
+        cSleep.tv_nsec = 0;
+    }
+
+    nanosleep(&cSleep, 0);
+
+    return ;
+}
+
diff --git a/modules/makefile b/modules/makefile
new file mode 100644
index 0000000..dad362d
--- /dev/null
+++ b/modules/makefile
@@ -0,0 +1,34 @@
+#********************************************************************************
+#      Copyright:  (C) 2023 LingYun IoT System Studio
+#                  All rights reserved.
+#
+#       Filename:  Makefile
+#    Description:  This file used to compile all the C file to respective binary,
+#                  and it will auto detect cross compile or local compile.
+#
+#        Version:  1.0.0(11/08/23)
+#         Author:  Guo Wenxue <guowenxue@gmail.com>
+#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+
+BUILD_ARCH=$(shell uname -m)
+ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
+	CROSS_COMPILE?=arm-linux-gnueabihf-
+endif
+
+CC=${CROSS_COMPILE}gcc
+
+LDFLAGS+=-lm -lgpiod
+
+SRCFILES = $(wildcard *.c)
+BINARIES=$(SRCFILES:%.c=%)
+
+all: ${BINARIES}
+
+%: %.c 
+	$(CC) $(CFLAGS) -o $@ $< $(LDFLAGS)
+
+clean:
+	rm -f ${BINARIES}
diff --git a/modules/pwm.c b/modules/pwm.c
new file mode 100644
index 0000000..ac1412f
--- /dev/null
+++ b/modules/pwm.c
@@ -0,0 +1,311 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  pwm.c
+ *    Description:  This file is used to control PWM buzzer/Led
+ *
+ * Pin connection:
+ *               PWM Module              Raspberry Pi Board
+ *                  VCC       <----->      5V
+ *                 buzzer     <----->      #Pin32(BCM GPIO12)
+ *                  Led       <----->      #Pin33(BCM GPIO13)
+ *                  GND       <----->      GND
+ *
+ * /boot/config.txt:
+ *
+ *          dtoverlay=pwm,pin=12,func=4 (Buzzer)
+ *          dtoverlay=pwm,pin=13,func=4 (Led)
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include <gpiod.h>
+
+#define PWMCHIP_PATH      "/sys/class/pwm/pwmchip0"
+
+#define ENABLE            1
+#define DISABLE           0
+
+int init_pwm(int channel, int freq, int duty);
+int turn_pwm(int channel, int status);
+int term_pwm(int channel);
+static inline void msleep(unsigned long ms);
+
+static inline void banner(const char *progname)
+{
+    printf("%s program Version v1.0.0\n", progname);
+    printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
+}
+
+static void program_usage(const char *progname)
+{
+
+    printf("Usage: %s [OPTION]...\n", progname);
+    printf(" This is pwm control program. \n");
+
+    printf(" -c[channel ]  Specify PWM channel, such as 0\n");
+    printf(" -f[freq    ]  Specify PWM frequency, default 2500(Hz)\n");
+    printf(" -d[duty    ]  Specify PWM duty, default 50(50%)\n");
+    printf(" -s[status  ]  Specify PWM status: 1->on(default), 0->off\n");
+    printf(" -h[help    ]  Display this help information\n");
+    printf(" -v[version ]  Display the program version\n");
+    printf("\n");
+
+	printf("Example buzzer : %s -c 0 -f 2750 -d 50 -s 1\n", progname);
+	printf("Example Led    : %s -c 1 -f 100 -d 50 -s 1\n", progname);
+	printf("Example disable: %s -c 0 -s 0\n", progname);
+
+    printf("\n");
+    banner(progname);
+    return;
+}
+
+int main(int argc, char **argv)
+{
+    int             rv;
+    char           *progname=NULL;
+    int             channel = -1;
+    int             freq = 2500;
+    int             duty = 50;
+    int             status = ENABLE;
+
+    struct option long_options[] = {
+        {"channel", required_argument, NULL, 'c'},
+        {"freq", required_argument, NULL, 'f'},
+        {"duty", required_argument, NULL, 'd'},
+        {"status", required_argument, NULL, 's'},
+        {"version", no_argument, NULL, 'v'},
+        {"help", no_argument, NULL, 'h'},
+        {NULL, 0, NULL, 0}
+    };
+
+    progname = basename(argv[0]);
+
+    /* Parser the command line parameters */
+    while ((rv = getopt_long(argc, argv, "c:f:d:s:vh", long_options, NULL)) != -1)
+    {
+        switch (rv)
+        {
+            case 'c': /*  Set pwm channel, such as 0,1 */
+                channel = atoi(optarg);
+                break;
+
+            case 'f': /*  Set pwm frequency */
+                freq = atoi(optarg);
+                break;
+
+            case 'd': /*  Set pwm duty cycle */
+                duty = atoi(optarg);
+                break;
+
+            case 's': /*  Set pwm status, 0 for disable and 1 for enable */
+                status = atoi(optarg);
+                break;
+
+            case 'v':  /* Get software version */
+                banner(progname);
+                return EXIT_SUCCESS;
+
+            case 'h':  /* Get help information */
+                program_usage(progname);
+                return 0;
+
+            default:
+                break;
+        }
+    }
+
+	if(channel < 0 )
+	{
+		program_usage(progname);
+		return 0;
+	}
+
+	if( status )
+	{
+		if( (rv=init_pwm(channel, freq, duty)) < 0 )
+		{
+			printf("initial PWM failure, rv=%d\n", rv);
+			return 1;
+		}
+
+		turn_pwm(channel, ENABLE);
+	}
+	else
+	{
+		term_pwm(channel);
+	}
+
+    return 0;
+}
+
+/* check PWM $channel export or not */
+int check_pwm(int channel)
+{
+	char          path[256];
+
+	/* check /sys/class/pwm/pwmchip0/pwmN exist or not */
+	snprintf(path, sizeof(path), "%s/pwm%d", PWMCHIP_PATH, channel);
+	return access(path, F_OK) ? 0 : 1;
+}
+
+/* export($export=1)/unexport($export=0) PWM $channel */
+int export_pwm(int channel, int export)
+{
+	int           fd;
+	char          buf[32];
+	char          path[256];
+
+	if( export && check_pwm(channel) )
+		return 0; /* export already when export  */
+	else if( !export && !check_pwm(channel) )
+		return 0; /* unexport already when unexport  */
+
+	/* export PWM channel by echo N > /sys/class/pwm/pwmchip0/export */
+	snprintf(path, sizeof(path), "%s/%s", PWMCHIP_PATH, export?"export":"unexport");
+	if( (fd=open(path, O_WRONLY)) < 0 )
+	{
+		printf("open '%s' failed: %s\n", path, strerror(errno));
+		return -3;
+	}
+
+	snprintf(buf, sizeof(buf), "%d", channel);
+	write(fd, buf, strlen(buf));
+
+	msleep(100);
+
+	if( export && check_pwm(channel) )
+		return 0; /* export already when export  */
+	else if( !export && !check_pwm(channel) )
+		return 0; /* unexport already when unexport  */
+
+	return -4;
+}
+
+/* configure PWM $channel */
+int config_pwm(int channel, int freq, int duty) 
+{
+	int           fd;
+	char          buf[32];
+	char          path[256];
+	int           period;
+	int           duty_cycle;
+
+	if( !check_pwm(channel) )
+		return -2;
+
+	/* open PWM period file and write period in ns */
+	snprintf(path, sizeof(path), "%s/pwm%d/period", PWMCHIP_PATH, channel);
+	if( (fd=open(path, O_WRONLY)) < 0 )
+	{
+		printf("open '%s' failed: %s\n", path, strerror(errno));
+		return -3;
+	}
+	period = 1000000000/freq;
+	snprintf(buf, sizeof(buf), "%d", period);
+	write(fd, buf, strlen(buf));
+
+	/* open PWM duty_cycle file and write duty */
+	snprintf(path, sizeof(path), "%s/pwm%d/duty_cycle", PWMCHIP_PATH, channel);
+	if( (fd=open(path, O_WRONLY)) < 0 )
+	{
+		printf("open '%s' failed: %s\n", path, strerror(errno));
+		return -3;
+	}
+	duty_cycle = (period*duty) / 100;
+	snprintf(buf, sizeof(buf), "%d", duty_cycle);
+	write(fd, buf, strlen(buf));
+
+	return 0;
+}
+
+int init_pwm(int channel, int freq, int duty)
+{
+	int           rv;
+	char          buf[32];
+	char          path[256];
+
+	if( (rv=export_pwm(channel, 1)) )
+	{
+		printf("export PWM channel[%d] failed, rv=%d\n", channel, rv);
+		return rv; 
+	}
+
+	if( (rv=config_pwm(channel, freq, duty)) )
+	{
+		printf("config PWM channel[%d] failed, rv=%d\n", channel, rv);
+		return rv;
+	}
+
+	printf("config pwm%d with freq[%d] duty[%d] okay\n", channel, freq, duty);
+
+	return 0;
+}
+
+int turn_pwm(int channel, int status)
+{
+	int           fd;
+	char          buf[32];
+	char          path[256];
+
+	if( !check_pwm(channel) )
+		return -1;
+
+	/* open PWM enable file and enable(1)/disable(0) it */
+	snprintf(path, sizeof(path), "%s/pwm%d/enable", PWMCHIP_PATH, channel);
+	if( (fd=open(path, O_WRONLY)) < 0 )
+	{
+		printf("open '%s' failed: %s\n", path, strerror(errno));
+		return -3;
+	}
+	snprintf(buf, sizeof(buf), "%d", status?1:0);
+	write(fd, buf, strlen(buf));
+
+	printf("pwm[%d] %s\n", channel, status?"enable":"disable");
+
+	return 0;
+}
+
+int term_pwm(int channel)
+{
+	if( !check_pwm(channel) )
+		return 0;
+
+	turn_pwm(channel, DISABLE);
+	export_pwm(channel, 0);
+
+	return 0;
+}
+
+static inline void msleep(unsigned long ms)
+{
+    struct timespec cSleep;
+    unsigned long ulTmp;
+
+    cSleep.tv_sec = ms / 1000;
+    if (cSleep.tv_sec == 0)
+    {
+        ulTmp = ms * 10000;
+        cSleep.tv_nsec = ulTmp * 100;
+    }
+    else
+    {
+        cSleep.tv_nsec = 0;
+    }
+
+    nanosleep(&cSleep, 0);
+
+    return ;
+}
diff --git a/modules/relay.c b/modules/relay.c
new file mode 100644
index 0000000..4f14801
--- /dev/null
+++ b/modules/relay.c
@@ -0,0 +1,286 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  relay.c
+ *    Description:  This file is used to control Relay
+ *
+ *
+ * Pin connection:
+ *                 Relay Module           Raspberry Pi Board
+ *                  VCC       <----->      5V
+ *                   I        <----->      #Pin16(BCM GPIO23)
+ *                  GND       <----->      GND
+ *
+ * System install:
+ *                  sudo apt install -y libgpiod-dev gpiod
+ *
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include <gpiod.h>
+
+#define DELAY     500
+
+#define ON        1
+#define OFF       0
+
+/* relay code */
+enum
+{
+    RELAY1 = 0,
+    RELAY_CNT,
+};
+
+/* Relay hardware information */
+typedef struct relay_info_s
+{
+    const char         *name;  /* Relay name  */
+    int                 gpio;  /* Relay BCM pin number */
+    int                 active;/* Relay active GPIO level: 0->low 1->high */
+    struct gpiod_line  *line;  /* libgpiod line */
+} relay_info_t;
+
+static relay_info_t    relay_info[RELAY_CNT] =
+{
+    {"relay1",  23, 1, NULL },
+};
+
+/* Relay API context */
+typedef struct relay_ctx_s
+{
+    struct gpiod_chip   *chip;
+    relay_info_t        *relay;
+    int                  count;
+} relay_ctx_t;
+
+int init_relay(relay_ctx_t *ctx);
+int term_relay(relay_ctx_t *ctx);
+int turn_relay(relay_ctx_t *ctx, int which, int cmd);
+static inline void msleep(unsigned long ms);
+
+static inline void banner(const char *progname)
+{
+    printf("%s program Version v1.0.0\n", progname);
+    printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
+}
+
+static void program_usage(const char *progname)
+{
+
+    printf("Usage: %s [OPTION]...\n", progname);
+    printf(" This is relay control program. \n");
+
+    printf(" -d[device  ]  Specify relay device, such as 0\n");
+    printf(" -s[status  ]  Specify relay status, 0 for open, 1 for close\n");
+    printf(" -h[help    ]  Display this help information\n");
+    printf(" -v[version ]  Display the program version\n");
+
+    printf("\n");
+    banner(progname);
+    return;
+}
+
+int main(int argc, char **argv)
+{
+    int             rv;
+    char           *progname=NULL;
+    int             which = -1;
+    int             status = ON;
+
+    relay_ctx_t     relay_ctx =
+    {
+        .chip  = NULL,
+        .relay  = relay_info,
+        .count = RELAY_CNT,
+    };
+
+    struct option long_options[] = {
+        {"device", required_argument, NULL, 'd'},
+        {"status", required_argument, NULL, 's'},
+        {"version", no_argument, NULL, 'v'},
+        {"help", no_argument, NULL, 'h'},
+        {NULL, 0, NULL, 0}
+    };
+
+
+    progname = basename(argv[0]);
+
+    /* Parser the command line parameters */
+    while ((rv = getopt_long(argc, argv, "d:s:vh", long_options, NULL)) != -1)
+    {
+        switch (rv)
+        {
+            case 'd': /*  Set relay number, such as 0...max */
+                which = atoi(optarg);
+                break;
+
+            case 's': /*  Set relay status, 0 for open and 1 for close */
+                status = atoi(optarg);
+                break;
+
+            case 'v':  /* Get software version */
+                banner(progname);
+                return EXIT_SUCCESS;
+
+            case 'h':  /* Get help information */
+                program_usage(progname);
+                return 0;
+
+            default:
+                break;
+        }
+    }
+
+    if( which<0 || which>=relay_ctx.count )
+    {
+        printf("ERROR: Invalid relay index [%d], max=%d\n", which, relay_ctx.count-1);
+        return 0;
+    }
+
+
+    if( (rv=init_relay(&relay_ctx)) < 0 )
+    {
+        printf("initial relay gpio failure, rv=%d\n", rv);
+        return 1;
+    }
+
+    turn_relay(&relay_ctx, which, status);
+
+    term_relay(&relay_ctx);
+    return 0;
+}
+
+int term_relay(relay_ctx_t *ctx)
+{
+    int            i;
+    relay_info_t  *relay;
+
+    if( !ctx )
+    {
+        printf("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( !ctx->chip )
+        return 0;
+
+    for(i=0; i<ctx->count; i++)
+    {
+        relay = &ctx->relay[i];
+
+        if( relay->line )
+            gpiod_line_release(relay->line);
+    }
+
+    gpiod_chip_close(ctx->chip);
+    return 0;
+}
+
+
+int init_relay(relay_ctx_t *ctx)
+{
+    int            i, rv;
+    relay_info_t  *relay;
+
+    if( !ctx )
+    {
+        printf("Invalid input arguments\n");
+        return -1;
+    }
+
+    ctx->chip = gpiod_chip_open_by_name("gpiochip0");
+    if( !ctx->chip )
+    {
+        printf("open gpiochip failure, maybe you need running as root\n");
+        return -2;
+    }
+
+
+    for(i=0; i<ctx->count; i++)
+    {
+        relay = &ctx->relay[i];
+
+        relay->line = gpiod_chip_get_line(ctx->chip, relay->gpio);
+        if( !relay->line )
+        {
+            printf("open gpioline for %s[%d] failed\n", relay->name, relay->gpio);
+            rv = -3;
+            goto failed;
+        }
+
+        rv  = gpiod_line_request_output(relay->line, relay->name, !relay->active);
+        if( rv )
+        {
+            printf("request gpio output for %5s[%d] failed\n", relay->name, relay->gpio);
+            rv = -4;
+            goto failed;
+        }
+
+        //printf("request %s[%d] for gpio output okay\n", relay->name, relay->gpio);
+    }
+
+    return 0;
+
+failed:
+    term_relay(ctx);
+    return rv;
+}
+
+int turn_relay(relay_ctx_t *ctx, int which, int cmd)
+{
+    int            rv = 0;
+    relay_info_t  *relay;
+
+    if( !ctx || which<0 || which>=ctx->count )
+    {
+        printf("Invalid input arguments\n");
+        return -1;
+    }
+
+    relay = &ctx->relay[which];
+
+    if( OFF == cmd )
+    {
+        gpiod_line_set_value(relay->line, !relay->active);
+    }
+    else
+    {
+        gpiod_line_set_value(relay->line, relay->active);
+    }
+
+    return 0;
+}
+
+static inline void msleep(unsigned long ms)
+{
+    struct timespec cSleep;
+    unsigned long ulTmp;
+
+    cSleep.tv_sec = ms / 1000;
+    if (cSleep.tv_sec == 0)
+    {
+        ulTmp = ms * 10000;
+        cSleep.tv_nsec = ulTmp * 100;
+    }
+    else
+    {
+        cSleep.tv_nsec = 0;
+    }
+
+    nanosleep(&cSleep, 0);
+
+    return ;
+}
diff --git a/modules/sht20.c b/modules/sht20.c
new file mode 100644
index 0000000..d84877f
--- /dev/null
+++ b/modules/sht20.c
@@ -0,0 +1,331 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  sht20.c
+ *    Description:  This file is temperature and relative humidity sensor SHT20 code
+ *
+ *        Version:  1.0.0(10/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
+ *
+ * Pin connection:
+ *               STH20 Module            Raspberry Pi Board
+ *                   VCC      <----->      #Pin1(3.3V)
+ *                   SDA      <----->      #Pin3(SDA, BCM GPIO2)
+ *                   SCL      <----->      #Pin5(SCL, BCM GPIO3)
+ *                   GND      <----->      GND
+ *
+ * /boot/config.txt:
+ *                  dtoverlay=i2c1,pins_2_3
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len);
+int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size);
+int sht20_checksum(uint8_t *data, int len, int8_t checksum);
+static inline void msleep(unsigned long ms);
+static inline void dump_buf(const char *prompt, uint8_t *buf, int size);
+
+static inline void banner(const char *progname)
+{
+    printf("%s program Version v1.0.0\n", progname);
+    printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
+}
+
+static void program_usage(const char *progname)
+{
+
+    printf("Usage: %s [OPTION]...\n", progname);
+    printf(" %s is SHT20 temperature and humidity sensor program. \n", progname);
+
+    printf(" -d[device  ]  Specify I2C device, such as /dev/i2c-1\n");
+    printf(" -h[help    ]  Display this help information\n");
+    printf(" -v[version ]  Display the program version\n");
+
+    printf("\n");
+    banner(progname);
+    return;
+}
+
+int main(int argc, char **argv)
+{
+    int             fd, rv;
+    float           temp, rh;
+    char           *dev = "/dev/i2c-1";
+    char           *progname=NULL;
+    uint8_t         buf[4];
+
+    struct option long_options[] = {
+        {"device", required_argument, NULL, 'd'},
+        {"version", no_argument, NULL, 'v'},
+        {"help", no_argument, NULL, 'h'},
+        {NULL, 0, NULL, 0}
+    };
+
+    progname = basename(argv[0]);
+
+    /* Parser the command line parameters */
+    while ((rv = getopt_long(argc, argv, "d:vh", long_options, NULL)) != -1)
+    {
+        switch (rv)
+        {
+            case 'd': /*  Set I2C device path: /dev/i2c-1 */
+                dev = optarg;
+                break;
+
+            case 'v':  /*  Get software version */
+                banner(progname);
+                return EXIT_SUCCESS;
+
+            case 'h':  /*  Get help information */
+                program_usage(progname);
+                return 0;
+
+            default:
+                break;
+        }
+    }
+
+
+    /*+--------------------------------+
+     *|     open /dev/i2c-x device     |
+     *+--------------------------------+*/
+    if( (fd=open(dev, O_RDWR)) < 0)
+    {
+        printf("i2c device '%s' open failed: %s\n", dev, strerror(errno));
+        return -1;
+    }
+
+    /*+--------------------------------+
+     *|   software reset SHT20 sensor  |
+     *+--------------------------------+*/
+
+    buf[0] = 0xFE;
+    i2c_write(fd, 0x40, buf, 1);
+
+    msleep(50);
+
+
+    /*+--------------------------------+
+     *|   trigger temperature measure  |
+     *+--------------------------------+*/
+
+    buf[0] = 0xF3;
+    i2c_write(fd, 0x40, buf, 1);
+
+    msleep(85); /* datasheet: typ=66, max=85 */
+
+    memset(buf, 0, sizeof(buf));
+    i2c_read(fd, 0x40, buf, 3);
+    dump_buf("Temperature sample data: ", buf, 3);
+
+    if( !sht20_checksum(buf, 2, buf[2]) )
+    {
+        printf("Temperature sample data CRC checksum failure.\n");
+        goto cleanup;
+    }
+
+    temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
+
+
+    /*+--------------------------------+
+     *|    trigger humidity measure    |
+     *+--------------------------------+*/
+
+    buf[0] = 0xF5;
+    i2c_write(fd, 0x40, buf, 1);
+
+    msleep(29); /* datasheet: typ=22, max=29 */
+
+    memset(buf, 0, sizeof(buf));
+    i2c_read(fd, 0x40, buf, 3);
+    dump_buf("Relative humidity sample data: ", buf, 3);
+
+    if( !sht20_checksum(buf, 2, buf[2]) )
+    {
+        printf("Relative humidity sample data CRC checksum failure.\n");
+        goto cleanup;
+    }
+
+    rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
+
+    /*+--------------------------------+
+     *|    print the measure result    |
+     *+--------------------------------+*/
+
+    printf("Temperature=%lf 'C relative humidity=%lf%%\n", temp, rh);
+
+cleanup:
+    close(fd);
+    return 0;
+}
+
+int sht20_checksum(uint8_t *data, int len, int8_t checksum)
+{
+    int8_t crc = 0;
+    int8_t bit;
+    int8_t byteCtr;
+
+    //calculates 8-Bit checksum with given polynomial: x^8 + x^5 + x^4 + 1
+    for (byteCtr = 0; byteCtr < len; ++byteCtr)
+    {
+        crc ^= (data[byteCtr]);
+        for ( bit = 8; bit > 0; --bit)
+        {
+            /* x^8 + x^5 + x^4 + 1 = 0001 0011 0001 = 0x131 */
+            if (crc & 0x80) crc = (crc << 1) ^ 0x131;
+            else crc = (crc << 1);
+        }
+    }
+
+    if (crc != checksum)
+        return 0;
+    else
+        return 1;
+}
+
+int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len)
+{
+    struct i2c_rdwr_ioctl_data i2cdata;
+    int rv = 0;
+
+    if( !data || len<= 0)
+    {
+        printf("%s() invalid input arguments!\n", __func__);
+        return -1;
+    }
+
+    i2cdata.nmsgs = 1;
+    i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs );
+    if ( !i2cdata.msgs )
+    {
+        printf("%s() msgs malloc failed!\n", __func__);
+        return -2;
+    }
+
+    i2cdata.msgs[0].addr = slave_addr;
+    i2cdata.msgs[0].flags = 0; //write
+    i2cdata.msgs[0].len = len;
+    i2cdata.msgs[0].buf = malloc(len);
+    if( !i2cdata.msgs[0].buf )
+    {
+        printf("%s() msgs malloc failed!\n", __func__);
+        rv = -3;
+        goto cleanup;
+    }
+    memcpy(i2cdata.msgs[0].buf, data, len);
+
+
+    if( ioctl(fd, I2C_RDWR, &i2cdata)<0 )
+    {
+        printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        rv = -4;
+        goto cleanup;
+    }
+
+cleanup:
+    if( i2cdata.msgs[0].buf )
+        free(i2cdata.msgs[0].buf);
+
+    if( i2cdata.msgs )
+        free(i2cdata.msgs);
+
+    return rv;
+}
+
+int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size)
+{
+    struct i2c_rdwr_ioctl_data i2cdata;
+    int rv = 0;
+
+    if( !buf || size<= 0)
+    {
+        printf("%s() invalid input arguments!\n", __func__);
+        return -1;
+    }
+
+    i2cdata.nmsgs = 1;
+    i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs );
+    if ( !i2cdata.msgs )
+    {
+        printf("%s() msgs malloc failed!\n", __func__);
+        return -2;
+    }
+
+    i2cdata.msgs[0].addr = slave_addr;
+    i2cdata.msgs[0].flags = I2C_M_RD; //read
+    i2cdata.msgs[0].len = size;
+    i2cdata.msgs[0].buf = buf;
+    memset(buf, 0, size);
+
+    if( ioctl(fd, I2C_RDWR, &i2cdata)<0 )
+    {
+        printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        rv = -4;
+    }
+
+    free( i2cdata.msgs );
+    return rv;
+}
+
+static inline void msleep(unsigned long ms)
+{
+    struct timespec cSleep;
+    unsigned long ulTmp;
+
+    cSleep.tv_sec = ms / 1000;
+    if (cSleep.tv_sec == 0)
+    {
+        ulTmp = ms * 10000;
+        cSleep.tv_nsec = ulTmp * 100;
+    }
+    else
+    {
+        cSleep.tv_nsec = 0;
+    }
+
+    nanosleep(&cSleep, 0);
+    return ;
+}
+
+static inline void dump_buf(const char *prompt, uint8_t *buf, int size)
+{
+    int          i;
+
+    if( !buf )
+    {
+        return ;
+    }
+
+    if( prompt )
+    {
+        printf("%-32s ", prompt);
+    }
+
+    for(i=0; i<size; i++)
+    {
+        printf("%02x ", buf[i]);
+    }
+    printf("\n");
+
+    return ;
+}
diff --git a/modules/tsl2561.c b/modules/tsl2561.c
new file mode 100644
index 0000000..20bd0c2
--- /dev/null
+++ b/modules/tsl2561.c
@@ -0,0 +1,337 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  sht20.c
+ *    Description:  This file is the Lux sensor TSL2561 code
+ *
+ *        Version:  1.0.0(10/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
+ *
+ * Pin connection:
+ *               STH20 Module            Raspberry Pi Board
+ *                   VCC      <----->      #Pin1(3.3V)
+ *                   SDA      <----->      #Pin27(SDA, BCM GPIO0)
+ *                   SCL      <----->      #Pin28(SCL, BCM GPIO1)
+ *                   GND      <----->      GND
+ *
+ * /boot/config.txt:
+ *                  dtoverlay=i2c0,pins_0_1
+ *
+ ********************************************************************************/
+
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <errno.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#define TSL2561_I2C_ADDR                0x39
+
+#define CONTROL_REG                     0x80
+#define REG_COUNT                       4
+
+#define POWER_UP                        0x03
+#define POWER_DOWN                      0x00
+
+#define OFF                             0
+#define ON                              1
+
+/* Register Address  */
+enum
+{
+    /* Channel_0 = DATA0HIGH<<8 + DATA0LOW */
+    DATA0LOW = 0x8C,
+    DATA0HIGH,
+
+    /* Channel_1 = DATA1HIGH<<8 + DATA1LOW */
+    DATA1LOW,
+    DATA1HIGH,
+};
+
+static const int  regs_addr[REG_COUNT]={DATA0LOW, DATA0HIGH, DATA1LOW, DATA1HIGH};
+
+float tsl2561_get_lux(int fd);
+static inline void print_datime(void);
+
+static inline void banner(const char *progname)
+{
+    printf("%s program Version v1.0.0\n", progname);
+    printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
+}
+
+static void program_usage(const char *progname)
+{
+
+    printf("Usage: %s [OPTION]...\n", progname);
+    printf(" %s is TSL2561 Lux sensor program.\n", progname);
+
+    printf(" -d[device  ]  Specify I2C device, such as /dev/i2c-0\n");
+    printf(" -h[help    ]  Display this help information\n");
+    printf(" -v[version ]  Display the program version\n");
+
+    printf("\n");
+    banner(progname);
+    return;
+}
+
+int main(int argc, char **argv)
+{
+    int             fd, rv;
+    float           lux;
+    char           *dev = "/dev/i2c-0";
+    char           *progname=NULL;
+
+    struct option long_options[] = {
+        {"device", required_argument, NULL, 'd'},
+        {"version", no_argument, NULL, 'v'},
+        {"help", no_argument, NULL, 'h'},
+        {NULL, 0, NULL, 0}
+    };
+
+    progname = basename(argv[0]);
+
+    /* Parser the command line parameters */
+    while ((rv = getopt_long(argc, argv, "d:vh", long_options, NULL)) != -1)
+    {
+        switch (rv)
+        {
+            case 'd': /*  Set I2C device path: /dev/i2c-1 */
+                dev = optarg;
+                break;
+
+            case 'v':  /*  Get software version */
+                banner(progname);
+                return EXIT_SUCCESS;
+
+            case 'h':  /*  Get help information */
+                program_usage(progname);
+                return 0;
+
+            default:
+                break;
+        }
+    }
+
+    /*+--------------------------------+
+     *|     open /dev/i2c-x device     |
+     *+--------------------------------+*/
+    if( (fd=open(dev, O_RDWR)) < 0)
+    {
+        printf("i2c device '%s' open failed: %s\n", dev, strerror(errno));
+        return -1;
+    }
+
+    while(1)
+    {
+        lux = tsl2561_get_lux(fd);
+
+        print_datime();
+        printf("TSLl2561 get lux: %.3f\n", lux);
+
+        sleep(1);
+    }
+
+    close(fd);
+    return 0;
+}
+
+static inline void print_datime(void)
+{
+    time_t         tmp;
+    struct tm     *p;
+
+    time(&tmp);
+
+    p=localtime(&tmp);
+
+
+    printf("%d-%02d-%02d %02d:%02d:%02d\t", (p->tm_year+1900),(p->tm_mon+1),
+            p->tm_mday, p->tm_hour, p->tm_min, p->tm_sec);
+
+}
+
+static inline void msleep(unsigned long ms)
+{
+    struct timespec cSleep;
+    unsigned long ulTmp;
+
+    cSleep.tv_sec = ms / 1000;
+    if (cSleep.tv_sec == 0)
+    {
+        ulTmp = ms * 10000;
+        cSleep.tv_nsec = ulTmp * 100;
+    }
+    else
+    {
+        cSleep.tv_nsec = 0;
+    }
+
+    nanosleep(&cSleep, 0);
+    return ;
+}
+
+void tsl2561_power(int fd, int cmd)
+{
+    struct i2c_msg               msg;
+    struct i2c_rdwr_ioctl_data   data;
+    unsigned char                buf[2];
+
+    msg.addr= TSL2561_I2C_ADDR;
+    msg.flags=0;  /* write */
+    msg.len= 1;
+    msg.buf= buf;
+
+    data.nmsgs= 1;
+    data.msgs= &msg;
+
+    msg.buf[0]=CONTROL_REG;
+    if( ioctl(fd, I2C_RDWR, &data) < 0 )
+    {
+        printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return ;
+    }
+
+
+    if( cmd )
+        msg.buf[0]=POWER_UP;
+    else
+        msg.buf[0]=POWER_DOWN;
+
+    if( ioctl(fd, I2C_RDWR, &data) < 0 )
+    {
+        printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return ;
+    }
+
+    return ;
+}
+
+int tsl2561_readreg(int fd, unsigned char regaddr, unsigned char *regval)
+{
+    struct i2c_msg               msg;
+    struct i2c_rdwr_ioctl_data   data;
+    unsigned char                buf[2];
+
+    msg.addr= TSL2561_I2C_ADDR;
+    msg.flags=0;  /* write */
+    msg.len= 1;
+    msg.buf= buf;
+    msg.buf[0] = regaddr;
+
+    data.nmsgs= 1;
+    data.msgs= &msg;
+
+    if( ioctl(fd, I2C_RDWR, &data) < 0 )
+    {
+        printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return -1;
+    }
+
+    memset(buf, 0, sizeof(buf));
+
+    msg.addr= TSL2561_I2C_ADDR;
+    msg.flags=I2C_M_RD;  /* read */
+    msg.len= 1;
+    msg.buf= buf;
+
+    data.nmsgs= 1;
+    data.msgs= &msg;
+
+    if( ioctl(fd, I2C_RDWR, &data) < 0 )
+    {
+        printf("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return -1;
+    }
+
+    *regval = msg.buf[0];
+    return 0;
+}
+
+float tsl2561_get_lux(int fd)
+{
+    int                 i;
+    unsigned char       reg_data[REG_COUNT];
+    unsigned char       buf;
+
+    int                 chn0_data = 0;
+    int                 chn1_data = 0;
+
+    float               div = 0.0;
+    float               lux = 0.0;
+
+    tsl2561_power(fd, ON);
+
+    msleep(410);  /* t(CONV) MAX 400ms */
+
+    /* Read register Channel0 and channel1 data from register */
+    for(i=0; i<REG_COUNT; i++)
+    {
+        tsl2561_readreg(fd, regs_addr[i], &reg_data[i]);
+    }
+
+    chn0_data = reg_data[1]*256 + reg_data[0]; /* Channel0 = DATA0HIGH<<8 + DATA0LOW  */
+    chn1_data = reg_data[3]*256 + reg_data[2]; /* channel1 = DATA1HIGH<<8 +  DATA1LOW */
+
+    if( chn0_data<=0 || chn1_data<0 )
+    {
+        lux = 0.0;
+        goto OUT;
+    }
+
+    div = (float)chn1_data / (float)chn0_data;
+
+    if( div>0 && div<=0.5 )
+        lux = 0.304*chn0_data-0.062*chn0_data*pow(div,1.4);
+
+    else if( div>0.5 && div<=0.61 )
+        lux = 0.0224*chn0_data-0.031*chn1_data;
+
+    else if( div>0.61 && div<=0.8 )
+        lux = 0.0128*chn0_data-0.0153*chn1_data;
+
+    else if( div>0.8 && div<=1.3 )
+        lux = 0.00146*chn0_data-0.00112*chn1_data;
+
+    else if( div>1.3 )
+        lux = 0.0;
+
+OUT:
+    tsl2561_power(fd, OFF);
+    return lux;
+}
+
+static inline void dump_buf(const char *prompt, char *buf, int size)
+{
+    int          i;
+
+    if( !buf )
+    {
+        return ;
+    }
+
+    if( prompt )
+    {
+        printf("%-32s ", prompt);
+    }
+
+    for(i=0; i<size; i++)
+    {
+        printf("%02x ", buf[i]);
+    }
+    printf("\n");
+
+    return ;
+}
diff --git a/project/booster/README.md b/project/booster/README.md
new file mode 100644
index 0000000..2b4e7a9
--- /dev/null
+++ b/project/booster/README.md
@@ -0,0 +1,4 @@
+## booster
+
+LingYun embedded C program basic library
+
diff --git a/project/booster/at-esp32.c b/project/booster/at-esp32.c
new file mode 100644
index 0000000..b89bfa1
--- /dev/null
+++ b/project/booster/at-esp32.c
@@ -0,0 +1,388 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  at-esp32.c
+ *    Description:  This file is ESP32 AT command low level API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#include "logger.h"
+#include "at-esp32.h"
+
+/*+------------------------+
+ *|    Baisc AT command    |
+ *+------------------------+*/
+
+static inline int check_at_ready(comport_t *comport)
+{
+    int             times = 6;
+    int             ready = 0;
+
+    while( times-- )
+    {
+        if( 0 == send_atcmd_check_ok(comport, "AT", 500) )
+        {
+            ready = 1;
+            break;
+        }
+    }
+
+    return ready;
+}
+
+/* AT command: AT+RST */
+int esp32_reset(comport_t *comport)
+{
+    int             rv;
+
+    rv = send_atcmd(comport, "AT+RST", 5000, "ready", AT_ERRSTR, NULL, 0);
+    if( rv < 0)
+    {
+        log_error("send AT command to reset ESP32 failed, rv=%d\n", rv);
+        return -1;
+    }
+
+    if( check_at_ready(comport) )
+    {
+        log_info("send AT to reset ESP32 and AT command ready\n");
+        return 0;
+    }
+    else
+    {
+        log_info("send AT to reset ESP32 but AT command not ready\n");
+        return -3;
+    }
+}
+
+/* AT command: AT+RESTORE */
+int esp32_restore(comport_t *comport)
+{
+    int             rv;
+
+    //rv = send_atcmd_check_ok(comport, "AT+RESTORE", 5000);
+    rv = send_atcmd(comport, "AT+RESTORE", 5000, "ready", AT_ERRSTR, NULL, 0);
+    if( rv < 0)
+    {
+        log_error("send AT command to restore ESP32 failed, rv=%d\n", rv);
+        return -1;
+    }
+
+    if( check_at_ready(comport) )
+    {
+        log_info("send AT command to resstore ESP32 ready\n");
+        return 0;
+    }
+    else
+    {
+        log_error("send AT command to restore ESP32 but AT not ready\n");
+        return -3;
+    }
+}
+
+/* AT command: ATE1 or ATE0 */
+int esp32_set_echo(comport_t *comport, int enable)
+{
+    char           *at = enable? "ATE1" : "ATE0";
+
+    return send_atcmd_check_ok(comport, at, 500);
+}
+
+/* AT command: AT+GMR */
+int esp32_get_firmware(comport_t *comport, char *version, int size)
+{
+    if( !version || size<=0 )
+        return -1;
+
+    return send_atcmd_check_value(comport, "AT+GMR", 2000, version, size);
+}
+
+/* AT command: AT+SYSSTORE */
+int esp32_set_sysstore(comport_t *comport, int enable)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+SYSSTORE=%d", enable?1:0);
+
+    return send_atcmd_check_ok(comport, at, 1000);
+}
+
+/*+------------------------+
+ *|    WiFi AT command     |
+ *+------------------------+*/
+
+/* AT command: AT+CWMODE */
+int esp32_set_wmode(comport_t *comport, workmode_t mode, int autoconn)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+CWMODE=%d,%d", mode, autoconn?1:0);
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSTAMAC/CIPAPMAC */
+int esp32_get_macaddr(comport_t *comport, workmode_t mode, char *mac)
+{
+    if( !mac )
+        return -1;
+
+    if( MODE_SOFTAP == mode )
+        return send_atcmd_check_request(comport, "AT+CIPAPMAC?", 3000, mac, MAC_LEN);
+    else
+        return send_atcmd_check_request(comport, "AT+CIPSTAMAC?", 3000, mac, MAC_LEN);
+}
+
+/* AT command: AT+CIPSTA/AT+CIPAP  */
+int esp32_set_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( !ip || !gateway )
+        return -1;
+
+    if( MODE_SOFTAP == mode )
+        snprintf(at, sizeof(at), "AT+CIPAP=\"%s\",\"%s\"", ip, gateway);
+    else
+        snprintf(at, sizeof(at), "AT+CIPSTA=\"%s\",\"%s\"", ip, gateway);
+
+    return send_atcmd_check_ok(comport, at, 2000);
+}
+
+/* AT command: AT+CIPSTA/AT+CIPAP  */
+int esp32_get_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway)
+{
+    char           *at = MODE_SOFTAP==mode? "AT+CIPAP?" : "AT+CIPSTA?";
+    char            buf[ATCMD_REPLY_LEN];
+    int             rv;
+
+    if( !ip || !gateway )
+        return -1;
+
+    rv = send_atcmd_check_value(comport, at, 3000, buf, sizeof(buf));
+    if( rv < 0)
+    {
+        log_error("AT command query IP address failed, rv=%d\n", rv);
+        return rv;
+    }
+
+    rv = parser_request_value(buf, "ip", ip, IP_LEN);
+    if( rv < 0 )
+    {
+        log_error("Parser query IP address failed, rv=%d\n", rv);
+        return rv;
+    }
+
+    rv = parser_request_value(buf, "gateway", gateway, IP_LEN);
+    if( rv < 0 )
+    {
+        log_error("Parser query gateway address failed, rv=%d\n", rv);
+        return rv;
+    }
+
+    return 0;
+}
+
+/* AT command: AT+CWDHCP */
+int esp32_set_dhcp(comport_t *comport, workmode_t mode, int enable)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+CWDHCP=%d,%d", enable?1:0, mode);
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CWAUTOCONN */
+int esp32_set_autoconn(comport_t *comport, int enable)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+CWAUTOCONN=%d", enable?1:0);
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CWLAP */
+int esp32_list_ap(comport_t *comport, char *buf, int size)
+{
+    if( !buf || size<=0 )
+        return -1;
+
+    return send_atcmd_check_value(comport, "AT+CWLAP", 8000, buf, size);
+}
+
+/* AT command: AT+CWJAP */
+int esp32_connect_ap(comport_t *comport, char *ssid, char *pwd)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( !ssid || !pwd )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CWJAP=\"%s\",\"%s\"", ssid, pwd);
+    return send_atcmd_check_ok(comport, at, 15000);
+}
+
+/* AT command: AT+CWQAP */
+int esp32_disconn_ap(comport_t *comport)
+{
+    return send_atcmd_check_ok(comport, "AT+CWQAP", 5000);
+}
+
+
+/* AT command: AT+CWSTATE?  status value:
+ * - 0: ESP32 station has not started any Wi-Fi connection.
+ * - 1: ESP32 station has connected to an AP, but does not get an IPv4 address yet.
+ * - 2: ESP32 station has connected to an AP, and got an IPv4 address.
+ * - 3: ESP32 station is in Wi-Fi connecting or reconnecting state.
+ * - 4: ESP32 station is in Wi-Fi disconnected state
+ */
+int esp32_join_status(comport_t *comport, int *status, char *ssid)
+{
+    char            buf[ATCMD_REPLY_LEN];
+    int             rv;
+
+    if( !status || !ssid )
+        return -1;
+
+    rv = send_atcmd_check_request(comport, "AT+CWSTATE?", 3000, buf, sizeof(buf));
+    if( rv < 0)
+    {
+        log_error("AT command query join status failed, rv=%d\n", rv);
+        return rv;
+    }
+
+    sscanf(buf, "%d,%s", status, ssid);
+
+    return 0;
+}
+
+/* AT command:  AT+PING */
+int esp32_ping(comport_t *comport, char *host, int timeout)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( !host )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+PING=\"%s\"", host);
+    return send_atcmd_check_ok(comport, at, timeout);
+}
+
+/* AT command:  AT+CWSAP */
+int esp32_set_softap(comport_t *comport, char *ssid, char *pwd, int channel, encmode_t encmode)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( !ssid || !pwd )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CWSAP=\"%s\",\"%s\",%d,%d", ssid, pwd, channel, encmode);
+    return send_atcmd_check_ok(comport, at, 5000);
+}
+
+/* AT command: AT+CWLIF */
+int esp32_list_client(comport_t *comport, char *buf, int size)
+{
+    if( !buf || size<=0 )
+        return -1;
+
+    return send_atcmd_check_value(comport, "AT+CWLIF", 8000, buf, size);
+}
+
+/*+------------------------+
+ *|   Socket AT command    |
+ *+------------------------+*/
+
+/* AT command: AT+CIPMUX */
+int esp32_set_socket_mux(comport_t *comport, int enable)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    snprintf(at, sizeof(at), "AT+CIPMUX=%d", enable?1:0);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSERVERMAXCONN */
+int esp32_set_socket_clients(comport_t *comport, int max)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( max <= 0 )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CIPSERVERMAXCONN=%d", max);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSTO, timeout unit second */
+int esp32_set_socket_timeout(comport_t *comport, int timeout)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( timeout <= 0 )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CIPSTO=%d", timeout);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSERVER */
+int esp32_set_tcp_server(comport_t *comport, int port)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( port <= 0 )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CIPSERVER=1,%d,\"TCP\"", port);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSERVER */
+int esp32_del_tcp_server(comport_t *comport, int port)
+{
+    char            at[ATCMD_LEN]={'\0'};
+
+    if( port <= 0 )
+        return -1;
+
+    snprintf(at, sizeof(at), "AT+CIPSERVER=0,%d,\"TCP\"", port);
+
+    return send_atcmd_check_ok(comport, at, 1500);
+}
+
+/* AT command: AT+CIPSTART */
+int esp32_set_tcp_client(comport_t *comport, int mux, char *host, int port)
+{
+    char            at[ATCMD_LEN]={'\0'};
+    char            buf[ATCMD_REPLY_LEN]={'\0'};
+    int             keepalive = 60; /* unit second */
+    int             rv,linkid = 0;
+
+    if( !host || port <= 0 )
+        return -1;
+
+    rv = esp32_set_socket_mux(comport, mux);
+    if(rv < 0)
+        return rv;
+
+    if( mux )
+        snprintf(at, sizeof(at), "AT+CIPSTART=1,\"TCP\",\"%s\",%d,%d", host, port, keepalive);
+    else
+        snprintf(at, sizeof(at), "AT+CIPSTART=\"TCP\",\"%s\",%d,%d", host, port, keepalive);
+
+    rv = send_atcmd_check_value(comport, at, 1500, buf, sizeof(buf));
+    if(rv < 0)
+        return rv;
+
+    sscanf(buf, "%d,", &linkid);
+
+    return linkid;
+}
diff --git a/project/booster/at-esp32.h b/project/booster/at-esp32.h
new file mode 100644
index 0000000..3a8b57a
--- /dev/null
+++ b/project/booster/at-esp32.h
@@ -0,0 +1,140 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  at-esp32.h
+ *    Description:  This file is ESP32 AT command low level API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#ifndef  _AT_ESP32_H_
+#define  _AT_ESP32_H_
+
+#include "atcmd.h"
+
+enum
+{
+    DISABLE = 0,    /* common disable */
+    ENABLE,         /* common enable  */
+};
+
+#define MAC_LEN     18 /* 11:22:33:44:55:66 */
+#define IP_LEN      16 /* 255.255.255.255 */
+
+/*+------------------------+
+ *|    Baisc AT command    |
+ *+------------------------+*/
+
+/* AT command: AT+RST */
+extern int esp32_reset(comport_t *comport);
+
+/* AT command: AT+RESTORE */
+extern int esp32_restore(comport_t *comport);
+
+/* AT command: ATE1 or ATE0 */
+extern int esp32_set_echo(comport_t *comport, int enable);
+
+/* AT command: AT+GMR */
+extern int esp32_get_firmware(comport_t *comport, char *version, int size);
+
+/* AT command: AT+SYSSTORE */
+extern int esp32_set_sysstore(comport_t *comport, int enable);
+
+
+/*+------------------------+
+ *|    WiFi AT command     |
+ *+------------------------+*/
+typedef enum
+{
+    MODE_DISABLE=0, /* Wi-Fi RF will be disabled */
+    MODE_STATION,   /* Station mode */
+    MODE_SOFTAP,    /* SoftAP mode */
+    MODE_WDS,       /* Wireless Distribution System: Both Station & SoftAP mode */
+} workmode_t;
+
+/* AT command: AT+CWMODE */
+extern int esp32_set_wmode(comport_t *comport, workmode_t mode, int autoconn);
+
+/* AT command: AT+CWDHCP */
+extern int esp32_set_dhcp(comport_t *comport, workmode_t mode, int enable);
+
+/* AT command: AT+CWAUTOCONN */
+extern int esp32_set_autoconn(comport_t *comport, int enable);
+
+/* AT command: AT+CIPSTAMAC/CIPAPMAC */
+extern int esp32_get_macaddr(comport_t *comport, workmode_t mode, char *mac);
+
+/* AT command: AT+CIPSTA/CIPAP */
+extern int esp32_get_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway);
+
+/* AT command: AT+CIPSTA/AT+CIPAP  */
+extern int esp32_set_ipaddr(comport_t *comport, workmode_t mode, char *ip, char *gateway);
+
+/* AT command: AT+CWLAP */
+extern int esp32_list_ap(comport_t *comport, char *buf, int size);
+
+/* AT command: AT+CWJAP */
+extern int esp32_connect_ap(comport_t *comport, char *ssid, char *pwd);
+
+/* AT command: AT+CWQAP */
+extern int esp32_disconn_ap(comport_t *comport);
+
+/* AT command: AT+CWSTATE? , status value:
+ * - 0: ESP32 station has not started any Wi-Fi connection.
+ * - 1: ESP32 station has connected to an AP, but does not get an IPv4 address yet.
+ * - 2: ESP32 station has connected to an AP, and got an IPv4 address.
+ * - 3: ESP32 station is in Wi-Fi connecting or reconnecting state.
+ * - 4: ESP32 station is in Wi-Fi disconnected state
+ */
+extern int esp32_join_status(comport_t *comport, int *status, char *ssid);
+
+/* AT command:  AT+PING */
+extern int esp32_ping(comport_t *comport, char *host, int timeout);
+
+/* AT command:  AT+CWSAP */
+typedef enum
+{
+    MODE_OPEN,
+    /* WEP not support */
+    MODE_WPAPSK = 2,
+    MODE_WPA2PSK,
+    MODE_WPA_WPA2PSK,
+} encmode_t;
+extern int esp32_set_softap(comport_t *comport, char *ssid, char *pwd, int channel, encmode_t encmode);
+
+/* AT command:  AT+CWLIF */
+extern int esp32_list_client(comport_t *comport, char *buf, int size);
+
+
+/*+------------------------+
+ *|   Socket AT command    |
+ *+------------------------+*/
+
+/* AT command: CIPMUX */
+extern int esp32_set_socket_mux(comport_t *comport, int enable);
+
+/* AT command: AT+CIPSERVERMAXCONN */
+extern int esp32_set_socket_clients(comport_t *comport, int max);
+
+/* AT command: AT+CIPSTO, timeout unit second */
+extern int esp32_set_socket_timeout(comport_t *comport, int timeout);
+
+/* AT command: AT+CIPSERVER */
+extern int esp32_set_tcp_server(comport_t *comport, int port);
+
+/* AT command: AT+CIPSERVER */
+extern int esp32_del_tcp_server(comport_t *comport, int port);
+
+/* AT command: AT+CIPSTART */
+extern int esp32_set_tcp_client(comport_t *comport, int mux, char *host, int port);
+
+/*+------------------------+
+ *|     BLE AT command     |
+ *+------------------------+*/
+// RFU
+
+#endif   /* ----- #ifndef _AT_ESP32_H_  ----- */
diff --git a/project/booster/atcmd.c b/project/booster/atcmd.c
new file mode 100644
index 0000000..425682f
--- /dev/null
+++ b/project/booster/atcmd.c
@@ -0,0 +1,300 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  atcmd.c
+ *    Description:  This file is lowlevel AT command send and parser API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#include <ctype.h>
+#include "logger.h"
+#include "atcmd.h"
+
+/*  Description: this function used to send an AT command from serial port and wait for reply message from
+ *               the communication module, and it will return once get expet/error string or timeout.
+ *
+ *    Arugments:
+ *         $comport: the serial port which connected to GPRS/3G/4G/NB-IoT/WiFi/BLE/Zigbee/LoRa...
+ *              $at: the AT command need to be sent, without "\r\n"
+ *         $timeout: wait for module reply for AT command timeout value, unit micro seconds(ms)
+ *          $exepct: the expect string reply from communication module
+ *           $error: the error string reply from communication module
+ *           $reply: the module reply message output buffer
+ *            $size: the output buffer ($reply) size
+ *
+ * Return value: <0: Function error   0: Got error string   1: Got expect string 2: Receive util timeout
+ *
+ */
+
+int send_atcmd(comport_t *comport, char *at, unsigned long timeout, char *expect, char *error, char *reply, int size)
+{
+    int                    i, rv = 0;
+    int                    res = ATRES_TIMEOUT;
+    int                    bytes = 0;
+    char                   buf[ATCMD_REPLY_LEN] = {'\0'};
+    char                   atcmd[ATCMD_LEN] = {'\0'};
+
+    if( !comport || !at )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( comport->fd <= 0 )
+    {
+        log_error("comport[%s] not opened\n");
+        return -2;
+    }
+
+    /* flushes both data received but not read, and data written but not transmitted in serial port */
+    tcflush(comport->fd, TCIOFLUSH);
+
+    snprintf(atcmd, sizeof(atcmd), "%s%s", at, AT_SUFFIX);
+    rv=comport_send( comport, atcmd, strlen(atcmd) );
+    if(rv < 0)
+    {
+        log_error("send AT command \"%s\" to \"%s\" failed, rv=%d\n", at, comport->devname, rv);
+        return -3;
+    }
+
+    res = ATRES_TIMEOUT;
+    memset( buf, 0, sizeof(buf) );
+
+    for(i=0; i<timeout/10; i++)
+    {
+        if( bytes >= sizeof(buf) )
+            break;
+
+        rv=comport_recv( comport, buf+bytes, sizeof(buf)-bytes, 10);
+        if(rv < 0)
+        {
+            log_error("send AT command \'%s\' to \'%s\' failed, rv=%d\n", at, comport->devname, rv);
+            return -3;
+        }
+
+        bytes += rv;
+
+        if( expect && strstr(buf, expect) )
+        {
+            log_debug("send AT command \"%s\" and got reply \"OK\"\n", at);
+            res = ATRES_EXPECT;
+            break;
+        }
+
+        if( error && strstr(buf, error) )
+        {
+            log_debug("send AT command \"%s\" and got reply \"ERROR\"\n", at);
+            res = ATRES_ERROR;
+            break;
+        }
+    }
+
+    if( bytes > 0 )
+        log_trace("AT command reply:%s", buf);
+
+    if( reply && size>0 )
+    {
+        bytes = strlen(buf)>size ? size : strlen(buf);
+        memset(reply, 0, size);
+        strncpy(reply, buf, bytes);
+
+        log_debug("copy out AT command \"%s\" reply message: \n%s", at, reply);
+    }
+
+    return res;
+}
+
+
+/*
+ *  Description: Send AT command which will only reply by "OK" or "ERROR", such as AT:
+ *                 Reply:   \r\nOK\r\n
+ * Return Value: 0: OK     -X: Failure
+ */
+int send_atcmd_check_ok(comport_t *comport, char *at, unsigned long timeout)
+{
+    int                     rv;
+
+    if( !comport || !at )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    rv=send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, NULL, 0);
+    if( ATRES_EXPECT == rv )
+    {
+        return 0;
+    }
+    else
+    {
+        return -2;
+    }
+}
+
+
+/*
+ *  Description: Send AT command which will reply by a value directly in a single line, such as AT+CGMM:
+ *                  Reply:   \r\nEC20F\r\nOK\r\n
+ *
+ * Return Value: 0: OK     -X: Failure
+ */
+int send_atcmd_check_value(comport_t *comport, char *at, unsigned long timeout, char *reply, int size)
+{
+    int                     rv, len;
+    char                    buf[ATCMD_REPLY_LEN];
+    char                   *ptr_start = buf;
+    char                   *ptr_end;
+
+    if( !comport || !at || !reply || size<=0 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    rv = send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, buf, ATCMD_REPLY_LEN);
+    if( rv <= 0 )
+    {
+        return -2;
+    }
+
+    /* Skip the echo back command line */
+    if( !strncmp(buf, at, strlen(at)) )
+    {
+        ptr_start=strchr(buf, '\n');
+        if( !ptr_start )
+        {
+            log_error("reply message got wrong\n");
+            return -3;
+        }
+
+        ptr_start++;   /* skip '\n'  */
+    }
+
+    /* find end reply string "\r\nOK\r\n"  */
+    ptr_end = strstr(ptr_start, AT_OKSTR);
+    if( ptr_end )
+    {
+        len = ptr_end - ptr_start;
+    }
+    else
+    {
+        len = strlen(buf) - (ptr_start-buf);
+    }
+
+    memset(reply, 0, size);
+
+    len = len>size ? size : len;
+    memcpy(reply, ptr_start, len);
+
+    return 0;
+}
+
+/*
+ *  Description: Parser the $value from $key like "xxx: " line, such as AT+CSQ:
+ *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
+ *
+ * Return Value: 0: OK     -X: Failure
+ */
+int parser_request_value(char *buf, char *key, char *value, int size)
+{
+    char                   *ptr;
+    int                    i = 0;
+
+    if( !buf || !key || !value || size<=0 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    ptr = strstr(buf, key);
+    if( !ptr )
+    {
+        log_debug("Not found key \"%s\" in %s\n", key, buf);
+        return -2;
+    }
+
+    ptr=strchr(ptr, ':');  /* found ':' before the value */
+    if( !ptr )
+    {
+        log_debug("Not found ':' before value\n");
+        return -3;
+    }
+    ptr++;   /* skip ':'  */
+
+    if( *ptr == '\"' ) /* skip " */
+        ptr++;
+
+    memset(value, 0, size);
+    while(*ptr!='\r' && i<size-1)
+    {
+        if( !isspace(*ptr) && *ptr!='\"') /* skip space,\r,\n ...  */
+            value[i++] = *ptr;
+        ptr ++;
+    }
+
+    ptr++; /* skip  */
+
+    return 0;
+}
+
+/*
+ *  Description: Send AT command which will reply by the value  with a prefix "+CMD: " line, such as AT+CSQ:
+ *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
+ *
+ * Return Value: 0: OK     -X: Failure
+ */
+int send_atcmd_check_request(comport_t *comport, char *at, unsigned long timeout, char *reply, int size)
+{
+    int                     i = 0;
+    int                     rv;
+    char                    buf[ATCMD_REPLY_LEN];
+    char                   *ptr;
+
+    if( !comport || !at || !reply || size<=0 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    rv = send_atcmd(comport, at, timeout, AT_OKSTR, AT_ERRSTR, buf, ATCMD_REPLY_LEN);
+    if( rv <= 0 )
+    {
+        return -2;
+    }
+
+    ptr=strchr(buf, '+');  /* found '+' before the value */
+    if( !ptr )
+    {
+        log_error("reply message got wrong\n");
+        return -3;
+    }
+    ptr++;   /* skip '+'  */
+
+
+    ptr=strchr(buf, ':');  /* found ':' before the value */
+    if( !ptr )
+    {
+        log_error("reply message got wrong\n");
+        return -3;
+    }
+    ptr++;   /* skip ':'  */
+
+    if( *ptr == '\"' ) /* skip " */
+        ptr++;
+
+    memset(reply, 0, size);
+    while(*ptr!='\r' && i<size-1)
+    {
+        if( !isspace(*ptr) && *ptr!='\"') /* skip space,\r,\n ...  */
+            reply[i++] = *ptr;
+        ptr ++;
+    }
+
+    return 0;
+}
+
diff --git a/project/booster/atcmd.h b/project/booster/atcmd.h
new file mode 100644
index 0000000..e8de8f1
--- /dev/null
+++ b/project/booster/atcmd.h
@@ -0,0 +1,93 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  atcmd.h
+ *    Description:  This file is lowlevel AT command send and parser API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+
+#ifndef  _ATCMD_H_
+#define  _ATCMD_H_
+
+#include "comport.h"
+
+/* AT command common reply message max length  */
+#define ATCMD_REPLY_LEN          1024 /* Max AT command reply message length */
+#define ATCMD_LEN                256 /* Max AT command length */
+
+/* AT command reply message got expect or error string */
+#define AT_OKSTR                 "\r\nOK\r\n"     /* expect string always be OK */
+#define AT_ERRSTR                "\r\nERROR\r\n"  /* error string always be ERROR */
+
+/* AT command should be end by $AT_SUFFIX */
+#define AT_SUFFIX                "\r\n"
+
+/* send_atcmd)( return value status */
+enum
+{
+    ATRES_ERROR,    /* AT command reply got error string, such as "ERROR\r\n" */
+    ATRES_EXPECT,   /* AT command reply got expect string, such as "OK\r\n" */
+    ATRES_TIMEOUT,  /* AT command not get error/expect string, receive util timeout */
+};
+
+/*  Description: this function used to send an AT command from serial port and wait for reply message from
+ *               the communication module, and it will return once get expet/error string or timeout.
+ *
+ *    Arugments:
+ *         $comport: the serial port which connected to GPRS/3G/4G/NB-IoT/WiFi/BLE/Zigbee/LoRa...
+ *              $at: the AT command need to be sent, without "\r\n"
+ *         $timeout: wait for module reply for AT command timeout value, unit micro seconds(ms)
+ *          $exepct: the expect string reply from communication module
+ *           $error: the error string reply from communication module
+ *           $reply: the module reply message output buffer
+ *            $size: the output buffer ($reply) size
+ *
+ * Return value: <0: Function error   0: Got error string   1: Got expect string 2: Receive util timeout
+ *
+ */
+int send_atcmd(comport_t *comport, char *at, unsigned long timeout, char *expect, char *error, char *reply, int size);
+
+
+/*
+ *  Description: Send AT command which will only reply by "OK" or "ERROR", such as AT:
+ *                  Reply:   \r\nOK\r\n
+ *
+ * Return Value:  0: OK     -X: ERROR
+ */
+int send_atcmd_check_ok(comport_t *comport, char *at, unsigned long timeout);
+
+
+/*
+ *  Description: Send AT command which will reply by a value directly in a single line, such as AT+CGMM:
+ *                  Reply:   \r\nEC20F\r\nOK\r\n
+ *
+ * Return Value:  0: OK     -X: ERROR
+ */
+int send_atcmd_check_value(comport_t *comport, char *at, unsigned long timeout, char *reply, int size);
+
+/*
+ *  Description: Parser the $value from $key like "xxx: " line, such as AT+CSQ:
+ *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
+ *
+ * Return Value: 0: OK     -X: Failure
+ */
+int parser_request_value(char *buf, char *key, char *value, int size);
+
+
+/*
+ *  Description: Send AT command which will reply by the value  with a prefix "+CMD: " line, such as AT+CSQ:
+ *                  Reply:   \r\n+CSQ: 26,99\r\nOK\r\n
+ *
+ * Return Value:  0: OK     -X: ERROR
+ */
+int send_atcmd_check_request(comport_t *comport, char *at, unsigned long timeout, char *repy, int size);
+
+
+#endif   /* ----- #ifndef _ATCMD_H_  ----- */
+
diff --git a/project/booster/comport.c b/project/booster/comport.c
new file mode 100644
index 0000000..86b3dba
--- /dev/null
+++ b/project/booster/comport.c
@@ -0,0 +1,520 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  comport.c
+ *    Description:  This file is linux comport common API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#include "comport.h"
+
+#define CONFIG_PRINT_LOGGER
+//#define CONFIG_PRINT_STDOUT
+
+#if ( defined CONFIG_PRINT_LOGGER )
+#include "logger.h"
+#define dbg_print(format,args...) log_error(format, ##args)
+
+#elif ( defined CONFIG_PRINT_STDOUT )
+#define dbg_print(format,args...) printf(format, ##args)
+
+#else
+#define dbg_print(format,args...) do{} while(0);
+#endif
+
+
+static inline void set_settings(comport_t * comport, const char *settings);
+
+/*
+ *  description: Open the serial port
+ *
+ *   input args: $comport:  corresponding comport point
+ *               $dev_name:  The comport device name path, such as '/dev/ttyS3'
+ *               $baudrate:  The baudrate, such as 115200
+ *               $settings:  The databit,parity,stopbit,flowctrl settings, such as '8N1N'
+ *
+ * return value: The comport opened file description, <0 means failure
+ */
+int comport_open(comport_t *comport, const char *devname, long baudrate, const char *settings)
+{
+    int                         rv = -1;
+    struct termios              old_cfg, new_cfg;
+    int                         old_flags;
+    long                        tmp;
+
+    if( !comport || !devname )
+    {
+        dbg_print("invalid input arugments\n");
+        return -1;
+    }
+
+    /*+-----------------------+
+     *| open the serial port  |
+     *+-----------------------+*/
+
+    memset(comport, 0, sizeof(*comport));
+    strncpy(comport->devname, devname, sizeof(comport->devname));
+    comport->baudrate = baudrate;
+    comport->fd = -1;
+    comport->fragsize = CONFIG_DEF_FRAGSIZE;
+    set_settings(comport, settings);
+
+    if( !strstr(comport->devname, "tty") )
+    {
+        dbg_print("comport device \"%s\" is not tty device\n", comport->devname);
+        return -2;
+    }
+
+    comport->fd = open(comport->devname, O_RDWR | O_NOCTTY | O_NONBLOCK);
+    if( comport->fd<0 )
+    {
+        dbg_print("comport open \"%s\" failed:%s\n", comport->devname, strerror(errno));
+        return -3;
+    }
+
+    if(   (-1 != (old_flags = fcntl(comport->fd, F_GETFL, 0)))
+       && (-1 != fcntl(comport->fd, F_SETFL, old_flags & ~O_NONBLOCK)) )
+    {
+        /* Flush input and output */
+        tcflush(comport->fd, TCIOFLUSH);
+    }
+    else
+    {
+        rv = -4;
+        goto CleanUp;
+    }
+
+    if (0 != tcgetattr(comport->fd, &old_cfg))
+    {
+        rv = -5;
+        goto CleanUp;
+    }
+
+
+    /*+-----------------------+
+     *| configure serial port |
+     *+-----------------------+*/
+
+    memset(&new_cfg, 0, sizeof(new_cfg));
+    new_cfg.c_cflag &= ~CSIZE;
+    new_cfg.c_lflag &= ~(ICANON | ECHO | ECHOE | ISIG);
+    new_cfg.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
+    new_cfg.c_oflag &= ~(OPOST);
+
+    /* Set the data bit */
+    switch (comport->databit)
+    {
+        case 0x07:
+            new_cfg.c_cflag |= CS7;
+            break;
+        case 0x06:
+            new_cfg.c_cflag |= CS6;
+            break;
+        case 0x05:
+            new_cfg.c_cflag |= CS5;
+            break;
+        default:
+            new_cfg.c_cflag |= CS8;
+            break;
+    }
+
+    /* Set the parity */
+    switch (comport->parity)
+    {
+        case 0x01:    /* Odd */
+            new_cfg.c_cflag |= (PARENB | PARODD);
+            new_cfg.c_cflag |= (INPCK | ISTRIP);
+            break;
+        case 0x02:    /* Even */
+            new_cfg.c_cflag |= PARENB;
+            new_cfg.c_cflag &= ~PARODD;;
+            new_cfg.c_cflag |= (INPCK | ISTRIP);
+            break;
+        case 0x03:
+            new_cfg.c_cflag &= ~PARENB;
+            new_cfg.c_cflag &= ~CSTOPB;
+            break;
+        default:
+            new_cfg.c_cflag &= ~PARENB;
+    }
+
+    /* Set Stop bit */
+    if (0x01 != comport->stopbit)
+    {
+        new_cfg.c_cflag |= CSTOPB;
+    }
+    else
+    {
+        new_cfg.c_cflag &= ~CSTOPB;
+    }
+
+    /* Set flow control */
+    switch (comport->flowctrl)
+    {
+        case 1:       /* Software control */
+        case 3:
+            new_cfg.c_cflag &= ~(CRTSCTS);
+            new_cfg.c_iflag |= (IXON | IXOFF);
+            break;
+        case 2:       /* Hardware control */
+            new_cfg.c_cflag |= CRTSCTS;
+            new_cfg.c_iflag &= ~(IXON | IXOFF);
+            break;
+        default:      /* NONE */
+            new_cfg.c_cflag &= ~(CRTSCTS);
+            new_cfg.c_iflag &= ~(IXON | IXOFF);
+            break;
+    }
+
+    /* Set baudrate */
+    switch (comport->baudrate)
+    {
+        /* Upper is not POSIX(bits/termios-baud.h) */
+        case 4000000:
+            tmp = B4000000;
+            break;
+        case 3500000:
+            tmp = B3500000;
+            break;
+        case 3000000:
+            tmp = B3000000;
+            break;
+        case 2500000:
+            tmp = B2500000;
+            break;
+        case 2000000:
+            tmp = B2000000;
+            break;
+        case 1500000:
+            tmp = B1500000;
+            break;
+        case 1152000:
+            tmp = B1152000;
+            break;
+        case 1000000:
+            tmp = B1000000;
+            break;
+        case 921600:
+            tmp = B921600;
+            break;
+        case 576000:
+            tmp = B576000;
+            break;
+        case 500000:
+            tmp = B500000;
+            break;
+        case 460800:
+            tmp = B460800;
+            break;
+        case 230400:
+            tmp = B230400;
+            break;
+        case 115200:
+            tmp = B115200;
+            break;
+        case 57600:
+            tmp = B57600;
+            break;
+
+        /* Below is POSIX(bits/termios.h) */
+        case 38400:
+            tmp = B38400;
+            break;
+        case 19200:
+            tmp = B19200;
+            break;
+        case 9600:
+            tmp = B9600;
+            break;
+        case 4800:
+            tmp = B4800;
+            break;
+        case 2400:
+            tmp = B2400;
+            break;
+        case 1800:
+            tmp = B1800;
+            break;
+        case 1200:
+            tmp = B1200;
+            break;
+        case 600:
+            tmp = B600;
+            break;
+        case 300:
+            tmp = B300;
+            break;
+        case 200:
+            tmp = B200;
+            break;
+        case 150:
+            tmp = B150;
+            break;
+        case 134:
+            tmp = B134;
+            break;
+        case 110:
+            tmp = B110;
+            break;
+        case 75:
+            tmp = B75;
+            break;
+        case 50:
+            tmp = B50;
+            break;
+        default:
+            tmp = B115200;
+    }
+    cfsetispeed(&new_cfg, tmp);
+    cfsetispeed(&new_cfg, tmp);
+
+    /* Set the Com port timeout settings */
+    new_cfg.c_cc[VMIN] = 0;
+    new_cfg.c_cc[VTIME] = 0;
+
+    tcflush(comport->fd, TCIFLUSH);
+    if (0 != tcsetattr(comport->fd, TCSANOW, &new_cfg))
+    {
+        rv = -6;          // Failed to set device com port settings
+        goto CleanUp;
+    }
+
+    rv = comport->fd;
+
+CleanUp:
+    return rv;
+}
+
+
+/*
+ *  description: close comport
+ *   input args: $comport:  corresponding comport point
+ */
+
+void comport_close(comport_t *comport)
+{
+    if( !comport )
+    {
+        dbg_print("invalid input arugments\n");
+        return ;
+    }
+
+    if ( comport->fd >= 0 )
+    {
+        close(comport->fd);
+    }
+
+    comport->fd = -1;
+    return ;
+}
+
+/*
+ *  description: write $data_bytes $data to $comport
+ * return value: 0: write ok  <0: write failure
+ */
+
+int comport_send(comport_t *comport, char *data, int data_bytes)
+{
+    char                       *ptr;
+    int                         left, bytes = 0;
+    int                         rv = 0;
+
+    if( !comport || !data || data_bytes<=0 )
+    {
+        dbg_print("invalid input arugments\n");
+        return -1;
+    }
+
+    if( comport->fd < 0 )
+    {
+        dbg_print("Serail port not opened\n");
+        return -2;
+    }
+
+    ptr = data;
+    left = data_bytes;
+
+    while( left > 0 )
+    {
+        /* Large data, then slice them to frag and send */
+        bytes = left>comport->fragsize ? comport->fragsize : left;
+
+        rv = write(comport->fd, ptr, bytes);
+        if( rv<0 )
+        {
+            rv = -3;
+            break;
+        }
+
+        left -= rv;
+        ptr += rv;
+    }
+
+    return rv;
+}
+
+
+/*
+ *  description: read data from $comport in $timeout <ms> to $buf no more than $buf_size bytes
+ * return value: the actual read data bytes, <0: read failure
+ */
+
+int comport_recv(comport_t *comport, char *buf, int buf_size, unsigned long timeout)
+{
+    fd_set                      rdfds, exfds;
+    struct timeval              to, *to_ptr = NULL;
+    int                         ret, rv = 0;
+    int                         bytes = 0;
+
+    if ( !comport || !buf || buf_size<=0 )
+    {
+        dbg_print("invalid input arugments\n");
+        return -1;
+    }
+
+    if ( comport->fd < 0 )
+    {
+        dbg_print("Serail port not opened\n");
+        return -2;
+    }
+
+    memset(buf, 0, buf_size);
+
+    FD_ZERO(&rdfds);
+    FD_ZERO(&exfds);
+    FD_SET(comport->fd, &rdfds);
+    FD_SET(comport->fd, &exfds);
+
+    if( TIMEOUT_NONE != timeout )
+    {
+        to.tv_sec = (time_t) (timeout / 1000);
+        to.tv_usec = (long)(1000 * (timeout % 1000));
+        to_ptr = &to;
+    }
+
+    while( 1 )
+    {
+        /* check got data arrive or not */
+        ret = select(comport->fd+1, &rdfds, 0, &exfds, to_ptr);
+        if( ret<0 )
+        {
+            /* EINTR means catch interrupt signal */
+            dbg_print("comport select() failed: %s\n", strerror(errno));
+            rv = EINTR==errno ? 0 : -3;
+            break;
+        }
+        else if( 0 == ret ) /* timeout */
+        {
+            break;
+        }
+
+        /* read data from comport */
+        ret = read(comport->fd, buf+bytes, buf_size-bytes);
+        if(ret <= 0)
+        {
+            dbg_print("comport read() failed: %s\n", strerror(errno));
+            break;
+        }
+
+        bytes += ret;
+        if( bytes >= buf_size )
+            break;
+
+        /* try to read data in 1ms again, if no data arrive it will break */
+        to.tv_sec = 0;
+        to.tv_usec = 10000;
+        to_ptr = &to;
+    }
+
+    if( !rv )
+        rv = bytes;
+
+    return rv;
+}
+
+
+/**************************************************************************************
+ *  Description: Set the comport databit,parity,stopbit,flowctrl into the comport structure
+ *   Input Args: comport: the comport_t pointer
+ *               settings: The databit/parity/stopbit/flowctrl settings as like "8N1N"
+ *  Output Args: NONE
+ * Return Value: NONE
+ *************************************************************************************/
+static inline void set_settings(comport_t * comport, const char *settings)
+{
+    if( !settings || !comport )
+    {
+        dbg_print("invalid input arugments\n");
+        return ;
+    }
+
+    switch (settings[0])        /* data bit */
+    {
+        case '7':
+            comport->databit = 7;
+            break;
+        case '8':
+        default:
+            comport->databit = 8;
+            break;
+    }
+
+    switch (settings[1])        /* parity */
+    {
+        case 'O':
+        case 'o':
+            comport->parity = 1;
+            break;
+        case 'E':
+        case 'e':
+            comport->parity = 2;
+            break;
+        case 'S':
+        case 's':
+            comport->parity = 3;
+            break;
+        case 'N':
+        case 'n':
+        default:
+            comport->parity = 0;
+            break;
+    }
+
+    switch (settings[2])        /* stop bit */
+    {
+        case '0':
+            comport->stopbit = 0;
+            break;
+        case '1':
+        default:
+            comport->stopbit = 1;
+            break;
+    }
+
+    switch (settings[3])        /* flow control */
+    {
+        case 'S':
+        case 's':
+            comport->flowctrl = 1;
+            break;
+        case 'H':
+        case 'h':
+            comport->flowctrl = 2;
+            break;
+        case 'B':
+        case 'b':
+            comport->flowctrl = 3;
+            break;
+        case 'N':
+        case 'n':
+        default:
+            comport->flowctrl = 0;
+            break;
+    }
+}
+
diff --git a/project/booster/comport.h b/project/booster/comport.h
new file mode 100644
index 0000000..8c18f8f
--- /dev/null
+++ b/project/booster/comport.h
@@ -0,0 +1,74 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  comport.c
+ *    Description:  This file is linux comport common API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#ifndef  _COMPORT_H_
+#define  _COMPORT_H_
+
+#include  <stdio.h>
+#include  <stdlib.h>
+#include  <unistd.h>
+#include  <string.h>
+#include  <getopt.h>
+#include  <fcntl.h>
+#include  <errno.h>
+#include  <termios.h>
+#include  <sys/stat.h>
+#include  <sys/wait.h>
+#include  <sys/types.h>
+#include  <sys/stat.h>
+#include  <sys/select.h>
+
+#define CONFIG_DEF_FRAGSIZE    128
+typedef struct comport_s
+{
+    char           devname[32];
+    unsigned char  databit, parity, stopbit, flowctrl;
+    long           baudrate;
+
+    int            fd;
+    int            fragsize; /* frag size when do large data send */
+} comport_t;
+
+
+/*
+ *  description: Open the comport and returned by $comport
+ *
+ *   input args: $comport:  corresponding comport handler
+ *               $devname:  The comport device name path, such as '/dev/ttyS3'
+ *               $baudrate: The baudrate, such as 115200
+ *               $settings: The databit,parity,stopbit,flowctrl settings, such as '8N1N'
+ *
+ * return value: The comport opened file description, <0 means failure
+ */
+extern int  comport_open(comport_t *comport, const char *devname, long baudrate, const char *settings);
+
+/*
+ *  description: close comport
+ *   input args: $comport:  corresponding comport handler
+ */
+extern void comport_close(comport_t *comport);
+
+/*
+ *  description: write $bytes $data to $comport
+ * return value: 0: write ok  <0: write failure
+ */
+extern int  comport_send(comport_t *comport, char *data, int data_bytes);
+
+/*
+ *  description: read data from $comport in $timeout <ms> to $buf no more than $buf_size bytes
+ * return value: the actual read data bytes, <0: read failure
+ */
+#define TIMEOUT_NONE           0
+extern int  comport_recv(comport_t *comport, char *buf, int buf_size, unsigned long timeout);
+
+#endif
diff --git a/project/booster/dictionary.c b/project/booster/dictionary.c
new file mode 100644
index 0000000..0fd5000
--- /dev/null
+++ b/project/booster/dictionary.c
@@ -0,0 +1,381 @@
+/*-------------------------------------------------------------------------*/
+/**
+   @file    dictionary.c
+   @author  N. Devillard
+   @brief   Implements a dictionary for string variables.
+   @url     https://github.com/ndevilla/iniparser
+
+   This module implements a simple dictionary object, i.e. a list
+   of string/string associations. This object is useful to store e.g.
+   informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+#include "dictionary.h"
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+/** Maximum value size for integers and doubles. */
+#define MAXVALSZ    1024
+
+/** Minimal allocated number of entries in a dictionary */
+#define DICTMINSZ   128
+
+/** Invalid key token */
+#define DICT_INVALID_KEY    ((char*)-1)
+
+/*---------------------------------------------------------------------------
+                            Private functions
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Duplicate a string
+  @param    s String to duplicate
+  @return   Pointer to a newly allocated string, to be freed with free()
+
+  This is a replacement for strdup(). This implementation is provided
+  for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(const char * s)
+{
+    char * t ;
+    size_t len ;
+    if (!s)
+        return NULL ;
+
+    len = strlen(s) + 1 ;
+    t = (char*) malloc(len) ;
+    if (t) {
+        memcpy(t, s, len) ;
+    }
+    return t ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Double the size of the dictionary
+  @param    d Dictionary to grow
+  @return   This function returns non-zero in case of failure
+ */
+/*--------------------------------------------------------------------------*/
+static int dictionary_grow(dictionary * d)
+{
+    char        ** new_val ;
+    char        ** new_key ;
+    unsigned     * new_hash ;
+
+    new_val  = (char**) calloc(d->size * 2, sizeof *d->val);
+    new_key  = (char**) calloc(d->size * 2, sizeof *d->key);
+    new_hash = (unsigned*) calloc(d->size * 2, sizeof *d->hash);
+    if (!new_val || !new_key || !new_hash) {
+        /* An allocation failed, leave the dictionary unchanged */
+        if (new_val)
+            free(new_val);
+        if (new_key)
+            free(new_key);
+        if (new_hash)
+            free(new_hash);
+        return -1 ;
+    }
+    /* Initialize the newly allocated space */
+    memcpy(new_val, d->val, d->size * sizeof(char *));
+    memcpy(new_key, d->key, d->size * sizeof(char *));
+    memcpy(new_hash, d->hash, d->size * sizeof(unsigned));
+    /* Delete previous data */
+    free(d->val);
+    free(d->key);
+    free(d->hash);
+    /* Actually update the dictionary */
+    d->size *= 2 ;
+    d->val = new_val;
+    d->key = new_key;
+    d->hash = new_hash;
+    return 0 ;
+}
+
+/*---------------------------------------------------------------------------
+                            Function codes
+ ---------------------------------------------------------------------------*/
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Compute the hash key for a string.
+  @param    key     Character string to use for key.
+  @return   1 unsigned int on at least 32 bits.
+
+  This hash function has been taken from an Article in Dr Dobbs Journal.
+  This is normally a collision-free function, distributing keys evenly.
+  The key is stored anyway in the struct so that collision can be avoided
+  by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key)
+{
+    size_t      len ;
+    unsigned    hash ;
+    size_t      i ;
+
+    if (!key)
+        return 0 ;
+
+    len = strlen(key);
+    for (hash=0, i=0 ; i<len ; i++) {
+        hash += (unsigned)key[i] ;
+        hash += (hash<<10);
+        hash ^= (hash>>6) ;
+    }
+    hash += (hash <<3);
+    hash ^= (hash >>11);
+    hash += (hash <<15);
+    return hash ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Create a new dictionary object.
+  @param    size    Optional initial size of the dictionary.
+  @return   1 newly allocated dictionary object.
+
+  This function allocates a new dictionary object of given size and returns
+  it. If you do not know in advance (roughly) the number of entries in the
+  dictionary, give size=0.
+ */
+/*-------------------------------------------------------------------------*/
+dictionary * dictionary_new(size_t size)
+{
+    dictionary  *   d ;
+
+    /* If no size was specified, allocate space for DICTMINSZ */
+    if (size<DICTMINSZ) size=DICTMINSZ ;
+
+    d = (dictionary*) calloc(1, sizeof *d) ;
+
+    if (d) {
+        d->size = size ;
+        d->val  = (char**) calloc(size, sizeof *d->val);
+        d->key  = (char**) calloc(size, sizeof *d->key);
+        d->hash = (unsigned*) calloc(size, sizeof *d->hash);
+    }
+    return d ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a dictionary object
+  @param    d   dictionary object to deallocate.
+  @return   void
+
+  Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * d)
+{
+    ssize_t  i ;
+
+    if (d==NULL) return ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]!=NULL)
+            free(d->key[i]);
+        if (d->val[i]!=NULL)
+            free(d->val[i]);
+    }
+    free(d->val);
+    free(d->key);
+    free(d->hash);
+    free(d);
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get a value from a dictionary.
+  @param    d       dictionary object to search.
+  @param    key     Key to look for in the dictionary.
+  @param    def     Default value to return if key not found.
+  @return   1 pointer to internally allocated character string.
+
+  This function locates a key in a dictionary and returns a pointer to its
+  value, or the passed 'def' pointer if no such key can be found in
+  dictionary. The returned character pointer points to data internal to the
+  dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+const char * dictionary_get(const dictionary * d, const char * key, const char * def)
+{
+    unsigned    hash ;
+    ssize_t      i ;
+
+    hash = dictionary_hash(key);
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        /* Compare hash */
+        if (hash==d->hash[i]) {
+            /* Compare string, to avoid hash collisions */
+            if (!strcmp(key, d->key[i])) {
+                return d->val[i] ;
+            }
+        }
+    }
+    return def ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set a value in a dictionary.
+  @param    d       dictionary object to modify.
+  @param    key     Key to modify or add.
+  @param    val     Value to add.
+  @return   int     0 if Ok, anything else otherwise
+
+  If the given key is found in the dictionary, the associated value is
+  replaced by the provided one. If the key cannot be found in the
+  dictionary, it is added to it.
+
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
+  or the key are considered as errors: the function will return immediately
+  in such a case.
+
+  Notice that if you dictionary_set a variable to NULL, a call to
+  dictionary_get will return a NULL value: the variable will be found, and
+  its value (NULL) is returned. In other words, setting the variable
+  content to NULL is equivalent to deleting the variable from the
+  dictionary. It is not possible (in this implementation) to have a key in
+  the dictionary without value.
+
+  This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * d, const char * key, const char * val)
+{
+    ssize_t         i ;
+    unsigned       hash ;
+
+    if (d==NULL || key==NULL) return -1 ;
+
+    /* Compute hash for this key */
+    hash = dictionary_hash(key) ;
+    /* Find if value is already in dictionary */
+    if (d->n>0) {
+        for (i=0 ; i<d->size ; i++) {
+            if (d->key[i]==NULL)
+                continue ;
+            if (hash==d->hash[i]) { /* Same hash value */
+                if (!strcmp(key, d->key[i])) {   /* Same key */
+                    /* Found a value: modify and return */
+                    if (d->val[i]!=NULL)
+                        free(d->val[i]);
+                    d->val[i] = (val ? xstrdup(val) : NULL);
+                    /* Value has been modified: return */
+                    return 0 ;
+                }
+            }
+        }
+    }
+    /* Add a new value */
+    /* See if dictionary needs to grow */
+    if (d->n==d->size) {
+        /* Reached maximum size: reallocate dictionary */
+        if (dictionary_grow(d) != 0)
+            return -1;
+    }
+
+    /* Insert key in the first empty slot. Start at d->n and wrap at
+       d->size. Because d->n < d->size this will necessarily
+       terminate. */
+    for (i=d->n ; d->key[i] ; ) {
+        if(++i == d->size) i = 0;
+    }
+    /* Copy key */
+    d->key[i]  = xstrdup(key);
+    d->val[i]  = (val ? xstrdup(val) : NULL) ;
+    d->hash[i] = hash;
+    d->n ++ ;
+    return 0 ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a key in a dictionary
+  @param    d       dictionary object to modify.
+  @param    key     Key to remove.
+  @return   void
+
+  This function deletes a key in a dictionary. Nothing is done if the
+  key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key)
+{
+    unsigned    hash ;
+    ssize_t      i ;
+
+    if (key == NULL || d == NULL) {
+        return;
+    }
+
+    hash = dictionary_hash(key);
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        /* Compare hash */
+        if (hash==d->hash[i]) {
+            /* Compare string, to avoid hash collisions */
+            if (!strcmp(key, d->key[i])) {
+                /* Found key */
+                break ;
+            }
+        }
+    }
+    if (i>=d->size)
+        /* Key not found */
+        return ;
+
+    free(d->key[i]);
+    d->key[i] = NULL ;
+    if (d->val[i]!=NULL) {
+        free(d->val[i]);
+        d->val[i] = NULL ;
+    }
+    d->hash[i] = 0 ;
+    d->n -- ;
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer.
+  @return   void
+
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+  output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(const dictionary * d, FILE * out)
+{
+    ssize_t  i ;
+
+    if (d==NULL || out==NULL) return ;
+    if (d->n<1) {
+        fprintf(out, "empty dictionary\n");
+        return ;
+    }
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]) {
+            fprintf(out, "%20s\t[%s]\n",
+                    d->key[i],
+                    d->val[i] ? d->val[i] : "UNDEF");
+        }
+    }
+    return ;
+}
diff --git a/project/booster/dictionary.h b/project/booster/dictionary.h
new file mode 100644
index 0000000..87d31d9
--- /dev/null
+++ b/project/booster/dictionary.h
@@ -0,0 +1,174 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    dictionary.h
+   @author  N. Devillard
+   @brief   Implements a dictionary for string variables.
+   @url     https://github.com/ndevilla/iniparser
+
+   This module implements a simple dictionary object, i.e. a list
+   of string/string associations. This object is useful to store e.g.
+   informations retrieved from a configuration file (ini files).
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _DICTIONARY_H_
+#define _DICTIONARY_H_
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*---------------------------------------------------------------------------
+                                New types
+ ---------------------------------------------------------------------------*/
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dictionary object
+
+  This object contains a list of string/string associations. Each
+  association is identified by a unique string key. Looking up values
+  in the dictionary is speeded up by the use of a (hopefully collision-free)
+  hash function.
+ */
+/*-------------------------------------------------------------------------*/
+typedef struct _dictionary_ {
+    int             n ;     /** Number of entries in dictionary */
+    ssize_t         size ;  /** Storage size */
+    char        **  val ;   /** List of string values */
+    char        **  key ;   /** List of string keys */
+    unsigned     *  hash ;  /** List of hash values for keys */
+} dictionary ;
+
+
+/*---------------------------------------------------------------------------
+                            Function prototypes
+ ---------------------------------------------------------------------------*/
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Compute the hash key for a string.
+  @param    key     Character string to use for key.
+  @return   1 unsigned int on at least 32 bits.
+
+  This hash function has been taken from an Article in Dr Dobbs Journal.
+  This is normally a collision-free function, distributing keys evenly.
+  The key is stored anyway in the struct so that collision can be avoided
+  by comparing the key itself in last resort.
+ */
+/*--------------------------------------------------------------------------*/
+unsigned dictionary_hash(const char * key);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Create a new dictionary object.
+  @param    size    Optional initial size of the dictionary.
+  @return   1 newly allocated dictionary object.
+
+  This function allocates a new dictionary object of given size and returns
+  it. If you do not know in advance (roughly) the number of entries in the
+  dictionary, give size=0.
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * dictionary_new(size_t size);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a dictionary object
+  @param    d   dictionary object to deallocate.
+  @return   void
+
+  Deallocate a dictionary object and all memory associated to it.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_del(dictionary * vd);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get a value from a dictionary.
+  @param    d       dictionary object to search.
+  @param    key     Key to look for in the dictionary.
+  @param    def     Default value to return if key not found.
+  @return   1 pointer to internally allocated character string.
+
+  This function locates a key in a dictionary and returns a pointer to its
+  value, or the passed 'def' pointer if no such key can be found in
+  dictionary. The returned character pointer points to data internal to the
+  dictionary object, you should not try to free it or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+const char * dictionary_get(const dictionary * d, const char * key, const char * def);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set a value in a dictionary.
+  @param    d       dictionary object to modify.
+  @param    key     Key to modify or add.
+  @param    val     Value to add.
+  @return   int     0 if Ok, anything else otherwise
+
+  If the given key is found in the dictionary, the associated value is
+  replaced by the provided one. If the key cannot be found in the
+  dictionary, it is added to it.
+
+  It is Ok to provide a NULL value for val, but NULL values for the dictionary
+  or the key are considered as errors: the function will return immediately
+  in such a case.
+
+  Notice that if you dictionary_set a variable to NULL, a call to
+  dictionary_get will return a NULL value: the variable will be found, and
+  its value (NULL) is returned. In other words, setting the variable
+  content to NULL is equivalent to deleting the variable from the
+  dictionary. It is not possible (in this implementation) to have a key in
+  the dictionary without value.
+
+  This function returns non-zero in case of failure.
+ */
+/*--------------------------------------------------------------------------*/
+int dictionary_set(dictionary * vd, const char * key, const char * val);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete a key in a dictionary
+  @param    d       dictionary object to modify.
+  @param    key     Key to remove.
+  @return   void
+
+  This function deletes a key in a dictionary. Nothing is done if the
+  key cannot be found.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_unset(dictionary * d, const char * key);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer.
+  @return   void
+
+  Dumps a dictionary onto an opened file pointer. Key pairs are printed out
+  as @c [Key]=[Value], one per line. It is Ok to provide stdout or stderr as
+  output file pointers.
+ */
+/*--------------------------------------------------------------------------*/
+void dictionary_dump(const dictionary * d, FILE * out);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/project/booster/esp32.c b/project/booster/esp32.c
new file mode 100644
index 0000000..c26836c
--- /dev/null
+++ b/project/booster/esp32.c
@@ -0,0 +1,166 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  esp32.c
+ *    Description:  This file is ESP32 high level logic API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#include "logger.h"
+#include "esp32.h"
+
+int esp32_init_module(comport_t *comport)
+{
+    int             rv;
+    char            version[256] = {0};
+
+    if( !comport )
+        return -1;
+
+    rv = esp32_reset(comport);
+    if( rv < 0)
+    {
+        log_error("Reset ESP32 WiFi module failed: %d\n", rv);
+        return rv;
+    }
+
+    esp32_set_echo(comport, DISABLE);
+
+    esp32_set_sysstore(comport, ENABLE);
+
+    rv = esp32_get_firmware(comport, version, sizeof(version));
+    if( rv < 0)
+    {
+        log_error("Query ESP32 firmware version failed: %d\n", rv);
+        return rv;
+    }
+
+    log_info("ESP32 firmware version:\n%s\n", version);
+
+    return 0;
+}
+
+int esp32_setup_softap(comport_t *comport, char *ssid, char *pwd)
+{
+    esp32_set_wmode(comport, MODE_SOFTAP, ENABLE);
+
+    esp32_set_ipaddr(comport, MODE_SOFTAP, DEF_SOFTAP_IPADDR, DEF_SOFTAP_IPADDR);
+
+    esp32_set_dhcp(comport, MODE_SOFTAP, ENABLE);
+
+    esp32_set_softap(comport, ssid, pwd, 11, MODE_WPA2PSK);
+
+    return 0;
+}
+
+int esp32_join_network(comport_t *comport, char *ssid, char *pwd)
+{
+    int             rv, status = 0;
+    int             times=10;
+    char            buf[128] = {0};
+
+    if( !comport )
+        return -1;
+
+    esp32_join_status(comport, &status, buf);
+    if( 2==status && !strcmp(buf, ssid) )
+    {
+        log_info("ESP32 connected to \"%s\" already\n", ssid);
+        return 0;
+    }
+
+    esp32_set_wmode(comport, MODE_STATION, ENABLE);
+
+    esp32_set_dhcp(comport, MODE_STATION, ENABLE);
+
+    rv = esp32_connect_ap(comport, ssid, pwd);
+    if( rv < 0 )
+    {
+        log_error("connect to AP \"%s\" failed, rv=%d", ssid, rv);
+        return rv;
+    }
+
+    while(times--)
+    {
+        rv = esp32_join_status(comport, &status, buf);
+        if( 2 == status )
+        {
+            log_info("ESP32 connected to \"%s\" already\n", ssid);
+            return 0;
+        }
+        rv = -3;
+    }
+
+    return rv;
+}
+
+
+int esp32_check_network(comport_t *comport)
+{
+    int             rv, times=5;
+    char            ip[IP_LEN] = {0};
+    char            gateway[IP_LEN] = {0};
+
+    memset(ip, 0, sizeof(ip));
+    memset(gateway, 0, sizeof(gateway));
+    rv = esp32_get_ipaddr(comport, MODE_STATION, ip, gateway);
+    if( rv<0 )
+        return rv;
+
+    if( !strlen(ip) || !strlen(gateway) )
+        return -3;
+
+    log_info("IP address: %s, netmask: %s\n", ip, gateway);
+
+    while( times-- )
+    {
+        if( !(rv=esp32_ping(comport, gateway, 5000)) )
+        {
+            rv = 0;
+            break;
+        }
+    }
+
+    return 0;
+}
+
+int esp32_setup_tcp_server(comport_t *comport, int port)
+{
+    int             rv;
+
+    rv = esp32_set_socket_mux(comport, ENABLE);
+    if(rv<0)
+        return rv;
+
+    rv = esp32_set_socket_clients(comport, 2);
+    if(rv<0)
+        return rv;
+
+    rv = esp32_set_tcp_server(comport, port);
+    if(rv<0)
+        return rv;
+
+    rv = esp32_set_socket_timeout(comport, 600);
+    if(rv<0)
+        return rv;
+
+    return 0;
+}
+
+int esp32_setup_tcp_client(comport_t *comport, char *host, int port)
+{
+    int             rv;
+
+    rv = esp32_set_tcp_client(comport, DISABLE, host, port);
+    if(rv<0)
+        return rv;
+
+
+    return 0;
+}
+
diff --git a/project/booster/esp32.h b/project/booster/esp32.h
new file mode 100644
index 0000000..deb8450
--- /dev/null
+++ b/project/booster/esp32.h
@@ -0,0 +1,35 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  esp32.h
+ *    Description:  This file is ESP32 high level logic API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#ifndef  _ESP32_H_
+#define  _ESP32_H_
+
+#include "at-esp32.h"
+
+#define DEF_SOFTAP_IPADDR    "192.168.8.1"
+#define DEF_SOFTAP_SSID      "Router_ESP32"
+#define DEF_SOFTAP_PWD       "12345678"
+
+extern int esp32_init_module(comport_t *comport);
+
+extern int esp32_setup_softap(comport_t *comport, char *ssid, char *pwd);
+
+extern int esp32_join_network(comport_t *comport, char *ssid, char *pwd);
+
+extern int esp32_check_network(comport_t *comport);
+
+extern int esp32_setup_tcp_server(comport_t *comport, int port);
+
+extern int esp32_setup_tcp_client(comport_t *comport, char *host, int port);
+
+#endif   /* ----- #ifndef _ESP32_H_  ----- */
diff --git a/project/booster/iniparser.c b/project/booster/iniparser.c
new file mode 100644
index 0000000..3a446e5
--- /dev/null
+++ b/project/booster/iniparser.c
@@ -0,0 +1,837 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    iniparser.c
+   @author  N. Devillard
+   @brief   Parser for ini files.
+   @url     https://github.com/ndevilla/iniparser
+*/
+/*--------------------------------------------------------------------------*/
+/*---------------------------- Includes ------------------------------------*/
+#include <ctype.h>
+#include <stdarg.h>
+#include "iniparser.h"
+
+/*---------------------------- Defines -------------------------------------*/
+#define ASCIILINESZ         (1024)
+#define INI_INVALID_KEY     ((char*)-1)
+
+/*---------------------------------------------------------------------------
+                        Private to this module
+ ---------------------------------------------------------------------------*/
+/**
+ * This enum stores the status for each parsed line (internal use only).
+ */
+typedef enum _line_status_ {
+    LINE_UNPROCESSED,
+    LINE_ERROR,
+    LINE_EMPTY,
+    LINE_COMMENT,
+    LINE_SECTION,
+    LINE_VALUE
+} line_status ;
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Convert a string to lowercase.
+  @param    in   String to convert.
+  @param    out Output buffer.
+  @param    len Size of the out buffer.
+  @return   ptr to the out buffer or NULL if an error occured.
+
+  This function convert a string into lowercase.
+  At most len - 1 elements of the input string will be converted.
+ */
+/*--------------------------------------------------------------------------*/
+static const char * strlwc(const char * in, char *out, unsigned len)
+{
+    unsigned i ;
+
+    if (in==NULL || out == NULL || len==0) return NULL ;
+    i=0 ;
+    while (in[i] != '\0' && i < len-1) {
+        out[i] = (char)tolower((int)in[i]);
+        i++ ;
+    }
+    out[i] = '\0';
+    return out ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Duplicate a string
+  @param    s String to duplicate
+  @return   Pointer to a newly allocated string, to be freed with free()
+
+  This is a replacement for strdup(). This implementation is provided
+  for systems that do not have it.
+ */
+/*--------------------------------------------------------------------------*/
+static char * xstrdup(const char * s)
+{
+    char * t ;
+    size_t len ;
+    if (!s)
+        return NULL ;
+
+    len = strlen(s) + 1 ;
+    t = (char*) malloc(len) ;
+    if (t) {
+        memcpy(t, s, len) ;
+    }
+    return t ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Remove blanks at the beginning and the end of a string.
+  @param    str  String to parse and alter.
+  @return   unsigned New size of the string.
+ */
+/*--------------------------------------------------------------------------*/
+static unsigned strstrip(char * s)
+{
+    char *last = NULL ;
+    char *dest = s;
+
+    if (s==NULL) return 0;
+
+    last = s + strlen(s);
+    while (isspace((int)*s) && *s) s++;
+    while (last > s) {
+        if (!isspace((int)*(last-1)))
+            break ;
+        last -- ;
+    }
+    *last = (char)0;
+
+    memmove(dest,s,last - s + 1);
+    return last - s;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Default error callback for iniparser: wraps `fprintf(stderr, ...)`.
+ */
+/*--------------------------------------------------------------------------*/
+static int default_error_callback(const char *format, ...)
+{
+  int ret;
+  va_list argptr;
+  va_start(argptr, format);
+  ret = vfprintf(stderr, format, argptr);
+  va_end(argptr);
+  return ret;
+}
+
+static int (*iniparser_error_callback)(const char*, ...) = default_error_callback;
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Configure a function to receive the error messages.
+  @param    errback  Function to call.
+
+  By default, the error will be printed on stderr. If a null pointer is passed
+  as errback the error callback will be switched back to default.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_set_error_callback(int (*errback)(const char *, ...))
+{
+  if (errback) {
+    iniparser_error_callback = errback;
+  } else {
+    iniparser_error_callback = default_error_callback;
+  }
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get number of sections in a dictionary
+  @param    d   Dictionary to examine
+  @return   int Number of sections found in dictionary
+
+  This function returns the number of sections found in a dictionary.
+  The test to recognize sections is done on the string stored in the
+  dictionary: a section name is given as "section" whereas a key is
+  stored as "section:key", thus the test looks for entries that do not
+  contain a colon.
+
+  This clearly fails in the case a section name contains a colon, but
+  this should simply be avoided.
+
+  This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getnsec(const dictionary * d)
+{
+    int i ;
+    int nsec ;
+
+    if (d==NULL) return -1 ;
+    nsec=0 ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (strchr(d->key[i], ':')==NULL) {
+            nsec ++ ;
+        }
+    }
+    return nsec ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get name for section n in a dictionary.
+  @param    d   Dictionary to examine
+  @param    n   Section number (from 0 to nsec-1).
+  @return   Pointer to char string
+
+  This function locates the n-th section in a dictionary and returns
+  its name as a pointer to a string statically allocated inside the
+  dictionary. Do not free or modify the returned string!
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+const char * iniparser_getsecname(const dictionary * d, int n)
+{
+    int i ;
+    int foundsec ;
+
+    if (d==NULL || n<0) return NULL ;
+    foundsec=0 ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (strchr(d->key[i], ':')==NULL) {
+            foundsec++ ;
+            if (foundsec>n)
+                break ;
+        }
+    }
+    if (foundsec<=n) {
+        return NULL ;
+    }
+    return d->key[i] ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump.
+  @param    f   Opened file pointer to dump to.
+  @return   void
+
+  This function prints out the contents of a dictionary, one element by
+  line, onto the provided file pointer. It is OK to specify @c stderr
+  or @c stdout as output files. This function is meant for debugging
+  purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(const dictionary * d, FILE * f)
+{
+    int     i ;
+
+    if (d==NULL || f==NULL) return ;
+    for (i=0 ; i<d->size ; i++) {
+        if (d->key[i]==NULL)
+            continue ;
+        if (d->val[i]!=NULL) {
+            fprintf(f, "[%s]=[%s]\n", d->key[i], d->val[i]);
+        } else {
+            fprintf(f, "[%s]=UNDEF\n", d->key[i]);
+        }
+    }
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given dictionary into a loadable ini file.
+  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump_ini(const dictionary * d, FILE * f)
+{
+    int          i ;
+    int          nsec ;
+    const char * secname ;
+
+    if (d==NULL || f==NULL) return ;
+
+    nsec = iniparser_getnsec(d);
+    if (nsec<1) {
+        /* No section in file: dump all keys as they are */
+        for (i=0 ; i<d->size ; i++) {
+            if (d->key[i]==NULL)
+                continue ;
+            fprintf(f, "%s = %s\n", d->key[i], d->val[i]);
+        }
+        return ;
+    }
+    for (i=0 ; i<nsec ; i++) {
+        secname = iniparser_getsecname(d, i) ;
+        iniparser_dumpsection_ini(d, secname, f);
+    }
+    fprintf(f, "\n");
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary section to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    s   Section name of dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given section of a given dictionary into a loadable ini
+  file.  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f)
+{
+    int     j ;
+    char    keym[ASCIILINESZ+1];
+    int     seclen ;
+
+    if (d==NULL || f==NULL) return ;
+    if (! iniparser_find_entry(d, s)) return ;
+
+    seclen  = (int)strlen(s);
+    fprintf(f, "\n[%s]\n", s);
+    sprintf(keym, "%s:", s);
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) {
+            fprintf(f,
+                    "%-30s = %s\n",
+                    d->key[j]+seclen+1,
+                    d->val[j] ? d->val[j] : "");
+        }
+    }
+    fprintf(f, "\n");
+    return ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(const dictionary * d, const char * s)
+{
+    int     seclen, nkeys ;
+    char    keym[ASCIILINESZ+1];
+    int j ;
+
+    nkeys = 0;
+
+    if (d==NULL) return nkeys;
+    if (! iniparser_find_entry(d, s)) return nkeys;
+
+    seclen  = (int)strlen(s);
+    strlwc(s, keym, sizeof(keym));
+    keym[seclen] = ':';
+
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1))
+            nkeys++;
+    }
+
+    return nkeys;
+
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d    Dictionary to examine
+  @param    s    Section name of dictionary to examine
+  @param    keys Already allocated array to store the keys in
+  @return   The pointer passed as `keys` argument or NULL in case of error
+
+  This function queries a dictionary and finds all keys in a given section.
+  The keys argument should be an array of pointers which size has been
+  determined by calling `iniparser_getsecnkeys` function prior to this one.
+
+  Each pointer in the returned char pointer-to-pointer is pointing to
+  a string allocated in the dictionary; do not free or modify them.
+ */
+/*--------------------------------------------------------------------------*/
+const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys)
+{
+    int i, j, seclen ;
+    char keym[ASCIILINESZ+1];
+
+    if (d==NULL || keys==NULL) return NULL;
+    if (! iniparser_find_entry(d, s)) return NULL;
+
+    seclen  = (int)strlen(s);
+    strlwc(s, keym, sizeof(keym));
+    keym[seclen] = ':';
+
+    i = 0;
+
+    for (j=0 ; j<d->size ; j++) {
+        if (d->key[j]==NULL)
+            continue ;
+        if (!strncmp(d->key[j], keym, seclen+1)) {
+            keys[i] = d->key[j];
+            i++;
+        }
+    }
+
+    return keys;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key
+  @param    d       Dictionary to search
+  @param    key     Key string to look for
+  @param    def     Default value to return if key not found.
+  @return   pointer to statically allocated character string
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the pointer passed as 'def' is returned.
+  The returned char pointer is pointing to a string allocated in
+  the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+const char * iniparser_getstring(const dictionary * d, const char * key, const char * def)
+{
+    const char * lc_key ;
+    const char * sval ;
+    char tmp_str[ASCIILINESZ+1];
+
+    if (d==NULL || key==NULL)
+        return def ;
+
+    lc_key = strlwc(key, tmp_str, sizeof(tmp_str));
+    sval = dictionary_get(d, lc_key, def);
+    return sval ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an long int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   long integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  "42"      ->  42
+  "042"     ->  34 (octal -> decimal)
+  "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+
+  Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound)
+{
+    const char * str ;
+
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (str==INI_INVALID_KEY) return notfound ;
+    return strtol(str, NULL, 0);
+}
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  "42"      ->  42
+  "042"     ->  34 (octal -> decimal)
+  "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+
+  Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(const dictionary * d, const char * key, int notfound)
+{
+    return (int)iniparser_getlongint(d, key, notfound);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a double
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   double
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(const dictionary * d, const char * key, double notfound)
+{
+    const char * str ;
+
+    str = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (str==INI_INVALID_KEY) return notfound ;
+    return atof(str);
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a boolean
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  A true boolean is found if one of the following is matched:
+
+  - A string starting with 'y'
+  - A string starting with 'Y'
+  - A string starting with 't'
+  - A string starting with 'T'
+  - A string starting with '1'
+
+  A false boolean is found if one of the following is matched:
+
+  - A string starting with 'n'
+  - A string starting with 'N'
+  - A string starting with 'f'
+  - A string starting with 'F'
+  - A string starting with '0'
+
+  The notfound value returned if no boolean is identified, does not
+  necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(const dictionary * d, const char * key, int notfound)
+{
+    int          ret ;
+    const char * c ;
+
+    c = iniparser_getstring(d, key, INI_INVALID_KEY);
+    if (c==INI_INVALID_KEY) return notfound ;
+    if (c[0]=='y' || c[0]=='Y' || c[0]=='1' || c[0]=='t' || c[0]=='T') {
+        ret = 1 ;
+    } else if (c[0]=='n' || c[0]=='N' || c[0]=='0' || c[0]=='f' || c[0]=='F') {
+        ret = 0 ;
+    } else {
+        ret = notfound ;
+    }
+    return ret;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Finds out if a given entry exists in a dictionary
+  @param    ini     Dictionary to search
+  @param    entry   Name of the entry to look for
+  @return   integer 1 if entry exists, 0 otherwise
+
+  Finds out if a given entry exists in the dictionary. Since sections
+  are stored as keys with NULL associated values, this is the only way
+  of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(const dictionary * ini, const char * entry)
+{
+    int found=0 ;
+    if (iniparser_getstring(ini, entry, INI_INVALID_KEY)!=INI_INVALID_KEY) {
+        found = 1 ;
+    }
+    return found ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set an entry in a dictionary.
+  @param    ini     Dictionary to modify.
+  @param    entry   Entry to modify (entry name)
+  @param    val     New value to associate to the entry.
+  @return   int 0 if Ok, -1 otherwise.
+
+  If the given entry can be found in the dictionary, it is modified to
+  contain the provided value. If it cannot be found, the entry is created.
+  It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val)
+{
+    char tmp_str[ASCIILINESZ+1];
+    return dictionary_set(ini, strlwc(entry, tmp_str, sizeof(tmp_str)), val) ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete an entry in a dictionary
+  @param    ini     Dictionary to modify
+  @param    entry   Entry to delete (entry name)
+  @return   void
+
+  If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry)
+{
+    char tmp_str[ASCIILINESZ+1];
+    dictionary_unset(ini, strlwc(entry, tmp_str, sizeof(tmp_str)));
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Load a single line from an INI file
+  @param    input_line  Input line, may be concatenated multi-line input
+  @param    section     Output space to store section
+  @param    key         Output space to store key
+  @param    value       Output space to store value
+  @return   line_status value
+ */
+/*--------------------------------------------------------------------------*/
+static line_status iniparser_line(
+    const char * input_line,
+    char * section,
+    char * key,
+    char * value)
+{
+    line_status sta ;
+    char * line = NULL;
+    size_t      len ;
+
+    line = xstrdup(input_line);
+    len = strstrip(line);
+
+    sta = LINE_UNPROCESSED ;
+    if (len<1) {
+        /* Empty line */
+        sta = LINE_EMPTY ;
+    } else if (line[0]=='#' || line[0]==';') {
+        /* Comment line */
+        sta = LINE_COMMENT ;
+    } else if (line[0]=='[' && line[len-1]==']') {
+        /* Section name */
+        sscanf(line, "[%[^]]", section);
+        strstrip(section);
+        strlwc(section, section, len);
+        sta = LINE_SECTION ;
+    } else if (sscanf (line, "%[^=] = \"%[^\"]\"", key, value) == 2
+           ||  sscanf (line, "%[^=] = '%[^\']'",   key, value) == 2) {
+        /* Usual key=value with quotes, with or without comments */
+        strstrip(key);
+        strlwc(key, key, len);
+        /* Don't strip spaces from values surrounded with quotes */
+        sta = LINE_VALUE ;
+    } else if (sscanf (line, "%[^=] = %[^;#]", key, value) == 2) {
+        /* Usual key=value without quotes, with or without comments */
+        strstrip(key);
+        strlwc(key, key, len);
+        strstrip(value);
+        /*
+         * sscanf cannot handle '' or "" as empty values
+         * this is done here
+         */
+        if (!strcmp(value, "\"\"") || (!strcmp(value, "''"))) {
+            value[0]=0 ;
+        }
+        sta = LINE_VALUE ;
+    } else if (sscanf(line, "%[^=] = %[;#]", key, value)==2
+           ||  sscanf(line, "%[^=] %[=]", key, value) == 2) {
+        /*
+         * Special cases:
+         * key=
+         * key=;
+         * key=#
+         */
+        strstrip(key);
+        strlwc(key, key, len);
+        value[0]=0 ;
+        sta = LINE_VALUE ;
+    } else {
+        /* Generate syntax error */
+        sta = LINE_ERROR ;
+    }
+
+    free(line);
+    return sta ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Parse an ini file and return an allocated dictionary object
+  @param    ininame Name of the ini file to read.
+  @return   Pointer to newly allocated dictionary
+
+  This is the parser for ini files. This function is called, providing
+  the name of the file to be read. It returns a dictionary object that
+  should not be accessed directly, but through accessor functions
+  instead.
+
+  The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame)
+{
+    FILE * in ;
+
+    char line    [ASCIILINESZ+1] ;
+    char section [ASCIILINESZ+1] ;
+    char key     [ASCIILINESZ+1] ;
+    char tmp     [(ASCIILINESZ * 2) + 2] ;
+    char val     [ASCIILINESZ+1] ;
+
+    int  last=0 ;
+    int  len ;
+    int  lineno=0 ;
+    int  errs=0;
+    int  mem_err=0;
+
+    dictionary * dict ;
+
+    if ((in=fopen(ininame, "r"))==NULL) {
+        iniparser_error_callback("iniparser: cannot open %s\n", ininame);
+        return NULL ;
+    }
+
+    dict = dictionary_new(0) ;
+    if (!dict) {
+        fclose(in);
+        return NULL ;
+    }
+
+    memset(line,    0, ASCIILINESZ);
+    memset(section, 0, ASCIILINESZ);
+    memset(key,     0, ASCIILINESZ);
+    memset(val,     0, ASCIILINESZ);
+    last=0 ;
+
+    while (fgets(line+last, ASCIILINESZ-last, in)!=NULL) {
+        lineno++ ;
+        len = (int)strlen(line)-1;
+        if (len<=0)
+            continue;
+        /* Safety check against buffer overflows */
+        if (line[len]!='\n' && !feof(in)) {
+            iniparser_error_callback(
+              "iniparser: input line too long in %s (%d)\n",
+              ininame,
+              lineno);
+            dictionary_del(dict);
+            fclose(in);
+            return NULL ;
+        }
+        /* Get rid of \n and spaces at end of line */
+        while ((len>=0) &&
+                ((line[len]=='\n') || (isspace(line[len])))) {
+            line[len]=0 ;
+            len-- ;
+        }
+        if (len < 0) { /* Line was entirely \n and/or spaces */
+            len = 0;
+        }
+        /* Detect multi-line */
+        if (line[len]=='\\') {
+            /* Multi-line value */
+            last=len ;
+            continue ;
+        } else {
+            last=0 ;
+        }
+        switch (iniparser_line(line, section, key, val)) {
+            case LINE_EMPTY:
+            case LINE_COMMENT:
+            break ;
+
+            case LINE_SECTION:
+            mem_err = dictionary_set(dict, section, NULL);
+            break ;
+
+            case LINE_VALUE:
+            sprintf(tmp, "%s:%s", section, key);
+            mem_err = dictionary_set(dict, tmp, val);
+            break ;
+
+            case LINE_ERROR:
+            iniparser_error_callback(
+              "iniparser: syntax error in %s (%d):\n-> %s\n",
+              ininame,
+              lineno,
+              line);
+            errs++ ;
+            break;
+
+            default:
+            break ;
+        }
+        memset(line, 0, ASCIILINESZ);
+        last=0;
+        if (mem_err<0) {
+            iniparser_error_callback("iniparser: memory allocation failure\n");
+            break ;
+        }
+    }
+    if (errs) {
+        dictionary_del(dict);
+        dict = NULL ;
+    }
+    fclose(in);
+    return dict ;
+}
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Free all memory associated to an ini dictionary
+  @param    d Dictionary to free
+  @return   void
+
+  Free all memory associated to an ini dictionary.
+  It is mandatory to call this function before the dictionary object
+  gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d)
+{
+    dictionary_del(d);
+}
diff --git a/project/booster/iniparser.h b/project/booster/iniparser.h
new file mode 100644
index 0000000..984f4b2
--- /dev/null
+++ b/project/booster/iniparser.h
@@ -0,0 +1,359 @@
+
+/*-------------------------------------------------------------------------*/
+/**
+   @file    iniparser.h
+   @author  N. Devillard
+   @brief   Parser for ini files. 
+   @url     https://github.com/ndevilla/iniparser
+*/
+/*--------------------------------------------------------------------------*/
+
+#ifndef _INIPARSER_H_
+#define _INIPARSER_H_
+
+/*---------------------------------------------------------------------------
+                                Includes
+ ---------------------------------------------------------------------------*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+/*
+ * The following #include is necessary on many Unixes but not Linux.
+ * It is not needed for Windows platforms.
+ * Uncomment it if needed.
+ */
+/* #include <unistd.h> */
+
+#include "dictionary.h"
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Configure a function to receive the error messages.
+  @param    errback  Function to call.
+
+  By default, the error will be printed on stderr. If a null pointer is passed
+  as errback the error callback will be switched back to default.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_set_error_callback(int (*errback)(const char *, ...));
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get number of sections in a dictionary
+  @param    d   Dictionary to examine
+  @return   int Number of sections found in dictionary
+
+  This function returns the number of sections found in a dictionary.
+  The test to recognize sections is done on the string stored in the
+  dictionary: a section name is given as "section" whereas a key is
+  stored as "section:key", thus the test looks for entries that do not
+  contain a colon.
+
+  This clearly fails in the case a section name contains a colon, but
+  this should simply be avoided.
+
+  This function returns -1 in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+int iniparser_getnsec(const dictionary * d);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get name for section n in a dictionary.
+  @param    d   Dictionary to examine
+  @param    n   Section number (from 0 to nsec-1).
+  @return   Pointer to char string
+
+  This function locates the n-th section in a dictionary and returns
+  its name as a pointer to a string statically allocated inside the
+  dictionary. Do not free or modify the returned string!
+
+  This function returns NULL in case of error.
+ */
+/*--------------------------------------------------------------------------*/
+
+const char * iniparser_getsecname(const dictionary * d, int n);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given dictionary into a loadable ini file.
+  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dump_ini(const dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Save a dictionary section to a loadable ini file
+  @param    d   Dictionary to dump
+  @param    s   Section name of dictionary to dump
+  @param    f   Opened file pointer to dump to
+  @return   void
+
+  This function dumps a given section of a given dictionary into a loadable ini
+  file.  It is Ok to specify @c stderr or @c stdout as output files.
+ */
+/*--------------------------------------------------------------------------*/
+
+void iniparser_dumpsection_ini(const dictionary * d, const char * s, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Dump a dictionary to an opened file pointer.
+  @param    d   Dictionary to dump.
+  @param    f   Opened file pointer to dump to.
+  @return   void
+
+  This function prints out the contents of a dictionary, one element by
+  line, onto the provided file pointer. It is OK to specify @c stderr
+  or @c stdout as output files. This function is meant for debugging
+  purposes mostly.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_dump(const dictionary * d, FILE * f);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d   Dictionary to examine
+  @param    s   Section name of dictionary to examine
+  @return   Number of keys in section
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getsecnkeys(const dictionary * d, const char * s);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the number of keys in a section of a dictionary.
+  @param    d    Dictionary to examine
+  @param    s    Section name of dictionary to examine
+  @param    keys Already allocated array to store the keys in
+  @return   The pointer passed as `keys` argument or NULL in case of error
+
+  This function queries a dictionary and finds all keys in a given section.
+  The keys argument should be an array of pointers which size has been
+  determined by calling `iniparser_getsecnkeys` function prior to this one.
+
+  Each pointer in the returned char pointer-to-pointer is pointing to
+  a string allocated in the dictionary; do not free or modify them.
+ */
+/*--------------------------------------------------------------------------*/
+const char ** iniparser_getseckeys(const dictionary * d, const char * s, const char ** keys);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key
+  @param    d       Dictionary to search
+  @param    key     Key string to look for
+  @param    def     Default value to return if key not found.
+  @return   pointer to statically allocated character string
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the pointer passed as 'def' is returned.
+  The returned char pointer is pointing to a string allocated in
+  the dictionary, do not free or modify it.
+ */
+/*--------------------------------------------------------------------------*/
+const char * iniparser_getstring(const dictionary * d, const char * key, const char * def);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  - "42"      ->  42
+  - "042"     ->  34 (octal -> decimal)
+  - "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+
+  Credits: Thanks to A. Becker for suggesting strtol()
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getint(const dictionary * d, const char * key, int notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to an long int
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  Supported values for integers include the usual C notation
+  so decimal, octal (starting with 0) and hexadecimal (starting with 0x)
+  are supported. Examples:
+
+  - "42"      ->  42
+  - "042"     ->  34 (octal -> decimal)
+  - "0x42"    ->  66 (hexa  -> decimal)
+
+  Warning: the conversion may overflow in various ways. Conversion is
+  totally outsourced to strtol(), see the associated man page for overflow
+  handling.
+ */
+/*--------------------------------------------------------------------------*/
+long int iniparser_getlongint(const dictionary * d, const char * key, long int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a double
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   double
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+ */
+/*--------------------------------------------------------------------------*/
+double iniparser_getdouble(const dictionary * d, const char * key, double notfound);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Get the string associated to a key, convert to a boolean
+  @param    d Dictionary to search
+  @param    key Key string to look for
+  @param    notfound Value to return in case of error
+  @return   integer
+
+  This function queries a dictionary for a key. A key as read from an
+  ini file is given as "section:key". If the key cannot be found,
+  the notfound value is returned.
+
+  A true boolean is found if one of the following is matched:
+
+  - A string starting with 'y'
+  - A string starting with 'Y'
+  - A string starting with 't'
+  - A string starting with 'T'
+  - A string starting with '1'
+
+  A false boolean is found if one of the following is matched:
+
+  - A string starting with 'n'
+  - A string starting with 'N'
+  - A string starting with 'f'
+  - A string starting with 'F'
+  - A string starting with '0'
+
+  The notfound value returned if no boolean is identified, does not
+  necessarily have to be 0 or 1.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_getboolean(const dictionary * d, const char * key, int notfound);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Set an entry in a dictionary.
+  @param    ini     Dictionary to modify.
+  @param    entry   Entry to modify (entry name)
+  @param    val     New value to associate to the entry.
+  @return   int     0 if Ok, -1 otherwise.
+
+  If the given entry can be found in the dictionary, it is modified to
+  contain the provided value. If it cannot be found, the entry is created.
+  It is Ok to set val to NULL.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_set(dictionary * ini, const char * entry, const char * val);
+
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Delete an entry in a dictionary
+  @param    ini     Dictionary to modify
+  @param    entry   Entry to delete (entry name)
+  @return   void
+
+  If the given entry can be found, it is deleted from the dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_unset(dictionary * ini, const char * entry);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Finds out if a given entry exists in a dictionary
+  @param    ini     Dictionary to search
+  @param    entry   Name of the entry to look for
+  @return   integer 1 if entry exists, 0 otherwise
+
+  Finds out if a given entry exists in the dictionary. Since sections
+  are stored as keys with NULL associated values, this is the only way
+  of querying for the presence of sections in a dictionary.
+ */
+/*--------------------------------------------------------------------------*/
+int iniparser_find_entry(const dictionary * ini, const char * entry) ;
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Parse an ini file and return an allocated dictionary object
+  @param    ininame Name of the ini file to read.
+  @return   Pointer to newly allocated dictionary
+
+  This is the parser for ini files. This function is called, providing
+  the name of the file to be read. It returns a dictionary object that
+  should not be accessed directly, but through accessor functions
+  instead.
+
+  The returned dictionary must be freed using iniparser_freedict().
+ */
+/*--------------------------------------------------------------------------*/
+dictionary * iniparser_load(const char * ininame);
+
+/*-------------------------------------------------------------------------*/
+/**
+  @brief    Free all memory associated to an ini dictionary
+  @param    d Dictionary to free
+  @return   void
+
+  Free all memory associated to an ini dictionary.
+  It is mandatory to call this function before the dictionary object
+  gets out of the current context.
+ */
+/*--------------------------------------------------------------------------*/
+void iniparser_freedict(dictionary * d);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/project/booster/list.h b/project/booster/list.h
new file mode 100644
index 0000000..ffd505e
--- /dev/null
+++ b/project/booster/list.h
@@ -0,0 +1,723 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2020 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  list.h
+ *    Description:  This file is copied from Linux kernel, which provide link list API.
+ *
+ *        Version:  1.0.0(08/09/2020)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "08/09/2020 02:24:34 AM"
+ *
+ ********************************************************************************/
+
+#ifndef _LINUX_LIST_H
+#define _LINUX_LIST_H
+
+#include <linux/stddef.h>
+
+
+/**
+ * container_of - cast a member of a structure out to the containing structure
+ * @ptr:    the pointer to the member.
+ * @type:   the type of the container struct this is embedded in.
+ * @member: the name of the member within the struct.
+ *
+ */
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+#define container_of(ptr, type, member) ({          \
+    const typeof( ((type *)0)->member ) *__mptr = (ptr);    \
+    (type *)( (char *)__mptr - offsetof(type,member) );})
+
+
+/*
+ * Architectures might want to move the poison pointer offset
+ * into some well-recognized area such as 0xdead000000000000,
+ * that is also not mappable by user-space exploits:
+ */
+#ifdef CONFIG_ILLEGAL_POINTER_VALUE
+# define POISON_POINTER_DELTA _AC(CONFIG_ILLEGAL_POINTER_VALUE, UL)
+#else
+# define POISON_POINTER_DELTA 0
+#endif
+
+/*
+ * These are non-NULL pointers that will result in page faults
+ * under normal circumstances, used to verify that nobody uses
+ * non-initialized list entries.
+ */
+#define LIST_POISON1  ((void *) 0x00100100 + POISON_POINTER_DELTA)
+#define LIST_POISON2  ((void *) 0x00200200 + POISON_POINTER_DELTA)
+
+#ifndef ARCH_HAS_PREFETCH
+#define ARCH_HAS_PREFETCH
+static inline void prefetch(const void *x) {;}
+#endif
+
+/*
+ * Simple doubly linked list implementation.
+ *
+ * Some of the internal functions ("__xxx") are useful when
+ * manipulating whole lists rather than single entries, as
+ * sometimes we already know the next/prev entries and we can
+ * generate better code by using them directly rather than
+ * using the generic single-entry routines.
+ */
+
+struct list_head {
+	struct list_head *next, *prev;
+};
+
+#define LIST_HEAD_INIT(name) { &(name), &(name) }
+
+#define LIST_HEAD(name) \
+	struct list_head name = LIST_HEAD_INIT(name)
+
+static inline void INIT_LIST_HEAD(struct list_head *list)
+{
+	list->next = list;
+	list->prev = list;
+}
+
+/*
+ * Insert a new entry between two known consecutive entries.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_add(struct list_head *new,
+			      struct list_head *prev,
+			      struct list_head *next)
+{
+	next->prev = new;
+	new->next = next;
+	new->prev = prev;
+	prev->next = new;
+}
+
+/**
+ * list_add - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it after
+ *
+ * Insert a new entry after the specified head.
+ * This is good for implementing stacks.
+ */
+static inline void list_add(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head, head->next);
+}
+
+/**
+ * list_add_tail - add a new entry
+ * @new: new entry to be added
+ * @head: list head to add it before
+ *
+ * Insert a new entry before the specified head.
+ * This is useful for implementing queues.
+ */
+static inline void list_add_tail(struct list_head *new, struct list_head *head)
+{
+	__list_add(new, head->prev, head);
+}
+
+/*
+ * Delete a list entry by making the prev/next entries
+ * point to each other.
+ *
+ * This is only for internal list manipulation where we know
+ * the prev/next entries already!
+ */
+static inline void __list_del(struct list_head *prev, struct list_head *next)
+{
+	next->prev = prev;
+	prev->next = next;
+}
+
+/**
+ * list_del - deletes entry from list.
+ * @entry: the element to delete from the list.
+ * Note: list_empty() on entry does not return true after this, the entry is
+ * in an undefined state.
+ */
+static inline void list_del(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	entry->next = LIST_POISON1;
+	entry->prev = LIST_POISON2;
+}
+
+/**
+ * list_replace - replace old entry by new one
+ * @old : the element to be replaced
+ * @new : the new element to insert
+ *
+ * If @old was empty, it will be overwritten.
+ */
+static inline void list_replace(struct list_head *old,
+				struct list_head *new)
+{
+	new->next = old->next;
+	new->next->prev = new;
+	new->prev = old->prev;
+	new->prev->next = new;
+}
+
+static inline void list_replace_init(struct list_head *old,
+					struct list_head *new)
+{
+	list_replace(old, new);
+	INIT_LIST_HEAD(old);
+}
+
+/**
+ * list_del_init - deletes entry from list and reinitialize it.
+ * @entry: the element to delete from the list.
+ */
+static inline void list_del_init(struct list_head *entry)
+{
+	__list_del(entry->prev, entry->next);
+	INIT_LIST_HEAD(entry);
+}
+
+/**
+ * list_move - delete from one list and add as another's head
+ * @list: the entry to move
+ * @head: the head that will precede our entry
+ */
+static inline void list_move(struct list_head *list, struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add(list, head);
+}
+
+/**
+ * list_move_tail - delete from one list and add as another's tail
+ * @list: the entry to move
+ * @head: the head that will follow our entry
+ */
+static inline void list_move_tail(struct list_head *list,
+				  struct list_head *head)
+{
+	__list_del(list->prev, list->next);
+	list_add_tail(list, head);
+}
+
+/**
+ * list_is_last - tests whether @list is the last entry in list @head
+ * @list: the entry to test
+ * @head: the head of the list
+ */
+static inline int list_is_last(const struct list_head *list,
+				const struct list_head *head)
+{
+	return list->next == head;
+}
+
+/**
+ * list_empty - tests whether a list is empty
+ * @head: the list to test.
+ */
+static inline int list_empty(const struct list_head *head)
+{
+	return head->next == head;
+}
+
+/**
+ * list_empty_careful - tests whether a list is empty and not being modified
+ * @head: the list to test
+ *
+ * Description:
+ * tests whether a list is empty _and_ checks that no other CPU might be
+ * in the process of modifying either member (next or prev)
+ *
+ * NOTE: using list_empty_careful() without synchronization
+ * can only be safe if the only activity that can happen
+ * to the list entry is list_del_init(). Eg. it cannot be used
+ * if another CPU could re-list_add() it.
+ */
+static inline int list_empty_careful(const struct list_head *head)
+{
+	struct list_head *next = head->next;
+	return (next == head) && (next == head->prev);
+}
+
+/**
+ * list_is_singular - tests whether a list has just one entry.
+ * @head: the list to test.
+ */
+static inline int list_is_singular(const struct list_head *head)
+{
+	return !list_empty(head) && (head->next == head->prev);
+}
+
+static inline void __list_cut_position(struct list_head *list,
+		struct list_head *head, struct list_head *entry)
+{
+	struct list_head *new_first = entry->next;
+	list->next = head->next;
+	list->next->prev = list;
+	list->prev = entry;
+	entry->next = list;
+	head->next = new_first;
+	new_first->prev = head;
+}
+
+/**
+ * list_cut_position - cut a list into two
+ * @list: a new list to add all removed entries
+ * @head: a list with entries
+ * @entry: an entry within head, could be the head itself
+ *	and if so we won't cut the list
+ *
+ * This helper moves the initial part of @head, up to and
+ * including @entry, from @head to @list. You should
+ * pass on @entry an element you know is on @head. @list
+ * should be an empty list or a list you do not care about
+ * losing its data.
+ *
+ */
+static inline void list_cut_position(struct list_head *list,
+		struct list_head *head, struct list_head *entry)
+{
+	if (list_empty(head))
+		return;
+	if (list_is_singular(head) &&
+		(head->next != entry && head != entry))
+		return;
+	if (entry == head)
+		INIT_LIST_HEAD(list);
+	else
+		__list_cut_position(list, head, entry);
+}
+
+static inline void __list_splice(const struct list_head *list,
+				 struct list_head *prev,
+				 struct list_head *next)
+{
+	struct list_head *first = list->next;
+	struct list_head *last = list->prev;
+
+	first->prev = prev;
+	prev->next = first;
+
+	last->next = next;
+	next->prev = last;
+}
+
+/**
+ * list_splice - join two lists, this is designed for stacks
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice(const struct list_head *list,
+				struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head, head->next);
+}
+
+/**
+ * list_splice_tail - join two lists, each list being a queue
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ */
+static inline void list_splice_tail(struct list_head *list,
+				struct list_head *head)
+{
+	if (!list_empty(list))
+		__list_splice(list, head->prev, head);
+}
+
+/**
+ * list_splice_init - join two lists and reinitialise the emptied list.
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_init(struct list_head *list,
+				    struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head, head->next);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_splice_tail_init - join two lists and reinitialise the emptied list
+ * @list: the new list to add.
+ * @head: the place to add it in the first list.
+ *
+ * Each of the lists is a queue.
+ * The list at @list is reinitialised
+ */
+static inline void list_splice_tail_init(struct list_head *list,
+					 struct list_head *head)
+{
+	if (!list_empty(list)) {
+		__list_splice(list, head->prev, head);
+		INIT_LIST_HEAD(list);
+	}
+}
+
+/**
+ * list_entry - get the struct for this entry
+ * @ptr:	the &struct list_head pointer.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_entry(ptr, type, member) \
+	container_of(ptr, type, member)
+
+/**
+ * list_first_entry - get the first element from a list
+ * @ptr:	the list head to take the element from.
+ * @type:	the type of the struct this is embedded in.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Note, that list is expected to be not empty.
+ */
+#define list_first_entry(ptr, type, member) \
+	list_entry((ptr)->next, type, member)
+
+/**
+ * list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each(pos, head) \
+	for (pos = (head)->next; prefetch(pos->next), pos != (head); \
+		pos = pos->next)
+
+/**
+ * __list_for_each	-	iterate over a list
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ *
+ * This variant differs from list_for_each() in that it's the
+ * simplest possible list iteration code, no prefetching is done.
+ * Use this for code that knows the list to be very short (empty
+ * or 1 entry) most of the time.
+ */
+#define __list_for_each(pos, head) \
+	for (pos = (head)->next; pos != (head); pos = pos->next)
+
+/**
+ * list_for_each_prev	-	iterate over a list backwards
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev(pos, head) \
+	for (pos = (head)->prev; prefetch(pos->prev), pos != (head); \
+		pos = pos->prev)
+
+/**
+ * list_for_each_safe - iterate over a list safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_safe(pos, n, head) \
+	for (pos = (head)->next, n = pos->next; pos != (head); \
+		pos = n, n = pos->next)
+
+/**
+ * list_for_each_prev_safe - iterate over a list backwards safe against removal of list entry
+ * @pos:	the &struct list_head to use as a loop cursor.
+ * @n:		another &struct list_head to use as temporary storage
+ * @head:	the head for your list.
+ */
+#define list_for_each_prev_safe(pos, n, head) \
+	for (pos = (head)->prev, n = pos->prev; \
+	     prefetch(pos->prev), pos != (head); \
+	     pos = n, n = pos->prev)
+
+/**
+ * list_for_each_entry	-	iterate over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry(pos, head, member)				\
+	for (pos = list_entry((head)->next, typeof(*pos), member);	\
+	     prefetch(pos->member.next), &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_reverse - iterate backwards over list of given type.
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_reverse(pos, head, member)			\
+	for (pos = list_entry((head)->prev, typeof(*pos), member);	\
+	     prefetch(pos->member.prev), &pos->member != (head);	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_prepare_entry - prepare a pos entry for use in list_for_each_entry_continue()
+ * @pos:	the type * to use as a start point
+ * @head:	the head of the list
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Prepares a pos entry for use as a start point in list_for_each_entry_continue().
+ */
+#define list_prepare_entry(pos, head, member) \
+	((pos) ? : list_entry(head, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue - continue iteration over list of given type
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Continue to iterate over list of given type, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue(pos, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member);	\
+	     prefetch(pos->member.next), &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_continue_reverse - iterate backwards from the given point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Start to iterate over list of given type backwards, continuing after
+ * the current position.
+ */
+#define list_for_each_entry_continue_reverse(pos, head, member)		\
+	for (pos = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     prefetch(pos->member.prev), &pos->member != (head);	\
+	     pos = list_entry(pos->member.prev, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_from - iterate over list of given type from the current point
+ * @pos:	the type * to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing from current position.
+ */
+#define list_for_each_entry_from(pos, head, member)			\
+	for (; prefetch(pos->member.next), &pos->member != (head);	\
+	     pos = list_entry(pos->member.next, typeof(*pos), member))
+
+/**
+ * list_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ */
+#define list_for_each_entry_safe(pos, n, head, member)			\
+	for (pos = list_entry((head)->next, typeof(*pos), member),	\
+		n = list_entry(pos->member.next, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_continue
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type, continuing after current point,
+ * safe against removal of list entry.
+ */
+#define list_for_each_entry_safe_continue(pos, n, head, member) 		\
+	for (pos = list_entry(pos->member.next, typeof(*pos), member),		\
+		n = list_entry(pos->member.next, typeof(*pos), member);		\
+	     &pos->member != (head);						\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_from
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate over list of given type from current point, safe against
+ * removal of list entry.
+ */
+#define list_for_each_entry_safe_from(pos, n, head, member)			\
+	for (n = list_entry(pos->member.next, typeof(*pos), member);		\
+	     &pos->member != (head);						\
+	     pos = n, n = list_entry(n->member.next, typeof(*n), member))
+
+/**
+ * list_for_each_entry_safe_reverse
+ * @pos:	the type * to use as a loop cursor.
+ * @n:		another type * to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the list_struct within the struct.
+ *
+ * Iterate backwards over list of given type, safe against removal
+ * of list entry.
+ */
+#define list_for_each_entry_safe_reverse(pos, n, head, member)		\
+	for (pos = list_entry((head)->prev, typeof(*pos), member),	\
+		n = list_entry(pos->member.prev, typeof(*pos), member);	\
+	     &pos->member != (head);					\
+	     pos = n, n = list_entry(n->member.prev, typeof(*n), member))
+
+/*
+ * Double linked lists with a single pointer list head.
+ * Mostly useful for hash tables where the two pointer list head is
+ * too wasteful.
+ * You lose the ability to access the tail in O(1).
+ */
+
+struct hlist_head {
+	struct hlist_node *first;
+};
+
+struct hlist_node {
+	struct hlist_node *next, **pprev;
+};
+
+#define HLIST_HEAD_INIT { .first = NULL }
+#define HLIST_HEAD(name) struct hlist_head name = {  .first = NULL }
+#define INIT_HLIST_HEAD(ptr) ((ptr)->first = NULL)
+static inline void INIT_HLIST_NODE(struct hlist_node *h)
+{
+	h->next = NULL;
+	h->pprev = NULL;
+}
+
+static inline int hlist_unhashed(const struct hlist_node *h)
+{
+	return !h->pprev;
+}
+
+static inline int hlist_empty(const struct hlist_head *h)
+{
+	return !h->first;
+}
+
+static inline void __hlist_del(struct hlist_node *n)
+{
+	struct hlist_node *next = n->next;
+	struct hlist_node **pprev = n->pprev;
+	*pprev = next;
+	if (next)
+		next->pprev = pprev;
+}
+
+static inline void hlist_del(struct hlist_node *n)
+{
+	__hlist_del(n);
+	n->next = LIST_POISON1;
+	n->pprev = LIST_POISON2;
+}
+
+static inline void hlist_del_init(struct hlist_node *n)
+{
+	if (!hlist_unhashed(n)) {
+		__hlist_del(n);
+		INIT_HLIST_NODE(n);
+	}
+}
+
+static inline void hlist_add_head(struct hlist_node *n, struct hlist_head *h)
+{
+	struct hlist_node *first = h->first;
+	n->next = first;
+	if (first)
+		first->pprev = &n->next;
+	h->first = n;
+	n->pprev = &h->first;
+}
+
+/* next must be != NULL */
+static inline void hlist_add_before(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	n->pprev = next->pprev;
+	n->next = next;
+	next->pprev = &n->next;
+	*(n->pprev) = n;
+}
+
+static inline void hlist_add_after(struct hlist_node *n,
+					struct hlist_node *next)
+{
+	next->next = n->next;
+	n->next = next;
+	next->pprev = &n->next;
+
+	if(next->next)
+		next->next->pprev  = &next->next;
+}
+
+#define hlist_entry(ptr, type, member) container_of(ptr,type,member)
+
+#define hlist_for_each(pos, head) \
+	for (pos = (head)->first; pos && ({ prefetch(pos->next); 1; }); \
+	     pos = pos->next)
+
+#define hlist_for_each_safe(pos, n, head) \
+	for (pos = (head)->first; pos && ({ n = pos->next; 1; }); \
+	     pos = n)
+
+/**
+ * hlist_for_each_entry	- iterate over list of given type
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct hlist_node to use as a loop cursor.
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry(tpos, pos, head, member)			 \
+	for (pos = (head)->first;					 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_continue - iterate over a hlist continuing after current point
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct hlist_node to use as a loop cursor.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_continue(tpos, pos, member)		 \
+	for (pos = (pos)->next;						 \
+	     pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_from - iterate over a hlist continuing from current point
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct hlist_node to use as a loop cursor.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_from(tpos, pos, member)			 \
+	for (; pos && ({ prefetch(pos->next); 1;}) &&			 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = pos->next)
+
+/**
+ * hlist_for_each_entry_safe - iterate over list of given type safe against removal of list entry
+ * @tpos:	the type * to use as a loop cursor.
+ * @pos:	the &struct hlist_node to use as a loop cursor.
+ * @n:		another &struct hlist_node to use as temporary storage
+ * @head:	the head for your list.
+ * @member:	the name of the hlist_node within the struct.
+ */
+#define hlist_for_each_entry_safe(tpos, pos, n, head, member)		 \
+	for (pos = (head)->first;					 \
+	     pos && ({ n = pos->next; 1; }) &&				 \
+		({ tpos = hlist_entry(pos, typeof(*tpos), member); 1;}); \
+	     pos = n)
+
+
+#endif
+
+
diff --git a/project/booster/logger.c b/project/booster/logger.c
new file mode 100644
index 0000000..217eb02
--- /dev/null
+++ b/project/booster/logger.c
@@ -0,0 +1,279 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  logger.c
+ *    Description:  This file is common logger API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <errno.h>
+#include <stdlib.h>
+#include <stdarg.h>
+#include <string.h>
+#include <time.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <pthread.h>
+
+#include "logger.h"
+
+typedef void (*log_LockFn)(void *udata, int lock);
+
+static struct {
+    char        file[32]; /* logger file name */
+    FILE       *fp;       /* logger file pointer */
+    long        size;     /* logger file max size */
+    int         level;    /* logger level */
+    log_LockFn  lockfn;   /* lock function */
+    void       *udata;    /* lock data */
+} L;
+
+static const char *level_names[] = {
+    "ERROR",
+    "WARN",
+    "INFO",
+    "DEBUG",
+    "TRACE"
+};
+
+static const char *level_colors[] = {
+    "\x1b[31m",
+    "\x1b[33m",
+    "\x1b[32m",
+    "\x1b[36m",
+    "\x1b[94m"
+};
+
+static inline void time_to_str(char *buf)
+{
+    struct timeval   tv;
+    struct tm       *tm;
+    int              len;
+
+    gettimeofday(&tv, NULL);
+    tm = localtime(&tv.tv_sec);
+
+    len = sprintf(buf, "%04d-%02d-%02d %02d:%02d:%02d.%06d ",
+            tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
+            tm->tm_hour, tm->tm_min, tm->tm_sec, (int)tv.tv_usec);
+
+    buf[len] = '\0';
+}
+
+static void mutex_lock(void *udata, int lock)
+{
+    int              err;
+    pthread_mutex_t *l = (pthread_mutex_t *) udata;
+
+    if (lock)
+    {
+        if ( (err = pthread_mutex_lock(l)) != 0 )
+            log_error("Unable to lock log lock: %s", strerror(err));
+    }
+    else
+    {
+        if ( (err = pthread_mutex_unlock(l) != 0) )
+            log_error("Unable to unlock log lock: %s", strerror(err));
+    }
+}
+
+int log_open(char *fname, int level, int size, int lock)
+{
+    FILE            *fp;
+
+    L.level = level;
+    L.size = size*1024;
+
+    if( !fname || !strcmp(fname, "console") || !strcmp(fname, "stderr") )
+    {
+        strcpy(L.file, "console");
+        L.fp = stderr;
+        L.size = 0; /* console don't need rollback */
+    }
+    else
+    {
+        if ( !(fp = fopen(fname, "a+")) )
+        {
+            fprintf(stderr, "%s() failed: %s\n", __func__, strerror(errno));
+            return -2;
+        }
+        L.fp = fp;
+        strncpy(L.file, fname, sizeof(L.file));
+    }
+
+
+    if( lock )
+    {
+        static pthread_mutex_t     log_lock;
+
+        pthread_mutex_init(&log_lock, NULL);
+        L.udata = (void *)&log_lock;
+        L.lockfn = mutex_lock;
+    }
+
+    fprintf(L.fp, "\n");
+    log_info("logger system(%s) start: file:\"%s\", level:%s, maxsize:%luKiB\n\n",
+            LOG_VERSION, L.file, level_names[level], size);
+
+    return 0;
+}
+
+void log_close(void)
+{
+    if( L.fp && L.fp!=stderr )
+        fclose(L.fp);
+
+    if (L.udata )
+        pthread_mutex_destroy( L.udata);
+}
+
+static void log_rollback(void)
+{
+    char       cmd[128]={0};
+    long       fsize;
+
+    /* don't need rollback */
+    if(L.size <= 0 )
+        return ;
+
+    fsize = ftell(L.fp);
+    if( fsize < L.size )
+        return ;
+
+    /* backup current log file  */
+    snprintf(cmd, sizeof(cmd), "cp %s %s.bak", L.file, L.file);
+    system(cmd);
+
+    /* rollback file */
+    fseek(L.fp, 0, SEEK_SET);
+    truncate(L.file, 0);
+
+    fprintf(L.fp, "\n");
+    log_info("logger system(%s) rollback: file:\"%s\", level:%s, maxsize:%luKiB\n\n",
+            LOG_VERSION, L.file, level_names[L.level], L.size/1024);
+
+    return ;
+}
+
+void _log_write(int level, const char *file, int line, const char *fmt, ...)
+{
+    va_list    args;
+    char       time_string[100];
+
+    if ( !L.fp || level>L.level )
+        return;
+
+    /* Acquire lock */
+    if ( L.lockfn )
+        L.lockfn(L.udata, 1);
+
+    log_rollback();
+
+    /* check and rollback file */
+    time_to_str(time_string);
+
+    /* Log to stderr */
+    if ( L.fp == stderr )
+    {
+        fprintf(L.fp, "%s %s %-5s\x1b[0m \x1b[90m%s:%03d:\x1b[0m ",
+                time_string, level_colors[level], level_names[level], file, line);
+    }
+    else /* Log to file */
+    {
+        fprintf(L.fp, "%s %-5s %s:%03d: ", time_string, level_names[level], file, line);
+    }
+
+    va_start(args, fmt);
+    vfprintf(L.fp, fmt, args);
+    va_end(args);
+
+    fflush(L.fp);
+
+    /* Release lock */
+    if ( L.lockfn )
+        L.lockfn(L.udata, 0);
+}
+
+#define LINELEN 81
+#define CHARS_PER_LINE 16
+static char *print_char =
+"                "
+"                "
+" !\"#$%&'()*+,-./"
+"0123456789:;<=>?"
+"@ABCDEFGHIJKLMNO"
+"PQRSTUVWXYZ[\\]^_"
+"`abcdefghijklmno"
+"pqrstuvwxyz{|}~ "
+"                "
+"                "
+" ???????????????"
+"????????????????"
+"????????????????"
+"????????????????"
+"????????????????"
+"????????????????";
+
+void log_dump(int level, const char *prompt, char *buf, size_t len)
+{
+    int rc;
+    int idx;
+    char prn[LINELEN];
+    char lit[CHARS_PER_LINE + 2];
+    char hc[4];
+    short line_done = 1;
+
+    if (!L.fp || level>L.level)
+        return;
+
+    if( prompt )
+        _log_write(level, __FILE__, __LINE__, "%s", prompt);
+
+    rc = len;
+    idx = 0;
+    lit[CHARS_PER_LINE] = '\0';
+
+    while (rc > 0)
+    {
+        if (line_done)
+            snprintf(prn, LINELEN, "%08X: ", idx);
+
+        do
+        {
+            unsigned char c = buf[idx];
+            snprintf(hc, 4, "%02X ", c);
+            strncat(prn, hc, LINELEN);
+
+            lit[idx % CHARS_PER_LINE] = print_char[c];
+        }
+        while (--rc > 0 && (++idx % CHARS_PER_LINE != 0));
+
+        line_done = (idx % CHARS_PER_LINE) == 0;
+        if (line_done)
+        {
+            if (L.fp)
+                fprintf(L.fp, "%s  %s\n", prn, lit);
+        }
+    }
+
+    if (!line_done)
+    {
+        int ldx = idx % CHARS_PER_LINE;
+        lit[ldx++] = print_char[(int)buf[idx]];
+        lit[ldx] = '\0';
+
+        while ((++idx % CHARS_PER_LINE) != 0)
+            strncat(prn, "   ", sizeof(prn)-strlen(prn));
+
+        if (L.fp)
+            fprintf(L.fp, "%s  %s\n", prn, lit);
+
+    }
+}
diff --git a/project/booster/logger.h b/project/booster/logger.h
new file mode 100644
index 0000000..6347beb
--- /dev/null
+++ b/project/booster/logger.h
@@ -0,0 +1,68 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  logger.h
+ *    Description:  This file is common logger API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#ifndef  _LOGGER_H_
+#define  _LOGGER_H_
+
+#include <stdio.h>
+#include <stdarg.h>
+
+#define LOG_VERSION "v0.1"
+
+/* log level */
+enum {
+    LOG_LEVEL_ERROR,
+    LOG_LEVEL_WARN,
+    LOG_LEVEL_INFO,
+    LOG_LEVEL_DEBUG,
+    LOG_LEVEL_TRACE,
+    LOG_LEVEL_MAX
+};
+
+enum {
+    LOG_LOCK_DISABLE, /* disable lock */
+    LOG_LOCK_ENABLE,  /* enable lock */
+};
+
+#define ROLLBACK_NONE   0
+
+/* description: Initial the logger system
+ * arguments  :
+ *             $fname: logger file name, NULL/"console"/"stderr" will log to console
+ *             $level: logger level above;
+ *             $size : logger file max size in KiB
+ *             $lock : thread lock enable or not
+ * return     : <0: Failed  ==0: Sucessfully
+ */
+int log_open(char *fname, int level, int size, int lock);
+
+
+/* description: Terminate the logger system */
+void log_close(void);
+
+
+/* description: log message into log file. Don't call this function directly. */
+void _log_write(int level, const char *file, int line, const char *fmt, ...);
+
+
+/* description: dump a buffer in hex to logger file */
+void log_dump(int level, const char *prompt, char *buf, size_t len);
+
+/* function: log message into logger file with different log level */
+#define log_trace(...) _log_write(LOG_LEVEL_TRACE, __FILE__, __LINE__, __VA_ARGS__)
+#define log_debug(...) _log_write(LOG_LEVEL_DEBUG, __FILE__, __LINE__, __VA_ARGS__)
+#define log_info(...)  _log_write(LOG_LEVEL_INFO,  __FILE__, __LINE__, __VA_ARGS__)
+#define log_warn(...)  _log_write(LOG_LEVEL_WARN,  __FILE__, __LINE__, __VA_ARGS__)
+#define log_error(...) _log_write(LOG_LEVEL_ERROR, __FILE__, __LINE__, __VA_ARGS__)
+
+#endif
diff --git a/project/booster/makefile b/project/booster/makefile
new file mode 100644
index 0000000..b6ece0e
--- /dev/null
+++ b/project/booster/makefile
@@ -0,0 +1,35 @@
+#********************************************************************************
+#      Copyright:  (C) 2023 LingYun IoT System Studio
+#                  All rights reserved.
+#
+#       Filename:  Makefile
+#    Description:  This file used compile all the source code to static library
+#
+#        Version:  1.0.0(11/08/23)
+#         Author:  Guo Wenxue <guowenxue@gmail.com>
+#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+PWD=$(shell pwd )
+
+BUILD_ARCH=$(shell uname -m)
+ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
+    CROSS_COMPILE?=arm-linux-gnueabihf-
+endif
+
+LIBNAME=$(shell basename ${PWD} )
+TOPDIR=$(shell dirname ${PWD} )
+CFLAGS+=-D_GNU_SOURCE
+
+all: clean
+	@rm -f *.o
+	@${CROSS_COMPILE}gcc ${CFLAGS} -I${TOPDIR} -c *.c
+	${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
+
+clean:
+	@rm -f *.o
+	@rm -f *.a
+
+distclean:
+	@make clean
diff --git a/project/booster/ringbuf.c b/project/booster/ringbuf.c
new file mode 100644
index 0000000..44412c1
--- /dev/null
+++ b/project/booster/ringbuf.c
@@ -0,0 +1,107 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  ringbuf.c
+ *    Description:  This file is common ring buffer API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#include <string.h>
+#include <assert.h>
+#include "ringbuf.h"
+
+void rb_init (struct ring_buffer *ring, unsigned char* buff, int size)
+{
+    memset (ring, 0, sizeof (struct ring_buffer));
+    ring->rd_pointer = 0;
+    ring->wr_pointer = 0;
+    ring->buffer= buff;
+    ring->size = size;
+}
+
+
+int rb_write (struct ring_buffer *rb, unsigned char * buf, int len)
+{
+    int                  total;
+    int                  i;
+
+    /* total = len = min(space, len) */
+    total = rb_free_size(rb);
+    if(len > total)
+        len = total;
+    else
+        total = len;
+
+    i = rb->wr_pointer;
+    if(i + len > rb->size)
+    {
+        memcpy(rb->buffer + i, buf, rb->size - i);
+        buf += rb->size - i;
+        len -= rb->size - i;
+        i = 0;
+    }
+
+    memcpy(rb->buffer + i, buf, len);
+    rb->wr_pointer = i + len;
+    return total;
+}
+
+
+int rb_free_size (struct ring_buffer *rb)
+{
+    return (rb->size - 1 - rb_data_size(rb));
+}
+
+
+int rb_read (struct ring_buffer *rb, unsigned char * buf, int max)
+{
+    int                  total;
+    int                  i;
+
+    /* total = len = min(used, len) */
+    total = rb_data_size(rb);
+
+    if(max > total)
+        max = total;
+    else
+        total = max;
+
+
+    i = rb->rd_pointer;
+    if(i + max > rb->size)
+    {
+        memcpy(buf, rb->buffer + i, rb->size - i);
+        buf += rb->size - i;
+        max -= rb->size - i;
+        i = 0;
+    }
+
+    memcpy(buf, rb->buffer + i, max);
+    rb->rd_pointer = i + max;
+
+    return total;
+}
+
+int rb_data_size (struct ring_buffer *rb)
+{
+    return ((rb->wr_pointer - rb->rd_pointer) & (rb->size-1));
+}
+
+void rb_clear (struct ring_buffer *rb)
+{
+    memset(rb->buffer,0,rb->size);
+    rb->rd_pointer=0;
+    rb->wr_pointer=0;
+}
+
+unsigned char rb_peek(struct ring_buffer* rb, int index)
+{
+	assert(index < rb_data_size(rb));
+
+	return rb->buffer[((rb->rd_pointer + index) % rb->size)];
+}
diff --git a/project/booster/ringbuf.h b/project/booster/ringbuf.h
new file mode 100644
index 0000000..88b978e
--- /dev/null
+++ b/project/booster/ringbuf.h
@@ -0,0 +1,57 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio.
+ *                  All rights reserved.
+ *
+ *       Filename:  ringbuf.h
+ *    Description:  This file is common ring buffer API functions
+ *                 
+ *        Version:  1.0.0(11/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+ *                 
+ ********************************************************************************/
+
+#ifndef  _RINGBUF_H_
+#define  _RINGBUF_H_
+
+struct ring_buffer
+{
+	unsigned char      *buffer;
+    int                 wr_pointer;
+    int                 rd_pointer;
+    int                 size;
+};
+
+
+/* Initial the ring buffer */
+void rb_init (struct ring_buffer *ring, unsigned char *buff, int size) ;
+
+
+/*  Description: Write $len bytes data in $buf into ring buffer $rb
+ * Return Value: The actual written into ring buffer data size, if ring buffer
+ * left space size small than $len, then only part of the data be written into.
+ */
+int rb_write (struct ring_buffer *rb, unsigned char *buf, int len) ;
+
+
+/* Get ring buffer left free size  */
+int rb_free_size (struct ring_buffer *rb);
+
+
+/* Read $max bytes data from ring buffer $rb to $buf */
+int rb_read (struct ring_buffer *rb, unsigned char *buf, int max);
+
+
+/* Read a specify $index byte data in ring buffer $rb  */
+unsigned char rb_peek(struct ring_buffer *rb, int index);
+
+
+/* Get data size in the ring buffer  */
+int rb_data_size (struct ring_buffer *rb);
+
+
+/* Clear the ring buffer data  */
+void rb_clear (struct ring_buffer *rb) ;
+
+#endif   /* ----- #ifndef _RINGBUF_H_  ----- */
+
diff --git a/project/booster/util_proc.c b/project/booster/util_proc.c
new file mode 100644
index 0000000..dfdabae
--- /dev/null
+++ b/project/booster/util_proc.c
@@ -0,0 +1,432 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2020 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  util_proc.c
+ *    Description:  This file is the process API
+ *
+ *        Version:  1.0.0(7/06/2020)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "7/06/2020 09:19:02 PM"
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <libgen.h>
+#include <pthread.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "util_proc.h"
+#include "logger.h"
+
+proc_signal_t     g_signal={0};
+
+void proc_default_sighandler(int sig)
+{
+    switch(sig)
+    {
+        case SIGINT:
+            log_warn("SIGINT - stopping\n");
+            g_signal.stop = 1;
+            break;
+
+        case SIGTERM:
+            log_warn("SIGTERM - stopping\n");
+            g_signal.stop = 1;
+            break;
+
+        case SIGSEGV:
+            log_warn("SIGSEGV - stopping\n");
+#if 0
+            if(g_signal.stop)
+                exit(0);
+
+            g_signal.stop = 1;
+#endif
+            break;
+
+        case SIGPIPE:
+            log_warn("SIGPIPE - warnning\n");
+            break;
+
+        default:
+            break;
+    }
+}
+
+
+/* install default signal process functions  */
+void install_default_signal(void)
+{
+    struct sigaction sigact, sigign;
+
+    log_info("Install default signal handler.\n");
+
+    /*  Initialize the catch signal structure. */
+    sigemptyset(&sigact.sa_mask);
+    sigact.sa_flags = 0;
+    sigact.sa_handler = proc_default_sighandler;
+
+    /*  Setup the ignore signal. */
+    sigemptyset(&sigign.sa_mask);
+    sigign.sa_flags = 0;
+    sigign.sa_handler = SIG_IGN;
+
+    sigaction(SIGTERM, &sigact, 0); /*  catch terminate signal "kill" command */
+    sigaction(SIGINT,  &sigact, 0); /*  catch interrupt signal CTRL+C */
+    //sigaction(SIGSEGV, &sigact, 0); /*  catch segmentation faults  */
+    sigaction(SIGPIPE, &sigact, 0); /*  catch broken pipe */
+#if 0
+    sigaction(SIGCHLD, &sigact, 0); /*  catch child process return */
+    sigaction(SIGUSR2, &sigact, 0); /*  catch USER signal */
+#endif
+}
+
+
+/* ****************************************************************************
+ * FunctionName: daemonize
+ * Description : Set the programe runs as daemon in background
+ * Inputs      : nodir: DON'T change the work directory to / :  1:NoChange 0:Change
+ *               noclose: close the opened file descrtipion or not 1:Noclose 0:Close
+ * Output      : NONE
+ * Return      : NONE
+ * *****************************************************************************/
+void daemonize(int nochdir, int noclose)
+{
+    int rv, fd;
+    int i;
+
+    /*  already a daemon */
+    if (1 == getppid())
+        return;
+
+    /*  fork error */
+    rv = fork();
+    if (rv < 0) exit(1);
+
+    /*  parent process exit */
+    if (rv > 0)
+        exit(0);
+
+    /*  obtain a new process session group */
+    setsid();
+
+    if (!noclose)
+    {
+        /*  close all descriptors */
+        for (i = getdtablesize(); i >= 0; --i)
+        {
+            //if (i != g_logPtr->fd)
+                close(i);
+        }
+
+        /*  Redirect Standard input [0] to /dev/null */
+        fd = open("/dev/null", O_RDWR);
+
+        /* Redirect Standard output [1] to /dev/null */
+        dup(fd);
+
+        /* Redirect Standard error [2] to /dev/null */
+        dup(fd);
+    }
+
+    umask(0);
+
+    if (!nochdir)
+        chdir("/");
+
+    return;
+}
+
+/* ****************************************************************************
+ * FunctionName: check_set_program_running
+ * Description : check program already running or not, if not then run it and
+ *               record pid into $pidfile
+ * Inputs      : daemon:  set program running in daemon or not
+ *               pid_file:The record PID file path
+ * Output      : NONE
+ * Return      : 0: Record successfully  Else: Failure
+ * *****************************************************************************/
+
+int check_set_program_running(int daemon, char *pidfile)
+{
+    if( !pidfile )
+        return 0;
+
+    if( check_daemon_running(pidfile) )
+    {
+        log_error("Program already running, process exit now");
+        return -1;
+    }
+
+    if( daemon )
+    {
+        if( set_daemon_running(pidfile) < 0 )
+        {
+            log_error("set program running as daemon failure\n");
+            return -2;
+        }
+    }
+    else
+    {
+        if( record_daemon_pid(pidfile) < 0 )
+        {
+            log_error("record program running PID failure\n");
+            return -3;
+        }
+    }
+
+    return 0;
+}
+
+
+
+/* ****************************************************************************
+ * FunctionName: record_daemon_pid
+ * Description : Record the running daemon program PID to the file "pid_file"
+ * Inputs      : pid_file:The record PID file path
+ * Output      : NONE
+ * Return      : 0: Record successfully  Else: Failure
+ * *****************************************************************************/
+int record_daemon_pid(const char *pid_file)
+{
+    struct stat fStatBuf;
+    int fd = -1;
+    int mode = S_IROTH | S_IXOTH | S_IRGRP | S_IXGRP | S_IRWXU;
+    char ipc_dir[64] = { 0 };
+
+    strncpy(ipc_dir, pid_file, 64);
+
+    /* dirname() will modify ipc_dir and save the result */
+    dirname(ipc_dir);
+
+    /* If folder pid_file PATH doesnot exist, then we will create it" */
+    if (stat(ipc_dir, &fStatBuf) < 0)
+    {
+        if (mkdir(ipc_dir, mode) < 0)
+        {
+            log_error("cannot create %s: %s\n", ipc_dir, strerror(errno));
+            return -1;
+        }
+
+        (void)chmod(ipc_dir, mode);
+    }
+
+    /*  Create the process running PID file */
+    mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH;
+    if ((fd = open(pid_file, O_RDWR | O_CREAT | O_TRUNC, mode)) >= 0)
+    {
+        char pid[PID_ASCII_SIZE];
+        snprintf(pid, sizeof(pid), "%u\n", (unsigned)getpid());
+        write(fd, pid, strlen(pid));
+        close(fd);
+
+        log_debug("Record PID<%u> to file %s.\n", getpid(), pid_file);
+    }
+    else
+    {
+        log_error("cannot create %s: %s\n", pid_file, strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+/* ****************************************************************************
+ * FunctionName: get_daemon_pid
+ * Description : Get the daemon process PID from the PID record file "pid_file"
+ * Inputs      : pid_file: the PID record file
+ * Output      : NONE
+ * Return      : pid_t: The daemon process PID number
+ * *****************************************************************************/
+pid_t get_daemon_pid(const char *pid_file)
+{
+    FILE *f;
+    pid_t pid;
+
+    if ((f = fopen(pid_file, "rb")) != NULL)
+    {
+        char pid_ascii[PID_ASCII_SIZE];
+        (void)fgets(pid_ascii, PID_ASCII_SIZE, f);
+        (void)fclose(f);
+        pid = atoi(pid_ascii);
+    }
+    else
+    {
+        log_error("Can't open PID record file %s: %s\n", pid_file, strerror(errno));
+        return -1;
+    }
+    return pid;
+}
+
+/* ****************************************************************************
+ * FunctionName: check_daemon_running
+ * Description : Check the daemon program already running or not
+ * Inputs      : pid_file: The record running daemon program PID
+ * Output      : NONE
+ * Return      : 1: The daemon program alread running   0: Not running
+ * *****************************************************************************/
+int check_daemon_running(const char *pid_file)
+{
+    int rv = -1;
+    struct stat fStatBuf;
+
+    rv = stat(pid_file, &fStatBuf);
+    if (0 == rv)
+    {
+        pid_t pid = -1;
+        printf("PID record file \"%s\" exist.\n", pid_file);
+
+        pid = get_daemon_pid(pid_file);
+        if (pid > 0)  /*  Process pid exist */
+        {
+            if ((rv = kill(pid, 0)) == 0)
+            {
+                printf("Program with PID[%d] seems running.\n", pid);
+                return 1;
+            }
+            else   /* Send signal to the old process get no reply. */
+            {
+                printf("Program with PID[%d] seems exit.\n", pid);
+                remove(pid_file);
+                return 0;
+            }
+        }
+        else if (0 == pid)
+        {
+            printf("Can not read program PID form record file.\n");
+            remove(pid_file);
+            return 0;
+        }
+        else  /* Read pid from file "pid_file" failure */
+        {
+            printf("Read record file \"%s\" failure, maybe program still running.\n", pid_file);
+            return 1;
+        }
+    }
+
+    return 0;
+}
+
+/* ****************************************************************************
+ * FunctionName: stop_daemon_running
+ * Description : Stop the daemon program running
+ * Inputs      : pid_file: The record running daemon program PID
+ * Output      : NONE
+ * Return      : 1: The daemon program alread running   0: Not running
+ * *****************************************************************************/
+int stop_daemon_running(const char *pid_file)
+{
+    pid_t            pid = -1;
+    struct stat      fStatBuf;
+
+    if ( stat(pid_file, &fStatBuf) < 0)
+        return 0;
+
+    printf("PID record file \"%s\" exist.\n", pid_file);
+    pid = get_daemon_pid(pid_file);
+    if (pid > 0)  /*  Process pid exist */
+    {
+        while ( (kill(pid, 0) ) == 0)
+        {
+            kill(pid, SIGTERM);
+            sleep(1);
+        }
+
+        remove(pid_file);
+    }
+
+    return 0;
+}
+
+
+
+/* ****************************************************************************
+ * FunctionName: set_daemon_running
+ * Description : Set the programe running as daemon if it's not running and record
+ *               its PID to the pid_file.
+ * Inputs      : pid_file: The record running daemon program PID
+ * Output      : NONE
+ * Return      : 0: Successfully. 1: Failure
+ * *****************************************************************************/
+int set_daemon_running(const char *pid_file)
+{
+    daemonize(0, 1);
+    log_info("Program running as daemon [PID:%d].\n", getpid());
+
+    if (record_daemon_pid(pid_file) < 0)
+    {
+        log_error("Record PID to file \"%s\" failure.\n", pid_file);
+        return -2;
+    }
+
+    return 0;
+}
+
+/* start a new thread to run $thread_workbody point function  */
+int thread_start(pthread_t *thread_id, thread_body_t thread_workbody, void *thread_arg)
+{
+    int                rv = 0;
+    pthread_t          tid;
+
+    pthread_attr_t     thread_attr;
+
+    /* Initialize the thread  attribute */
+    rv = pthread_attr_init(&thread_attr);
+    if(rv)
+        return -1;
+
+    /* Set the stack size of the thread */
+    rv = pthread_attr_setstacksize(&thread_attr, 120 * 1024);
+    if(rv)
+        goto CleanUp;
+
+    /* Set thread to detached state:Don`t need pthread_join */
+    rv = pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_DETACHED);
+    if(rv)
+        goto CleanUp;
+
+    /* Create the thread */
+    rv = pthread_create(&tid, &thread_attr, thread_workbody, thread_arg);
+    if(rv)
+        goto CleanUp;
+
+CleanUp:
+
+
+    if( thread_id )
+    {
+        if( rv )
+            *thread_id = 0;
+        else
+            *thread_id = tid;
+    }
+
+    /* Destroy the  attributes  of  thread */
+    pthread_attr_destroy(&thread_attr);
+    return rv;
+}
+
+
+/* excute a linux command by system() */
+void exec_system_cmd(const char *format, ...)
+{
+    char                cmd[256];
+    va_list             args;
+
+    memset(cmd, 0, sizeof(cmd));
+
+    va_start(args, format);
+    vsnprintf(cmd, sizeof(cmd), format, args);
+    va_end(args);
+
+    system(cmd);
+}
+
+
diff --git a/project/booster/util_proc.h b/project/booster/util_proc.h
new file mode 100644
index 0000000..89856c6
--- /dev/null
+++ b/project/booster/util_proc.h
@@ -0,0 +1,89 @@
+/********************************************************************************
+ *      Copyright:  (C) 2020 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  util_proc.h
+ *    Description:  This head file is for Linux process/thread API
+ *
+ *        Version:  1.0.0(7/06/2012~)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "7/06/2012 09:21:33 PM"
+ *
+ ********************************************************************************/
+
+#ifndef __UTIL_PROC_H_
+#define __UTIL_PROC_H_
+
+#include <signal.h>
+#include <time.h>
+
+#define PID_ASCII_SIZE  11
+
+typedef struct proc_signal_s
+{
+    int       signal;
+    unsigned  stop;     /* 0: Not term  1: Stop  */
+}  proc_signal_t;
+
+typedef void *(* thread_body_t) (void *thread_arg);
+
+extern proc_signal_t    g_signal;
+
+/* install default signal process functions  */
+extern void install_default_signal(void);
+
+/* excute a linux command by system() */
+extern void exec_system_cmd(const char *format, ...);
+
+/* check program already running or not, if not then run it and record pid into $pidfile */
+extern int check_set_program_running(int daemon, char *pidfile);
+
+/* check program already running or not from $pid_file  */
+extern int check_daemon_running(const char *pid_file);
+
+/* set program daemon running and record pid in $pid_file  */
+extern int set_daemon_running(const char *pid_file);
+
+/* record proces ID into $pid_file  */
+extern int record_daemon_pid(const char *pid_file);
+
+/* stop program running from $pid_file  */
+extern int stop_daemon_running(const char *pid_file);
+
+/* my implementation for set program running in daemon   */
+extern void daemonize(int nochdir, int noclose);
+
+/* start a new thread to run $thread_workbody point function  */
+extern int thread_start(pthread_t *thread_id, thread_body_t thread_workbody, void *thread_arg);
+
+/* +---------------------+
+ * |   Low level API     |
+ * +---------------------+*/
+
+/* get daemon process ID from $pid_file   */
+extern pid_t get_daemon_pid(const char *pid_file);
+
+/* +------------------------+
+ * |  inline functions API  |
+ * +------------------------+*/
+static inline void msleep(unsigned long ms)
+{
+    struct timespec cSleep;
+    unsigned long ulTmp;
+
+    cSleep.tv_sec = ms / 1000;
+    if (cSleep.tv_sec == 0)
+    {
+        ulTmp = ms * 10000;
+        cSleep.tv_nsec = ulTmp * 100;
+    }
+    else
+    {
+        cSleep.tv_nsec = 0;
+    }
+
+    nanosleep(&cSleep, 0);
+    return ;
+}
+
+#endif
diff --git a/project/coreMQTT/conf.c b/project/coreMQTT/conf.c
new file mode 100644
index 0000000..4c0ee20
--- /dev/null
+++ b/project/coreMQTT/conf.c
@@ -0,0 +1,200 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  conf.c
+ *    Description:  This file is mqttd configure file parser function
+ *                 
+ *        Version:  1.0.0(2019年06月25日)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2019年06月25日 22时23分55秒"
+ *                 
+ ********************************************************************************/
+#include "conf.h"
+#include "logger.h"
+#include "iniparser.h"
+
+
+int mqttd_parser_conf(const char *conf_file, mqtt_ctx_t *ctx, int debug)
+{
+    dictionary          *ini;
+    const char          *str;
+    int                  val; 
+    int                  rv = 0; 
+
+    if( !conf_file || !ctx )
+    { 
+        fprintf(stderr, "%s() Invalid input arguments\n", __func__);
+        return -1;
+    }
+
+    memset(ctx, 0, sizeof(*ctx));
+
+    ini = iniparser_load(conf_file);
+    if( !ini )
+    {
+        fprintf(stderr, "ERROR: Configure file '%s' load failed\n", conf_file);
+        return -2;
+    }
+
+    /*+------------------------------------------------------+
+     *|    parser logger settings and start logger system    |
+     *+------------------------------------------------------+*/
+    if( !debug ) 
+    {
+        str = iniparser_getstring(ini, "logger:file", "/tmp/mqttd.log");
+        strncpy(ctx->logfile, str, sizeof(ctx->logfile));
+        ctx->logsize = iniparser_getint(ini, "logger:size", 1024);
+        ctx->loglevel = iniparser_getint(ini, "logger:level", LOG_LEVEL_INFO);
+    }
+    else
+    {
+        strncpy(ctx->logfile, "console", sizeof(ctx->logfile));
+        ctx->loglevel = LOG_LEVEL_DEBUG;
+        ctx->logsize = 0;
+    }
+
+    if( log_open(ctx->logfile, ctx->loglevel, ctx->logsize, LOG_LOCK_DISABLE) < 0 ) 
+    {
+        fprintf(stderr, "Logger system initialise failure\n");
+        return -2;
+    }
+
+    log_info("Logger system initialise ok\n");
+
+
+    /*+------------------------------------------------------+
+     *|               parser production ID                   |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "common:devid", NULL)) )
+    {
+        log_error("ERROR: Parser device ID failure\n");
+        rv = -3;
+        goto cleanup;
+    }
+    /* cJSON parser ID will get ""  */
+    snprintf(ctx->devid, sizeof(ctx->devid), "\"%s\"", str);
+    log_info("Parser device ID [%s]\n", ctx->devid);
+
+
+    /*+------------------------------------------------------+
+     *|       parser hardware module configuration           |
+     *+------------------------------------------------------+*/
+
+    /* relay */
+    ctx->hwconf.relay=iniparser_getint(ini, "hardware:relay", 0);
+    if( !ctx->hwconf.relay )
+        log_warn("Parser relay module disabled\n");
+    else
+        log_info("Parser relay module enabled\n");
+
+    /* RGB 3-colors LED */
+    ctx->hwconf.led=iniparser_getint(ini, "hardware:rgbled", 0);
+    if( !ctx->hwconf.led )
+        log_warn("Parser RGB 3-colors Led module disabled\n");
+    else
+        log_info("Parser RGB 3-colors Led module enabled\n");
+
+    /* beeper */
+    ctx->hwconf.beeper=iniparser_getint(ini, "hardware:beep", 0);
+    if( !ctx->hwconf.beeper )
+        log_warn("Parser beeper module disabled\n");
+    else
+        log_info("Parser beeper module enabled\n");
+
+    /* DS18B20 temperature module */
+    ctx->hwconf.ds18b20=iniparser_getint(ini, "hardware:ds18b20", 0);
+    if( !ctx->hwconf.ds18b20 )
+        log_warn("Parser DS18B20 temperature module disabled\n");
+    else
+        log_info("Parser DS18B20 temperature module enabled\n");
+
+    /* SHT20 temperature and hummidity module */
+    ctx->hwconf.sht2x=iniparser_getint(ini, "hardware:sht2x", 0);
+    if( !ctx->hwconf.sht2x )
+        log_warn("Parser SHT2X temperature and hummidity module disabled\n");
+    else
+        log_info("Parser SHT2X temperature and hummidity module enabled\n");
+
+    /* TSL2561 light intensity sensor module */
+    ctx->hwconf.tsl2561=iniparser_getint(ini, "hardware:tsl2561", 0);
+    if( !ctx->hwconf.tsl2561 )
+        log_warn("Parser TSL2561 light intensity sensor module disabled\n");
+    else
+        log_info("Parser TSL2561 light intensity sensor module enabled\n");
+
+    /*+------------------------------------------------------+
+     *|              parser broker settings                  |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "broker:hostname", NULL)) )
+    {
+        log_error("ERROR: Parser MQTT broker server hostname failure\n");
+        rv = -4;
+        goto cleanup;
+    }
+    strncpy(ctx->host, str, sizeof(ctx->host) );
+
+    if( (val=iniparser_getint(ini, "broker:port", -1)) < 0 ) 
+    {
+        log_error("ERROR: Parser MQTT broker server port failure\n");
+        rv = -5;
+        goto cleanup;
+    }
+    ctx->port = val;
+    log_info("Parser MQTT broker server [%s:%d]\n", ctx->host, ctx->port);
+
+    str=iniparser_getstring(ini, "broker:username", NULL);
+    strncpy(ctx->uid, str, sizeof(ctx->uid) );
+
+    str=iniparser_getstring(ini, "broker:password", NULL);
+    strncpy(ctx->pwd, str, sizeof(ctx->pwd) );
+
+    if( ctx->uid && ctx->pwd )
+        log_info("Parser broker author by [%s:%s]\n", ctx->uid, ctx->pwd);
+
+    ctx->keepalive = iniparser_getint(ini, "broker:keepalive", DEF_KEEPALIVE);
+    log_info("Parser broker keepalive timeout [%d] seconds\n", ctx->keepalive);
+
+    /*+------------------------------------------------------+
+     *|             parser publisher settings                |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "publisher:pubTopic", NULL)) )
+    {
+        log_error("ERROR: Parser MQTT broker publisher topic failure\n");
+        rv = -6;
+        goto cleanup;
+    }
+    strncpy(ctx->pubTopic, str, sizeof(ctx->pubTopic) );
+
+    ctx->pubQos = iniparser_getint(ini, "publisher:pubQos", DEF_QOS);
+    ctx->interval = iniparser_getint(ini, "publisher:interval", DEF_PUBINTERVAL);
+    log_info("Parser publisher topic \"%s\" with Qos[%d] interval[%d]\n", ctx->pubTopic, ctx->pubQos, ctx->interval);
+
+    /*+------------------------------------------------------+
+     *|             parser subscriber settings               |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "subsciber:subTopic", NULL)) )
+    {
+        log_error("ERROR: Parser MQTT broker publisher topic failure\n");
+        rv = -7;
+        goto cleanup;
+    }
+    strncpy(ctx->subTopic, str, sizeof(ctx->subTopic) );
+
+    ctx->subQos = iniparser_getint(ini, "subsciber:subQos", DEF_QOS);
+    log_info("Parser subscriber topic \"%s\" with Qos[%d]\n", ctx->subTopic, ctx->subQos);
+
+cleanup:
+    if( ini )
+        iniparser_freedict(ini);
+
+    if( rv )
+        log_close();
+
+    return rv;
+}
+
diff --git a/project/coreMQTT/conf.h b/project/coreMQTT/conf.h
new file mode 100644
index 0000000..89d3a66
--- /dev/null
+++ b/project/coreMQTT/conf.h
@@ -0,0 +1,71 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  conf.h
+ *    Description:  This file is mqttd configure file parser function
+ *                 
+ *        Version:  1.0.0(2019年06月25日)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2019年06月25日 22时23分55秒"
+ *                 
+ ********************************************************************************/
+#ifndef  __CONF_H_
+#define  __CONF_H_
+
+enum
+{
+    Qos0, /* 发送者只发送一次消息,不进行重试,Broker不会返回确认消息。在Qos0情况下,Broker可能没有接受到消息 */
+    Qos1, /* 发送者最少发送一次消息,确保消息到达Broker,Broker需要返回确认消息PUBACK。在Qos1情况下,Broker可能接受到重复消息 */
+    Qos2, /* Qos2使用两阶段确认来保证消息的不丢失和不重复。在Qos2情况下,Broker肯定会收到消息,且只收到一次  */
+};
+
+#define DEF_KEEPALIVE       30
+#define DEF_PUBINTERVAL     120
+#define DEF_QOS             Qos2
+
+typedef struct hwconf_s 
+{
+    int            relay;   /* relay aviable or not.   0:Disable  1: Enable */
+    int            led;     /* RGB led aviable or not. 0:Disable  1: Enable */
+    int            beeper;  /* beeper aviable or not.  0:Disable  1: Enable */
+    int            ds18b20; /* DS1B820 aviable or not. 0:Disable  1: Enable */
+    int            sht2x;   /* SHT20  aviable or not.  0:Disable  1: Enable */
+    int            tsl2561; /* TSL2561 aviable or not.  0:Disable  1: Enable */
+} hwconf_t;  
+
+
+typedef struct mqtt_ctx_s
+{
+    char          devid[32];    /*  device ID */
+
+    /* hardware configuration */
+    hwconf_t      hwconf;
+
+    /* logger settings */
+    char          logfile[128]; /* logger record file */
+    int           loglevel;     /* logger level  */
+    int           logsize;      /* logger file maxsize, oversize will rollback */
+
+    /* Broker settings  */
+    char          host[128];  /* MQTT broker server name  */
+    int           port;       /* MQTT broker listen port  */
+    char          uid[64];    /* username */
+    char          pwd[64];    /* password */
+    int           keepalive;  /* MQTT broker send PING message to subsciber/publisher keepalive timeout<seconds> */
+
+    /* Publisher settings */
+    char          pubTopic[256]; /* Publisher topic  */
+    int           pubQos;        /* Publisher Qos */
+    int           interval ;     /* Publish sensor data interval time, unit seconds */
+
+    /* Subscriber settings */
+    char          subTopic[256]; /* Subscriber topic */
+    int           subQos;        /* Subscriber Qos  */
+} mqtt_ctx_t;
+
+
+extern int mqttd_parser_conf(const char *conf_file, mqtt_ctx_t *ctx, int debug);
+
+#endif   /* ----- #ifndef _CONF_H_  ----- */
+
diff --git a/project/coreMQTT/coreJSON/README.md b/project/coreMQTT/coreJSON/README.md
new file mode 100644
index 0000000..d08f61f
--- /dev/null
+++ b/project/coreMQTT/coreJSON/README.md
@@ -0,0 +1,65 @@
+## coreJSON Library
+
+This repository contains the coreJSON library, a parser that strictly enforces the ECMA-404 JSON standard and is suitable for low memory footprint embedded devices. The coreJSON library is distributed under the [MIT Open Source License](LICENSE).
+
+This library has gone through code quality checks including verification that no function has a [GNU Complexity](https://www.gnu.org/software/complexity/manual/complexity.html) score over 8, and checks against deviations from mandatory rules in the [MISRA coding standard](https://www.misra.org.uk). Deviations from the MISRA C:2012 guidelines are documented under [MISRA Deviations](MISRA.md). This library has also undergone both static code analysis from [Coverity static analysis](https://scan.coverity.com/), and validation of memory safety through the [CBMC automated reasoning tool](https://www.cprover.org/cbmc/).
+
+See memory requirements for this library [here](./docs/doxygen/include/size_table.md).
+
+**coreJSON v3.2.0 [source code](https://github.com/FreeRTOS/coreJSON/tree/v3.2.0/source) is part of the [FreeRTOS 202210.00 LTS](https://github.com/FreeRTOS/FreeRTOS-LTS/tree/202210.00-LTS) release.**
+
+**coreJSON v3.0.0 [source code](https://github.com/FreeRTOS/coreJSON/tree/v3.0.0/source) is part of the [FreeRTOS 202012.00 LTS](https://github.com/FreeRTOS/FreeRTOS-LTS/tree/202012.00-LTS) release.**
+
+## Reference example
+
+```c
+#include <stdio.h>
+#include "core_json.h"
+
+int main()
+{
+    // Variables used in this example.
+    JSONStatus_t result;
+    char buffer[] = "{\"foo\":\"abc\",\"bar\":{\"foo\":\"xyz\"}}";
+    size_t bufferLength = sizeof( buffer ) - 1;
+    char queryKey[] = "bar.foo";
+    size_t queryKeyLength = sizeof( queryKey ) - 1;
+    char * value;
+    size_t valueLength;
+    
+    // Calling JSON_Validate() is not necessary if the document is guaranteed to be valid.
+    result = JSON_Validate( buffer, bufferLength );
+    
+    if( result == JSONSuccess )
+    {
+        result = JSON_Search( buffer, bufferLength, queryKey, queryKeyLength,
+                              &value, &valueLength );
+    }
+    
+    if( result == JSONSuccess )
+    {
+        // The pointer "value" will point to a location in the "buffer".
+        char save = value[ valueLength ];
+        // After saving the character, set it to a null byte for printing.
+        value[ valueLength ] = '\0';
+        // "Found: bar.foo -> xyz" will be printed.
+        printf( "Found: %s -> %s\n", queryKey, value );
+        // Restore the original character.
+        value[ valueLength ] = save;
+    }
+
+    return 0;
+}
+```
+A search may descend through nested objects when the `queryKey` contains matching key strings joined by a separator, `.`. In the example above, `bar` has the value `{"foo":"xyz"}`. Therefore, a search for query key `bar.foo` would output `xyz`.
+
+## Documentation
+
+For pre-generated documentation, please see the documentation linked in the locations below:
+
+| Location |
+| :-: |
+| [FreeRTOS.org](https://freertos.org/Documentation/api-ref/coreJSON/docs/doxygen/output/html/index.html) |
+
+Note that the latest included version of the coreJSON library may differ across repositories.
+
diff --git a/project/coreMQTT/coreJSON/core_json.c b/project/coreMQTT/coreJSON/core_json.c
new file mode 100644
index 0000000..06a0cb4
--- /dev/null
+++ b/project/coreMQTT/coreJSON/core_json.c
@@ -0,0 +1,1818 @@
+/*
+ * coreJSON v3.2.0
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_json.c
+ * @brief The source file that implements the user-facing functions in core_json.h.
+ */
+
+#include <assert.h>
+#include <limits.h>
+#include <stddef.h>
+#include <stdint.h>
+#include "core_json.h"
+
+/** @cond DO_NOT_DOCUMENT */
+
+/* A compromise to satisfy both MISRA and CBMC */
+typedef union
+{
+    char c;
+    uint8_t u;
+} char_;
+
+#if ( CHAR_MIN == 0 )
+    #define isascii_( x )    ( ( x ) <= '\x7F' )
+#else
+    #define isascii_( x )    ( ( x ) >= '\0' )
+#endif
+#define iscntrl_( x )        ( isascii_( x ) && ( ( x ) < ' ' ) )
+#define isdigit_( x )        ( ( ( x ) >= '0' ) && ( ( x ) <= '9' ) )
+/* NB. This is whitespace as defined by the JSON standard (ECMA-404). */
+#define isspace_( x )                          \
+    ( ( ( x ) == ' ' ) || ( ( x ) == '\t' ) || \
+      ( ( x ) == '\n' ) || ( ( x ) == '\r' ) )
+
+#define isOpenBracket_( x )           ( ( ( x ) == '{' ) || ( ( x ) == '[' ) )
+#define isCloseBracket_( x )          ( ( ( x ) == '}' ) || ( ( x ) == ']' ) )
+#define isCurlyPair_( x, y )          ( ( ( x ) == '{' ) && ( ( y ) == '}' ) )
+#define isSquarePair_( x, y )         ( ( ( x ) == '[' ) && ( ( y ) == ']' ) )
+#define isMatchingBracket_( x, y )    ( isCurlyPair_( x, y ) || isSquarePair_( x, y ) )
+#define isSquareOpen_( x )            ( ( x ) == '[' )
+#define isSquareClose_( x )           ( ( x ) == ']' )
+
+/**
+ * @brief Advance buffer index beyond whitespace.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ */
+static void skipSpace( const char * buf,
+                       size_t * start,
+                       size_t max )
+{
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    for( i = *start; i < max; i++ )
+    {
+        if( !isspace_( buf[ i ] ) )
+        {
+            break;
+        }
+    }
+
+    *start = i;
+}
+
+/**
+ * @brief Count the leading 1s in a byte.
+ *
+ * The high-order 1 bits of the first byte in a UTF-8 encoding
+ * indicate the number of additional bytes to follow.
+ *
+ * @return the count
+ */
+static size_t countHighBits( uint8_t c )
+{
+    uint8_t n = c;
+    size_t i = 0;
+
+    while( ( n & 0x80U ) != 0U )
+    {
+        i++;
+        n = ( n & 0x7FU ) << 1U;
+    }
+
+    return i;
+}
+
+/**
+ * @brief Is the value a legal Unicode code point and encoded with
+ * the fewest bytes?
+ *
+ * The last Unicode code point is 0x10FFFF.
+ *
+ * Unicode 3.1 disallows UTF-8 interpretation of non-shortest form sequences.
+ * 1 byte encodes 0 through 7 bits
+ * 2 bytes encode 8 through 5+6 = 11 bits
+ * 3 bytes encode 12 through 4+6+6 = 16 bits
+ * 4 bytes encode 17 through 3+6+6+6 = 21 bits
+ *
+ * Unicode 3.2 disallows UTF-8 code point values in the surrogate range,
+ * [U+D800 to U+DFFF].
+ *
+ * @note Disallow ASCII, as this is called only for multibyte sequences.
+ */
+static bool shortestUTF8( size_t length,
+                          uint32_t value )
+{
+    bool ret = false;
+    uint32_t min, max;
+
+    assert( ( length >= 2U ) && ( length <= 4U ) );
+
+    switch( length )
+    {
+        case 2:
+            min = ( uint32_t ) 1 << 7U;
+            max = ( ( uint32_t ) 1 << 11U ) - 1U;
+            break;
+
+        case 3:
+            min = ( uint32_t ) 1 << 11U;
+            max = ( ( uint32_t ) 1 << 16U ) - 1U;
+            break;
+
+        default:
+            min = ( uint32_t ) 1 << 16U;
+            max = 0x10FFFFU;
+            break;
+    }
+
+    if( ( value >= min ) && ( value <= max ) &&
+        ( ( value < 0xD800U ) || ( value > 0xDFFFU ) ) )
+    {
+        ret = true;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond a UTF-8 code point.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @return true if a valid code point was present;
+ * false otherwise.
+ *
+ * 00-7F    Single-byte character
+ * 80-BF    Trailing byte
+ * C0-DF    Leading byte of two-byte character
+ * E0-EF    Leading byte of three-byte character
+ * F0-F7    Leading byte of four-byte character
+ * F8-FB    Illegal (formerly leading byte of five-byte character)
+ * FC-FD    Illegal (formerly leading byte of six-byte character)
+ * FE-FF    Illegal
+ *
+ * The octet values C0, C1, and F5 to FF are illegal, since C0 and C1
+ * would introduce a non-shortest sequence, and F5 or above would
+ * introduce a value greater than the last code point, 0x10FFFF.
+ */
+static bool skipUTF8MultiByte( const char * buf,
+                               size_t * start,
+                               size_t max )
+{
+    bool ret = false;
+    size_t i, bitCount, j;
+    uint32_t value = 0;
+    char_ c;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+    assert( i < max );
+    assert( !isascii_( buf[ i ] ) );
+
+    c.c = buf[ i ];
+
+    if( ( c.u > 0xC1U ) && ( c.u < 0xF5U ) )
+    {
+        bitCount = countHighBits( c.u );
+        value = ( ( uint32_t ) c.u ) & ( ( ( uint32_t ) 1 << ( 7U - bitCount ) ) - 1U );
+
+        /* The bit count is 1 greater than the number of bytes,
+         * e.g., when j is 2, we skip one more byte. */
+        for( j = bitCount - 1U; j > 0U; j-- )
+        {
+            i++;
+
+            if( i >= max )
+            {
+                break;
+            }
+
+            c.c = buf[ i ];
+
+            /* Additional bytes must match 10xxxxxx. */
+            if( ( c.u & 0xC0U ) != 0x80U )
+            {
+                break;
+            }
+
+            value = ( value << 6U ) | ( c.u & 0x3FU );
+        }
+
+        if( ( j == 0U ) && ( shortestUTF8( bitCount, value ) == true ) )
+        {
+            *start = i + 1U;
+            ret = true;
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond an ASCII or UTF-8 code point.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @return true if a valid code point was present;
+ * false otherwise.
+ */
+static bool skipUTF8( const char * buf,
+                      size_t * start,
+                      size_t max )
+{
+    bool ret = false;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    if( *start < max )
+    {
+        if( isascii_( buf[ *start ] ) )
+        {
+            *start += 1U;
+            ret = true;
+        }
+        else
+        {
+            ret = skipUTF8MultiByte( buf, start, max );
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Convert a hexadecimal character to an integer.
+ *
+ * @param[in] c  The character to convert.
+ *
+ * @return the integer value upon success or NOT_A_HEX_CHAR on failure.
+ */
+#define NOT_A_HEX_CHAR    ( 0x10U )
+static uint8_t hexToInt( char c )
+{
+    char_ n;
+
+    n.c = c;
+
+    if( ( c >= 'a' ) && ( c <= 'f' ) )
+    {
+        n.c -= 'a';
+        n.u += 10U;
+    }
+    else if( ( c >= 'A' ) && ( c <= 'F' ) )
+    {
+        n.c -= 'A';
+        n.u += 10U;
+    }
+    else if( isdigit_( c ) )
+    {
+        n.c -= '0';
+    }
+    else
+    {
+        n.u = NOT_A_HEX_CHAR;
+    }
+
+    return n.u;
+}
+
+/**
+ * @brief Advance buffer index beyond a single \u Unicode
+ * escape sequence and output the value.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ * @param[out] outValue  The value of the hex digits.
+ *
+ * @return true if a valid escape sequence was present;
+ * false otherwise.
+ *
+ * @note For the sake of security, \u0000 is disallowed.
+ */
+static bool skipOneHexEscape( const char * buf,
+                              size_t * start,
+                              size_t max,
+                              uint16_t * outValue )
+{
+    bool ret = false;
+    size_t i, end;
+    uint16_t value = 0;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+    assert( outValue != NULL );
+
+    i = *start;
+#define HEX_ESCAPE_LENGTH    ( 6U )   /* e.g., \u1234 */
+    /* MISRA Ref 14.3.1 [Configuration dependent invariant] */
+    /* More details at: https://github.com/FreeRTOS/coreJSON/blob/main/MISRA.md#rule-143 */
+    /* coverity[misra_c_2012_rule_14_3_violation] */
+    end = ( i <= ( SIZE_MAX - HEX_ESCAPE_LENGTH ) ) ? ( i + HEX_ESCAPE_LENGTH ) : SIZE_MAX;
+
+    if( ( end < max ) && ( buf[ i ] == '\\' ) && ( buf[ i + 1U ] == 'u' ) )
+    {
+        for( i += 2U; i < end; i++ )
+        {
+            uint8_t n = hexToInt( buf[ i ] );
+
+            if( n == NOT_A_HEX_CHAR )
+            {
+                break;
+            }
+
+            value = ( value << 4U ) | n;
+        }
+    }
+
+    if( ( i == end ) && ( value > 0U ) )
+    {
+        ret = true;
+        *outValue = value;
+        *start = i;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond one or a pair of \u Unicode escape sequences.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * Surrogate pairs are two escape sequences that together denote
+ * a code point outside the Basic Multilingual Plane.  They must
+ * occur as a pair with the first "high" value in [U+D800, U+DBFF],
+ * and the second "low" value in [U+DC00, U+DFFF].
+ *
+ * @return true if a valid escape sequence was present;
+ * false otherwise.
+ *
+ * @note For the sake of security, \u0000 is disallowed.
+ */
+#define isHighSurrogate( x )    ( ( ( x ) >= 0xD800U ) && ( ( x ) <= 0xDBFFU ) )
+#define isLowSurrogate( x )     ( ( ( x ) >= 0xDC00U ) && ( ( x ) <= 0xDFFFU ) )
+
+static bool skipHexEscape( const char * buf,
+                           size_t * start,
+                           size_t max )
+{
+    bool ret = false;
+    size_t i;
+    uint16_t value;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+
+    if( skipOneHexEscape( buf, &i, max, &value ) == true )
+    {
+        if( isHighSurrogate( value ) )
+        {
+            if( ( skipOneHexEscape( buf, &i, max, &value ) == true ) &&
+                ( isLowSurrogate( value ) ) )
+            {
+                ret = true;
+            }
+        }
+        else if( isLowSurrogate( value ) )
+        {
+            /* premature low surrogate */
+        }
+        else
+        {
+            ret = true;
+        }
+    }
+
+    if( ret == true )
+    {
+        *start = i;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond an escape sequence.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @return true if a valid escape sequence was present;
+ * false otherwise.
+ *
+ * @note For the sake of security, \NUL is disallowed.
+ */
+static bool skipEscape( const char * buf,
+                        size_t * start,
+                        size_t max )
+{
+    bool ret = false;
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+
+    if( ( i < ( max - 1U ) ) && ( buf[ i ] == '\\' ) )
+    {
+        char c = buf[ i + 1U ];
+
+        switch( c )
+        {
+            case '\0':
+                break;
+
+            case 'u':
+                ret = skipHexEscape( buf, &i, max );
+                break;
+
+            case '"':
+            case '\\':
+            case '/':
+            case 'b':
+            case 'f':
+            case 'n':
+            case 'r':
+            case 't':
+                i += 2U;
+                ret = true;
+                break;
+
+            default:
+
+                /* a control character: (NUL,SPACE) */
+                if( iscntrl_( c ) )
+                {
+                    i += 2U;
+                    ret = true;
+                }
+
+                break;
+        }
+    }
+
+    if( ret == true )
+    {
+        *start = i;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond a double-quoted string.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @return true if a valid string was present;
+ * false otherwise.
+ */
+static bool skipString( const char * buf,
+                        size_t * start,
+                        size_t max )
+{
+    bool ret = false;
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+
+    if( ( i < max ) && ( buf[ i ] == '"' ) )
+    {
+        i++;
+
+        while( i < max )
+        {
+            if( buf[ i ] == '"' )
+            {
+                ret = true;
+                i++;
+                break;
+            }
+
+            if( buf[ i ] == '\\' )
+            {
+                if( skipEscape( buf, &i, max ) != true )
+                {
+                    break;
+                }
+            }
+            /* An unescaped control character is not allowed. */
+            else if( iscntrl_( buf[ i ] ) )
+            {
+                break;
+            }
+            else if( skipUTF8( buf, &i, max ) != true )
+            {
+                break;
+            }
+            else
+            {
+                /* MISRA 15.7 */
+            }
+        }
+    }
+
+    if( ret == true )
+    {
+        *start = i;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Compare the leading n bytes of two character sequences.
+ *
+ * @param[in] a  first character sequence
+ * @param[in] b  second character sequence
+ * @param[in] n  number of bytes
+ *
+ * @return true if the sequences are the same;
+ * false otherwise
+ */
+static bool strnEq( const char * a,
+                    const char * b,
+                    size_t n )
+{
+    size_t i;
+
+    assert( ( a != NULL ) && ( b != NULL ) );
+
+    for( i = 0; i < n; i++ )
+    {
+        if( a[ i ] != b[ i ] )
+        {
+            break;
+        }
+    }
+
+    return ( i == n ) ? true : false;
+}
+
+/**
+ * @brief Advance buffer index beyond a literal.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ * @param[in] literal  The type of literal.
+ * @param[in] length  The length of the literal.
+ *
+ * @return true if the literal was present;
+ * false otherwise.
+ */
+static bool skipLiteral( const char * buf,
+                         size_t * start,
+                         size_t max,
+                         const char * literal,
+                         size_t length )
+{
+    bool ret = false;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+    assert( literal != NULL );
+
+    if( ( *start < max ) && ( length <= ( max - *start ) ) )
+    {
+        ret = strnEq( &buf[ *start ], literal, length );
+    }
+
+    if( ret == true )
+    {
+        *start += length;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond a JSON literal.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @return true if a valid literal was present;
+ * false otherwise.
+ */
+static bool skipAnyLiteral( const char * buf,
+                            size_t * start,
+                            size_t max )
+{
+    bool ret = false;
+
+#define skipLit_( x ) \
+    ( skipLiteral( buf, start, max, ( x ), ( sizeof( x ) - 1UL ) ) == true )
+
+    if( skipLit_( "true" ) || skipLit_( "false" ) || skipLit_( "null" ) )
+    {
+        ret = true;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond one or more digits.
+ * Optionally, output the integer value of the digits.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ * @param[out] outValue  The integer value of the digits.
+ *
+ * @note outValue may be NULL.  If not NULL, and the output
+ * exceeds ~2 billion, then -1 is output.
+ *
+ * @return true if a digit was present;
+ * false otherwise.
+ */
+#define MAX_FACTOR    ( MAX_INDEX_VALUE / 10 )
+static bool skipDigits( const char * buf,
+                        size_t * start,
+                        size_t max,
+                        int32_t * outValue )
+{
+    bool ret = false;
+    size_t i, saveStart;
+    int32_t value = 0;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    saveStart = *start;
+
+    for( i = *start; i < max; i++ )
+    {
+        if( !isdigit_( buf[ i ] ) )
+        {
+            break;
+        }
+
+        if( ( outValue != NULL ) && ( value > -1 ) )
+        {
+            int8_t n = ( int8_t ) hexToInt( buf[ i ] );
+
+            if( value <= MAX_FACTOR )
+            {
+                value = ( value * 10 ) + n;
+            }
+            else
+            {
+                value = -1;
+            }
+        }
+    }
+
+    if( i > saveStart )
+    {
+        ret = true;
+        *start = i;
+
+        if( outValue != NULL )
+        {
+            *outValue = value;
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond the decimal portion of a number.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ */
+static void skipDecimals( const char * buf,
+                          size_t * start,
+                          size_t max )
+{
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+
+    if( ( i < max ) && ( buf[ i ] == '.' ) )
+    {
+        i++;
+
+        if( skipDigits( buf, &i, max, NULL ) == true )
+        {
+            *start = i;
+        }
+    }
+}
+
+/**
+ * @brief Advance buffer index beyond the exponent portion of a number.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ */
+static void skipExponent( const char * buf,
+                          size_t * start,
+                          size_t max )
+{
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+
+    if( ( i < max ) && ( ( buf[ i ] == 'e' ) || ( buf[ i ] == 'E' ) ) )
+    {
+        i++;
+
+        if( ( i < max ) && ( ( buf[ i ] == '-' ) || ( buf[ i ] == '+' ) ) )
+        {
+            i++;
+        }
+
+        if( skipDigits( buf, &i, max, NULL ) == true )
+        {
+            *start = i;
+        }
+    }
+}
+
+/**
+ * @brief Advance buffer index beyond a number.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @return true if a valid number was present;
+ * false otherwise.
+ */
+static bool skipNumber( const char * buf,
+                        size_t * start,
+                        size_t max )
+{
+    bool ret = false;
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+
+    if( ( i < max ) && ( buf[ i ] == '-' ) )
+    {
+        i++;
+    }
+
+    if( i < max )
+    {
+        /* JSON disallows superfluous leading zeroes, so an
+         * initial zero must either be alone, or followed by
+         * a decimal or exponent.
+         *
+         * Should there be a digit after the zero, that digit
+         * will not be skipped by this function, and later parsing
+         * will judge this an illegal document. */
+        if( buf[ i ] == '0' )
+        {
+            ret = true;
+            i++;
+        }
+        else
+        {
+            ret = skipDigits( buf, &i, max, NULL );
+        }
+    }
+
+    if( ret == true )
+    {
+        skipDecimals( buf, &i, max );
+        skipExponent( buf, &i, max );
+        *start = i;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond a scalar value.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @return true if a scalar value was present;
+ * false otherwise.
+ */
+static bool skipAnyScalar( const char * buf,
+                           size_t * start,
+                           size_t max )
+{
+    bool ret = false;
+
+    if( ( skipString( buf, start, max ) == true ) ||
+        ( skipAnyLiteral( buf, start, max ) == true ) ||
+        ( skipNumber( buf, start, max ) == true ) )
+    {
+        ret = true;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond a comma separator
+ * and surrounding whitespace.
+ *
+ * JSON uses a comma to separate values in an array and key-value
+ * pairs in an object.  JSON does not permit a trailing comma.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @return true if a non-terminal comma was present;
+ * false otherwise.
+ */
+static bool skipSpaceAndComma( const char * buf,
+                               size_t * start,
+                               size_t max )
+{
+    bool ret = false;
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    skipSpace( buf, start, max );
+    i = *start;
+
+    if( ( i < max ) && ( buf[ i ] == ',' ) )
+    {
+        i++;
+        skipSpace( buf, &i, max );
+
+        if( ( i < max ) && !isCloseBracket_( buf[ i ] ) )
+        {
+            ret = true;
+            *start = i;
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond the scalar values of an array.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @note Stops advance if a value is an object or array.
+ */
+static void skipArrayScalars( const char * buf,
+                              size_t * start,
+                              size_t max )
+{
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+
+    while( i < max )
+    {
+        if( skipAnyScalar( buf, &i, max ) != true )
+        {
+            break;
+        }
+
+        if( skipSpaceAndComma( buf, &i, max ) != true )
+        {
+            break;
+        }
+    }
+
+    *start = i;
+}
+
+/**
+ * @brief Advance buffer index beyond the scalar key-value pairs
+ * of an object.
+ *
+ * In JSON, objects consist of comma-separated key-value pairs.
+ * A key is always a string (a scalar) while a value may be a
+ * scalar, an object, or an array.  A colon must appear between
+ * each key and value.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @note Stops advance if a value is an object or array.
+ */
+static void skipObjectScalars( const char * buf,
+                               size_t * start,
+                               size_t max )
+{
+    size_t i;
+    bool comma;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+
+    while( i < max )
+    {
+        if( skipString( buf, &i, max ) != true )
+        {
+            break;
+        }
+
+        skipSpace( buf, &i, max );
+
+        if( ( i < max ) && ( buf[ i ] != ':' ) )
+        {
+            break;
+        }
+
+        i++;
+        skipSpace( buf, &i, max );
+
+        if( ( i < max ) && isOpenBracket_( buf[ i ] ) )
+        {
+            *start = i;
+            break;
+        }
+
+        if( skipAnyScalar( buf, &i, max ) != true )
+        {
+            break;
+        }
+
+        comma = skipSpaceAndComma( buf, &i, max );
+        *start = i;
+
+        if( comma != true )
+        {
+            break;
+        }
+    }
+}
+
+/**
+ * @brief Advance buffer index beyond one or more scalars.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ * @param[in] mode  The first character of an array '[' or object '{'.
+ */
+static void skipScalars( const char * buf,
+                         size_t * start,
+                         size_t max,
+                         char mode )
+{
+    assert( isOpenBracket_( mode ) );
+
+    skipSpace( buf, start, max );
+
+    if( mode == '[' )
+    {
+        skipArrayScalars( buf, start, max );
+    }
+    else
+    {
+        skipObjectScalars( buf, start, max );
+    }
+}
+
+/**
+ * @brief Advance buffer index beyond a collection and handle nesting.
+ *
+ * A stack is used to continue parsing the prior collection type
+ * when a nested collection is finished.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ *
+ * @return #JSONSuccess if the buffer contents are a valid JSON collection;
+ * #JSONIllegalDocument if the buffer contents are NOT valid JSON;
+ * #JSONMaxDepthExceeded if object and array nesting exceeds a threshold;
+ * #JSONPartial if the buffer contents are potentially valid but incomplete.
+ */
+#ifndef JSON_MAX_DEPTH
+    #define JSON_MAX_DEPTH    32
+#endif
+static JSONStatus_t skipCollection( const char * buf,
+                                    size_t * start,
+                                    size_t max )
+{
+    JSONStatus_t ret = JSONPartial;
+    char c, stack[ JSON_MAX_DEPTH ];
+    int16_t depth = -1;
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+
+    i = *start;
+
+    while( i < max )
+    {
+        c = buf[ i ];
+        i++;
+
+        switch( c )
+        {
+            case '{':
+            case '[':
+                depth++;
+
+                if( depth == JSON_MAX_DEPTH )
+                {
+                    ret = JSONMaxDepthExceeded;
+                    break;
+                }
+
+                stack[ depth ] = c;
+                skipScalars( buf, &i, max, stack[ depth ] );
+                break;
+
+            case '}':
+            case ']':
+
+                if( ( depth > 0 ) && isMatchingBracket_( stack[ depth ], c ) )
+                {
+                    depth--;
+
+                    if( skipSpaceAndComma( buf, &i, max ) == true )
+                    {
+                        skipScalars( buf, &i, max, stack[ depth ] );
+                    }
+
+                    break;
+                }
+
+                ret = ( ( depth == 0 ) && isMatchingBracket_( stack[ depth ], c ) ) ?
+                      JSONSuccess : JSONIllegalDocument;
+                break;
+
+            default:
+                ret = JSONIllegalDocument;
+                break;
+        }
+
+        if( ret != JSONPartial )
+        {
+            break;
+        }
+    }
+
+    if( ret == JSONSuccess )
+    {
+        *start = i;
+    }
+
+    return ret;
+}
+
+/** @endcond */
+
+/**
+ * See core_json.h for docs.
+ *
+ * Verify that the entire buffer contains exactly one scalar
+ * or collection within optional whitespace.
+ */
+JSONStatus_t JSON_Validate( const char * buf,
+                            size_t max )
+{
+    JSONStatus_t ret;
+    size_t i = 0;
+
+    if( buf == NULL )
+    {
+        ret = JSONNullParameter;
+    }
+    else if( max == 0U )
+    {
+        ret = JSONBadParameter;
+    }
+    else
+    {
+        skipSpace( buf, &i, max );
+
+        /** @cond DO_NOT_DOCUMENT */
+        #ifndef JSON_VALIDATE_COLLECTIONS_ONLY
+            if( skipAnyScalar( buf, &i, max ) == true )
+            {
+                ret = JSONSuccess;
+            }
+            else
+        #endif
+        /** @endcond */
+        {
+            ret = skipCollection( buf, &i, max );
+        }
+    }
+
+    if( ( ret == JSONSuccess ) && ( i < max ) )
+    {
+        skipSpace( buf, &i, max );
+
+        if( i != max )
+        {
+            ret = JSONIllegalDocument;
+        }
+    }
+
+    return ret;
+}
+
+/** @cond DO_NOT_DOCUMENT */
+
+/**
+ * @brief Output index and length for the next value.
+ *
+ * Also advances the buffer index beyond the value.
+ * The value may be a scalar or a collection.
+ * The start index should point to the beginning of the value.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ * @param[out] value  A pointer to receive the index of the value.
+ * @param[out] valueLength  A pointer to receive the length of the value.
+ *
+ * @return true if a value was present;
+ * false otherwise.
+ */
+static bool nextValue( const char * buf,
+                       size_t * start,
+                       size_t max,
+                       size_t * value,
+                       size_t * valueLength )
+{
+    bool ret = true;
+    size_t i, valueStart;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+    assert( ( value != NULL ) && ( valueLength != NULL ) );
+
+    i = *start;
+    valueStart = i;
+
+    if( ( skipAnyScalar( buf, &i, max ) == true ) ||
+        ( skipCollection( buf, &i, max ) == JSONSuccess ) )
+    {
+        *value = valueStart;
+        *valueLength = i - valueStart;
+    }
+    else
+    {
+        ret = false;
+    }
+
+    if( ret == true )
+    {
+        *start = i;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Output indexes for the next key-value pair of an object.
+ *
+ * Also advances the buffer index beyond the key-value pair.
+ * The value may be a scalar or a collection.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ * @param[out] key  A pointer to receive the index of the key.
+ * @param[out] keyLength  A pointer to receive the length of the key.
+ * @param[out] value  A pointer to receive the index of the value.
+ * @param[out] valueLength  A pointer to receive the length of the value.
+ *
+ * @return true if a key-value pair was present;
+ * false otherwise.
+ */
+static bool nextKeyValuePair( const char * buf,
+                              size_t * start,
+                              size_t max,
+                              size_t * key,
+                              size_t * keyLength,
+                              size_t * value,
+                              size_t * valueLength )
+{
+    bool ret = true;
+    size_t i, keyStart;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( max > 0U ) );
+    assert( ( key != NULL ) && ( keyLength != NULL ) );
+    assert( ( value != NULL ) && ( valueLength != NULL ) );
+
+    i = *start;
+    keyStart = i;
+
+    if( skipString( buf, &i, max ) == true )
+    {
+        *key = keyStart + 1U;
+        *keyLength = i - keyStart - 2U;
+    }
+    else
+    {
+        ret = false;
+    }
+
+    if( ret == true )
+    {
+        skipSpace( buf, &i, max );
+
+        if( ( i < max ) && ( buf[ i ] == ':' ) )
+        {
+            i++;
+            skipSpace( buf, &i, max );
+        }
+        else
+        {
+            ret = false;
+        }
+    }
+
+    if( ret == true )
+    {
+        ret = nextValue( buf, &i, max, value, valueLength );
+    }
+
+    if( ret == true )
+    {
+        *start = i;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Find a key in a JSON object and output a pointer to its value.
+ *
+ * @param[in] buf  The buffer to search.
+ * @param[in] max  size of the buffer.
+ * @param[in] query  The object keys and array indexes to search for.
+ * @param[in] queryLength  Length of the key.
+ * @param[out] outValue  A pointer to receive the index of the value found.
+ * @param[out] outValueLength  A pointer to receive the length of the value found.
+ *
+ * Iterate over the key-value pairs of an object, looking for a matching key.
+ *
+ * @return true if the query is matched and the value output;
+ * false otherwise.
+ *
+ * @note Parsing stops upon finding a match.
+ */
+static bool objectSearch( const char * buf,
+                          size_t max,
+                          const char * query,
+                          size_t queryLength,
+                          size_t * outValue,
+                          size_t * outValueLength )
+{
+    bool ret = false;
+
+    size_t i = 0, key, keyLength, value = 0, valueLength = 0;
+
+    assert( ( buf != NULL ) && ( query != NULL ) );
+    assert( ( outValue != NULL ) && ( outValueLength != NULL ) );
+
+    skipSpace( buf, &i, max );
+
+    if( ( i < max ) && ( buf[ i ] == '{' ) )
+    {
+        i++;
+        skipSpace( buf, &i, max );
+
+        while( i < max )
+        {
+            if( nextKeyValuePair( buf, &i, max, &key, &keyLength,
+                                  &value, &valueLength ) != true )
+            {
+                break;
+            }
+
+            if( ( queryLength == keyLength ) &&
+                ( strnEq( query, &buf[ key ], keyLength ) == true ) )
+            {
+                ret = true;
+                break;
+            }
+
+            if( skipSpaceAndComma( buf, &i, max ) != true )
+            {
+                break;
+            }
+        }
+    }
+
+    if( ret == true )
+    {
+        *outValue = value;
+        *outValueLength = valueLength;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Find an index in a JSON array and output a pointer to its value.
+ *
+ * @param[in] buf  The buffer to search.
+ * @param[in] max  size of the buffer.
+ * @param[in] queryIndex  The index to search for.
+ * @param[out] outValue  A pointer to receive the index of the value found.
+ * @param[out] outValueLength  A pointer to receive the length of the value found.
+ *
+ * Iterate over the values of an array, looking for a matching index.
+ *
+ * @return true if the queryIndex is found and the value output;
+ * false otherwise.
+ *
+ * @note Parsing stops upon finding a match.
+ */
+static bool arraySearch( const char * buf,
+                         size_t max,
+                         uint32_t queryIndex,
+                         size_t * outValue,
+                         size_t * outValueLength )
+{
+    bool ret = false;
+    size_t i = 0, value = 0, valueLength = 0;
+    uint32_t currentIndex = 0;
+
+    assert( buf != NULL );
+    assert( ( outValue != NULL ) && ( outValueLength != NULL ) );
+
+    skipSpace( buf, &i, max );
+
+    if( ( i < max ) && ( buf[ i ] == '[' ) )
+    {
+        i++;
+        skipSpace( buf, &i, max );
+
+        while( i < max )
+        {
+            if( nextValue( buf, &i, max, &value, &valueLength ) != true )
+            {
+                break;
+            }
+
+            if( currentIndex == queryIndex )
+            {
+                ret = true;
+                break;
+            }
+
+            if( skipSpaceAndComma( buf, &i, max ) != true )
+            {
+                break;
+            }
+
+            currentIndex++;
+        }
+    }
+
+    if( ret == true )
+    {
+        *outValue = value;
+        *outValueLength = valueLength;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Advance buffer index beyond a query part.
+ *
+ * The part is the portion of the query which is not
+ * a separator or array index.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in,out] start  The index at which to begin.
+ * @param[in] max  The size of the buffer.
+ * @param[out] outLength  The length of the query part.
+ *
+ * @return true if a valid string was present;
+ * false otherwise.
+ */
+#ifndef JSON_QUERY_KEY_SEPARATOR
+    #define JSON_QUERY_KEY_SEPARATOR    '.'
+#endif
+#define isSeparator_( x )    ( ( x ) == JSON_QUERY_KEY_SEPARATOR )
+static bool skipQueryPart( const char * buf,
+                           size_t * start,
+                           size_t max,
+                           size_t * outLength )
+{
+    bool ret = false;
+    size_t i;
+
+    assert( ( buf != NULL ) && ( start != NULL ) && ( outLength != NULL ) );
+    assert( max > 0U );
+
+    i = *start;
+
+    while( ( i < max ) &&
+           !isSeparator_( buf[ i ] ) &&
+           !isSquareOpen_( buf[ i ] ) )
+    {
+        i++;
+    }
+
+    if( i > *start )
+    {
+        ret = true;
+        *outLength = i - *start;
+        *start = i;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Handle a nested search by iterating over the parts of the query.
+ *
+ * @param[in] buf  The buffer to search.
+ * @param[in] max  size of the buffer.
+ * @param[in] query  The object keys and array indexes to search for.
+ * @param[in] queryLength  Length of the key.
+ * @param[out] outValue  A pointer to receive the index of the value found.
+ * @param[out] outValueLength  A pointer to receive the length of the value found.
+ *
+ * @return #JSONSuccess if the query is matched and the value output;
+ * #JSONBadParameter if the query is empty, or any part is empty,
+ * or an index is too large to convert;
+ * #JSONNotFound if the query is NOT found.
+ *
+ * @note Parsing stops upon finding a match.
+ */
+static JSONStatus_t multiSearch( const char * buf,
+                                 size_t max,
+                                 const char * query,
+                                 size_t queryLength,
+                                 size_t * outValue,
+                                 size_t * outValueLength )
+{
+    JSONStatus_t ret = JSONSuccess;
+    size_t i = 0, start = 0, queryStart = 0, value = 0, length = max;
+
+    assert( ( buf != NULL ) && ( query != NULL ) );
+    assert( ( outValue != NULL ) && ( outValueLength != NULL ) );
+    assert( ( max > 0U ) && ( queryLength > 0U ) );
+
+    while( i < queryLength )
+    {
+        bool found = false;
+
+        if( isSquareOpen_( query[ i ] ) )
+        {
+            int32_t queryIndex = -1;
+            i++;
+
+            ( void ) skipDigits( query, &i, queryLength, &queryIndex );
+
+            if( ( queryIndex < 0 ) ||
+                ( i >= queryLength ) || !isSquareClose_( query[ i ] ) )
+            {
+                ret = JSONBadParameter;
+                break;
+            }
+
+            i++;
+
+            found = arraySearch( &buf[ start ], length, ( uint32_t ) queryIndex, &value, &length );
+        }
+        else
+        {
+            size_t keyLength = 0;
+
+            queryStart = i;
+
+            if( ( skipQueryPart( query, &i, queryLength, &keyLength ) != true ) ||
+                /* catch an empty key part or a trailing separator */
+                ( i == ( queryLength - 1U ) ) )
+            {
+                ret = JSONBadParameter;
+                break;
+            }
+
+            found = objectSearch( &buf[ start ], length, &query[ queryStart ], keyLength, &value, &length );
+        }
+
+        if( found == false )
+        {
+            ret = JSONNotFound;
+            break;
+        }
+
+        start += value;
+
+        if( ( i < queryLength ) && isSeparator_( query[ i ] ) )
+        {
+            i++;
+        }
+    }
+
+    if( ret == JSONSuccess )
+    {
+        *outValue = start;
+        *outValueLength = length;
+    }
+
+    return ret;
+}
+
+/**
+ * @brief Return a JSON type based on a separator character or
+ * the first character of a value.
+ *
+ * @param[in] c  The character to classify.
+ *
+ * @return an enum of JSONTypes_t
+ */
+static JSONTypes_t getType( char c )
+{
+    JSONTypes_t t;
+
+    switch( c )
+    {
+        case '"':
+            t = JSONString;
+            break;
+
+        case '{':
+            t = JSONObject;
+            break;
+
+        case '[':
+            t = JSONArray;
+            break;
+
+        case 't':
+            t = JSONTrue;
+            break;
+
+        case 'f':
+            t = JSONFalse;
+            break;
+
+        case 'n':
+            t = JSONNull;
+            break;
+
+        default:
+            t = JSONNumber;
+            break;
+    }
+
+    return t;
+}
+
+/** @endcond */
+
+/**
+ * See core_json.h for docs.
+ */
+JSONStatus_t JSON_SearchConst( const char * buf,
+                               size_t max,
+                               const char * query,
+                               size_t queryLength,
+                               const char ** outValue,
+                               size_t * outValueLength,
+                               JSONTypes_t * outType )
+{
+    JSONStatus_t ret;
+    size_t value = 0U;
+
+    if( ( buf == NULL ) || ( query == NULL ) ||
+        ( outValue == NULL ) || ( outValueLength == NULL ) )
+    {
+        ret = JSONNullParameter;
+    }
+    else if( ( max == 0U ) || ( queryLength == 0U ) )
+    {
+        ret = JSONBadParameter;
+    }
+    else
+    {
+        ret = multiSearch( buf, max, query, queryLength, &value, outValueLength );
+    }
+
+    if( ret == JSONSuccess )
+    {
+        JSONTypes_t t = getType( buf[ value ] );
+
+        if( t == JSONString )
+        {
+            /* strip the surrounding quotes */
+            value++;
+            *outValueLength -= 2U;
+        }
+
+        *outValue = &buf[ value ];
+
+        if( outType != NULL )
+        {
+            *outType = t;
+        }
+    }
+
+    return ret;
+}
+
+/**
+ * See core_json.h for docs.
+ */
+JSONStatus_t JSON_SearchT( char * buf,
+                           size_t max,
+                           const char * query,
+                           size_t queryLength,
+                           char ** outValue,
+                           size_t * outValueLength,
+                           JSONTypes_t * outType )
+{
+    /* MISRA Ref 11.3.1 [Misaligned access] */
+    /* More details at: https://github.com/FreeRTOS/coreJSON/blob/main/MISRA.md#rule-113 */
+    /* coverity[misra_c_2012_rule_11_3_violation] */
+    return JSON_SearchConst( ( const char * ) buf, max, query, queryLength,
+                             ( const char ** ) outValue, outValueLength, outType );
+}
+
+/** @cond DO_NOT_DOCUMENT */
+
+/**
+ * @brief Output the next key-value pair or value from a collection.
+ *
+ * @param[in] buf  The buffer to search.
+ * @param[in] max  size of the buffer.
+ * @param[in] start  The index at which the collection begins.
+ * @param[in,out] next  The index at which to seek the next value.
+ * @param[out] outKey  A pointer to receive the index of the value found.
+ * @param[out] outKeyLength  A pointer to receive the length of the value found.
+ * @param[out] outValue  A pointer to receive the index of the value found.
+ * @param[out] outValueLength  A pointer to receive the length of the value found.
+ *
+ * @return #JSONSuccess if a value is output;
+ * #JSONIllegalDocument if the buffer does not begin with '[' or '{';
+ * #JSONNotFound if there are no further values in the collection.
+ */
+static JSONStatus_t iterate( const char * buf,
+                             size_t max,
+                             size_t * start,
+                             size_t * next,
+                             size_t * outKey,
+                             size_t * outKeyLength,
+                             size_t * outValue,
+                             size_t * outValueLength )
+{
+    JSONStatus_t ret = JSONNotFound;
+    bool found = false;
+
+    assert( ( buf != NULL ) && ( max > 0U ) );
+    assert( ( start != NULL ) && ( next != NULL ) );
+    assert( ( outKey != NULL ) && ( outKeyLength != NULL ) );
+    assert( ( outValue != NULL ) && ( outValueLength != NULL ) );
+
+    if( *start < max )
+    {
+        switch( buf[ *start ] )
+        {
+            case '[':
+                found = nextValue( buf, next, max, outValue, outValueLength );
+
+                if( found == true )
+                {
+                    *outKey = 0;
+                    *outKeyLength = 0;
+                }
+
+                break;
+
+            case '{':
+                found = nextKeyValuePair( buf, next, max, outKey, outKeyLength,
+                                          outValue, outValueLength );
+                break;
+
+            default:
+                ret = JSONIllegalDocument;
+                break;
+        }
+    }
+
+    if( found == true )
+    {
+        ret = JSONSuccess;
+        ( void ) skipSpaceAndComma( buf, next, max );
+    }
+
+    return ret;
+}
+
+/** @endcond */
+
+/**
+ * See core_json.h for docs.
+ */
+JSONStatus_t JSON_Iterate( const char * buf,
+                           size_t max,
+                           size_t * start,
+                           size_t * next,
+                           JSONPair_t * outPair )
+{
+    JSONStatus_t ret;
+    size_t key, keyLength, value, valueLength;
+
+    if( ( buf == NULL ) || ( start == NULL ) || ( next == NULL ) ||
+        ( outPair == NULL ) )
+    {
+        ret = JSONNullParameter;
+    }
+    else if( ( max == 0U ) || ( *start >= max ) || ( *next > max ) )
+    {
+        ret = JSONBadParameter;
+    }
+    else
+    {
+        skipSpace( buf, start, max );
+
+        if( *next <= *start )
+        {
+            *next = *start + 1U;
+            skipSpace( buf, next, max );
+        }
+
+        ret = iterate( buf, max, start, next, &key, &keyLength,
+                       &value, &valueLength );
+    }
+
+    if( ret == JSONSuccess )
+    {
+        JSONTypes_t t = getType( buf[ value ] );
+
+        if( t == JSONString )
+        {
+            /* strip the surrounding quotes */
+            value++;
+            valueLength -= 2U;
+        }
+
+        outPair->key = ( key == 0U ) ? NULL : &buf[ key ];
+        outPair->keyLength = keyLength;
+        outPair->value = &buf[ value ];
+        outPair->valueLength = valueLength;
+        outPair->jsonType = t;
+    }
+
+    return ret;
+}
diff --git a/project/coreMQTT/coreJSON/core_json.h b/project/coreMQTT/coreJSON/core_json.h
new file mode 100644
index 0000000..102a24f
--- /dev/null
+++ b/project/coreMQTT/coreJSON/core_json.h
@@ -0,0 +1,339 @@
+/*
+ * coreJSON v3.2.0
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_json.h
+ * @brief Include this header file to use coreJSON in your application.
+ */
+
+#ifndef CORE_JSON_H_
+#define CORE_JSON_H_
+
+#include <stdbool.h>
+#include <stddef.h>
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/**
+ * @ingroup json_enum_types
+ * @brief Return codes from coreJSON library functions.
+ */
+typedef enum
+{
+    JSONPartial = 0,      /**< @brief JSON document is valid so far but incomplete. */
+    JSONSuccess,          /**< @brief JSON document is valid and complete. */
+    JSONIllegalDocument,  /**< @brief JSON document is invalid or malformed. */
+    JSONMaxDepthExceeded, /**< @brief JSON document has nesting that exceeds JSON_MAX_DEPTH. */
+    JSONNotFound,         /**< @brief Query key could not be found in the JSON document. */
+    JSONNullParameter,    /**< @brief Pointer parameter passed to a function is NULL. */
+    JSONBadParameter      /**< @brief Query key is empty, or any subpart is empty, or max is 0. */
+} JSONStatus_t;
+
+/**
+ * @brief Parse a buffer to determine if it contains a valid JSON document.
+ *
+ * @param[in] buf  The buffer to parse.
+ * @param[in] max  The size of the buffer.
+ *
+ * @note The maximum nesting depth may be specified by defining the macro
+ * JSON_MAX_DEPTH.  The default is 32 of sizeof(char).
+ *
+ * @note By default, a valid JSON document may contain a single element
+ * (e.g., string, boolean, number).  To require that a valid document
+ * contain an object or array, define JSON_VALIDATE_COLLECTIONS_ONLY.
+ *
+ * @return #JSONSuccess if the buffer contents are valid JSON;
+ * #JSONNullParameter if buf is NULL;
+ * #JSONBadParameter if max is 0;
+ * #JSONIllegalDocument if the buffer contents are NOT valid JSON;
+ * #JSONMaxDepthExceeded if object and array nesting exceeds a threshold;
+ * #JSONPartial if the buffer contents are potentially valid but incomplete.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *     // Variables used in this example.
+ *     JSONStatus_t result;
+ *     char buffer[] = "{\"foo\":\"abc\",\"bar\":{\"foo\":\"xyz\"}}";
+ *     size_t bufferLength = sizeof( buffer ) - 1;
+ *
+ *     result = JSON_Validate( buffer, bufferLength );
+ *
+ *     // JSON document is valid.
+ *     assert( result == JSONSuccess );
+ * @endcode
+ */
+/* @[declare_json_validate] */
+JSONStatus_t JSON_Validate( const char * buf,
+                            size_t max );
+/* @[declare_json_validate] */
+
+/**
+ * @brief Find a key or array index in a JSON document and output the
+ * pointer @p outValue to its value.
+ *
+ * Any value may also be an object or an array to a maximum depth.  A search
+ * may descend through nested objects or arrays when the query contains matching
+ * key strings or array indexes joined by a separator.
+ *
+ * For example, if the provided buffer contains <code>{"foo":"abc","bar":{"foo":"xyz"}}</code>,
+ * then a search for 'foo' would output <code>abc</code>, 'bar' would output
+ * <code>{"foo":"xyz"}</code>, and a search for 'bar.foo' would output
+ * <code>xyz</code>.
+ *
+ * If the provided buffer contains <code>[123,456,{"foo":"abc","bar":[88,99]}]</code>,
+ * then a search for '[1]' would output <code>456</code>, '[2].foo' would output
+ * <code>abc</code>, and '[2].bar[0]' would output <code>88</code>.
+ *
+ * On success, the pointer @p outValue points to a location in buf.  No null
+ * termination is done for the value.  For valid JSON it is safe to place
+ * a null character at the end of the value, so long as the character
+ * replaced is put back before running another search.
+ *
+ * @param[in] buf  The buffer to search.
+ * @param[in] max  size of the buffer.
+ * @param[in] query  The object keys and array indexes to search for.
+ * @param[in] queryLength  Length of the key.
+ * @param[out] outValue  A pointer to receive the address of the value found.
+ * @param[out] outValueLength  A pointer to receive the length of the value found.
+ *
+ * @note The maximum nesting depth may be specified by defining the macro
+ * JSON_MAX_DEPTH.  The default is 32 of sizeof(char).
+ *
+ * @note JSON_Search() performs validation, but stops upon finding a matching
+ * key and its value. To validate the entire JSON document, use JSON_Validate().
+ *
+ * @return #JSONSuccess if the query is matched and the value output;
+ * #JSONNullParameter if any pointer parameters are NULL;
+ * #JSONBadParameter if the query is empty, or the portion after a separator is empty,
+ * or max is 0, or an index is too large to convert to a signed 32-bit integer;
+ * #JSONNotFound if the query has no match.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *     // Variables used in this example.
+ *     JSONStatus_t result;
+ *     char buffer[] = "{\"foo\":\"abc\",\"bar\":{\"foo\":\"xyz\"}}";
+ *     size_t bufferLength = sizeof( buffer ) - 1;
+ *     char query[] = "bar.foo";
+ *     size_t queryLength = sizeof( query ) - 1;
+ *     char * value;
+ *     size_t valueLength;
+ *
+ *     // Calling JSON_Validate() is not necessary if the document is guaranteed to be valid.
+ *     result = JSON_Validate( buffer, bufferLength );
+ *
+ *     if( result == JSONSuccess )
+ *     {
+ *         result = JSON_Search( buffer, bufferLength, query, queryLength,
+ *                               &value, &valueLength );
+ *     }
+ *
+ *     if( result == JSONSuccess )
+ *     {
+ *         // The pointer "value" will point to a location in the "buffer".
+ *         char save = value[ valueLength ];
+ *         // After saving the character, set it to a null byte for printing.
+ *         value[ valueLength ] = '\0';
+ *         // "Found: bar.foo -> xyz" will be printed.
+ *         printf( "Found: %s -> %s\n", query, value );
+ *         // Restore the original character.
+ *         value[ valueLength ] = save;
+ *     }
+ * @endcode
+ *
+ * @note The maximum index value is ~2 billion ( 2^31 - 9 ).
+ */
+/* @[declare_json_search] */
+#define JSON_Search( buf, max, query, queryLength, outValue, outValueLength ) \
+    JSON_SearchT( buf, max, query, queryLength, outValue, outValueLength, NULL )
+/* @[declare_json_search] */
+
+/**
+ * @brief The largest value usable as an array index in a query
+ * for JSON_Search(), ~2 billion.
+ */
+#define MAX_INDEX_VALUE    ( 0x7FFFFFF7 )   /* 2^31 - 9 */
+
+/**
+ * @ingroup json_enum_types
+ * @brief Value types from the JSON standard.
+ */
+typedef enum
+{
+    JSONInvalid = 0, /**< @brief Not a valid JSON type. */
+    JSONString,      /**< @brief A quote delimited sequence of Unicode characters. */
+    JSONNumber,      /**< @brief A rational number. */
+    JSONTrue,        /**< @brief The literal value true. */
+    JSONFalse,       /**< @brief The literal value false. */
+    JSONNull,        /**< @brief The literal value null. */
+    JSONObject,      /**< @brief A collection of zero or more key-value pairs. */
+    JSONArray        /**< @brief A collection of zero or more values. */
+} JSONTypes_t;
+
+/**
+ * @brief Same as JSON_Search(), but also outputs a type for the value found
+ *
+ * See @ref JSON_Search for documentation of common behavior.
+ *
+ * @param[in] buf  The buffer to search.
+ * @param[in] max  size of the buffer.
+ * @param[in] query  The object keys and array indexes to search for.
+ * @param[in] queryLength  Length of the key.
+ * @param[out] outValue  A pointer to receive the address of the value found.
+ * @param[out] outValueLength  A pointer to receive the length of the value found.
+ * @param[out] outType  An enum indicating the JSON-specific type of the value.
+ */
+/* @[declare_json_searcht] */
+JSONStatus_t JSON_SearchT( char * buf,
+                           size_t max,
+                           const char * query,
+                           size_t queryLength,
+                           char ** outValue,
+                           size_t * outValueLength,
+                           JSONTypes_t * outType );
+/* @[declare_json_searcht] */
+
+/**
+ * @brief Same as JSON_SearchT(), but with const qualified buf and outValue arguments.
+ *
+ * See @ref JSON_Search for documentation of common behavior.
+ *
+ * @param[in] buf  The buffer to search.
+ * @param[in] max  size of the buffer.
+ * @param[in] query  The object keys and array indexes to search for.
+ * @param[in] queryLength  Length of the key.
+ * @param[out] outValue  A pointer to receive the address of the value found.
+ * @param[out] outValueLength  A pointer to receive the length of the value found.
+ * @param[out] outType  An enum indicating the JSON-specific type of the value.
+ */
+/* @[declare_json_searchconst] */
+JSONStatus_t JSON_SearchConst( const char * buf,
+                               size_t max,
+                               const char * query,
+                               size_t queryLength,
+                               const char ** outValue,
+                               size_t * outValueLength,
+                               JSONTypes_t * outType );
+/* @[declare_json_searchconst] */
+
+/**
+ * @ingroup json_struct_types
+ * @brief Structure to represent a key-value pair.
+ */
+typedef struct
+{
+    const char * key;     /**< @brief Pointer to the code point sequence for key. */
+    size_t keyLength;     /**< @brief Length of the code point sequence for key. */
+    const char * value;   /**< @brief Pointer to the code point sequence for value. */
+    size_t valueLength;   /**< @brief Length of the code point sequence for value. */
+    JSONTypes_t jsonType; /**< @brief JSON-specific type of the value. */
+} JSONPair_t;
+
+/**
+ * @brief Output the next key-value pair or value from a collection.
+ *
+ * This function may be used in a loop to output each key-value pair from an object,
+ * or each value from an array.  For the first invocation, the integers pointed to by
+ * start and next should be initialized to 0.  These will be updated by the function.
+ * If another key-value pair or value is present, the output structure is populated
+ * and #JSONSuccess is returned; otherwise the structure is unchanged and #JSONNotFound
+ * is returned.
+ *
+ * @param[in] buf  The buffer to search.
+ * @param[in] max  size of the buffer.
+ * @param[in,out] start  The index at which the collection begins.
+ * @param[in,out] next  The index at which to seek the next value.
+ * @param[out] outPair  A pointer to receive the next key-value pair.
+ *
+ * @note This function expects a valid JSON document; run JSON_Validate() first.
+ *
+ * @note For an object, the outPair structure will reference a key and its value.
+ * For an array, only the value will be referenced (i.e., outPair.key will be NULL).
+ *
+ * @return #JSONSuccess if a value is output;
+ * #JSONIllegalDocument if the buffer does not contain a collection;
+ * #JSONNotFound if there are no further values in the collection.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *     // Variables used in this example.
+ *     static char * json_types[] =
+ *     {
+ *         "invalid",
+ *         "string",
+ *         "number",
+ *         "true",
+ *         "false",
+ *         "null",
+ *         "object",
+ *         "array"
+ *     };
+ *
+ *     void show( const char * json,
+ *                size_t length )
+ *     {
+ *         size_t start = 0, next = 0;
+ *         JSONPair_t pair = { 0 };
+ *         JSONStatus_t result;
+ *
+ *         result = JSON_Validate( json, length );
+ *         if( result == JSONSuccess )
+ *         {
+ *             result = JSON_Iterate( json, length, &start, &next, &pair );
+ *         }
+ *
+ *         while( result == JSONSuccess )
+ *         {
+ *             if( pair.key != NULL )
+ *             {
+ *                 printf( "key: %.*s\t", ( int ) pair.keyLength, pair.key );
+ *             }
+ *
+ *             printf( "value: (%s) %.*s\n", json_types[ pair.jsonType ],
+ *                     ( int ) pair.valueLength, pair.value );
+ *
+ *             result = JSON_Iterate( json, length, &start, &next, &pair );
+ *         }
+ *     }
+ * @endcode
+ */
+/* @[declare_json_iterate] */
+JSONStatus_t JSON_Iterate( const char * buf,
+                           size_t max,
+                           size_t * start,
+                           size_t * next,
+                           JSONPair_t * outPair );
+/* @[declare_json_iterate] */
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    }
+#endif
+/* *INDENT-ON* */
+
+#endif /* ifndef CORE_JSON_H_ */
diff --git a/project/coreMQTT/coreJSON/makefile b/project/coreMQTT/coreJSON/makefile
new file mode 100644
index 0000000..b6ece0e
--- /dev/null
+++ b/project/coreMQTT/coreJSON/makefile
@@ -0,0 +1,35 @@
+#********************************************************************************
+#      Copyright:  (C) 2023 LingYun IoT System Studio
+#                  All rights reserved.
+#
+#       Filename:  Makefile
+#    Description:  This file used compile all the source code to static library
+#
+#        Version:  1.0.0(11/08/23)
+#         Author:  Guo Wenxue <guowenxue@gmail.com>
+#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+PWD=$(shell pwd )
+
+BUILD_ARCH=$(shell uname -m)
+ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
+    CROSS_COMPILE?=arm-linux-gnueabihf-
+endif
+
+LIBNAME=$(shell basename ${PWD} )
+TOPDIR=$(shell dirname ${PWD} )
+CFLAGS+=-D_GNU_SOURCE
+
+all: clean
+	@rm -f *.o
+	@${CROSS_COMPILE}gcc ${CFLAGS} -I${TOPDIR} -c *.c
+	${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
+
+clean:
+	@rm -f *.o
+	@rm -f *.a
+
+distclean:
+	@make clean
diff --git a/project/coreMQTT/coreMQTT/README.md b/project/coreMQTT/coreMQTT/README.md
new file mode 100644
index 0000000..16355a1
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/README.md
@@ -0,0 +1,35 @@
+## coreMQTT Client Library
+
+This repository contains the coreMQTT library that has been optimized for a low memory footprint.  The coreMQTT library is compliant with the [MQTT 3.1.1](https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/mqtt-v3.1.1.html) standard.  It has no dependencies on any additional libraries other than the standard C library, a customer-implemented network transport interface, and *optionally* a user-implemented platform time function.  This library is distributed under the [MIT Open Source License](LICENSE).
+
+This library has gone through code quality checks including verification that no function has a [GNU Complexity](https://www.gnu.org/software/complexity/manual/complexity.html) score over 8, and checks against deviations from mandatory rules in the [MISRA coding standard](https://www.misra.org.uk).  Deviations from the MISRA C:2012 guidelines are documented under [MISRA Deviations](MISRA.md). This library has also undergone both static code analysis from [Coverity static analysis](https://scan.coverity.com/), and validation of memory safety through the [CBMC automated reasoning tool](https://www.cprover.org/cbmc/).
+
+See memory requirements for this library [here](./docs/doxygen/include/size_table.md).
+
+**coreMQTT v2.1.1 [source code](https://github.com/FreeRTOS/coreMQTT/tree/v2.1.1/source) is part of the [FreeRTOS 202210.01 LTS](https://github.com/FreeRTOS/FreeRTOS-LTS/tree/202210.01-LTS) release.**
+
+## MQTT Config File
+
+The MQTT client library exposes build configuration macros that are required for building the library.
+A list of all the configurations and their default values are defined in [core_mqtt_config_defaults.h](source/include/core_mqtt_config_defaults.h). 
+To provide custom values for the configuration macros, a custom config file named `core_mqtt_config.h` can be
+provided by the application to the library.
+
+By default, a `core_mqtt_config.h` custom config is required to build the library. To disable this requirement
+and build the library with default configuration values, provide `MQTT_DO_NOT_USE_CUSTOM_CONFIG` as a compile time preprocessor macro.
+
+**Thus, the MQTT library can be built by either**:
+* Defining a `core_mqtt_config.h` file in the application, and adding it to the include directories list of the library  
+**OR**
+* Defining the `MQTT_DO_NOT_USE_CUSTOM_CONFIG` preprocessor macro for the library build.
+
+## Documentation
+
+For pre-generated documentation, please see the documentation linked in the locations below:
+
+| Location |
+| :-: |
+| [FreeRTOS.org](https://freertos.org/Documentation/api-ref/coreMQTT/docs/doxygen/output/html/index.html) |
+
+Note that the latest included version of coreMQTT may differ across repositories.
+
diff --git a/project/coreMQTT/coreMQTT/core_mqtt.c b/project/coreMQTT/coreMQTT/core_mqtt.c
new file mode 100644
index 0000000..633f61b
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/core_mqtt.c
@@ -0,0 +1,3314 @@
+/*
+ * coreMQTT v2.1.1
+ * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_mqtt.c
+ * @brief Implements the user-facing functions in core_mqtt.h.
+ */
+#include <string.h>
+#include <assert.h>
+
+#include "core_mqtt.h"
+#include "core_mqtt_state.h"
+
+/* Include config defaults header to get default values of configs. */
+#include "core_mqtt_config_defaults.h"
+
+#include "core_mqtt_default_logging.h"
+
+#ifndef MQTT_PRE_SEND_HOOK
+
+/**
+ * @brief Hook called before a 'send' operation is executed.
+ */
+    #define MQTT_PRE_SEND_HOOK( pContext )
+#endif /* !MQTT_PRE_SEND_HOOK */
+
+#ifndef MQTT_POST_SEND_HOOK
+
+/**
+ * @brief Hook called after the 'send' operation is complete.
+ */
+    #define MQTT_POST_SEND_HOOK( pContext )
+#endif /* !MQTT_POST_SEND_HOOK */
+
+#ifndef MQTT_PRE_STATE_UPDATE_HOOK
+
+/**
+ * @brief Hook called just before an update to the MQTT state is made.
+ */
+    #define MQTT_PRE_STATE_UPDATE_HOOK( pContext )
+#endif /* !MQTT_PRE_STATE_UPDATE_HOOK */
+
+#ifndef MQTT_POST_STATE_UPDATE_HOOK
+
+/**
+ * @brief Hook called just after an update to the MQTT state has
+ * been made.
+ */
+    #define MQTT_POST_STATE_UPDATE_HOOK( pContext )
+#endif /* !MQTT_POST_STATE_UPDATE_HOOK */
+
+/*-----------------------------------------------------------*/
+
+/**
+ * @brief Sends provided buffer to network using transport send.
+ *
+ * @brief param[in] pContext Initialized MQTT context.
+ * @brief param[in] pBufferToSend Buffer to be sent to network.
+ * @brief param[in] bytesToSend Number of bytes to be sent.
+ *
+ * @note This operation may call the transport send function
+ * repeatedly to send bytes over the network until either:
+ * 1. The requested number of bytes @a bytesToSend have been sent.
+ *                    OR
+ * 2. MQTT_SEND_TIMEOUT_MS milliseconds have gone by since entering this
+ * function.
+ *                    OR
+ * 3. There is an error in sending data over the network.
+ *
+ * @return Total number of bytes sent, or negative value on network error.
+ */
+static int32_t sendBuffer( MQTTContext_t * pContext,
+                           const uint8_t * pBufferToSend,
+                           size_t bytesToSend );
+
+/**
+ * @brief Sends MQTT connect without copying the users data into any buffer.
+ *
+ * @brief param[in] pContext Initialized MQTT context.
+ * @brief param[in] pConnectInfo MQTT CONNECT packet information.
+ * @brief param[in] pWillInfo Last Will and Testament. Pass NULL if Last Will and
+ * Testament is not used.
+ * @brief param[in] remainingLength the length of the connect packet.
+ *
+ * @note This operation may call the transport send function
+ * repeatedly to send bytes over the network until either:
+ * 1. The requested number of bytes @a remainingLength have been sent.
+ *                    OR
+ * 2. MQTT_SEND_TIMEOUT_MS milliseconds have gone by since entering this
+ * function.
+ *                    OR
+ * 3. There is an error in sending data over the network.
+ *
+ * @return #MQTTSendFailed or #MQTTSuccess.
+ */
+static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext,
+                                            const MQTTConnectInfo_t * pConnectInfo,
+                                            const MQTTPublishInfo_t * pWillInfo,
+                                            size_t remainingLength );
+
+/**
+ * @brief Sends the vector array passed through the parameters over the network.
+ *
+ * @note The preference is given to 'writev' function if it is present in the
+ * transport interface. Otherwise, a send call is made repeatedly to achieve the
+ * result.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] pIoVec The vector array to be sent.
+ * @param[in] ioVecCount The number of elements in the array.
+ *
+ * @note This operation may call the transport send or writev functions
+ * repeatedly to send bytes over the network until either:
+ * 1. The requested number of bytes have been sent.
+ *                    OR
+ * 2. MQTT_SEND_TIMEOUT_MS milliseconds have gone by since entering this
+ * function.
+ *                    OR
+ * 3. There is an error in sending data over the network.
+ *
+ * @return The total number of bytes sent or the error code as received from the
+ * transport interface.
+ */
+static int32_t sendMessageVector( MQTTContext_t * pContext,
+                                  TransportOutVector_t * pIoVec,
+                                  size_t ioVecCount );
+
+/**
+ * @brief Add a string and its length after serializing it in a manner outlined by
+ * the MQTT specification.
+ *
+ * @param[in] serailizedLength Array of two bytes to which the vector will point.
+ * The array must remain in scope until the message has been sent.
+ * @param[in] string The string to be serialized.
+ * @param[in] length The length of the string to be serialized.
+ * @param[in] iterator The iterator pointing to the first element in the
+ * transport interface IO array.
+ * @param[out] updatedLength This parameter will be added to with the number of
+ * bytes added to the vector.
+ *
+ * @return The number of vectors added.
+ */
+static size_t addEncodedStringToVector( uint8_t serailizedLength[ 2 ],
+                                        const char * const string,
+                                        uint16_t length,
+                                        TransportOutVector_t * iterator,
+                                        size_t * updatedLength );
+
+/**
+ * @brief Send MQTT SUBSCRIBE message without copying the user data into a buffer and
+ * directly sending it.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The count of elements in the list.
+ * @param[in] packetId The packet ID of the subscribe packet
+ * @param[in] remainingLength The remaining length of the subscribe packet.
+ *
+ * @return #MQTTSuccess or #MQTTSendFailed.
+ */
+static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext,
+                                              const MQTTSubscribeInfo_t * pSubscriptionList,
+                                              size_t subscriptionCount,
+                                              uint16_t packetId,
+                                              size_t remainingLength );
+
+/**
+ * @brief Send MQTT UNSUBSCRIBE message without copying the user data into a buffer and
+ * directly sending it.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] pSubscriptionList MQTT subscription info.
+ * @param[in] subscriptionCount The count of elements in the list.
+ * @param[in] packetId The packet ID of the unsubscribe packet.
+ * @param[in] remainingLength The remaining length of the unsubscribe packet.
+ *
+ * @return #MQTTSuccess or #MQTTSendFailed.
+ */
+static MQTTStatus_t sendUnsubscribeWithoutCopy( MQTTContext_t * pContext,
+                                                const MQTTSubscribeInfo_t * pSubscriptionList,
+                                                size_t subscriptionCount,
+                                                uint16_t packetId,
+                                                size_t remainingLength );
+
+/**
+ * @brief Calculate the interval between two millisecond timestamps, including
+ * when the later value has overflowed.
+ *
+ * @note In C, the operands are promoted to signed integers in subtraction.
+ * Using this function avoids the need to cast the result of subtractions back
+ * to uint32_t.
+ *
+ * @param[in] later The later time stamp, in milliseconds.
+ * @param[in] start The earlier time stamp, in milliseconds.
+ *
+ * @return later - start.
+ */
+static uint32_t calculateElapsedTime( uint32_t later,
+                                      uint32_t start );
+
+/**
+ * @brief Convert a byte indicating a publish ack type to an #MQTTPubAckType_t.
+ *
+ * @param[in] packetType First byte of fixed header.
+ *
+ * @return Type of ack.
+ */
+static MQTTPubAckType_t getAckFromPacketType( uint8_t packetType );
+
+/**
+ * @brief Receive bytes into the network buffer.
+ *
+ * @param[in] pContext Initialized MQTT Context.
+ * @param[in] bytesToRecv Number of bytes to receive.
+ *
+ * @note This operation calls the transport receive function
+ * repeatedly to read bytes from the network until either:
+ * 1. The requested number of bytes @a bytesToRecv are read.
+ *                    OR
+ * 2. No data is received from the network for MQTT_RECV_POLLING_TIMEOUT_MS duration.
+ *
+ *                    OR
+ * 3. There is an error in reading from the network.
+ *
+ *
+ * @return Number of bytes received, or negative number on network error.
+ */
+static int32_t recvExact( const MQTTContext_t * pContext,
+                          size_t bytesToRecv );
+
+/**
+ * @brief Discard a packet from the transport interface.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] remainingLength Remaining length of the packet to dump.
+ * @param[in] timeoutMs Time remaining to discard the packet.
+ *
+ * @return #MQTTRecvFailed or #MQTTNoDataAvailable.
+ */
+static MQTTStatus_t discardPacket( const MQTTContext_t * pContext,
+                                   size_t remainingLength,
+                                   uint32_t timeoutMs );
+
+/**
+ * @brief Discard a packet from the MQTT buffer and the transport interface.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] pPacketInfo Information struct of the packet to be discarded.
+ *
+ * @return #MQTTRecvFailed or #MQTTNoDataAvailable.
+ */
+static MQTTStatus_t discardStoredPacket( MQTTContext_t * pContext,
+                                         const MQTTPacketInfo_t * pPacketInfo );
+
+/**
+ * @brief Receive a packet from the transport interface.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] incomingPacket packet struct with remaining length.
+ * @param[in] remainingTimeMs Time remaining to receive the packet.
+ *
+ * @return #MQTTSuccess or #MQTTRecvFailed.
+ */
+static MQTTStatus_t receivePacket( const MQTTContext_t * pContext,
+                                   MQTTPacketInfo_t incomingPacket,
+                                   uint32_t remainingTimeMs );
+
+/**
+ * @brief Get the correct ack type to send.
+ *
+ * @param[in] state Current state of publish.
+ *
+ * @return Packet Type byte of PUBACK, PUBREC, PUBREL, or PUBCOMP if one of
+ * those should be sent, else 0.
+ */
+static uint8_t getAckTypeToSend( MQTTPublishState_t state );
+
+/**
+ * @brief Send acks for received QoS 1/2 publishes.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] packetId packet ID of original PUBLISH.
+ * @param[in] publishState Current publish state in record.
+ *
+ * @return #MQTTSuccess, #MQTTIllegalState or #MQTTSendFailed.
+ */
+static MQTTStatus_t sendPublishAcks( MQTTContext_t * pContext,
+                                     uint16_t packetId,
+                                     MQTTPublishState_t publishState );
+
+/**
+ * @brief Send a keep alive PINGREQ if the keep alive interval has elapsed.
+ *
+ * @param[in] pContext Initialized MQTT Context.
+ *
+ * @return #MQTTKeepAliveTimeout if a PINGRESP is not received in time,
+ * #MQTTSendFailed if the PINGREQ cannot be sent, or #MQTTSuccess.
+ */
+static MQTTStatus_t handleKeepAlive( MQTTContext_t * pContext );
+
+/**
+ * @brief Handle received MQTT PUBLISH packet.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] pIncomingPacket Incoming packet.
+ *
+ * @return MQTTSuccess, MQTTIllegalState or deserialization error.
+ */
+static MQTTStatus_t handleIncomingPublish( MQTTContext_t * pContext,
+                                           MQTTPacketInfo_t * pIncomingPacket );
+
+/**
+ * @brief Handle received MQTT publish acks.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] pIncomingPacket Incoming packet.
+ *
+ * @return MQTTSuccess, MQTTIllegalState, or deserialization error.
+ */
+static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext,
+                                       MQTTPacketInfo_t * pIncomingPacket );
+
+/**
+ * @brief Handle received MQTT ack.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] pIncomingPacket Incoming packet.
+ * @param[in] manageKeepAlive Flag indicating if PINGRESPs should not be given
+ * to the application
+ *
+ * @return MQTTSuccess, MQTTIllegalState, or deserialization error.
+ */
+static MQTTStatus_t handleIncomingAck( MQTTContext_t * pContext,
+                                       MQTTPacketInfo_t * pIncomingPacket,
+                                       bool manageKeepAlive );
+
+/**
+ * @brief Run a single iteration of the receive loop.
+ *
+ * @param[in] pContext MQTT Connection context.
+ * @param[in] manageKeepAlive Flag indicating if keep alive should be handled.
+ *
+ * @return #MQTTRecvFailed if a network error occurs during reception;
+ * #MQTTSendFailed if a network error occurs while sending an ACK or PINGREQ;
+ * #MQTTBadResponse if an invalid packet is received;
+ * #MQTTKeepAliveTimeout if the server has not sent a PINGRESP before
+ * #MQTT_PINGRESP_TIMEOUT_MS milliseconds;
+ * #MQTTIllegalState if an incoming QoS 1/2 publish or ack causes an
+ * invalid transition for the internal state machine;
+ * #MQTTSuccess on success.
+ */
+static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext,
+                                            bool manageKeepAlive );
+
+/**
+ * @brief Validates parameters of #MQTT_Subscribe or #MQTT_Unsubscribe.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[in] packetId Packet identifier.
+ *
+ * @return #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ */
+static MQTTStatus_t validateSubscribeUnsubscribeParams( const MQTTContext_t * pContext,
+                                                        const MQTTSubscribeInfo_t * pSubscriptionList,
+                                                        size_t subscriptionCount,
+                                                        uint16_t packetId );
+
+/**
+ * @brief Receives a CONNACK MQTT packet.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] timeoutMs Timeout for waiting for CONNACK packet.
+ * @param[in] cleanSession Clean session flag set by application.
+ * @param[out] pIncomingPacket List of MQTT subscription info.
+ * @param[out] pSessionPresent Whether a previous session was present.
+ * Only relevant if not establishing a clean session.
+ *
+ * @return #MQTTBadResponse if a bad response is received;
+ * #MQTTNoDataAvailable if no data available for transport recv;
+ * ##MQTTRecvFailed if transport recv failed;
+ * #MQTTSuccess otherwise.
+ */
+static MQTTStatus_t receiveConnack( const MQTTContext_t * pContext,
+                                    uint32_t timeoutMs,
+                                    bool cleanSession,
+                                    MQTTPacketInfo_t * pIncomingPacket,
+                                    bool * pSessionPresent );
+
+/**
+ * @brief Resends pending acks for a re-established MQTT session, or
+ * clears existing state records for a clean session.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] sessionPresent Session present flag received from the MQTT broker.
+ *
+ * @return #MQTTSendFailed if transport send during resend failed;
+ * #MQTTSuccess otherwise.
+ */
+static MQTTStatus_t handleSessionResumption( MQTTContext_t * pContext,
+                                             bool sessionPresent );
+
+
+/**
+ * @brief Send the publish packet without copying the topic string and payload in
+ * the buffer.
+ *
+ * @brief param[in] pContext Initialized MQTT context.
+ * @brief param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @brief param[in] pMqttHeader the serialized MQTT header with the header byte;
+ * the encoded length of the packet; and the encoded length of the topic string.
+ * @brief param[in] headerSize Size of the serialized PUBLISH header.
+ * @brief param[in] packetId Packet Id of the publish packet.
+ *
+ * @return #MQTTSendFailed if transport send during resend failed;
+ * #MQTTSuccess otherwise.
+ */
+static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext,
+                                            const MQTTPublishInfo_t * pPublishInfo,
+                                            const uint8_t * pMqttHeader,
+                                            size_t headerSize,
+                                            uint16_t packetId );
+
+/**
+ * @brief Function to validate #MQTT_Publish parameters.
+ *
+ * @brief param[in] pContext Initialized MQTT context.
+ * @brief param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @brief param[in] packetId Packet Id for the MQTT PUBLISH packet.
+ *
+ * @return #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ */
+static MQTTStatus_t validatePublishParams( const MQTTContext_t * pContext,
+                                           const MQTTPublishInfo_t * pPublishInfo,
+                                           uint16_t packetId );
+
+/**
+ * @brief Performs matching for special cases when a topic filter ends
+ * with a wildcard character.
+ *
+ * When the topic name has been consumed but there are remaining characters to
+ * to match in topic filter, this function handles the following 2 cases:
+ * - When the topic filter ends with "/+" or "/#" characters, but the topic
+ * name only ends with '/'.
+ * - When the topic filter ends with "/#" characters, but the topic name
+ * ends at the parent level.
+ *
+ * @note This function ASSUMES that the topic name been consumed in linear
+ * matching with the topic filer, but the topic filter has remaining characters
+ * to be matched.
+ *
+ * @param[in] pTopicFilter The topic filter containing the wildcard.
+ * @param[in] topicFilterLength Length of the topic filter being examined.
+ * @param[in] filterIndex Index of the topic filter being examined.
+ *
+ * @return Returns whether the topic filter and the topic name match.
+ */
+static bool matchEndWildcardsSpecialCases( const char * pTopicFilter,
+                                           uint16_t topicFilterLength,
+                                           uint16_t filterIndex );
+
+/**
+ * @brief Attempt to match topic name with a topic filter starting with a wildcard.
+ *
+ * If the topic filter starts with a '+' (single-level) wildcard, the function
+ * advances the @a pNameIndex by a level in the topic name.
+ * If the topic filter starts with a '#' (multi-level) wildcard, the function
+ * concludes that both the topic name and topic filter match.
+ *
+ * @param[in] pTopicName The topic name to match.
+ * @param[in] topicNameLength Length of the topic name.
+ * @param[in] pTopicFilter The topic filter to match.
+ * @param[in] topicFilterLength Length of the topic filter.
+ * @param[in,out] pNameIndex Current index in the topic name being examined. It is
+ * advanced by one level for `+` wildcards.
+ * @param[in, out] pFilterIndex Current index in the topic filter being examined.
+ * It is advanced to position of '/' level separator for '+' wildcard.
+ * @param[out] pMatch Whether the topic filter and topic name match.
+ *
+ * @return `true` if the caller of this function should exit; `false` if the
+ * caller should continue parsing the topics.
+ */
+static bool matchWildcards( const char * pTopicName,
+                            uint16_t topicNameLength,
+                            const char * pTopicFilter,
+                            uint16_t topicFilterLength,
+                            uint16_t * pNameIndex,
+                            uint16_t * pFilterIndex,
+                            bool * pMatch );
+
+/**
+ * @brief Match a topic name and topic filter allowing the use of wildcards.
+ *
+ * @param[in] pTopicName The topic name to check.
+ * @param[in] topicNameLength Length of the topic name.
+ * @param[in] pTopicFilter The topic filter to check.
+ * @param[in] topicFilterLength Length of topic filter.
+ *
+ * @return `true` if the topic name and topic filter match; `false` otherwise.
+ */
+static bool matchTopicFilter( const char * pTopicName,
+                              uint16_t topicNameLength,
+                              const char * pTopicFilter,
+                              uint16_t topicFilterLength );
+
+/*-----------------------------------------------------------*/
+
+static bool matchEndWildcardsSpecialCases( const char * pTopicFilter,
+                                           uint16_t topicFilterLength,
+                                           uint16_t filterIndex )
+{
+    bool matchFound = false;
+
+    assert( pTopicFilter != NULL );
+    assert( topicFilterLength != 0U );
+
+    /* Check if the topic filter has 2 remaining characters and it ends in
+     * "/#". This check handles the case to match filter "sport/#" with topic
+     * "sport". The reason is that the '#' wildcard represents the parent and
+     * any number of child levels in the topic name.*/
+    if( ( topicFilterLength >= 3U ) &&
+        ( filterIndex == ( topicFilterLength - 3U ) ) &&
+        ( pTopicFilter[ filterIndex + 1U ] == '/' ) &&
+        ( pTopicFilter[ filterIndex + 2U ] == '#' ) )
+
+    {
+        matchFound = true;
+    }
+
+    /* Check if the next character is "#" or "+" and the topic filter ends in
+     * "/#" or "/+". This check handles the cases to match:
+     *
+     * - Topic filter "sport/+" with topic "sport/".
+     * - Topic filter "sport/#" with topic "sport/".
+     */
+    if( ( filterIndex == ( topicFilterLength - 2U ) ) &&
+        ( pTopicFilter[ filterIndex ] == '/' ) )
+    {
+        /* Check that the last character is a wildcard. */
+        matchFound = ( pTopicFilter[ filterIndex + 1U ] == '+' ) ||
+                     ( pTopicFilter[ filterIndex + 1U ] == '#' );
+    }
+
+    return matchFound;
+}
+
+/*-----------------------------------------------------------*/
+
+static bool matchWildcards( const char * pTopicName,
+                            uint16_t topicNameLength,
+                            const char * pTopicFilter,
+                            uint16_t topicFilterLength,
+                            uint16_t * pNameIndex,
+                            uint16_t * pFilterIndex,
+                            bool * pMatch )
+{
+    bool shouldStopMatching = false;
+    bool locationIsValidForWildcard;
+
+    assert( pTopicName != NULL );
+    assert( topicNameLength != 0U );
+    assert( pTopicFilter != NULL );
+    assert( topicFilterLength != 0U );
+    assert( pNameIndex != NULL );
+    assert( pFilterIndex != NULL );
+    assert( pMatch != NULL );
+
+    /* Wild card in a topic filter is only valid either at the starting position
+     * or when it is preceded by a '/'.*/
+    locationIsValidForWildcard = ( *pFilterIndex == 0u ) ||
+                                 ( pTopicFilter[ *pFilterIndex - 1U ] == '/' );
+
+    if( ( pTopicFilter[ *pFilterIndex ] == '+' ) && ( locationIsValidForWildcard == true ) )
+    {
+        bool nextLevelExistsInTopicName = false;
+        bool nextLevelExistsinTopicFilter = false;
+
+        /* Move topic name index to the end of the current level. The end of the
+         * current level is identified by the last character before the next level
+         * separator '/'. */
+        while( *pNameIndex < topicNameLength )
+        {
+            /* Exit the loop if we hit the level separator. */
+            if( pTopicName[ *pNameIndex ] == '/' )
+            {
+                nextLevelExistsInTopicName = true;
+                break;
+            }
+
+            ( *pNameIndex )++;
+        }
+
+        /* Determine if the topic filter contains a child level after the current level
+         * represented by the '+' wildcard. */
+        if( ( *pFilterIndex < ( topicFilterLength - 1U ) ) &&
+            ( pTopicFilter[ *pFilterIndex + 1U ] == '/' ) )
+        {
+            nextLevelExistsinTopicFilter = true;
+        }
+
+        /* If the topic name contains a child level but the topic filter ends at
+         * the current level, then there does not exist a match. */
+        if( ( nextLevelExistsInTopicName == true ) &&
+            ( nextLevelExistsinTopicFilter == false ) )
+        {
+            *pMatch = false;
+            shouldStopMatching = true;
+        }
+
+        /* If the topic name and topic filter have child levels, then advance the
+         * filter index to the level separator in the topic filter, so that match
+         * can be performed in the next level.
+         * Note: The name index already points to the level separator in the topic
+         * name. */
+        else if( nextLevelExistsInTopicName == true )
+        {
+            ( *pFilterIndex )++;
+        }
+        else
+        {
+            /* If we have reached here, the the loop terminated on the
+             * ( *pNameIndex < topicNameLength) condition, which means that have
+             * reached past the end of the topic name, and thus, we decrement the
+             * index to the last character in the topic name.*/
+            ( *pNameIndex )--;
+        }
+    }
+
+    /* '#' matches everything remaining in the topic name. It must be the
+     * last character in a topic filter. */
+    else if( ( pTopicFilter[ *pFilterIndex ] == '#' ) &&
+             ( *pFilterIndex == ( topicFilterLength - 1U ) ) &&
+             ( locationIsValidForWildcard == true ) )
+    {
+        /* Subsequent characters don't need to be checked for the
+         * multi-level wildcard. */
+        *pMatch = true;
+        shouldStopMatching = true;
+    }
+    else
+    {
+        /* Any character mismatch other than '+' or '#' means the topic
+         * name does not match the topic filter. */
+        *pMatch = false;
+        shouldStopMatching = true;
+    }
+
+    return shouldStopMatching;
+}
+
+/*-----------------------------------------------------------*/
+
+static bool matchTopicFilter( const char * pTopicName,
+                              uint16_t topicNameLength,
+                              const char * pTopicFilter,
+                              uint16_t topicFilterLength )
+{
+    bool matchFound = false, shouldStopMatching = false;
+    uint16_t nameIndex = 0, filterIndex = 0;
+
+    assert( pTopicName != NULL );
+    assert( topicNameLength != 0 );
+    assert( pTopicFilter != NULL );
+    assert( topicFilterLength != 0 );
+
+    while( ( nameIndex < topicNameLength ) && ( filterIndex < topicFilterLength ) )
+    {
+        /* Check if the character in the topic name matches the corresponding
+         * character in the topic filter string. */
+        if( pTopicName[ nameIndex ] == pTopicFilter[ filterIndex ] )
+        {
+            /* If the topic name has been consumed but the topic filter has not
+             * been consumed, match for special cases when the topic filter ends
+             * with wildcard character. */
+            if( nameIndex == ( topicNameLength - 1U ) )
+            {
+                matchFound = matchEndWildcardsSpecialCases( pTopicFilter,
+                                                            topicFilterLength,
+                                                            filterIndex );
+            }
+        }
+        else
+        {
+            /* Check for matching wildcards. */
+            shouldStopMatching = matchWildcards( pTopicName,
+                                                 topicNameLength,
+                                                 pTopicFilter,
+                                                 topicFilterLength,
+                                                 &nameIndex,
+                                                 &filterIndex,
+                                                 &matchFound );
+        }
+
+        if( ( matchFound == true ) || ( shouldStopMatching == true ) )
+        {
+            break;
+        }
+
+        /* Increment indexes. */
+        nameIndex++;
+        filterIndex++;
+    }
+
+    if( matchFound == false )
+    {
+        /* If the end of both strings has been reached, they match. This represents the
+         * case when the topic filter contains the '+' wildcard at a non-starting position.
+         * For example, when matching either of "sport/+/player" OR "sport/hockey/+" topic
+         * filters with "sport/hockey/player" topic name. */
+        matchFound = ( nameIndex == topicNameLength ) &&
+                     ( filterIndex == topicFilterLength );
+    }
+
+    return matchFound;
+}
+
+/*-----------------------------------------------------------*/
+
+static int32_t sendMessageVector( MQTTContext_t * pContext,
+                                  TransportOutVector_t * pIoVec,
+                                  size_t ioVecCount )
+{
+    int32_t sendResult;
+    uint32_t timeoutMs;
+    TransportOutVector_t * pIoVectIterator;
+    size_t vectorsToBeSent = ioVecCount;
+    size_t bytesToSend = 0U;
+    int32_t bytesSentOrError = 0;
+
+    assert( pContext != NULL );
+    assert( pIoVec != NULL );
+    assert( pContext->getTime != NULL );
+    /* Send must always be defined */
+    assert( pContext->transportInterface.send != NULL );
+
+    /* Count the total number of bytes to be sent as outlined in the vector. */
+    for( pIoVectIterator = pIoVec; pIoVectIterator <= &( pIoVec[ ioVecCount - 1U ] ); pIoVectIterator++ )
+    {
+        bytesToSend += pIoVectIterator->iov_len;
+    }
+
+    /* Reset the iterator to point to the first entry in the array. */
+    pIoVectIterator = pIoVec;
+
+    /* Set the timeout. */
+    timeoutMs = pContext->getTime() + MQTT_SEND_TIMEOUT_MS;
+
+    while( ( bytesSentOrError < ( int32_t ) bytesToSend ) && ( bytesSentOrError >= 0 ) )
+    {
+        if( pContext->transportInterface.writev != NULL )
+        {
+            sendResult = pContext->transportInterface.writev( pContext->transportInterface.pNetworkContext,
+                                                              pIoVectIterator,
+                                                              vectorsToBeSent );
+        }
+        else
+        {
+            sendResult = pContext->transportInterface.send( pContext->transportInterface.pNetworkContext,
+                                                            pIoVectIterator->iov_base,
+                                                            pIoVectIterator->iov_len );
+        }
+
+        if( sendResult > 0 )
+        {
+            /* It is a bug in the application's transport send implementation if
+             * more bytes than expected are sent. */
+            assert( sendResult <= ( ( int32_t ) bytesToSend - bytesSentOrError ) );
+
+            bytesSentOrError += sendResult;
+
+            /* Set last transmission time. */
+            pContext->lastPacketTxTime = pContext->getTime();
+
+            LogDebug( ( "sendMessageVector: Bytes Sent=%ld, Bytes Remaining=%lu",
+                        ( long int ) sendResult,
+                        ( unsigned long ) ( bytesToSend - ( size_t ) bytesSentOrError ) ) );
+        }
+        else if( sendResult < 0 )
+        {
+            bytesSentOrError = sendResult;
+            LogError( ( "sendMessageVector: Unable to send packet: Network Error." ) );
+        }
+        else
+        {
+            /* MISRA Empty body */
+        }
+
+        /* Check for timeout. */
+        if( pContext->getTime() >= timeoutMs )
+        {
+            LogError( ( "sendMessageVector: Unable to send packet: Timed out." ) );
+            break;
+        }
+
+        /* Update the send pointer to the correct vector and offset. */
+        while( ( pIoVectIterator <= &( pIoVec[ ioVecCount - 1U ] ) ) &&
+               ( sendResult >= ( int32_t ) pIoVectIterator->iov_len ) )
+        {
+            sendResult -= ( int32_t ) pIoVectIterator->iov_len;
+            pIoVectIterator++;
+            /* Update the number of vector which are yet to be sent. */
+            vectorsToBeSent--;
+        }
+
+        /* Some of the bytes from this vector were sent as well, update the length
+         * and the pointer to data in this vector. */
+        if( ( sendResult > 0 ) &&
+            ( pIoVectIterator <= &( pIoVec[ ioVecCount - 1U ] ) ) )
+        {
+            pIoVectIterator->iov_base = ( const void * ) &( ( ( const uint8_t * ) pIoVectIterator->iov_base )[ sendResult ] );
+            pIoVectIterator->iov_len -= ( size_t ) sendResult;
+        }
+    }
+
+    return bytesSentOrError;
+}
+
+static int32_t sendBuffer( MQTTContext_t * pContext,
+                           const uint8_t * pBufferToSend,
+                           size_t bytesToSend )
+{
+    int32_t sendResult;
+    uint32_t timeoutMs;
+    int32_t bytesSentOrError = 0;
+    const uint8_t * pIndex = pBufferToSend;
+
+    assert( pContext != NULL );
+    assert( pContext->getTime != NULL );
+    assert( pContext->transportInterface.send != NULL );
+    assert( pIndex != NULL );
+
+    /* Set the timeout. */
+    timeoutMs = pContext->getTime() + MQTT_SEND_TIMEOUT_MS;
+
+    while( ( bytesSentOrError < ( int32_t ) bytesToSend ) && ( bytesSentOrError >= 0 ) )
+    {
+        sendResult = pContext->transportInterface.send( pContext->transportInterface.pNetworkContext,
+                                                        pIndex,
+                                                        bytesToSend - ( size_t ) bytesSentOrError );
+
+        if( sendResult > 0 )
+        {
+            /* It is a bug in the application's transport send implementation if
+             * more bytes than expected are sent. */
+            assert( sendResult <= ( ( int32_t ) bytesToSend - bytesSentOrError ) );
+
+            bytesSentOrError += sendResult;
+            pIndex = &pIndex[ sendResult ];
+
+            /* Set last transmission time. */
+            pContext->lastPacketTxTime = pContext->getTime();
+
+            LogDebug( ( "sendBuffer: Bytes Sent=%ld, Bytes Remaining=%lu",
+                        ( long int ) sendResult,
+                        ( unsigned long ) ( bytesToSend - ( size_t ) bytesSentOrError ) ) );
+        }
+        else if( sendResult < 0 )
+        {
+            bytesSentOrError = sendResult;
+            LogError( ( "sendBuffer: Unable to send packet: Network Error." ) );
+        }
+        else
+        {
+            /* MISRA Empty body */
+        }
+
+        /* Check for timeout. */
+        if( pContext->getTime() >= timeoutMs )
+        {
+            LogError( ( "sendBuffer: Unable to send packet: Timed out." ) );
+            break;
+        }
+    }
+
+    return bytesSentOrError;
+}
+
+/*-----------------------------------------------------------*/
+
+static uint32_t calculateElapsedTime( uint32_t later,
+                                      uint32_t start )
+{
+    return later - start;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTPubAckType_t getAckFromPacketType( uint8_t packetType )
+{
+    MQTTPubAckType_t ackType = MQTTPuback;
+
+    switch( packetType )
+    {
+        case MQTT_PACKET_TYPE_PUBACK:
+            ackType = MQTTPuback;
+            break;
+
+        case MQTT_PACKET_TYPE_PUBREC:
+            ackType = MQTTPubrec;
+            break;
+
+        case MQTT_PACKET_TYPE_PUBREL:
+            ackType = MQTTPubrel;
+            break;
+
+        case MQTT_PACKET_TYPE_PUBCOMP:
+        default:
+
+            /* This function is only called after checking the type is one of
+             * the above four values, so packet type must be PUBCOMP here. */
+            assert( packetType == MQTT_PACKET_TYPE_PUBCOMP );
+            ackType = MQTTPubcomp;
+            break;
+    }
+
+    return ackType;
+}
+
+/*-----------------------------------------------------------*/
+
+static int32_t recvExact( const MQTTContext_t * pContext,
+                          size_t bytesToRecv )
+{
+    uint8_t * pIndex = NULL;
+    size_t bytesRemaining = bytesToRecv;
+    int32_t totalBytesRecvd = 0, bytesRecvd;
+    uint32_t lastDataRecvTimeMs = 0U, timeSinceLastRecvMs = 0U;
+    TransportRecv_t recvFunc = NULL;
+    MQTTGetCurrentTimeFunc_t getTimeStampMs = NULL;
+    bool receiveError = false;
+
+    assert( pContext != NULL );
+    assert( bytesToRecv <= pContext->networkBuffer.size );
+    assert( pContext->getTime != NULL );
+    assert( pContext->transportInterface.recv != NULL );
+    assert( pContext->networkBuffer.pBuffer != NULL );
+
+    pIndex = pContext->networkBuffer.pBuffer;
+    recvFunc = pContext->transportInterface.recv;
+    getTimeStampMs = pContext->getTime;
+
+    /* Part of the MQTT packet has been read before calling this function. */
+    lastDataRecvTimeMs = getTimeStampMs();
+
+    while( ( bytesRemaining > 0U ) && ( receiveError == false ) )
+    {
+        bytesRecvd = recvFunc( pContext->transportInterface.pNetworkContext,
+                               pIndex,
+                               bytesRemaining );
+
+        if( bytesRecvd < 0 )
+        {
+            LogError( ( "Network error while receiving packet: ReturnCode=%ld.",
+                        ( long int ) bytesRecvd ) );
+            totalBytesRecvd = bytesRecvd;
+            receiveError = true;
+        }
+        else if( bytesRecvd > 0 )
+        {
+            /* Reset the starting time as we have received some data from the network. */
+            lastDataRecvTimeMs = getTimeStampMs();
+
+            /* It is a bug in the application's transport receive implementation
+             * if more bytes than expected are received. To avoid a possible
+             * overflow in converting bytesRemaining from unsigned to signed,
+             * this assert must exist after the check for bytesRecvd being
+             * negative. */
+            assert( ( size_t ) bytesRecvd <= bytesRemaining );
+
+            bytesRemaining -= ( size_t ) bytesRecvd;
+            totalBytesRecvd += ( int32_t ) bytesRecvd;
+            /* Increment the index. */
+            pIndex = &pIndex[ bytesRecvd ];
+            LogDebug( ( "BytesReceived=%ld, BytesRemaining=%lu, TotalBytesReceived=%ld.",
+                        ( long int ) bytesRecvd,
+                        ( unsigned long ) bytesRemaining,
+                        ( long int ) totalBytesRecvd ) );
+        }
+        else
+        {
+            /* No bytes were read from the network. */
+            timeSinceLastRecvMs = calculateElapsedTime( getTimeStampMs(), lastDataRecvTimeMs );
+
+            /* Check for timeout if we have been waiting to receive any byte on the network. */
+            if( timeSinceLastRecvMs >= MQTT_RECV_POLLING_TIMEOUT_MS )
+            {
+                LogError( ( "Unable to receive packet: Timed out in transport recv." ) );
+                receiveError = true;
+            }
+        }
+    }
+
+    return totalBytesRecvd;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t discardPacket( const MQTTContext_t * pContext,
+                                   size_t remainingLength,
+                                   uint32_t timeoutMs )
+{
+    MQTTStatus_t status = MQTTRecvFailed;
+    int32_t bytesReceived = 0;
+    size_t bytesToReceive = 0U;
+    uint32_t totalBytesReceived = 0U;
+    uint32_t entryTimeMs = 0U;
+    uint32_t elapsedTimeMs = 0U;
+    MQTTGetCurrentTimeFunc_t getTimeStampMs = NULL;
+    bool receiveError = false;
+
+    assert( pContext != NULL );
+    assert( pContext->getTime != NULL );
+
+    bytesToReceive = pContext->networkBuffer.size;
+    getTimeStampMs = pContext->getTime;
+
+    entryTimeMs = getTimeStampMs();
+
+    while( ( totalBytesReceived < remainingLength ) && ( receiveError == false ) )
+    {
+        if( ( remainingLength - totalBytesReceived ) < bytesToReceive )
+        {
+            bytesToReceive = remainingLength - totalBytesReceived;
+        }
+
+        bytesReceived = recvExact( pContext, bytesToReceive );
+
+        if( bytesReceived != ( int32_t ) bytesToReceive )
+        {
+            LogError( ( "Receive error while discarding packet."
+                        "ReceivedBytes=%ld, ExpectedBytes=%lu.",
+                        ( long int ) bytesReceived,
+                        ( unsigned long ) bytesToReceive ) );
+            receiveError = true;
+        }
+        else
+        {
+            totalBytesReceived += ( uint32_t ) bytesReceived;
+
+            elapsedTimeMs = calculateElapsedTime( getTimeStampMs(), entryTimeMs );
+
+            /* Check for timeout. */
+            if( elapsedTimeMs >= timeoutMs )
+            {
+                LogError( ( "Time expired while discarding packet." ) );
+                receiveError = true;
+            }
+        }
+    }
+
+    if( totalBytesReceived == remainingLength )
+    {
+        LogError( ( "Dumped packet. DumpedBytes=%lu.",
+                    ( unsigned long ) totalBytesReceived ) );
+        /* Packet dumped, so no data is available. */
+        status = MQTTNoDataAvailable;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t discardStoredPacket( MQTTContext_t * pContext,
+                                         const MQTTPacketInfo_t * pPacketInfo )
+{
+    MQTTStatus_t status = MQTTRecvFailed;
+    int32_t bytesReceived = 0;
+    size_t bytesToReceive = 0U;
+    uint32_t totalBytesReceived = 0U;
+    bool receiveError = false;
+    size_t mqttPacketSize = 0;
+    size_t remainingLength;
+
+    assert( pContext != NULL );
+    assert( pPacketInfo != NULL );
+
+    mqttPacketSize = pPacketInfo->remainingLength + pPacketInfo->headerLength;
+
+    /* Assert that the packet being discarded is bigger than the
+     * receive buffer. */
+    assert( mqttPacketSize > pContext->networkBuffer.size );
+
+    /* Discard these many bytes at a time. */
+    bytesToReceive = pContext->networkBuffer.size;
+
+    /* Number of bytes depicted by 'index' have already been received. */
+    remainingLength = mqttPacketSize - pContext->index;
+
+    while( ( totalBytesReceived < remainingLength ) && ( receiveError == false ) )
+    {
+        if( ( remainingLength - totalBytesReceived ) < bytesToReceive )
+        {
+            bytesToReceive = remainingLength - totalBytesReceived;
+        }
+
+        bytesReceived = recvExact( pContext, bytesToReceive );
+
+        if( bytesReceived != ( int32_t ) bytesToReceive )
+        {
+            LogError( ( "Receive error while discarding packet."
+                        "ReceivedBytes=%ld, ExpectedBytes=%lu.",
+                        ( long int ) bytesReceived,
+                        ( unsigned long ) bytesToReceive ) );
+            receiveError = true;
+        }
+        else
+        {
+            totalBytesReceived += ( uint32_t ) bytesReceived;
+        }
+    }
+
+    if( totalBytesReceived == remainingLength )
+    {
+        LogError( ( "Dumped packet. DumpedBytes=%lu.",
+                    ( unsigned long ) totalBytesReceived ) );
+        /* Packet dumped, so no data is available. */
+        status = MQTTNoDataAvailable;
+    }
+
+    /* Clear the buffer */
+    ( void ) memset( pContext->networkBuffer.pBuffer,
+                     0,
+                     pContext->networkBuffer.size );
+
+    /* Reset the index. */
+    pContext->index = 0;
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t receivePacket( const MQTTContext_t * pContext,
+                                   MQTTPacketInfo_t incomingPacket,
+                                   uint32_t remainingTimeMs )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    int32_t bytesReceived = 0;
+    size_t bytesToReceive = 0U;
+
+    assert( pContext != NULL );
+    assert( pContext->networkBuffer.pBuffer != NULL );
+
+    if( incomingPacket.remainingLength > pContext->networkBuffer.size )
+    {
+        LogError( ( "Incoming packet will be dumped: "
+                    "Packet length exceeds network buffer size."
+                    "PacketSize=%lu, NetworkBufferSize=%lu.",
+                    ( unsigned long ) incomingPacket.remainingLength,
+                    ( unsigned long ) pContext->networkBuffer.size ) );
+        status = discardPacket( pContext,
+                                incomingPacket.remainingLength,
+                                remainingTimeMs );
+    }
+    else
+    {
+        bytesToReceive = incomingPacket.remainingLength;
+        bytesReceived = recvExact( pContext, bytesToReceive );
+
+        if( bytesReceived == ( int32_t ) bytesToReceive )
+        {
+            /* Receive successful, bytesReceived == bytesToReceive. */
+            LogDebug( ( "Packet received. ReceivedBytes=%ld.",
+                        ( long int ) bytesReceived ) );
+        }
+        else
+        {
+            LogError( ( "Packet reception failed. ReceivedBytes=%ld, "
+                        "ExpectedBytes=%lu.",
+                        ( long int ) bytesReceived,
+                        ( unsigned long ) bytesToReceive ) );
+            status = MQTTRecvFailed;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static uint8_t getAckTypeToSend( MQTTPublishState_t state )
+{
+    uint8_t packetTypeByte = 0U;
+
+    switch( state )
+    {
+        case MQTTPubAckSend:
+            packetTypeByte = MQTT_PACKET_TYPE_PUBACK;
+            break;
+
+        case MQTTPubRecSend:
+            packetTypeByte = MQTT_PACKET_TYPE_PUBREC;
+            break;
+
+        case MQTTPubRelSend:
+            packetTypeByte = MQTT_PACKET_TYPE_PUBREL;
+            break;
+
+        case MQTTPubCompSend:
+            packetTypeByte = MQTT_PACKET_TYPE_PUBCOMP;
+            break;
+
+        case MQTTPubAckPending:
+        case MQTTPubCompPending:
+        case MQTTPubRecPending:
+        case MQTTPubRelPending:
+        case MQTTPublishDone:
+        case MQTTPublishSend:
+        case MQTTStateNull:
+        default:
+            /* Take no action for states that do not require sending an ack. */
+            break;
+    }
+
+    return packetTypeByte;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t sendPublishAcks( MQTTContext_t * pContext,
+                                     uint16_t packetId,
+                                     MQTTPublishState_t publishState )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    MQTTPublishState_t newState = MQTTStateNull;
+    int32_t sendResult = 0;
+    uint8_t packetTypeByte = 0U;
+    MQTTPubAckType_t packetType;
+    MQTTFixedBuffer_t localBuffer;
+    uint8_t pubAckPacket[ MQTT_PUBLISH_ACK_PACKET_SIZE ];
+
+    localBuffer.pBuffer = pubAckPacket;
+    localBuffer.size = MQTT_PUBLISH_ACK_PACKET_SIZE;
+
+    assert( pContext != NULL );
+
+    packetTypeByte = getAckTypeToSend( publishState );
+
+    if( packetTypeByte != 0U )
+    {
+        packetType = getAckFromPacketType( packetTypeByte );
+
+        status = MQTT_SerializeAck( &localBuffer,
+                                    packetTypeByte,
+                                    packetId );
+
+        if( status == MQTTSuccess )
+        {
+            MQTT_PRE_SEND_HOOK( pContext );
+
+            /* Here, we are not using the vector approach for efficiency. There is just one buffer
+             * to be sent which can be achieved with a normal send call. */
+            sendResult = sendBuffer( pContext,
+                                     localBuffer.pBuffer,
+                                     MQTT_PUBLISH_ACK_PACKET_SIZE );
+
+            MQTT_POST_SEND_HOOK( pContext );
+        }
+
+        if( sendResult == ( int32_t ) MQTT_PUBLISH_ACK_PACKET_SIZE )
+        {
+            pContext->controlPacketSent = true;
+
+            MQTT_PRE_STATE_UPDATE_HOOK( pContext );
+
+            status = MQTT_UpdateStateAck( pContext,
+                                          packetId,
+                                          packetType,
+                                          MQTT_SEND,
+                                          &newState );
+
+            MQTT_POST_STATE_UPDATE_HOOK( pContext );
+
+            if( status != MQTTSuccess )
+            {
+                LogError( ( "Failed to update state of publish %hu.",
+                            ( unsigned short ) packetId ) );
+            }
+        }
+        else
+        {
+            LogError( ( "Failed to send ACK packet: PacketType=%02x, SentBytes=%ld, "
+                        "PacketSize=%lu.",
+                        ( unsigned int ) packetTypeByte, ( long int ) sendResult,
+                        MQTT_PUBLISH_ACK_PACKET_SIZE ) );
+            status = MQTTSendFailed;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t handleKeepAlive( MQTTContext_t * pContext )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    uint32_t now = 0U;
+    uint32_t packetTxTimeoutMs = 0U;
+
+    assert( pContext != NULL );
+    assert( pContext->getTime != NULL );
+
+    now = pContext->getTime();
+
+    packetTxTimeoutMs = 1000U * ( uint32_t ) pContext->keepAliveIntervalSec;
+
+    if( PACKET_TX_TIMEOUT_MS < packetTxTimeoutMs )
+    {
+        packetTxTimeoutMs = PACKET_TX_TIMEOUT_MS;
+    }
+
+    /* If keep alive interval is 0, it is disabled. */
+    if( pContext->waitingForPingResp == true )
+    {
+        /* Has time expired? */
+        if( calculateElapsedTime( now, pContext->pingReqSendTimeMs ) >
+            MQTT_PINGRESP_TIMEOUT_MS )
+        {
+            status = MQTTKeepAliveTimeout;
+        }
+    }
+    else
+    {
+        if( ( packetTxTimeoutMs != 0U ) && ( calculateElapsedTime( now, pContext->lastPacketTxTime ) >= packetTxTimeoutMs ) )
+        {
+            status = MQTT_Ping( pContext );
+        }
+        else
+        {
+            const uint32_t timeElapsed = calculateElapsedTime( now, pContext->lastPacketRxTime );
+
+            if( ( timeElapsed != 0U ) && ( timeElapsed >= PACKET_RX_TIMEOUT_MS ) )
+            {
+                status = MQTT_Ping( pContext );
+            }
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t handleIncomingPublish( MQTTContext_t * pContext,
+                                           MQTTPacketInfo_t * pIncomingPacket )
+{
+    MQTTStatus_t status = MQTTBadParameter;
+    MQTTPublishState_t publishRecordState = MQTTStateNull;
+    uint16_t packetIdentifier = 0U;
+    MQTTPublishInfo_t publishInfo;
+    MQTTDeserializedInfo_t deserializedInfo;
+    bool duplicatePublish = false;
+
+    assert( pContext != NULL );
+    assert( pIncomingPacket != NULL );
+    assert( pContext->appCallback != NULL );
+
+    status = MQTT_DeserializePublish( pIncomingPacket, &packetIdentifier, &publishInfo );
+    LogInfo( ( "De-serialized incoming PUBLISH packet: DeserializerResult=%s.",
+               MQTT_Status_strerror( status ) ) );
+
+    if( ( status == MQTTSuccess ) &&
+        ( pContext->incomingPublishRecords == NULL ) &&
+        ( publishInfo.qos > MQTTQoS0 ) )
+    {
+        LogError( ( "Incoming publish has QoS > MQTTQoS0 but incoming "
+                    "publish records have not been initialized. Dropping the "
+                    "incoming publish. Please call MQTT_InitStatefulQoS to enable "
+                    "use of QoS1 and QoS2 publishes." ) );
+        status = MQTTRecvFailed;
+    }
+
+    if( status == MQTTSuccess )
+    {
+        MQTT_PRE_STATE_UPDATE_HOOK( pContext );
+
+        status = MQTT_UpdateStatePublish( pContext,
+                                          packetIdentifier,
+                                          MQTT_RECEIVE,
+                                          publishInfo.qos,
+                                          &publishRecordState );
+
+        MQTT_POST_STATE_UPDATE_HOOK( pContext );
+
+        if( status == MQTTSuccess )
+        {
+            LogInfo( ( "State record updated. New state=%s.",
+                       MQTT_State_strerror( publishRecordState ) ) );
+        }
+
+        /* Different cases in which an incoming publish with duplicate flag is
+         * handled are as listed below.
+         * 1. No collision - This is the first instance of the incoming publish
+         *    packet received or an earlier received packet state is lost. This
+         *    will be handled as a new incoming publish for both QoS1 and QoS2
+         *    publishes.
+         * 2. Collision - The incoming packet was received before and a state
+         *    record is present in the state engine. For QoS1 and QoS2 publishes
+         *    this case can happen at 2 different cases and handling is
+         *    different.
+         *    a. QoS1 - If a PUBACK is not successfully sent for the incoming
+         *       publish due to a connection issue, it can result in broker
+         *       sending out a duplicate publish with dup flag set, when a
+         *       session is reestablished. It can result in a collision in
+         *       state engine. This will be handled by processing the incoming
+         *       publish as a new publish ignoring the
+         *       #MQTTStateCollision status from the state engine. The publish
+         *       data is not passed to the application.
+         *    b. QoS2 - If a PUBREC is not successfully sent for the incoming
+         *       publish or the PUBREC sent is not successfully received by the
+         *       broker due to a connection issue, it can result in broker
+         *       sending out a duplicate publish with dup flag set, when a
+         *       session is reestablished. It can result in a collision in
+         *       state engine. This will be handled by ignoring the
+         *       #MQTTStateCollision status from the state engine. The publish
+         *       data is not passed to the application. */
+        else if( status == MQTTStateCollision )
+        {
+            status = MQTTSuccess;
+            duplicatePublish = true;
+
+            /* Calculate the state for the ack packet that needs to be sent out
+             * for the duplicate incoming publish. */
+            publishRecordState = MQTT_CalculateStatePublish( MQTT_RECEIVE,
+                                                             publishInfo.qos );
+
+            LogDebug( ( "Incoming publish packet with packet id %hu already exists.",
+                        ( unsigned short ) packetIdentifier ) );
+
+            if( publishInfo.dup == false )
+            {
+                LogError( ( "DUP flag is 0 for duplicate packet (MQTT-3.3.1.-1)." ) );
+            }
+        }
+        else
+        {
+            LogError( ( "Error in updating publish state for incoming publish with packet id %hu."
+                        " Error is %s",
+                        ( unsigned short ) packetIdentifier,
+                        MQTT_Status_strerror( status ) ) );
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Set fields of deserialized struct. */
+        deserializedInfo.packetIdentifier = packetIdentifier;
+        deserializedInfo.pPublishInfo = &publishInfo;
+        deserializedInfo.deserializationResult = status;
+
+        /* Invoke application callback to hand the buffer over to application
+         * before sending acks.
+         * Application callback will be invoked for all publishes, except for
+         * duplicate incoming publishes. */
+        if( duplicatePublish == false )
+        {
+            pContext->appCallback( pContext,
+                                   pIncomingPacket,
+                                   &deserializedInfo );
+        }
+
+        /* Send PUBACK or PUBREC if necessary. */
+        status = sendPublishAcks( pContext,
+                                  packetIdentifier,
+                                  publishRecordState );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t handlePublishAcks( MQTTContext_t * pContext,
+                                       MQTTPacketInfo_t * pIncomingPacket )
+{
+    MQTTStatus_t status = MQTTBadResponse;
+    MQTTPublishState_t publishRecordState = MQTTStateNull;
+    uint16_t packetIdentifier;
+    MQTTPubAckType_t ackType;
+    MQTTEventCallback_t appCallback;
+    MQTTDeserializedInfo_t deserializedInfo;
+
+    assert( pContext != NULL );
+    assert( pIncomingPacket != NULL );
+    assert( pContext->appCallback != NULL );
+
+    appCallback = pContext->appCallback;
+
+    ackType = getAckFromPacketType( pIncomingPacket->type );
+    status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL );
+    LogInfo( ( "Ack packet deserialized with result: %s.",
+               MQTT_Status_strerror( status ) ) );
+
+    if( status == MQTTSuccess )
+    {
+        MQTT_PRE_STATE_UPDATE_HOOK( pContext );
+
+        status = MQTT_UpdateStateAck( pContext,
+                                      packetIdentifier,
+                                      ackType,
+                                      MQTT_RECEIVE,
+                                      &publishRecordState );
+
+        MQTT_POST_STATE_UPDATE_HOOK( pContext );
+
+        if( status == MQTTSuccess )
+        {
+            LogInfo( ( "State record updated. New state=%s.",
+                       MQTT_State_strerror( publishRecordState ) ) );
+        }
+        else
+        {
+            LogError( ( "Updating the state engine for packet id %hu"
+                        " failed with error %s.",
+                        ( unsigned short ) packetIdentifier,
+                        MQTT_Status_strerror( status ) ) );
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Set fields of deserialized struct. */
+        deserializedInfo.packetIdentifier = packetIdentifier;
+        deserializedInfo.deserializationResult = status;
+        deserializedInfo.pPublishInfo = NULL;
+
+        /* Invoke application callback to hand the buffer over to application
+         * before sending acks. */
+        appCallback( pContext, pIncomingPacket, &deserializedInfo );
+
+        /* Send PUBREL or PUBCOMP if necessary. */
+        status = sendPublishAcks( pContext,
+                                  packetIdentifier,
+                                  publishRecordState );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t handleIncomingAck( MQTTContext_t * pContext,
+                                       MQTTPacketInfo_t * pIncomingPacket,
+                                       bool manageKeepAlive )
+{
+    MQTTStatus_t status = MQTTBadResponse;
+    uint16_t packetIdentifier = MQTT_PACKET_ID_INVALID;
+    MQTTDeserializedInfo_t deserializedInfo;
+
+    /* We should always invoke the app callback unless we receive a PINGRESP
+     * and are managing keep alive, or if we receive an unknown packet. We
+     * initialize this to false since the callback must be invoked before
+     * sending any PUBREL or PUBCOMP. However, for other cases, we invoke it
+     * at the end to reduce the complexity of this function. */
+    bool invokeAppCallback = false;
+    MQTTEventCallback_t appCallback = NULL;
+
+    assert( pContext != NULL );
+    assert( pIncomingPacket != NULL );
+    assert( pContext->appCallback != NULL );
+
+    appCallback = pContext->appCallback;
+
+    LogDebug( ( "Received packet of type %02x.",
+                ( unsigned int ) pIncomingPacket->type ) );
+
+    switch( pIncomingPacket->type )
+    {
+        case MQTT_PACKET_TYPE_PUBACK:
+        case MQTT_PACKET_TYPE_PUBREC:
+        case MQTT_PACKET_TYPE_PUBREL:
+        case MQTT_PACKET_TYPE_PUBCOMP:
+
+            /* Handle all the publish acks. The app callback is invoked here. */
+            status = handlePublishAcks( pContext, pIncomingPacket );
+
+            break;
+
+        case MQTT_PACKET_TYPE_PINGRESP:
+            status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL );
+            invokeAppCallback = ( status == MQTTSuccess ) && !manageKeepAlive;
+
+            if( ( status == MQTTSuccess ) && ( manageKeepAlive == true ) )
+            {
+                pContext->waitingForPingResp = false;
+            }
+
+            break;
+
+        case MQTT_PACKET_TYPE_SUBACK:
+        case MQTT_PACKET_TYPE_UNSUBACK:
+            /* Deserialize and give these to the app provided callback. */
+            status = MQTT_DeserializeAck( pIncomingPacket, &packetIdentifier, NULL );
+            invokeAppCallback = ( status == MQTTSuccess ) || ( status == MQTTServerRefused );
+            break;
+
+        default:
+            /* Bad response from the server. */
+            LogError( ( "Unexpected packet type from server: PacketType=%02x.",
+                        ( unsigned int ) pIncomingPacket->type ) );
+            status = MQTTBadResponse;
+            break;
+    }
+
+    if( invokeAppCallback == true )
+    {
+        /* Set fields of deserialized struct. */
+        deserializedInfo.packetIdentifier = packetIdentifier;
+        deserializedInfo.deserializationResult = status;
+        deserializedInfo.pPublishInfo = NULL;
+        appCallback( pContext, pIncomingPacket, &deserializedInfo );
+        /* In case a SUBACK indicated refusal, reset the status to continue the loop. */
+        status = MQTTSuccess;
+    }
+
+    return status;
+}
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t receiveSingleIteration( MQTTContext_t * pContext,
+                                            bool manageKeepAlive )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    MQTTPacketInfo_t incomingPacket = { 0 };
+    int32_t recvBytes;
+    size_t totalMQTTPacketLength = 0;
+
+    assert( pContext != NULL );
+    assert( pContext->networkBuffer.pBuffer != NULL );
+
+    /* Read as many bytes as possible into the network buffer. */
+    recvBytes = pContext->transportInterface.recv( pContext->transportInterface.pNetworkContext,
+                                                   &( pContext->networkBuffer.pBuffer[ pContext->index ] ),
+                                                   pContext->networkBuffer.size - pContext->index );
+
+    if( recvBytes < 0 )
+    {
+        /* The receive function has failed. Bubble up the error up to the user. */
+        status = MQTTRecvFailed;
+    }
+    else if( ( recvBytes == 0 ) && ( pContext->index == 0U ) )
+    {
+        /* No more bytes available since the last read and neither is anything in
+         * the buffer. */
+        status = MQTTNoDataAvailable;
+    }
+
+    /* Either something was received, or there is still data to be processed in the
+     * buffer, or both. */
+    else
+    {
+        /* Update the number of bytes in the MQTT fixed buffer. */
+        pContext->index += ( size_t ) recvBytes;
+
+        status = MQTT_ProcessIncomingPacketTypeAndLength( pContext->networkBuffer.pBuffer,
+                                                          &pContext->index,
+                                                          &incomingPacket );
+
+        totalMQTTPacketLength = incomingPacket.remainingLength + incomingPacket.headerLength;
+    }
+
+    /* No data was received, check for keep alive timeout. */
+    if( recvBytes == 0 )
+    {
+        if( manageKeepAlive == true )
+        {
+            /* Keep the copy of the status to be reset later. */
+            MQTTStatus_t statusCopy = status;
+
+            /* Assign status so an error can be bubbled up to application,
+             * but reset it on success. */
+            status = handleKeepAlive( pContext );
+
+            if( status == MQTTSuccess )
+            {
+                /* Reset the status. */
+                status = statusCopy;
+            }
+            else
+            {
+                LogError( ( "Handling of keep alive failed. Status=%s",
+                            MQTT_Status_strerror( status ) ) );
+            }
+        }
+    }
+
+    /* Check whether there is data available before processing the packet further. */
+    if( ( status == MQTTNeedMoreBytes ) || ( status == MQTTNoDataAvailable ) )
+    {
+        /* Do nothing as there is nothing to be processed right now. The proper
+         * error code will be bubbled up to the user. */
+    }
+    /* Any other error code. */
+    else if( status != MQTTSuccess )
+    {
+        LogError( ( "Call to receiveSingleIteration failed. Status=%s",
+                    MQTT_Status_strerror( status ) ) );
+    }
+    /* If the MQTT Packet size is bigger than the buffer itself. */
+    else if( totalMQTTPacketLength > pContext->networkBuffer.size )
+    {
+        /* Discard the packet from the receive buffer and drain the pending
+         * data from the socket buffer. */
+        status = discardStoredPacket( pContext,
+                                      &incomingPacket );
+    }
+    /* If the total packet is of more length than the bytes we have available. */
+    else if( totalMQTTPacketLength > pContext->index )
+    {
+        status = MQTTNeedMoreBytes;
+    }
+    else
+    {
+        /* MISRA else. */
+    }
+
+    /* Handle received packet. If incomplete data was read then this will not execute. */
+    if( status == MQTTSuccess )
+    {
+        incomingPacket.pRemainingData = &pContext->networkBuffer.pBuffer[ incomingPacket.headerLength ];
+
+        /* PUBLISH packets allow flags in the lower four bits. For other
+         * packet types, they are reserved. */
+        if( ( incomingPacket.type & 0xF0U ) == MQTT_PACKET_TYPE_PUBLISH )
+        {
+            status = handleIncomingPublish( pContext, &incomingPacket );
+        }
+        else
+        {
+            status = handleIncomingAck( pContext, &incomingPacket, manageKeepAlive );
+        }
+
+        /* Update the index to reflect the remaining bytes in the buffer.  */
+        pContext->index -= totalMQTTPacketLength;
+
+        /* Move the remaining bytes to the front of the buffer. */
+        ( void ) memmove( pContext->networkBuffer.pBuffer,
+                          &( pContext->networkBuffer.pBuffer[ totalMQTTPacketLength ] ),
+                          pContext->index );
+    }
+
+    if( status == MQTTNoDataAvailable )
+    {
+        /* No data available is not an error. Reset to MQTTSuccess so the
+         * return code will indicate success. */
+        status = MQTTSuccess;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t validateSubscribeUnsubscribeParams( const MQTTContext_t * pContext,
+                                                        const MQTTSubscribeInfo_t * pSubscriptionList,
+                                                        size_t subscriptionCount,
+                                                        uint16_t packetId )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    size_t iterator;
+
+    /* Validate all the parameters. */
+    if( ( pContext == NULL ) || ( pSubscriptionList == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pContext=%p, "
+                    "pSubscriptionList=%p.",
+                    ( void * ) pContext,
+                    ( void * ) pSubscriptionList ) );
+        status = MQTTBadParameter;
+    }
+    else if( subscriptionCount == 0UL )
+    {
+        LogError( ( "Subscription count is 0." ) );
+        status = MQTTBadParameter;
+    }
+    else if( packetId == 0U )
+    {
+        LogError( ( "Packet Id for subscription packet is 0." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        if( pContext->incomingPublishRecords == NULL )
+        {
+            for( iterator = 0; iterator < subscriptionCount; iterator++ )
+            {
+                if( pSubscriptionList->qos > MQTTQoS0 )
+                {
+                    LogError( ( "The incoming publish record list is not "
+                                "initialised for QoS1/QoS2 records. Please call "
+                                " MQTT_InitStatefulQoS to enable use of QoS1 and "
+                                " QoS2 packets." ) );
+                    status = MQTTBadParameter;
+                    break;
+                }
+            }
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static size_t addEncodedStringToVector( uint8_t serailizedLength[ 2 ],
+                                        const char * const string,
+                                        uint16_t length,
+                                        TransportOutVector_t * iterator,
+                                        size_t * updatedLength )
+{
+    size_t packetLength = 0U;
+    const size_t seralizedLengthFieldSize = 2U;
+    TransportOutVector_t * pLocalIterator = iterator;
+    /* This function always adds 2 vectors. */
+    size_t vectorsAdded = 0U;
+
+    /* When length is non-zero, the string must be non-NULL. */
+    assert( ( length != 0U ) == ( string != NULL ) );
+
+    serailizedLength[ 0 ] = ( ( uint8_t ) ( ( length ) >> 8 ) );
+    serailizedLength[ 1 ] = ( ( uint8_t ) ( ( length ) & 0x00ffU ) );
+
+    /* Add the serialized length of the string first. */
+    pLocalIterator[ 0 ].iov_base = serailizedLength;
+    pLocalIterator[ 0 ].iov_len = seralizedLengthFieldSize;
+    vectorsAdded++;
+    packetLength = seralizedLengthFieldSize;
+
+    /* Sometimes the string can be NULL that is, of 0 length. In that case,
+     * only the length field should be encoded in the vector. */
+    if( ( string != NULL ) && ( length != 0U ) )
+    {
+        /* Then add the pointer to the string itself. */
+        pLocalIterator[ 1 ].iov_base = string;
+        pLocalIterator[ 1 ].iov_len = length;
+        vectorsAdded++;
+        packetLength += length;
+    }
+
+    ( *updatedLength ) = ( *updatedLength ) + packetLength;
+
+    return vectorsAdded;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t sendSubscribeWithoutCopy( MQTTContext_t * pContext,
+                                              const MQTTSubscribeInfo_t * pSubscriptionList,
+                                              size_t subscriptionCount,
+                                              uint16_t packetId,
+                                              size_t remainingLength )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    uint8_t subscribeheader[ 7 ];
+    uint8_t * pIndex;
+    TransportOutVector_t pIoVector[ MQTT_SUB_UNSUB_MAX_VECTORS ];
+    TransportOutVector_t * pIterator;
+    uint8_t serializedTopicFieldLength[ MQTT_SUB_UNSUB_MAX_VECTORS ][ 2 ];
+    size_t totalPacketLength = 0U;
+    size_t ioVectorLength = 0U;
+    size_t subscriptionsSent = 0U;
+    /* For subscribe, only three vector slots are required per topic string. */
+    const size_t subscriptionStringVectorSlots = 3U;
+    size_t vectorsAdded;
+    size_t topicFieldLengthIndex;
+
+    /* The vector array should be at least three element long as the topic
+     * string needs these many vector elements to be stored. */
+    assert( MQTT_SUB_UNSUB_MAX_VECTORS >= subscriptionStringVectorSlots );
+
+    pIndex = subscribeheader;
+    pIterator = pIoVector;
+
+    pIndex = MQTT_SerializeSubscribeHeader( remainingLength,
+                                            pIndex,
+                                            packetId );
+
+    /* The header is to be sent first. */
+    pIterator->iov_base = subscribeheader;
+    /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */
+    /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */
+    /* coverity[misra_c_2012_rule_18_2_violation] */
+    /* coverity[misra_c_2012_rule_10_8_violation] */
+    pIterator->iov_len = ( size_t ) ( pIndex - subscribeheader );
+    totalPacketLength += pIterator->iov_len;
+    pIterator++;
+    ioVectorLength++;
+
+    while( ( status == MQTTSuccess ) && ( subscriptionsSent < subscriptionCount ) )
+    {
+        /* Reset the index for next iteration. */
+        topicFieldLengthIndex = 0;
+
+        /* Check whether the subscription topic (with QoS) will fit in the
+         * given vector. */
+        while( ( ioVectorLength <= ( MQTT_SUB_UNSUB_MAX_VECTORS - subscriptionStringVectorSlots ) ) &&
+               ( subscriptionsSent < subscriptionCount ) )
+        {
+            /* The topic filter gets sent next. */
+            vectorsAdded = addEncodedStringToVector( serializedTopicFieldLength[ topicFieldLengthIndex ],
+                                                     pSubscriptionList[ subscriptionsSent ].pTopicFilter,
+                                                     pSubscriptionList[ subscriptionsSent ].topicFilterLength,
+                                                     pIterator,
+                                                     &totalPacketLength );
+
+            /* Update the pointer after the above operation. */
+            pIterator = &pIterator[ vectorsAdded ];
+
+            /* Lastly, the QoS gets sent. */
+            pIterator->iov_base = &( pSubscriptionList[ subscriptionsSent ].qos );
+            pIterator->iov_len = 1U;
+            totalPacketLength += pIterator->iov_len;
+
+            /* Increment the pointer. */
+            pIterator++;
+
+            /* Two slots get used by the topic string length and topic string.
+             * One slot gets used by the quality of service. */
+            ioVectorLength += vectorsAdded + 1U;
+
+            subscriptionsSent++;
+
+            /* The index needs to be updated for next iteration. */
+            topicFieldLengthIndex++;
+        }
+
+        if( sendMessageVector( pContext,
+                               pIoVector,
+                               ioVectorLength ) != ( int32_t ) totalPacketLength )
+        {
+            status = MQTTSendFailed;
+        }
+
+        /* Update the iterator for the next potential loop iteration. */
+        pIterator = pIoVector;
+        /* Reset the vector length for the next potential loop iteration. */
+        ioVectorLength = 0U;
+        /* Reset the packet length for the next potential loop iteration. */
+        totalPacketLength = 0U;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t sendUnsubscribeWithoutCopy( MQTTContext_t * pContext,
+                                                const MQTTSubscribeInfo_t * pSubscriptionList,
+                                                size_t subscriptionCount,
+                                                uint16_t packetId,
+                                                size_t remainingLength )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    uint8_t unsubscribeheader[ 7 ];
+    uint8_t * pIndex;
+    TransportOutVector_t pIoVector[ MQTT_SUB_UNSUB_MAX_VECTORS ];
+    TransportOutVector_t * pIterator;
+    uint8_t serializedTopicFieldLength[ MQTT_SUB_UNSUB_MAX_VECTORS ][ 2 ];
+    size_t totalPacketLength = 0U;
+    size_t unsubscriptionsSent = 0U;
+    size_t ioVectorLength = 0U;
+    /* For unsubscribe, only two vector slots are required per topic string. */
+    const size_t unsubscribeStringVectorSlots = 2U;
+    size_t vectorsAdded;
+    size_t topicFieldLengthIndex;
+
+    /* The vector array should be at least three element long as the topic
+     * string needs these many vector elements to be stored. */
+    assert( MQTT_SUB_UNSUB_MAX_VECTORS >= unsubscribeStringVectorSlots );
+
+    pIndex = unsubscribeheader;
+    pIterator = pIoVector;
+
+    pIndex = MQTT_SerializeUnsubscribeHeader( remainingLength,
+                                              pIndex,
+                                              packetId );
+
+    /* The header is to be sent first. */
+    pIterator->iov_base = unsubscribeheader;
+    /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */
+    /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */
+    /* coverity[misra_c_2012_rule_18_2_violation] */
+    /* coverity[misra_c_2012_rule_10_8_violation] */
+    pIterator->iov_len = ( size_t ) ( pIndex - unsubscribeheader );
+    totalPacketLength += pIterator->iov_len;
+    pIterator++;
+    ioVectorLength++;
+
+    while( ( status == MQTTSuccess ) && ( unsubscriptionsSent < subscriptionCount ) )
+    {
+        /* Reset the index for next iteration. */
+        topicFieldLengthIndex = 0;
+
+        /* Check whether the subscription topic will fit in the given vector. */
+        while( ( ioVectorLength <= ( MQTT_SUB_UNSUB_MAX_VECTORS - unsubscribeStringVectorSlots ) ) &&
+               ( unsubscriptionsSent < subscriptionCount ) )
+        {
+            /* The topic filter gets sent next. */
+            vectorsAdded = addEncodedStringToVector( serializedTopicFieldLength[ topicFieldLengthIndex ],
+                                                     pSubscriptionList[ unsubscriptionsSent ].pTopicFilter,
+                                                     pSubscriptionList[ unsubscriptionsSent ].topicFilterLength,
+                                                     pIterator,
+                                                     &totalPacketLength );
+
+            /* Update the iterator to point to the next empty location. */
+            pIterator = &pIterator[ vectorsAdded ];
+            /* Update the total count based on how many vectors were added. */
+            ioVectorLength += vectorsAdded;
+
+            unsubscriptionsSent++;
+
+            /* Update the index for next iteration. */
+            topicFieldLengthIndex++;
+        }
+
+        if( sendMessageVector( pContext, pIoVector, ioVectorLength ) != ( int32_t ) totalPacketLength )
+        {
+            status = MQTTSendFailed;
+        }
+
+        /* Update the iterator for the next potential loop iteration. */
+        pIterator = pIoVector;
+        /* Reset the vector length for the next potential loop iteration. */
+        ioVectorLength = 0U;
+        /* Reset the packet length for the next potential loop iteration. */
+        totalPacketLength = 0U;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t sendPublishWithoutCopy( MQTTContext_t * pContext,
+                                            const MQTTPublishInfo_t * pPublishInfo,
+                                            const uint8_t * pMqttHeader,
+                                            size_t headerSize,
+                                            uint16_t packetId )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    uint8_t serializedPacketID[ 2 ];
+    TransportOutVector_t pIoVector[ 4 ];
+    size_t ioVectorLength;
+    size_t totalMessageLength;
+    const size_t packetIDLength = 2U;
+
+    /* The header is sent first. */
+    pIoVector[ 0U ].iov_base = pMqttHeader;
+    pIoVector[ 0U ].iov_len = headerSize;
+    totalMessageLength = headerSize;
+
+    /* Then the topic name has to be sent. */
+    pIoVector[ 1U ].iov_base = pPublishInfo->pTopicName;
+    pIoVector[ 1U ].iov_len = pPublishInfo->topicNameLength;
+    totalMessageLength += pPublishInfo->topicNameLength;
+
+    /* The next field's index should be 2 as the first two fields
+     * have been filled in. */
+    ioVectorLength = 2U;
+
+    if( pPublishInfo->qos > MQTTQoS0 )
+    {
+        /* Encode the packet ID. */
+        serializedPacketID[ 0 ] = ( ( uint8_t ) ( ( packetId ) >> 8 ) );
+        serializedPacketID[ 1 ] = ( ( uint8_t ) ( ( packetId ) & 0x00ffU ) );
+
+        pIoVector[ ioVectorLength ].iov_base = serializedPacketID;
+        pIoVector[ ioVectorLength ].iov_len = packetIDLength;
+
+        ioVectorLength++;
+        totalMessageLength += packetIDLength;
+    }
+
+    /* Publish packets are allowed to contain no payload. */
+    if( pPublishInfo->payloadLength > 0U )
+    {
+        pIoVector[ ioVectorLength ].iov_base = pPublishInfo->pPayload;
+        pIoVector[ ioVectorLength ].iov_len = pPublishInfo->payloadLength;
+
+        ioVectorLength++;
+        totalMessageLength += pPublishInfo->payloadLength;
+    }
+
+    if( sendMessageVector( pContext, pIoVector, ioVectorLength ) != ( int32_t ) totalMessageLength )
+    {
+        status = MQTTSendFailed;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t sendConnectWithoutCopy( MQTTContext_t * pContext,
+                                            const MQTTConnectInfo_t * pConnectInfo,
+                                            const MQTTPublishInfo_t * pWillInfo,
+                                            size_t remainingLength )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    TransportOutVector_t * iterator;
+    size_t ioVectorLength = 0U;
+    size_t totalMessageLength = 0U;
+    int32_t bytesSentOrError;
+
+    /* Connect packet header can be of maximum 15 bytes. */
+    uint8_t connectPacketHeader[ 15 ];
+    uint8_t * pIndex = connectPacketHeader;
+    TransportOutVector_t pIoVector[ 11 ];
+    uint8_t serializedClientIDLength[ 2 ];
+    uint8_t serializedTopicLength[ 2 ];
+    uint8_t serializedPayloadLength[ 2 ];
+    uint8_t serializedUsernameLength[ 2 ];
+    uint8_t serializedPasswordLength[ 2 ];
+    size_t vectorsAdded;
+
+    iterator = pIoVector;
+
+    /* Validate arguments. */
+    if( ( pWillInfo != NULL ) && ( pWillInfo->pTopicName == NULL ) )
+    {
+        LogError( ( "pWillInfo->pTopicName cannot be NULL if Will is present." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        pIndex = MQTT_SerializeConnectFixedHeader( pIndex,
+                                                   pConnectInfo,
+                                                   pWillInfo,
+                                                   remainingLength );
+
+        assert( ( pIndex - connectPacketHeader ) <= 15 );
+
+        /* The header gets sent first. */
+        iterator->iov_base = connectPacketHeader;
+        /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-182 */
+        /* More details at: https://github.com/FreeRTOS/coreMQTT/blob/main/MISRA.md#rule-108 */
+        /* coverity[misra_c_2012_rule_18_2_violation] */
+        /* coverity[misra_c_2012_rule_10_8_violation] */
+        iterator->iov_len = ( size_t ) ( pIndex - connectPacketHeader );
+        totalMessageLength += iterator->iov_len;
+        iterator++;
+        ioVectorLength++;
+
+        /* Serialize the client ID. */
+        vectorsAdded = addEncodedStringToVector( serializedClientIDLength,
+                                                 pConnectInfo->pClientIdentifier,
+                                                 pConnectInfo->clientIdentifierLength,
+                                                 iterator,
+                                                 &totalMessageLength );
+
+        /* Update the iterator to point to the next empty slot. */
+        iterator = &iterator[ vectorsAdded ];
+        ioVectorLength += vectorsAdded;
+
+        if( pWillInfo != NULL )
+        {
+            /* Serialize the topic. */
+            vectorsAdded = addEncodedStringToVector( serializedTopicLength,
+                                                     pWillInfo->pTopicName,
+                                                     pWillInfo->topicNameLength,
+                                                     iterator,
+                                                     &totalMessageLength );
+
+            /* Update the iterator to point to the next empty slot. */
+            iterator = &iterator[ vectorsAdded ];
+            ioVectorLength += vectorsAdded;
+
+
+            /* Serialize the payload. Payload of last will and testament can be NULL. */
+            vectorsAdded = addEncodedStringToVector( serializedPayloadLength,
+                                                     pWillInfo->pPayload,
+                                                     ( uint16_t ) pWillInfo->payloadLength,
+                                                     iterator,
+                                                     &totalMessageLength );
+
+            /* Update the iterator to point to the next empty slot. */
+            iterator = &iterator[ vectorsAdded ];
+            ioVectorLength += vectorsAdded;
+        }
+
+        /* Encode the user name if provided. */
+        if( pConnectInfo->pUserName != NULL )
+        {
+            /* Serialize the user name string. */
+            vectorsAdded = addEncodedStringToVector( serializedUsernameLength,
+                                                     pConnectInfo->pUserName,
+                                                     pConnectInfo->userNameLength,
+                                                     iterator,
+                                                     &totalMessageLength );
+
+            /* Update the iterator to point to the next empty slot. */
+            iterator = &iterator[ vectorsAdded ];
+            ioVectorLength += vectorsAdded;
+        }
+
+        /* Encode the password if provided. */
+        if( pConnectInfo->pPassword != NULL )
+        {
+            /* Serialize the user name string. */
+            vectorsAdded = addEncodedStringToVector( serializedPasswordLength,
+                                                     pConnectInfo->pPassword,
+                                                     pConnectInfo->passwordLength,
+                                                     iterator,
+                                                     &totalMessageLength );
+            /* Update the iterator to point to the next empty slot. */
+            iterator = &iterator[ vectorsAdded ];
+            ioVectorLength += vectorsAdded;
+        }
+
+        bytesSentOrError = sendMessageVector( pContext, pIoVector, ioVectorLength );
+
+        if( bytesSentOrError != ( int32_t ) totalMessageLength )
+        {
+            status = MQTTSendFailed;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t receiveConnack( const MQTTContext_t * pContext,
+                                    uint32_t timeoutMs,
+                                    bool cleanSession,
+                                    MQTTPacketInfo_t * pIncomingPacket,
+                                    bool * pSessionPresent )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    MQTTGetCurrentTimeFunc_t getTimeStamp = NULL;
+    uint32_t entryTimeMs = 0U, remainingTimeMs = 0U, timeTakenMs = 0U;
+    bool breakFromLoop = false;
+    uint16_t loopCount = 0U;
+
+    assert( pContext != NULL );
+    assert( pIncomingPacket != NULL );
+    assert( pContext->getTime != NULL );
+
+    getTimeStamp = pContext->getTime;
+
+    /* Get the entry time for the function. */
+    entryTimeMs = getTimeStamp();
+
+    do
+    {
+        /* Transport read for incoming CONNACK packet type and length.
+         * MQTT_GetIncomingPacketTypeAndLength is a blocking call and it is
+         * returned after a transport receive timeout, an error, or a successful
+         * receive of packet type and length. */
+        status = MQTT_GetIncomingPacketTypeAndLength( pContext->transportInterface.recv,
+                                                      pContext->transportInterface.pNetworkContext,
+                                                      pIncomingPacket );
+
+        /* The loop times out based on 2 conditions.
+         * 1. If timeoutMs is greater than 0:
+         *    Loop times out based on the timeout calculated by getTime()
+         *    function.
+         * 2. If timeoutMs is 0:
+         *    Loop times out based on the maximum number of retries config
+         *    MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT. This config will control
+         *    maximum the number of retry attempts to read the CONNACK packet.
+         *    A value of 0 for the config will try once to read CONNACK. */
+        if( timeoutMs > 0U )
+        {
+            breakFromLoop = calculateElapsedTime( getTimeStamp(), entryTimeMs ) >= timeoutMs;
+        }
+        else
+        {
+            breakFromLoop = loopCount >= MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT;
+            loopCount++;
+        }
+
+        /* Loop until there is data to read or if we have exceeded the timeout/retries. */
+    } while( ( status == MQTTNoDataAvailable ) && ( breakFromLoop == false ) );
+
+    if( status == MQTTSuccess )
+    {
+        /* Time taken in this function so far. */
+        timeTakenMs = calculateElapsedTime( getTimeStamp(), entryTimeMs );
+
+        if( timeTakenMs < timeoutMs )
+        {
+            /* Calculate remaining time for receiving the remainder of
+             * the packet. */
+            remainingTimeMs = timeoutMs - timeTakenMs;
+        }
+
+        /* Reading the remainder of the packet by transport recv.
+         * Attempt to read once even if the timeout has expired.
+         * Invoking receivePacket with remainingTime as 0 would attempt to
+         * recv from network once. If using retries, the remainder of the
+         * CONNACK packet is tried to be read only once. Reading once would be
+         * good as the packet type and remaining length was already read. Hence,
+         * the probability of the remaining 2 bytes available to read is very high. */
+        if( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK )
+        {
+            status = receivePacket( pContext,
+                                    *pIncomingPacket,
+                                    remainingTimeMs );
+        }
+        else
+        {
+            LogError( ( "Incorrect packet type %X received while expecting"
+                        " CONNACK(%X).",
+                        ( unsigned int ) pIncomingPacket->type,
+                        MQTT_PACKET_TYPE_CONNACK ) );
+            status = MQTTBadResponse;
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Update the packet info pointer to the buffer read. */
+        pIncomingPacket->pRemainingData = pContext->networkBuffer.pBuffer;
+
+        /* Deserialize CONNACK. */
+        status = MQTT_DeserializeAck( pIncomingPacket, NULL, pSessionPresent );
+    }
+
+    /* If a clean session is requested, a session present should not be set by
+     * broker. */
+    if( status == MQTTSuccess )
+    {
+        if( ( cleanSession == true ) && ( *pSessionPresent == true ) )
+        {
+            LogError( ( "Unexpected session present flag in CONNACK response from broker."
+                        " CONNECT request with clean session was made with broker." ) );
+            status = MQTTBadResponse;
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        LogDebug( ( "Received MQTT CONNACK successfully from broker." ) );
+    }
+    else
+    {
+        LogError( ( "CONNACK recv failed with status = %s.",
+                    MQTT_Status_strerror( status ) ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t handleSessionResumption( MQTTContext_t * pContext,
+                                             bool sessionPresent )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    MQTTStateCursor_t cursor = MQTT_STATE_CURSOR_INITIALIZER;
+    uint16_t packetId = MQTT_PACKET_ID_INVALID;
+    MQTTPublishState_t state = MQTTStateNull;
+
+    assert( pContext != NULL );
+
+    /* Reset the index and clear the buffer when a new session is established. */
+    pContext->index = 0;
+    ( void ) memset( pContext->networkBuffer.pBuffer, 0, pContext->networkBuffer.size );
+
+    if( sessionPresent == true )
+    {
+        /* Get the next packet ID for which a PUBREL need to be resent. */
+        packetId = MQTT_PubrelToResend( pContext, &cursor, &state );
+
+        /* Resend all the PUBREL acks after session is reestablished. */
+        while( ( packetId != MQTT_PACKET_ID_INVALID ) &&
+               ( status == MQTTSuccess ) )
+        {
+            status = sendPublishAcks( pContext, packetId, state );
+
+            packetId = MQTT_PubrelToResend( pContext, &cursor, &state );
+        }
+    }
+    else
+    {
+        /* Clear any existing records if a new session is established. */
+        if( pContext->outgoingPublishRecordMaxCount > 0U )
+        {
+            ( void ) memset( pContext->outgoingPublishRecords,
+                             0x00,
+                             pContext->outgoingPublishRecordMaxCount * sizeof( *pContext->outgoingPublishRecords ) );
+        }
+
+        if( pContext->incomingPublishRecordMaxCount > 0U )
+        {
+            ( void ) memset( pContext->incomingPublishRecords,
+                             0x00,
+                             pContext->incomingPublishRecordMaxCount * sizeof( *pContext->incomingPublishRecords ) );
+        }
+    }
+
+    return status;
+}
+
+static MQTTStatus_t validatePublishParams( const MQTTContext_t * pContext,
+                                           const MQTTPublishInfo_t * pPublishInfo,
+                                           uint16_t packetId )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    /* Validate arguments. */
+    if( ( pContext == NULL ) || ( pPublishInfo == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pContext=%p, "
+                    "pPublishInfo=%p.",
+                    ( void * ) pContext,
+                    ( void * ) pPublishInfo ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pPublishInfo->qos != MQTTQoS0 ) && ( packetId == 0U ) )
+    {
+        LogError( ( "Packet Id is 0 for PUBLISH with QoS=%u.",
+                    ( unsigned int ) pPublishInfo->qos ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pPublishInfo->payloadLength > 0U ) && ( pPublishInfo->pPayload == NULL ) )
+    {
+        LogError( ( "A nonzero payload length requires a non-NULL payload: "
+                    "payloadLength=%lu, pPayload=%p.",
+                    ( unsigned long ) pPublishInfo->payloadLength,
+                    pPublishInfo->pPayload ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pContext->outgoingPublishRecords == NULL ) && ( pPublishInfo->qos > MQTTQoS0 ) )
+    {
+        LogError( ( "Trying to publish a QoS > MQTTQoS0 packet when outgoing publishes "
+                    "for QoS1/QoS2 have not been enabled. Please, call MQTT_InitStatefulQoS "
+                    "to initialize and enable the use of QoS1/QoS2 publishes." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* MISRA else */
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_Init( MQTTContext_t * pContext,
+                        const TransportInterface_t * pTransportInterface,
+                        MQTTGetCurrentTimeFunc_t getTimeFunction,
+                        MQTTEventCallback_t userCallback,
+                        const MQTTFixedBuffer_t * pNetworkBuffer )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    /* Validate arguments. */
+    if( ( pContext == NULL ) || ( pTransportInterface == NULL ) ||
+        ( pNetworkBuffer == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pContext=%p, "
+                    "pTransportInterface=%p, "
+                    "pNetworkBuffer=%p",
+                    ( void * ) pContext,
+                    ( void * ) pTransportInterface,
+                    ( void * ) pNetworkBuffer ) );
+        status = MQTTBadParameter;
+    }
+    else if( getTimeFunction == NULL )
+    {
+        LogError( ( "Invalid parameter: getTimeFunction is NULL" ) );
+        status = MQTTBadParameter;
+    }
+    else if( userCallback == NULL )
+    {
+        LogError( ( "Invalid parameter: userCallback is NULL" ) );
+        status = MQTTBadParameter;
+    }
+    else if( pTransportInterface->recv == NULL )
+    {
+        LogError( ( "Invalid parameter: pTransportInterface->recv is NULL" ) );
+        status = MQTTBadParameter;
+    }
+    else if( pTransportInterface->send == NULL )
+    {
+        LogError( ( "Invalid parameter: pTransportInterface->send is NULL" ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        ( void ) memset( pContext, 0x00, sizeof( MQTTContext_t ) );
+
+        pContext->connectStatus = MQTTNotConnected;
+        pContext->transportInterface = *pTransportInterface;
+        pContext->getTime = getTimeFunction;
+        pContext->appCallback = userCallback;
+        pContext->networkBuffer = *pNetworkBuffer;
+
+        /* Zero is not a valid packet ID per MQTT spec. Start from 1. */
+        pContext->nextPacketId = 1;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext,
+                                   MQTTPubAckInfo_t * pOutgoingPublishRecords,
+                                   size_t outgoingPublishCount,
+                                   MQTTPubAckInfo_t * pIncomingPublishRecords,
+                                   size_t incomingPublishCount )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( pContext == NULL )
+    {
+        LogError( ( "Argument cannot be NULL: pContext=%p\n",
+                    ( void * ) pContext ) );
+        status = MQTTBadParameter;
+    }
+
+    /* Check whether the arguments make sense. Not equal here behaves
+     * like an exclusive-or operator for boolean values. */
+    else if( ( outgoingPublishCount == 0U ) !=
+             ( pOutgoingPublishRecords == NULL ) )
+    {
+        LogError( ( "Arguments do not match: pOutgoingPublishRecords=%p, "
+                    "outgoingPublishCount=%lu",
+                    ( void * ) pOutgoingPublishRecords,
+                    outgoingPublishCount ) );
+        status = MQTTBadParameter;
+    }
+
+    /* Check whether the arguments make sense. Not equal here behaves
+     * like an exclusive-or operator for boolean values. */
+    else if( ( incomingPublishCount == 0U ) !=
+             ( pIncomingPublishRecords == NULL ) )
+    {
+        LogError( ( "Arguments do not match: pIncomingPublishRecords=%p, "
+                    "incomingPublishCount=%lu",
+                    ( void * ) pIncomingPublishRecords,
+                    incomingPublishCount ) );
+        status = MQTTBadParameter;
+    }
+    else if( pContext->appCallback == NULL )
+    {
+        LogError( ( "MQTT_InitStatefulQoS must be called only after MQTT_Init has"
+                    " been called succesfully.\n" ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        pContext->incomingPublishRecordMaxCount = incomingPublishCount;
+        pContext->incomingPublishRecords = pIncomingPublishRecords;
+        pContext->outgoingPublishRecordMaxCount = outgoingPublishCount;
+        pContext->outgoingPublishRecords = pOutgoingPublishRecords;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_CancelCallback( const MQTTContext_t * pContext,
+                                  uint16_t packetId )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( pContext == NULL )
+    {
+        LogWarn( ( "pContext is NULL\n" ) );
+        status = MQTTBadParameter;
+    }
+    else if( pContext->outgoingPublishRecords == NULL )
+    {
+        LogError( ( "QoS1/QoS2 is not initialized for use. Please, "
+                    "call MQTT_InitStatefulQoS to enable QoS1 and QoS2 "
+                    "publishes.\n" ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        MQTT_PRE_STATE_UPDATE_HOOK( pContext );
+
+        status = MQTT_RemoveStateRecord( pContext,
+                                         packetId );
+
+        MQTT_POST_STATE_UPDATE_HOOK( pContext );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext,
+                           const MQTTConnectInfo_t * pConnectInfo,
+                           const MQTTPublishInfo_t * pWillInfo,
+                           uint32_t timeoutMs,
+                           bool * pSessionPresent )
+{
+    size_t remainingLength = 0UL, packetSize = 0UL;
+    MQTTStatus_t status = MQTTSuccess;
+    MQTTPacketInfo_t incomingPacket = { 0 };
+
+    incomingPacket.type = ( uint8_t ) 0;
+
+    if( ( pContext == NULL ) || ( pConnectInfo == NULL ) || ( pSessionPresent == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pContext=%p, "
+                    "pConnectInfo=%p, pSessionPresent=%p.",
+                    ( void * ) pContext,
+                    ( void * ) pConnectInfo,
+                    ( void * ) pSessionPresent ) );
+        status = MQTTBadParameter;
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Get MQTT connect packet size and remaining length. */
+        status = MQTT_GetConnectPacketSize( pConnectInfo,
+                                            pWillInfo,
+                                            &remainingLength,
+                                            &packetSize );
+        LogDebug( ( "CONNECT packet size is %lu and remaining length is %lu.",
+                    ( unsigned long ) packetSize,
+                    ( unsigned long ) remainingLength ) );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        MQTT_PRE_SEND_HOOK( pContext );
+
+        status = sendConnectWithoutCopy( pContext,
+                                         pConnectInfo,
+                                         pWillInfo,
+                                         remainingLength );
+
+        MQTT_POST_SEND_HOOK( pContext );
+    }
+
+    /* Read CONNACK from transport layer. */
+    if( status == MQTTSuccess )
+    {
+        status = receiveConnack( pContext,
+                                 timeoutMs,
+                                 pConnectInfo->cleanSession,
+                                 &incomingPacket,
+                                 pSessionPresent );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Resend PUBRELs when reestablishing a session, or clear records for new sessions. */
+        status = handleSessionResumption( pContext, *pSessionPresent );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        LogInfo( ( "MQTT connection established with the broker." ) );
+        pContext->connectStatus = MQTTConnected;
+        /* Initialize keep-alive fields after a successful connection. */
+        pContext->keepAliveIntervalSec = pConnectInfo->keepAliveSeconds;
+        pContext->waitingForPingResp = false;
+        pContext->pingReqSendTimeMs = 0U;
+    }
+    else
+    {
+        LogError( ( "MQTT connection failed with status = %s.",
+                    MQTT_Status_strerror( status ) ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext,
+                             const MQTTSubscribeInfo_t * pSubscriptionList,
+                             size_t subscriptionCount,
+                             uint16_t packetId )
+{
+    size_t remainingLength = 0UL, packetSize = 0UL;
+
+    /* Validate arguments. */
+    MQTTStatus_t status = validateSubscribeUnsubscribeParams( pContext,
+                                                              pSubscriptionList,
+                                                              subscriptionCount,
+                                                              packetId );
+
+    if( status == MQTTSuccess )
+    {
+        /* Get the remaining length and packet size.*/
+        status = MQTT_GetSubscribePacketSize( pSubscriptionList,
+                                              subscriptionCount,
+                                              &remainingLength,
+                                              &packetSize );
+        LogDebug( ( "SUBSCRIBE packet size is %lu and remaining length is %lu.",
+                    ( unsigned long ) packetSize,
+                    ( unsigned long ) remainingLength ) );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        MQTT_PRE_SEND_HOOK( pContext );
+
+        /* Send MQTT SUBSCRIBE packet. */
+        status = sendSubscribeWithoutCopy( pContext,
+                                           pSubscriptionList,
+                                           subscriptionCount,
+                                           packetId,
+                                           remainingLength );
+
+        MQTT_POST_SEND_HOOK( pContext );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext,
+                           const MQTTPublishInfo_t * pPublishInfo,
+                           uint16_t packetId )
+{
+    size_t headerSize = 0UL;
+    size_t remainingLength = 0UL;
+    size_t packetSize = 0UL;
+    MQTTPublishState_t publishStatus = MQTTStateNull;
+    bool stateUpdateHookExecuted = false;
+
+    /* 1 header byte + 4 bytes (maximum) required for encoding the length +
+     * 2 bytes for topic string. */
+    uint8_t mqttHeader[ 7 ];
+
+    /* Validate arguments. */
+    MQTTStatus_t status = validatePublishParams( pContext, pPublishInfo, packetId );
+
+    if( status == MQTTSuccess )
+    {
+        /* Get the remaining length and packet size.*/
+        status = MQTT_GetPublishPacketSize( pPublishInfo,
+                                            &remainingLength,
+                                            &packetSize );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        status = MQTT_SerializePublishHeaderWithoutTopic( pPublishInfo,
+                                                          remainingLength,
+                                                          mqttHeader,
+                                                          &headerSize );
+    }
+
+    if( ( status == MQTTSuccess ) && ( pPublishInfo->qos > MQTTQoS0 ) )
+    {
+        MQTT_PRE_STATE_UPDATE_HOOK( pContext );
+
+        /* Set the flag so that the corresponding hook can be called later. */
+        stateUpdateHookExecuted = true;
+
+        status = MQTT_ReserveState( pContext,
+                                    packetId,
+                                    pPublishInfo->qos );
+
+        /* State already exists for a duplicate packet.
+         * If a state doesn't exist, it will be handled as a new publish in
+         * state engine. */
+        if( ( status == MQTTStateCollision ) && ( pPublishInfo->dup == true ) )
+        {
+            status = MQTTSuccess;
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Take the mutex as multiple send calls are required for sending this
+         * packet. */
+        MQTT_PRE_SEND_HOOK( pContext );
+
+        status = sendPublishWithoutCopy( pContext,
+                                         pPublishInfo,
+                                         mqttHeader,
+                                         headerSize,
+                                         packetId );
+
+        /* Give the mutex away for the next taker. */
+        MQTT_POST_SEND_HOOK( pContext );
+    }
+
+    if( ( status == MQTTSuccess ) &&
+        ( pPublishInfo->qos > MQTTQoS0 ) )
+    {
+        /* Update state machine after PUBLISH is sent.
+         * Only to be done for QoS1 or QoS2. */
+        status = MQTT_UpdateStatePublish( pContext,
+                                          packetId,
+                                          MQTT_SEND,
+                                          pPublishInfo->qos,
+                                          &publishStatus );
+
+        if( status != MQTTSuccess )
+        {
+            LogError( ( "Update state for publish failed with status %s."
+                        " However PUBLISH packet was sent to the broker."
+                        " Any further handling of ACKs for the packet Id"
+                        " will fail.",
+                        MQTT_Status_strerror( status ) ) );
+        }
+    }
+
+    if( stateUpdateHookExecuted == true )
+    {
+        /* Regardless of the status, if the mutex was taken due to the
+         * packet being of QoS > QoS0, then it should be relinquished. */
+        MQTT_POST_STATE_UPDATE_HOOK( pContext );
+    }
+
+    if( status != MQTTSuccess )
+    {
+        LogError( ( "MQTT PUBLISH failed with status %s.",
+                    MQTT_Status_strerror( status ) ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext )
+{
+    int32_t sendResult = 0;
+    MQTTStatus_t status = MQTTSuccess;
+    size_t packetSize = 0U;
+    /* MQTT ping packets are of fixed length. */
+    uint8_t pingreqPacket[ 2U ];
+    MQTTFixedBuffer_t localBuffer;
+
+    localBuffer.pBuffer = pingreqPacket;
+    localBuffer.size = 2U;
+
+    if( pContext == NULL )
+    {
+        LogError( ( "pContext is NULL." ) );
+        status = MQTTBadParameter;
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Get MQTT PINGREQ packet size. */
+        status = MQTT_GetPingreqPacketSize( &packetSize );
+
+        if( status == MQTTSuccess )
+        {
+            LogDebug( ( "MQTT PINGREQ packet size is %lu.",
+                        ( unsigned long ) packetSize ) );
+        }
+        else
+        {
+            LogError( ( "Failed to get the PINGREQ packet size." ) );
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Serialize MQTT PINGREQ. */
+        status = MQTT_SerializePingreq( &localBuffer );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Take the mutex as the send call should not be interrupted in
+         * between. */
+        MQTT_PRE_SEND_HOOK( pContext );
+
+        /* Send the serialized PINGREQ packet to transport layer.
+         * Here, we do not use the vectored IO approach for efficiency as the
+         * Ping packet does not have numerous fields which need to be copied
+         * from the user provided buffers. Thus it can be sent directly. */
+        sendResult = sendBuffer( pContext,
+                                 localBuffer.pBuffer,
+                                 2U );
+
+        /* Give the mutex away. */
+        MQTT_POST_SEND_HOOK( pContext );
+
+        /* It is an error to not send the entire PINGREQ packet. */
+        if( sendResult < ( int32_t ) packetSize )
+        {
+            LogError( ( "Transport send failed for PINGREQ packet." ) );
+            status = MQTTSendFailed;
+        }
+        else
+        {
+            pContext->pingReqSendTimeMs = pContext->lastPacketTxTime;
+            pContext->waitingForPingResp = true;
+            LogDebug( ( "Sent %ld bytes of PINGREQ packet.",
+                        ( long int ) sendResult ) );
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext,
+                               const MQTTSubscribeInfo_t * pSubscriptionList,
+                               size_t subscriptionCount,
+                               uint16_t packetId )
+{
+    size_t remainingLength = 0UL, packetSize = 0UL;
+
+    /* Validate arguments. */
+    MQTTStatus_t status = validateSubscribeUnsubscribeParams( pContext,
+                                                              pSubscriptionList,
+                                                              subscriptionCount,
+                                                              packetId );
+
+    if( status == MQTTSuccess )
+    {
+        /* Get the remaining length and packet size.*/
+        status = MQTT_GetUnsubscribePacketSize( pSubscriptionList,
+                                                subscriptionCount,
+                                                &remainingLength,
+                                                &packetSize );
+        LogDebug( ( "UNSUBSCRIBE packet size is %lu and remaining length is %lu.",
+                    ( unsigned long ) packetSize,
+                    ( unsigned long ) remainingLength ) );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Take the mutex because the below call should not be interrupted. */
+        MQTT_PRE_SEND_HOOK( pContext );
+
+        status = sendUnsubscribeWithoutCopy( pContext,
+                                             pSubscriptionList,
+                                             subscriptionCount,
+                                             packetId,
+                                             remainingLength );
+
+        /* Give the mutex away. */
+        MQTT_POST_SEND_HOOK( pContext );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext )
+{
+    size_t packetSize = 0U;
+    int32_t sendResult = 0;
+    MQTTStatus_t status = MQTTSuccess;
+    MQTTFixedBuffer_t localBuffer;
+    uint8_t disconnectPacket[ 2U ];
+
+    localBuffer.pBuffer = disconnectPacket;
+    localBuffer.size = 2U;
+
+    /* Validate arguments. */
+    if( pContext == NULL )
+    {
+        LogError( ( "pContext cannot be NULL." ) );
+        status = MQTTBadParameter;
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Get MQTT DISCONNECT packet size. */
+        status = MQTT_GetDisconnectPacketSize( &packetSize );
+        LogDebug( ( "MQTT DISCONNECT packet size is %lu.",
+                    ( unsigned long ) packetSize ) );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Serialize MQTT DISCONNECT packet. */
+        status = MQTT_SerializeDisconnect( &localBuffer );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Take the mutex because the below call should not be interrupted. */
+        MQTT_PRE_SEND_HOOK( pContext );
+
+        /* Here we do not use vectors as the disconnect packet has fixed fields
+         * which do not reside in user provided buffers. Thus, it can be sent
+         * using a simple send call. */
+        sendResult = sendBuffer( pContext,
+                                 localBuffer.pBuffer,
+                                 packetSize );
+
+        /* Give the mutex away. */
+        MQTT_POST_SEND_HOOK( pContext );
+
+        if( sendResult < ( int32_t ) packetSize )
+        {
+            LogError( ( "Transport send failed for DISCONNECT packet." ) );
+            status = MQTTSendFailed;
+        }
+        else
+        {
+            LogDebug( ( "Sent %ld bytes of DISCONNECT packet.",
+                        ( long int ) sendResult ) );
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        LogInfo( ( "Disconnected from the broker." ) );
+        pContext->connectStatus = MQTTNotConnected;
+
+        /* Reset the index and clean the buffer on a successful disconnect. */
+        pContext->index = 0;
+        ( void ) memset( pContext->networkBuffer.pBuffer, 0, pContext->networkBuffer.size );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_ProcessLoop( MQTTContext_t * pContext )
+{
+    MQTTStatus_t status = MQTTBadParameter;
+
+    if( pContext == NULL )
+    {
+        LogError( ( "Invalid input parameter: MQTT Context cannot be NULL." ) );
+    }
+    else if( pContext->getTime == NULL )
+    {
+        LogError( ( "Invalid input parameter: MQTT Context must have valid getTime." ) );
+    }
+    else if( pContext->networkBuffer.pBuffer == NULL )
+    {
+        LogError( ( "Invalid input parameter: The MQTT context's networkBuffer must not be NULL." ) );
+    }
+    else
+    {
+        pContext->controlPacketSent = false;
+        status = receiveSingleIteration( pContext, true );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_ReceiveLoop( MQTTContext_t * pContext )
+{
+    MQTTStatus_t status = MQTTBadParameter;
+
+    if( pContext == NULL )
+    {
+        LogError( ( "Invalid input parameter: MQTT Context cannot be NULL." ) );
+    }
+    else if( pContext->getTime == NULL )
+    {
+        LogError( ( "Invalid input parameter: MQTT Context must have a valid getTime function." ) );
+    }
+    else if( pContext->networkBuffer.pBuffer == NULL )
+    {
+        LogError( ( "Invalid input parameter: MQTT context's networkBuffer must not be NULL." ) );
+    }
+    else
+    {
+        status = receiveSingleIteration( pContext, false );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+uint16_t MQTT_GetPacketId( MQTTContext_t * pContext )
+{
+    uint16_t packetId = 0U;
+
+    if( pContext != NULL )
+    {
+        MQTT_PRE_STATE_UPDATE_HOOK( pContext );
+
+        packetId = pContext->nextPacketId;
+
+        /* A packet ID of zero is not a valid packet ID. When the max ID
+         * is reached the next one should start at 1. */
+        if( pContext->nextPacketId == ( uint16_t ) UINT16_MAX )
+        {
+            pContext->nextPacketId = 1;
+        }
+        else
+        {
+            pContext->nextPacketId++;
+        }
+
+        MQTT_POST_STATE_UPDATE_HOOK( pContext );
+    }
+
+    return packetId;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_MatchTopic( const char * pTopicName,
+                              const uint16_t topicNameLength,
+                              const char * pTopicFilter,
+                              const uint16_t topicFilterLength,
+                              bool * pIsMatch )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    bool topicFilterStartsWithWildcard = false;
+    bool matchStatus = false;
+
+    if( ( pTopicName == NULL ) || ( topicNameLength == 0u ) )
+    {
+        LogError( ( "Invalid paramater: Topic name should be non-NULL and its "
+                    "length should be > 0: TopicName=%p, TopicNameLength=%hu",
+                    ( void * ) pTopicName,
+                    ( unsigned short ) topicNameLength ) );
+
+        status = MQTTBadParameter;
+    }
+    else if( ( pTopicFilter == NULL ) || ( topicFilterLength == 0u ) )
+    {
+        LogError( ( "Invalid paramater: Topic filter should be non-NULL and "
+                    "its length should be > 0: TopicName=%p, TopicFilterLength=%hu",
+                    ( void * ) pTopicFilter,
+                    ( unsigned short ) topicFilterLength ) );
+        status = MQTTBadParameter;
+    }
+    else if( pIsMatch == NULL )
+    {
+        LogError( ( "Invalid paramater: Output parameter, pIsMatch, is NULL" ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Check for an exact match if the incoming topic name and the registered
+         * topic filter length match. */
+        if( topicNameLength == topicFilterLength )
+        {
+            matchStatus = strncmp( pTopicName, pTopicFilter, topicNameLength ) == 0;
+        }
+
+        if( matchStatus == false )
+        {
+            /* If an exact match was not found, match against wildcard characters in
+             * topic filter.*/
+
+            /* Determine if topic filter starts with a wildcard. */
+            topicFilterStartsWithWildcard = ( pTopicFilter[ 0 ] == '+' ) ||
+                                            ( pTopicFilter[ 0 ] == '#' );
+
+            /* Note: According to the MQTT 3.1.1 specification, incoming PUBLISH topic names
+             * starting with "$" character cannot be matched against topic filter starting with
+             * a wildcard, i.e. for example, "$SYS/sport" cannot be matched with "#" or
+             * "+/sport" topic filters. */
+            if( !( ( pTopicName[ 0 ] == '$' ) && ( topicFilterStartsWithWildcard == true ) ) )
+            {
+                matchStatus = matchTopicFilter( pTopicName, topicNameLength, pTopicFilter, topicFilterLength );
+            }
+        }
+
+        /* Update the output parameter with the match result. */
+        *pIsMatch = matchStatus;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetSubAckStatusCodes( const MQTTPacketInfo_t * pSubackPacket,
+                                        uint8_t ** pPayloadStart,
+                                        size_t * pPayloadSize )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( pSubackPacket == NULL )
+    {
+        LogError( ( "Invalid parameter: pSubackPacket is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( pPayloadStart == NULL )
+    {
+        LogError( ( "Invalid parameter: pPayloadStart is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( pPayloadSize == NULL )
+    {
+        LogError( ( "Invalid parameter: pPayloadSize is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( pSubackPacket->type != MQTT_PACKET_TYPE_SUBACK )
+    {
+        LogError( ( "Invalid parameter: Input packet is not a SUBACK packet: "
+                    "ExpectedType=%02x, InputType=%02x",
+                    ( int ) MQTT_PACKET_TYPE_SUBACK,
+                    ( int ) pSubackPacket->type ) );
+        status = MQTTBadParameter;
+    }
+    else if( pSubackPacket->pRemainingData == NULL )
+    {
+        LogError( ( "Invalid parameter: pSubackPacket->pRemainingData is NULL" ) );
+        status = MQTTBadParameter;
+    }
+
+    /* A SUBACK must have a remaining length of at least 3 to accommodate the
+     * packet identifier and at least 1 return code. */
+    else if( pSubackPacket->remainingLength < 3U )
+    {
+        LogError( ( "Invalid parameter: Packet remaining length is invalid: "
+                    "Should be greater than 2 for SUBACK packet: InputRemainingLength=%lu",
+                    ( unsigned long ) pSubackPacket->remainingLength ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* According to the MQTT 3.1.1 protocol specification, the "Remaining Length" field is a
+         * length of the variable header (2 bytes) plus the length of the payload.
+         * Therefore, we add 2 positions for the starting address of the payload, and
+         * subtract 2 bytes from the remaining length for the length of the payload.*/
+        *pPayloadStart = &pSubackPacket->pRemainingData[ sizeof( uint16_t ) ];
+        *pPayloadSize = pSubackPacket->remainingLength - sizeof( uint16_t );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+const char * MQTT_Status_strerror( MQTTStatus_t status )
+{
+    const char * str = NULL;
+
+    switch( status )
+    {
+        case MQTTSuccess:
+            str = "MQTTSuccess";
+            break;
+
+        case MQTTBadParameter:
+            str = "MQTTBadParameter";
+            break;
+
+        case MQTTNoMemory:
+            str = "MQTTNoMemory";
+            break;
+
+        case MQTTSendFailed:
+            str = "MQTTSendFailed";
+            break;
+
+        case MQTTRecvFailed:
+            str = "MQTTRecvFailed";
+            break;
+
+        case MQTTBadResponse:
+            str = "MQTTBadResponse";
+            break;
+
+        case MQTTServerRefused:
+            str = "MQTTServerRefused";
+            break;
+
+        case MQTTNoDataAvailable:
+            str = "MQTTNoDataAvailable";
+            break;
+
+        case MQTTIllegalState:
+            str = "MQTTIllegalState";
+            break;
+
+        case MQTTStateCollision:
+            str = "MQTTStateCollision";
+            break;
+
+        case MQTTKeepAliveTimeout:
+            str = "MQTTKeepAliveTimeout";
+            break;
+
+        default:
+            str = "Invalid MQTT Status code";
+            break;
+    }
+
+    return str;
+}
+
+/*-----------------------------------------------------------*/
diff --git a/project/coreMQTT/coreMQTT/core_mqtt.h b/project/coreMQTT/coreMQTT/core_mqtt.h
new file mode 100644
index 0000000..a9f14c5
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/core_mqtt.h
@@ -0,0 +1,1015 @@
+/*
+ * coreMQTT v2.1.1
+ * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_mqtt.h
+ * @brief User-facing functions of the MQTT 3.1.1 library.
+ */
+#ifndef CORE_MQTT_H
+#define CORE_MQTT_H
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/* Include MQTT serializer library. */
+#include "core_mqtt_serializer.h"
+
+/* Include transport interface. */
+#include "transport_interface.h"
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * The current version of this library.
+ */
+#define MQTT_LIBRARY_VERSION    "v2.1.1"
+/** @endcond */
+
+/**
+ * @ingroup mqtt_constants
+ * @brief Invalid packet identifier.
+ *
+ * Zero is an invalid packet identifier as per MQTT v3.1.1 spec.
+ */
+#define MQTT_PACKET_ID_INVALID    ( ( uint16_t ) 0U )
+
+/* Structures defined in this file. */
+struct MQTTPubAckInfo;
+struct MQTTContext;
+struct MQTTDeserializedInfo;
+
+/**
+ * @ingroup mqtt_callback_types
+ * @brief Application provided function to query the time elapsed since a given
+ * epoch in milliseconds.
+ *
+ * @note The timer should be a monotonic timer. It just needs to provide an
+ * incrementing count of milliseconds elapsed since a given epoch.
+ *
+ * @return The time elapsed in milliseconds.
+ */
+typedef uint32_t (* MQTTGetCurrentTimeFunc_t )( void );
+
+/**
+ * @ingroup mqtt_callback_types
+ * @brief Application callback for receiving incoming publishes and incoming
+ * acks.
+ *
+ * @note This callback will be called only if packets are deserialized with a
+ * result of #MQTTSuccess or #MQTTServerRefused. The latter can be obtained
+ * when deserializing a SUBACK, indicating a broker's rejection of a subscribe.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] pPacketInfo Information on the type of incoming MQTT packet.
+ * @param[in] pDeserializedInfo Deserialized information from incoming packet.
+ */
+typedef void (* MQTTEventCallback_t )( struct MQTTContext * pContext,
+                                       struct MQTTPacketInfo * pPacketInfo,
+                                       struct MQTTDeserializedInfo * pDeserializedInfo );
+
+/**
+ * @ingroup mqtt_enum_types
+ * @brief Values indicating if an MQTT connection exists.
+ */
+typedef enum MQTTConnectionStatus
+{
+    MQTTNotConnected, /**< @brief MQTT Connection is inactive. */
+    MQTTConnected     /**< @brief MQTT Connection is active. */
+} MQTTConnectionStatus_t;
+
+/**
+ * @ingroup mqtt_enum_types
+ * @brief The state of QoS 1 or QoS 2 MQTT publishes, used in the state engine.
+ */
+typedef enum MQTTPublishState
+{
+    MQTTStateNull = 0,  /**< @brief An empty state with no corresponding PUBLISH. */
+    MQTTPublishSend,    /**< @brief The library will send an outgoing PUBLISH packet. */
+    MQTTPubAckSend,     /**< @brief The library will send a PUBACK for a received PUBLISH. */
+    MQTTPubRecSend,     /**< @brief The library will send a PUBREC for a received PUBLISH. */
+    MQTTPubRelSend,     /**< @brief The library will send a PUBREL for a received PUBREC. */
+    MQTTPubCompSend,    /**< @brief The library will send a PUBCOMP for a received PUBREL. */
+    MQTTPubAckPending,  /**< @brief The library is awaiting a PUBACK for an outgoing PUBLISH. */
+    MQTTPubRecPending,  /**< @brief The library is awaiting a PUBREC for an outgoing PUBLISH. */
+    MQTTPubRelPending,  /**< @brief The library is awaiting a PUBREL for an incoming PUBLISH. */
+    MQTTPubCompPending, /**< @brief The library is awaiting a PUBCOMP for an outgoing PUBLISH. */
+    MQTTPublishDone     /**< @brief The PUBLISH has been completed. */
+} MQTTPublishState_t;
+
+/**
+ * @ingroup mqtt_enum_types
+ * @brief Packet types used in acknowledging QoS 1 or QoS 2 publishes.
+ */
+typedef enum MQTTPubAckType
+{
+    MQTTPuback, /**< @brief PUBACKs are sent in response to a QoS 1 PUBLISH. */
+    MQTTPubrec, /**< @brief PUBRECs are sent in response to a QoS 2 PUBLISH. */
+    MQTTPubrel, /**< @brief PUBRELs are sent in response to a PUBREC. */
+    MQTTPubcomp /**< @brief PUBCOMPs are sent in response to a PUBREL. */
+} MQTTPubAckType_t;
+
+/**
+ * @ingroup mqtt_enum_types
+ * @brief The status codes in the SUBACK response to a subscription request.
+ */
+typedef enum MQTTSubAckStatus
+{
+    MQTTSubAckSuccessQos0 = 0x00, /**< @brief Success with a maximum delivery at QoS 0. */
+    MQTTSubAckSuccessQos1 = 0x01, /**< @brief Success with a maximum delivery at QoS 1. */
+    MQTTSubAckSuccessQos2 = 0x02, /**< @brief Success with a maximum delivery at QoS 2. */
+    MQTTSubAckFailure = 0x80      /**< @brief Failure. */
+} MQTTSubAckStatus_t;
+
+/**
+ * @ingroup mqtt_struct_types
+ * @brief An element of the state engine records for QoS 1 or Qos 2 publishes.
+ */
+typedef struct MQTTPubAckInfo
+{
+    uint16_t packetId;               /**< @brief The packet ID of the original PUBLISH. */
+    MQTTQoS_t qos;                   /**< @brief The QoS of the original PUBLISH. */
+    MQTTPublishState_t publishState; /**< @brief The current state of the publish process. */
+} MQTTPubAckInfo_t;
+
+/**
+ * @ingroup mqtt_struct_types
+ * @brief A struct representing an MQTT connection.
+ */
+typedef struct MQTTContext
+{
+    /**
+     * @brief State engine records for outgoing publishes.
+     */
+    MQTTPubAckInfo_t * outgoingPublishRecords;
+
+    /**
+     * @brief State engine records for incoming publishes.
+     */
+    MQTTPubAckInfo_t * incomingPublishRecords;
+
+    /**
+     * @brief The maximum number of outgoing publish records.
+     */
+    size_t outgoingPublishRecordMaxCount;
+
+    /**
+     * @brief The maximum number of incoming publish records.
+     */
+    size_t incomingPublishRecordMaxCount;
+
+    /**
+     * @brief The transport interface used by the MQTT connection.
+     */
+    TransportInterface_t transportInterface;
+
+    /**
+     * @brief The buffer used in sending and receiving packets from the network.
+     */
+    MQTTFixedBuffer_t networkBuffer;
+
+    /**
+     * @brief The next available ID for outgoing MQTT packets.
+     */
+    uint16_t nextPacketId;
+
+    /**
+     * @brief Whether the context currently has a connection to the broker.
+     */
+    MQTTConnectionStatus_t connectStatus;
+
+    /**
+     * @brief Function used to get millisecond timestamps.
+     */
+    MQTTGetCurrentTimeFunc_t getTime;
+
+    /**
+     * @brief Callback function used to give deserialized MQTT packets to the application.
+     */
+    MQTTEventCallback_t appCallback;
+
+    /**
+     * @brief Timestamp of the last packet sent by the library.
+     */
+    uint32_t lastPacketTxTime;
+
+    /**
+     * @brief Timestamp of the last packet received by the library.
+     */
+    uint32_t lastPacketRxTime;
+
+    /**
+     * @brief Whether the library sent a packet during a call of #MQTT_ProcessLoop or
+     * #MQTT_ReceiveLoop.
+     */
+    bool controlPacketSent;
+
+    /**
+     * @brief Index to keep track of the number of bytes received in network buffer.
+     */
+    size_t index;
+
+    /* Keep alive members. */
+    uint16_t keepAliveIntervalSec; /**< @brief Keep Alive interval. */
+    uint32_t pingReqSendTimeMs;    /**< @brief Timestamp of the last sent PINGREQ. */
+    bool waitingForPingResp;       /**< @brief If the library is currently awaiting a PINGRESP. */
+} MQTTContext_t;
+
+/**
+ * @ingroup mqtt_struct_types
+ * @brief Struct to hold deserialized packet information for an #MQTTEventCallback_t
+ * callback.
+ */
+typedef struct MQTTDeserializedInfo
+{
+    uint16_t packetIdentifier;          /**< @brief Packet ID of deserialized packet. */
+    MQTTPublishInfo_t * pPublishInfo;   /**< @brief Pointer to deserialized publish info. */
+    MQTTStatus_t deserializationResult; /**< @brief Return code of deserialization. */
+} MQTTDeserializedInfo_t;
+
+/**
+ * @brief Initialize an MQTT context.
+ *
+ * This function must be called on an #MQTTContext_t before any other function.
+ *
+ * @note The #MQTTGetCurrentTimeFunc_t function for querying time must be defined. If
+ * there is no time implementation, it is the responsibility of the application
+ * to provide a dummy function to always return 0, provide 0 timeouts for
+ * all calls to #MQTT_Connect, #MQTT_ProcessLoop, and #MQTT_ReceiveLoop and configure
+ * the #MQTT_RECV_POLLING_TIMEOUT_MS and #MQTT_SEND_TIMEOUT_MS configurations
+ * to be 0. This will result in loop functions running for a single iteration, and
+ * #MQTT_Connect relying on #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT to receive the CONNACK packet.
+ *
+ * @param[in] pContext The context to initialize.
+ * @param[in] pTransportInterface The transport interface to use with the context.
+ * @param[in] getTimeFunction The time utility function which can return the amount of time
+ *    (in milliseconds) elapsed since a given epoch. This function will be used to ensure that
+ *    timeouts in the API calls are met and keep-alive messages are sent on time.
+ * @param[in] userCallback The user callback to use with the context to notify about incoming
+ *     packet events.
+ * @param[in] pNetworkBuffer Network buffer provided for the context. This buffer will be used
+ *     to receive incoming messages from the broker. This buffer must remain valid and in scope
+ *     for the entire lifetime of the @p pContext and must not be used by another context and/or
+ *     application.
+ *
+ * @return #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Function for obtaining a timestamp.
+ * uint32_t getTimeStampMs();
+ * // Callback function for receiving packets.
+ * void eventCallback(
+ *      MQTTContext_t * pContext,
+ *      MQTTPacketInfo_t * pPacketInfo,
+ *      MQTTDeserializedInfo_t * pDeserializedInfo
+ * );
+ * // Network send.
+ * int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes );
+ * // Network receive.
+ * int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes );
+ *
+ * MQTTContext_t mqttContext;
+ * TransportInterface_t transport;
+ * MQTTFixedBuffer_t fixedBuffer;
+ * // Create a globally accessible buffer which remains in scope for the entire duration
+ * // of the MQTT context.
+ * uint8_t buffer[ 1024 ];
+ *
+ * // Clear context.
+ * memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) );
+ *
+ * // Set transport interface members.
+ * transport.pNetworkContext = &someTransportContext;
+ * transport.send = networkSend;
+ * transport.recv = networkRecv;
+ *
+ * // Set buffer members.
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = 1024;
+ *
+ * status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // Do something with mqttContext. The transport and fixedBuffer structs were
+ *      // copied into the context, so the original structs do not need to stay in scope.
+ *      // However, the memory pointed to by the fixedBuffer.pBuffer must remain in scope.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_init] */
+MQTTStatus_t MQTT_Init( MQTTContext_t * pContext,
+                        const TransportInterface_t * pTransportInterface,
+                        MQTTGetCurrentTimeFunc_t getTimeFunction,
+                        MQTTEventCallback_t userCallback,
+                        const MQTTFixedBuffer_t * pNetworkBuffer );
+/* @[declare_mqtt_init] */
+
+/**
+ * @brief Initialize an MQTT context for QoS > 0.
+ *
+ * This function must be called on an #MQTTContext_t after MQTT_Init and before any other function.
+ *
+ * @param[in] pContext The context to initialize.
+ * @param[in] pOutgoingPublishRecords Pointer to memory which will be used to store state of outgoing
+ * publishes.
+ * @param[in] outgoingPublishCount Maximum number of records which can be kept in the memory
+ * pointed to by @p pOutgoingPublishRecords.
+ * @param[in] pIncomingPublishRecords Pointer to memory which will be used to store state of incoming
+ * publishes.
+ * @param[in] incomingPublishCount Maximum number of records which can be kept in the memory
+ * pointed to by @p pIncomingPublishRecords.
+ *
+ * @return #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Function for obtaining a timestamp.
+ * uint32_t getTimeStampMs();
+ * // Callback function for receiving packets.
+ * void eventCallback(
+ *      MQTTContext_t * pContext,
+ *      MQTTPacketInfo_t * pPacketInfo,
+ *      MQTTDeserializedInfo_t * pDeserializedInfo
+ * );
+ * // Network send.
+ * int32_t networkSend( NetworkContext_t * pContext, const void * pBuffer, size_t bytes );
+ * // Network receive.
+ * int32_t networkRecv( NetworkContext_t * pContext, void * pBuffer, size_t bytes );
+ *
+ * MQTTContext_t mqttContext;
+ * TransportInterface_t transport;
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ 1024 ];
+ * const size_t outgoingPublishCount = 30;
+ * MQTTPubAckInfo_t outgoingPublishes[ outgoingPublishCount ];
+ *
+ * // Clear context.
+ * memset( ( void * ) &mqttContext, 0x00, sizeof( MQTTContext_t ) );
+ *
+ * // Set transport interface members.
+ * transport.pNetworkContext = &someTransportContext;
+ * transport.send = networkSend;
+ * transport.recv = networkRecv;
+ *
+ * // Set buffer members.
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = 1024;
+ *
+ * status = MQTT_Init( &mqttContext, &transport, getTimeStampMs, eventCallback, &fixedBuffer );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // We do not expect any incoming publishes in this example, therefore the incoming
+ *      // publish pointer is NULL and the count is zero.
+ *      status = MQTT_InitStatefulQoS( &mqttContext, outgoingPublishes, outgoingPublishCount, NULL, 0 );
+ *
+ *      // Now QoS1 and/or QoS2 publishes can be sent with this context.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_initstatefulqos] */
+MQTTStatus_t MQTT_InitStatefulQoS( MQTTContext_t * pContext,
+                                   MQTTPubAckInfo_t * pOutgoingPublishRecords,
+                                   size_t outgoingPublishCount,
+                                   MQTTPubAckInfo_t * pIncomingPublishRecords,
+                                   size_t incomingPublishCount );
+/* @[declare_mqtt_initstatefulqos] */
+
+/**
+ * @brief Establish an MQTT session.
+ *
+ * This function will send MQTT CONNECT packet and receive a CONNACK packet. The
+ * send and receive from the network is done through the transport interface.
+ *
+ * The maximum time this function waits for a CONNACK is decided in one of the
+ * following ways:
+ * 1. If @p timeoutMs is greater than 0:
+ *    #MQTTContext_t.getTime is used to ensure that the function does not wait
+ *    more than @p timeoutMs for CONNACK.
+ * 2. If @p timeoutMs is 0:
+ *    The network receive for CONNACK is retried up to the number of times
+ *    configured by #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT.
+ *
+ * @note If a dummy #MQTTGetCurrentTimeFunc_t was passed to #MQTT_Init, then a
+ * timeout value of 0 MUST be passed to the API, and the #MQTT_RECV_POLLING_TIMEOUT_MS
+ * and #MQTT_SEND_TIMEOUT_MS timeout configurations MUST be set to 0.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] pConnectInfo MQTT CONNECT packet information.
+ * @param[in] pWillInfo Last Will and Testament. Pass NULL if Last Will and
+ * Testament is not used.
+ * @param[in] timeoutMs Maximum time in milliseconds to wait for a CONNACK packet.
+ * A zero timeout makes use of the retries for receiving CONNACK as configured with
+ * #MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT.
+ * @param[out] pSessionPresent This value will be set to true if a previous session
+ * was present; otherwise it will be set to false. It is only relevant if not
+ * establishing a clean session.
+ *
+ * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to
+ * hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSendFailed if transport send failed;
+ * #MQTTRecvFailed if transport receive failed for CONNACK;
+ * #MQTTNoDataAvailable if no data available to receive in transport until
+ * the @p timeoutMs for CONNACK;
+ * #MQTTSuccess otherwise.
+ *
+ * @note This API may spend more time than provided in the timeoutMS parameters in
+ * certain conditions as listed below:
+ *
+ * 1. Timeouts are incorrectly configured - If the timeoutMS is less than the
+ *    transport receive timeout and if a CONNACK packet is not received within
+ *    the transport receive timeout, the API will spend the transport receive
+ *    timeout (which is more time than the timeoutMs). It is the case of incorrect
+ *    timeout configuration as the timeoutMs parameter passed to this API must be
+ *    greater than the transport receive timeout. Please refer to the transport
+ *    interface documentation for more details about timeout configurations.
+ *
+ * 2. Partial CONNACK packet is received right before the expiry of the timeout - It
+ *    is possible that first two bytes of CONNACK packet (packet type and remaining
+ *    length) are received right before the expiry of the timeoutMS. In that case,
+ *    the API makes one more network receive call in an attempt to receive the remaining
+ *    2 bytes. In the worst case, it can happen that the remaining 2 bytes are never
+ *    received and this API will end up spending timeoutMs + transport receive timeout.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTConnectInfo_t connectInfo = { 0 };
+ * MQTTPublishInfo_t willInfo = { 0 };
+ * bool sessionPresent;
+ * // This is assumed to have been initialized before calling this function.
+ * MQTTContext_t * pContext;
+ *
+ * // True for creating a new session with broker, false if we want to resume an old one.
+ * connectInfo.cleanSession = true;
+ * // Client ID must be unique to broker. This field is required.
+ * connectInfo.pClientIdentifier = "someClientID";
+ * connectInfo.clientIdentifierLength = strlen( connectInfo.pClientIdentifier );
+ *
+ * // The following fields are optional.
+ * // Value for keep alive.
+ * connectInfo.keepAliveSeconds = 60;
+ * // Optional username and password.
+ * connectInfo.pUserName = "someUserName";
+ * connectInfo.userNameLength = strlen( connectInfo.pUserName );
+ * connectInfo.pPassword = "somePassword";
+ * connectInfo.passwordLength = strlen( connectInfo.pPassword );
+ *
+ * // The last will and testament is optional, it will be published by the broker
+ * // should this client disconnect without sending a DISCONNECT packet.
+ * willInfo.qos = MQTTQoS0;
+ * willInfo.pTopicName = "/lwt/topic/name";
+ * willInfo.topicNameLength = strlen( willInfo.pTopicName );
+ * willInfo.pPayload = "LWT Message";
+ * willInfo.payloadLength = strlen( "LWT Message" );
+ *
+ * // Send the connect packet. Use 100 ms as the timeout to wait for the CONNACK packet.
+ * status = MQTT_Connect( pContext, &connectInfo, &willInfo, 100, &sessionPresent );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // Since we requested a clean session, this must be false
+ *      assert( sessionPresent == false );
+ *
+ *      // Do something with the connection.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_connect] */
+MQTTStatus_t MQTT_Connect( MQTTContext_t * pContext,
+                           const MQTTConnectInfo_t * pConnectInfo,
+                           const MQTTPublishInfo_t * pWillInfo,
+                           uint32_t timeoutMs,
+                           bool * pSessionPresent );
+/* @[declare_mqtt_connect] */
+
+/**
+ * @brief Sends MQTT SUBSCRIBE for the given list of topic filters to
+ * the broker.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] pSubscriptionList Array of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in @ pSubscriptionList
+ * array.
+ * @param[in] packetId Packet ID generated by #MQTT_GetPacketId.
+ *
+ * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to
+ * hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSendFailed if transport write failed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 };
+ * uint16_t packetId;
+ * // This context is assumed to be initialized and connected.
+ * MQTTContext_t * pContext;
+ * // This is assumed to be a list of filters we want to subscribe to.
+ * const char * filters[ NUMBER_OF_SUBSCRIPTIONS ];
+ *
+ * // Set each subscription.
+ * for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )
+ * {
+ *      subscriptionList[ i ].qos = MQTTQoS0;
+ *      // Each subscription needs a topic filter.
+ *      subscriptionList[ i ].pTopicFilter = filters[ i ];
+ *      subscriptionList[ i ].topicFilterLength = strlen( filters[ i ] );
+ * }
+ *
+ * // Obtain a new packet id for the subscription.
+ * packetId = MQTT_GetPacketId( pContext );
+ *
+ * status = MQTT_Subscribe( pContext, &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // We must now call MQTT_ReceiveLoop() or MQTT_ProcessLoop() to receive the SUBACK.
+ *      // If the broker accepts the subscription we can now receive publishes
+ *      // on the requested topics.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_subscribe] */
+MQTTStatus_t MQTT_Subscribe( MQTTContext_t * pContext,
+                             const MQTTSubscribeInfo_t * pSubscriptionList,
+                             size_t subscriptionCount,
+                             uint16_t packetId );
+/* @[declare_mqtt_subscribe] */
+
+/**
+ * @brief Publishes a message to the given topic name.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[in] packetId packet ID generated by #MQTT_GetPacketId.
+ *
+ * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSendFailed if transport write failed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPublishInfo_t publishInfo;
+ * uint16_t packetId;
+ * // This context is assumed to be initialized and connected.
+ * MQTTContext_t * pContext;
+ *
+ * // QoS of publish.
+ * publishInfo.qos = MQTTQoS1;
+ * publishInfo.pTopicName = "/some/topic/name";
+ * publishInfo.topicNameLength = strlen( publishInfo.pTopicName );
+ * publishInfo.pPayload = "Hello World!";
+ * publishInfo.payloadLength = strlen( "Hello World!" );
+ *
+ * // Packet ID is needed for QoS > 0.
+ * packetId = MQTT_GetPacketId( pContext );
+ *
+ * status = MQTT_Publish( pContext, &publishInfo, packetId );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // Since the QoS is > 0, we will need to call MQTT_ReceiveLoop()
+ *      // or MQTT_ProcessLoop() to process the publish acknowledgments.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_publish] */
+MQTTStatus_t MQTT_Publish( MQTTContext_t * pContext,
+                           const MQTTPublishInfo_t * pPublishInfo,
+                           uint16_t packetId );
+/* @[declare_mqtt_publish] */
+
+/**
+ * @brief Cancels an outgoing publish callback (only for QoS > QoS0) by
+ * removing it from the pending ACK list.
+ *
+ * @note This cannot cancel the actual publish as that might have already
+ * been sent to the broker. This only removes the details of the given packet
+ * ID from the list of unACKed packet. That allows the caller to free any memory
+ * associated with the publish payload, topic string etc. Also, after this API
+ * call, the user provided callback will not be invoked when the ACK packet is
+ * received.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] packetId packet ID corresponding to the outstanding publish.
+ *
+ * @return #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ */
+/* @[declare_mqtt_cancelcallback] */
+MQTTStatus_t MQTT_CancelCallback( const MQTTContext_t * pContext,
+                                  uint16_t packetId );
+/* @[declare_mqtt_cancelcallback] */
+
+/**
+ * @brief Sends an MQTT PINGREQ to broker.
+ *
+ * @param[in] pContext Initialized and connected MQTT context.
+ *
+ * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSendFailed if transport write failed;
+ * #MQTTSuccess otherwise.
+ */
+/* @[declare_mqtt_ping] */
+MQTTStatus_t MQTT_Ping( MQTTContext_t * pContext );
+/* @[declare_mqtt_ping] */
+
+/**
+ * @brief Sends MQTT UNSUBSCRIBE for the given list of topic filters to
+ * the broker.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[in] packetId packet ID generated by #MQTT_GetPacketId.
+ *
+ * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to
+ * hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSendFailed if transport write failed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTSubscribeInfo_t unsubscribeList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 };
+ * uint16_t packetId;
+ * // This context is assumed to be initialized and connected.
+ * MQTTContext_t * pContext;
+ * // This is assumed to be a list of filters we want to unsubscribe from.
+ * const char * filters[ NUMBER_OF_SUBSCRIPTIONS ];
+ *
+ * // Set information for each unsubscribe request.
+ * for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )
+ * {
+ *      unsubscribeList[ i ].pTopicFilter = filters[ i ];
+ *      unsubscribeList[ i ].topicFilterLength = strlen( filters[ i ] );
+ *
+ *      // The QoS field of MQTT_SubscribeInfo_t is unused for unsubscribing.
+ * }
+ *
+ * // Obtain a new packet id for the unsubscribe request.
+ * packetId = MQTT_GetPacketId( pContext );
+ *
+ * status = MQTT_Unsubscribe( pContext, &unsubscribeList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, packetId );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // We must now call MQTT_ReceiveLoop() or MQTT_ProcessLoop() to receive the UNSUBACK.
+ *      // After this the broker should no longer send publishes for these topics.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_unsubscribe] */
+MQTTStatus_t MQTT_Unsubscribe( MQTTContext_t * pContext,
+                               const MQTTSubscribeInfo_t * pSubscriptionList,
+                               size_t subscriptionCount,
+                               uint16_t packetId );
+/* @[declare_mqtt_unsubscribe] */
+
+/**
+ * @brief Disconnect an MQTT session.
+ *
+ * @param[in] pContext Initialized and connected MQTT context.
+ *
+ * @return #MQTTNoMemory if the #MQTTContext_t.networkBuffer is too small to
+ * hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSendFailed if transport send failed;
+ * #MQTTSuccess otherwise.
+ */
+/* @[declare_mqtt_disconnect] */
+MQTTStatus_t MQTT_Disconnect( MQTTContext_t * pContext );
+/* @[declare_mqtt_disconnect] */
+
+/**
+ * @brief Loop to receive packets from the transport interface. Handles keep
+ * alive.
+ *
+ * @note If a dummy timer function, #MQTTGetCurrentTimeFunc_t, is passed to the library,
+ * then the keep-alive mechanism is not supported by the #MQTT_ProcessLoop API.
+ * In that case, the #MQTT_ReceiveLoop API function should be used instead.
+ *
+ * @param[in] pContext Initialized and connected MQTT context.
+ *
+ * @note Calling this function blocks the calling context for a time period that
+ * depends on the passed the configuration macros, #MQTT_RECV_POLLING_TIMEOUT_MS
+ * and #MQTT_SEND_TIMEOUT_MS, and the underlying transport interface implementation
+ * timeouts, unless an error occurs. The blocking period also depends on the execution time of the
+ * #MQTTEventCallback_t callback supplied to the library. It is recommended that the supplied
+ * #MQTTEventCallback_t callback does not contain blocking operations to prevent potential
+ * non-deterministic blocking period of the #MQTT_ProcessLoop API call.
+ *
+ * @return #MQTTBadParameter if context is NULL;
+ * #MQTTRecvFailed if a network error occurs during reception;
+ * #MQTTSendFailed if a network error occurs while sending an ACK or PINGREQ;
+ * #MQTTBadResponse if an invalid packet is received;
+ * #MQTTKeepAliveTimeout if the server has not sent a PINGRESP before
+ * #MQTT_PINGRESP_TIMEOUT_MS milliseconds;
+ * #MQTTIllegalState if an incoming QoS 1/2 publish or ack causes an
+ * invalid transition for the internal state machine;
+ * #MQTTNeedMoreBytes if MQTT_ProcessLoop has received
+ * incomplete data; it should be called again (probably after a delay);
+ * #MQTTSuccess on success.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * // This context is assumed to be initialized and connected.
+ * MQTTContext_t * pContext;
+ *
+ * while( true )
+ * {
+ *      status = MQTT_ProcessLoop( pContext );
+ *
+ *      if( status != MQTTSuccess && status != MQTTNeedMoreBytes )
+ *      {
+ *          // Determine the error. It's possible we might need to disconnect
+ *          // the underlying transport connection.
+ *      }
+ *      else
+ *      {
+ *          // Other application functions.
+ *      }
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_processloop] */
+MQTTStatus_t MQTT_ProcessLoop( MQTTContext_t * pContext );
+/* @[declare_mqtt_processloop] */
+
+/**
+ * @brief Loop to receive packets from the transport interface. Does not handle
+ * keep alive.
+ *
+ * @note If a dummy #MQTTGetCurrentTimeFunc_t was passed to #MQTT_Init, then the
+ * #MQTT_RECV_POLLING_TIMEOUT_MS and #MQTT_SEND_TIMEOUT_MS timeout configurations
+ * MUST be set to 0.
+ *
+ * @param[in] pContext Initialized and connected MQTT context.
+ *
+ * @note Calling this function blocks the calling context for a time period that
+ * depends on the the configuration macros, #MQTT_RECV_POLLING_TIMEOUT_MS and
+ * #MQTT_SEND_TIMEOUT_MS, and the underlying transport interface implementation
+ * timeouts, unless an error occurs. The blocking period also depends on the execution time of the
+ * #MQTTEventCallback_t callback supplied to the library. It is recommended that the supplied
+ * #MQTTEventCallback_t callback does not contain blocking operations to prevent potential
+ * non-deterministic blocking period of the #MQTT_ReceiveLoop API call.
+ *
+ * @return #MQTTBadParameter if context is NULL;
+ * #MQTTRecvFailed if a network error occurs during reception;
+ * #MQTTSendFailed if a network error occurs while sending an ACK or PINGREQ;
+ * #MQTTBadResponse if an invalid packet is received;
+ * #MQTTIllegalState if an incoming QoS 1/2 publish or ack causes an
+ * invalid transition for the internal state machine;
+ * #MQTTNeedMoreBytes if MQTT_ReceiveLoop has received
+ * incomplete data; it should be called again (probably after a delay);
+ * #MQTTSuccess on success.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * uint32_t keepAliveMs = 60 * 1000;
+ * // This context is assumed to be initialized and connected.
+ * MQTTContext_t * pContext;
+ *
+ * while( true )
+ * {
+ *      status = MQTT_ReceiveLoop( pContext );
+ *
+ *      if( status != MQTTSuccess && status != MQTTNeedMoreBytes )
+ *      {
+ *          // Determine the error. It's possible we might need to disconnect
+ *          // the underlying transport connection.
+ *      }
+ *      else
+ *      {
+ *          // Since this function does not send pings, the application may need
+ *          // to in order to comply with keep alive.
+ *          if( ( pContext->getTime() - pContext->lastPacketTxTime ) > keepAliveMs )
+ *          {
+ *              status = MQTT_Ping( pContext );
+ *          }
+ *
+ *          // Other application functions.
+ *      }
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_receiveloop] */
+MQTTStatus_t MQTT_ReceiveLoop( MQTTContext_t * pContext );
+/* @[declare_mqtt_receiveloop] */
+
+/**
+ * @brief Get a packet ID that is valid according to the MQTT 3.1.1 spec.
+ *
+ * @param[in] pContext Initialized MQTT context.
+ *
+ * @return A non-zero number.
+ */
+/* @[declare_mqtt_getpacketid] */
+uint16_t MQTT_GetPacketId( MQTTContext_t * pContext );
+/* @[declare_mqtt_getpacketid] */
+
+/**
+ * @brief A utility function that determines whether the passed topic filter and
+ * topic name match according to the MQTT 3.1.1 protocol specification.
+ *
+ * @param[in] pTopicName The topic name to check.
+ * @param[in] topicNameLength Length of the topic name.
+ * @param[in] pTopicFilter The topic filter to check.
+ * @param[in] topicFilterLength Length of topic filter.
+ * @param[out] pIsMatch If the match is performed without any error, that is if the
+ * return value is MQTTSuccess, then and only then the value in this parameter is valid
+ * and updated. In such a case, if the topic filter and the topic name match, then this
+ * value is set to true; otherwise if there is no match then it is set to false.
+ *
+ * @note The API assumes that the passed topic name is valid to meet the
+ * requirements of the MQTT 3.1.1 specification. Invalid topic names (for example,
+ * containing wildcard characters) should not be passed to the function.
+ * Also, the API checks validity of topic filter for wildcard characters ONLY if
+ * the passed topic name and topic filter do not have an exact string match.
+ *
+ * @return Returns one of the following:
+ * - #MQTTBadParameter, if any of the input parameters is invalid.
+ * - #MQTTSuccess, if the matching operation was performed.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * const char * pTopic = "topic/match/1";
+ * const char * pFilter = "topic/#";
+ * MQTTStatus_t status = MQTTSuccess;
+ * bool match = false;
+ *
+ * status = MQTT_MatchTopic( pTopic, strlen( pTopic ), pFilter, strlen( pFilter ), &match );
+ * // Our parameters were valid, so this will return success.
+ * assert( status == MQTTSuccess );
+ *
+ * // For this specific example, we already know this value is true. This
+ * // check is placed here as an example for use with variable topic names.
+ * if( match )
+ * {
+ *      // Application can decide what to do with the matching topic name.
+ * }
+ * @endcode
+ */
+MQTTStatus_t MQTT_MatchTopic( const char * pTopicName,
+                              const uint16_t topicNameLength,
+                              const char * pTopicFilter,
+                              const uint16_t topicFilterLength,
+                              bool * pIsMatch );
+
+/**
+ * @brief Parses the payload of an MQTT SUBACK packet that contains status codes
+ * corresponding to topic filter subscription requests from the original
+ * subscribe packet.
+ *
+ * Each return code in the SUBACK packet corresponds to a topic filter in the
+ * SUBSCRIBE Packet being acknowledged.
+ * The status codes can be one of the following:
+ *  - 0x00 - Success - Maximum QoS 0
+ *  - 0x01 - Success - Maximum QoS 1
+ *  - 0x02 - Success - Maximum QoS 2
+ *  - 0x80 - Failure
+ * Refer to #MQTTSubAckStatus_t for the status codes.
+ *
+ * @param[in] pSubackPacket The SUBACK packet whose payload is to be parsed.
+ * @param[out] pPayloadStart This is populated with the starting address
+ * of the payload (or return codes for topic filters) in the SUBACK packet.
+ * @param[out] pPayloadSize This is populated with the size of the payload
+ * in the SUBACK packet. It represents the number of topic filters whose
+ * SUBACK status is present in the packet.
+ *
+ * @return Returns one of the following:
+ * - #MQTTBadParameter if the input SUBACK packet is invalid.
+ * - #MQTTSuccess if parsing the payload was successful.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Global variable used in this example.
+ * // This is assumed to be the subscription list in the original SUBSCRIBE packet.
+ * MQTTSubscribeInfo_t pSubscribes[ NUMBER_OF_SUBSCRIPTIONS ];
+ *
+ * // MQTT_GetSubAckStatusCodes is intended to be used from the application
+ * // callback that is called by the library in MQTT_ProcessLoop or MQTT_ReceiveLoop.
+ * void eventCallback(
+ *      MQTTContext_t * pContext,
+ *      MQTTPacketInfo_t * pPacketInfo,
+ *      MQTTDeserializedInfo_t * pDeserializedInfo
+ * )
+ * {
+ *      MQTTStatus_t status = MQTTSuccess;
+ *      uint8_t * pCodes;
+ *      size_t numCodes;
+ *
+ *      if( pPacketInfo->type == MQTT_PACKET_TYPE_SUBACK )
+ *      {
+ *          status = MQTT_GetSubAckStatusCodes( pPacketInfo, &pCodes, &numCodes );
+ *
+ *          // Since the pointers to the payload and payload size are not NULL, and
+ *          // we use the packet info struct passed to the app callback (verified
+ *          // to be valid by the library), this function must return success.
+ *          assert( status == MQTTSuccess );
+ *          // The server must send a response code for each topic filter in the
+ *          // original SUBSCRIBE packet.
+ *          assert( numCodes == NUMBER_OF_SUBSCRIPTIONS );
+ *
+ *          for( int i = 0; i < numCodes; i++ )
+ *          {
+ *              // The only failure code is 0x80 = MQTTSubAckFailure.
+ *              if( pCodes[ i ] == MQTTSubAckFailure )
+ *              {
+ *                  // The subscription failed, we may want to retry the
+ *                  // subscription in pSubscribes[ i ] outside of this callback.
+ *              }
+ *              else
+ *              {
+ *                  // The subscription was granted, but the maximum QoS may be
+ *                  // lower than what was requested. We can verify the granted QoS.
+ *                  if( pSubscribes[ i ].qos != pCodes[ i ] )
+ *                  {
+ *                      LogWarn( (
+ *                          "Requested QoS %u, but granted QoS %u for %s",
+ *                          pSubscribes[ i ].qos, pCodes[ i ], pSubscribes[ i ].pTopicFilter
+ *                      ) );
+ *                  }
+ *              }
+ *          }
+ *      }
+ *      // Handle other packet types.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_getsubackstatuscodes] */
+MQTTStatus_t MQTT_GetSubAckStatusCodes( const MQTTPacketInfo_t * pSubackPacket,
+                                        uint8_t ** pPayloadStart,
+                                        size_t * pPayloadSize );
+/* @[declare_mqtt_getsubackstatuscodes] */
+
+/**
+ * @brief Error code to string conversion for MQTT statuses.
+ *
+ * @param[in] status The status to convert to a string.
+ *
+ * @return The string representation of the status.
+ */
+/* @[declare_mqtt_status_strerror] */
+const char * MQTT_Status_strerror( MQTTStatus_t status );
+/* @[declare_mqtt_status_strerror] */
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    }
+#endif
+/* *INDENT-ON* */
+
+#endif /* ifndef CORE_MQTT_H */
diff --git a/project/coreMQTT/coreMQTT/core_mqtt_config_defaults.h b/project/coreMQTT/coreMQTT/core_mqtt_config_defaults.h
new file mode 100644
index 0000000..35b42d5
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/core_mqtt_config_defaults.h
@@ -0,0 +1,204 @@
+/*
+ * coreMQTT v2.1.1
+ * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_mqtt_config_defaults.h
+ * @brief This represents the default values for the configuration macros
+ * for the MQTT library.
+ *
+ * @note This file SHOULD NOT be modified. If custom values are needed for
+ * any configuration macro, a core_mqtt_config.h file should be provided to
+ * the MQTT library to override the default values defined in this file.
+ * To use the custom config file, the MQTT_DO_NOT_USE_CUSTOM_CONFIG preprocessor
+ * macro SHOULD NOT be set.
+ */
+
+#ifndef CORE_MQTT_CONFIG_DEFAULTS_H_
+#define CORE_MQTT_CONFIG_DEFAULTS_H_
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/* MQTT_DO_NOT_USE_CUSTOM_CONFIG allows building the MQTT library
+ * without a custom config. If a custom config is provided, the
+ * MQTT_DO_NOT_USE_CUSTOM_CONFIG macro should not be defined. */
+#ifndef MQTT_DO_NOT_USE_CUSTOM_CONFIG
+/* Include custom config file before other headers. */
+    #include "core_mqtt_config.h"
+#endif
+
+/* The macro definition for MQTT_DO_NOT_USE_CUSTOM_CONFIG is for Doxygen
+ * documentation only. */
+
+/**
+ * @brief Define this macro to build the MQTT library without the custom config
+ * file core_mqtt_config.h.
+ *
+ * Without the custom config, the MQTT library builds with
+ * default values of config macros defined in core_mqtt_config_defaults.h file.
+ *
+ * If a custom config is provided, then MQTT_DO_NOT_USE_CUSTOM_CONFIG should not
+ * be defined.
+ */
+#ifdef DOXYGEN
+    #define MQTT_DO_NOT_USE_CUSTOM_CONFIG
+#endif
+
+/**
+ * @ingroup mqtt_constants
+ * @brief Maximum number of vectors in subscribe and unsubscribe packet.
+ */
+#ifndef MQTT_SUB_UNSUB_MAX_VECTORS
+    #define MQTT_SUB_UNSUB_MAX_VECTORS    ( 4U )
+#endif
+
+/**
+ * @brief The number of retries for receiving CONNACK.
+ *
+ * The MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT will be used only when the
+ * timeoutMs parameter of #MQTT_Connect is passed as 0 . The transport
+ * receive for CONNACK will be retried MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT
+ * times before timing out. A value of 0 for this config will cause the
+ * transport receive for CONNACK  to be invoked only once.
+ *
+ * <b>Possible values:</b> Any positive 16 bit integer. <br>
+ * <b>Default value:</b> `5`
+ */
+#ifndef MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT
+/* Default value for the CONNACK receive retries. */
+    #define MQTT_MAX_CONNACK_RECEIVE_RETRY_COUNT    ( 5U )
+#endif
+
+/**
+ * @brief Maximum number of milliseconds to wait for a ping response to a ping
+ * request as part of the keep-alive mechanism.
+ *
+ * If a ping response is not received before this timeout, then
+ * #MQTT_ProcessLoop will return #MQTTKeepAliveTimeout.
+ *
+ * @note If this value is more than half of the keep alive interval, and the
+ * server does not receive the previous ping request, then it is likely that the
+ * server will disconnect the client before #MQTTKeepAliveTimeout can be returned.
+ *
+ * @note If a dummy implementation of the #MQTTGetCurrentTimeFunc_t timer function,
+ * is supplied to the library, then the keep-alive mechanism is not supported by the
+ * #MQTT_ProcessLoop API function. In that case, the value of #MQTT_PINGRESP_TIMEOUT_MS
+ * is irrelevant to the behavior of the library.
+ *
+ * <b>Possible values:</b> Any positive integer up to SIZE_MAX. <br>
+ * <b>Default value:</b> `5000`
+ */
+#ifndef MQTT_PINGRESP_TIMEOUT_MS
+/* Wait 5 seconds by default for a ping response. */
+    #define MQTT_PINGRESP_TIMEOUT_MS    ( 5000U )
+#endif
+
+/**
+ * @brief Maximum number of milliseconds of TX inactivity to wait
+ * before initiating a PINGREQ
+ *
+ * @note If this value is less than the keep alive interval than
+ * it will be used instead.
+ *
+ * <b>Possible values:</b> Any positive integer up to SIZE_MAX. <br>
+ * <b>Default value:</b> '30000'
+ */
+#ifndef PACKET_TX_TIMEOUT_MS
+    #define PACKET_TX_TIMEOUT_MS    ( 30000U )
+#endif
+
+/**
+ * @brief Maximum number of milliseconds of RX inactivity to wait
+ * before initiating a PINGREQ
+ *
+ * <b>Possible values:</b> Any positive integer up to SIZE_MAX. <br>
+ * <b>Default value:</b> '30000'
+ *
+ */
+#ifndef PACKET_RX_TIMEOUT_MS
+    #define PACKET_RX_TIMEOUT_MS    ( 30000U )
+#endif
+
+/**
+ * @brief The maximum duration between non-empty network reads while
+ * receiving an MQTT packet via the #MQTT_ProcessLoop or #MQTT_ReceiveLoop
+ * API functions.
+ *
+ * When an incoming MQTT packet is detected, the transport receive function
+ * may be called multiple times until all of the expected number of bytes of the
+ * packet are received. This timeout represents the maximum polling duration that
+ * is allowed without any data reception from the network for the incoming packet.
+ *
+ * If the timeout expires, the #MQTT_ProcessLoop and #MQTT_ReceiveLoop functions
+ * return #MQTTRecvFailed.
+ *
+ * @note If a dummy implementation of the #MQTTGetCurrentTimeFunc_t timer function,
+ * is supplied to the library, then #MQTT_RECV_POLLING_TIMEOUT_MS MUST be set to 0.
+ *
+ * <b>Possible values:</b> Any positive 32 bit integer. Recommended to use a
+ * small timeout value. <br>
+ * <b>Default value:</b> `10`
+ *
+ */
+#ifndef MQTT_RECV_POLLING_TIMEOUT_MS
+    #define MQTT_RECV_POLLING_TIMEOUT_MS    ( 10U )
+#endif
+
+/**
+ * @brief The maximum duration allowed to send an MQTT packet over the transport
+ * interface.
+ *
+ * When sending an MQTT packet, the transport send or writev functions may be
+ * called multiple times until all of the required number of bytes are sent.
+ * This timeout represents the maximum duration that is allowed to send the MQTT
+ * packet while calling the transport send or writev functions.
+ *
+ * If the timeout expires, #MQTTSendFailed will be returned by the public API
+ * functions.
+ *
+ * @note If a dummy implementation of the #MQTTGetCurrentTimeFunc_t timer function,
+ * is supplied to the library, then #MQTT_SEND_TIMEOUT_MS MUST be set to 0.
+ *
+ * <b>Possible values:</b> Any positive 32 bit integer. <br>
+ * <b>Default value:</b> `20000`
+ *
+ */
+#ifndef MQTT_SEND_TIMEOUT_MS
+    #define MQTT_SEND_TIMEOUT_MS    ( 20000U )
+#endif
+
+#ifdef MQTT_SEND_RETRY_TIMEOUT_MS
+    #error MQTT_SEND_RETRY_TIMEOUT_MS is deprecated. Instead use MQTT_SEND_TIMEOUT_MS.
+#endif
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    }
+#endif
+/* *INDENT-ON* */
+
+#endif /* ifndef CORE_MQTT_CONFIG_DEFAULTS_H_ */
diff --git a/project/coreMQTT/coreMQTT/core_mqtt_default_logging.h b/project/coreMQTT/coreMQTT/core_mqtt_default_logging.h
new file mode 100644
index 0000000..dcacf90
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/core_mqtt_default_logging.h
@@ -0,0 +1,132 @@
+/*
+ * coreMQTT v2.1.1
+ * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_mqtt_default_logging.h
+ * @brief This represents the default values for the logging macros for the MQTT
+ * library.
+ *
+ * @note This file SHOULD NOT be modified. If custom values are needed for
+ * any configuration macro, a core_mqtt_config.h file should be provided to
+ * the MQTT library to override the default values defined in this file.
+ * To use the custom config file, the MQTT_DO_NOT_USE_CUSTOM_CONFIG preprocessor
+ * macro SHOULD NOT be set.
+ */
+
+#ifndef CORE_MQTT_DEFAULT_LOGGING_H_
+#define CORE_MQTT_DEFAULT_LOGGING_H_
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/**
+ * @brief Macro that is called in the MQTT library for logging "Error" level
+ * messages.
+ *
+ * To enable error level logging in the MQTT library, this macro should be mapped to the
+ * application-specific logging implementation that supports error logging.
+ *
+ * @note This logging macro is called in the MQTT library with parameters wrapped in
+ * double parentheses to be ISO C89/C90 standard compliant. For a reference
+ * POSIX implementation of the logging macros, refer to core_mqtt_config.h files, and the
+ * logging-stack in demos folder of the
+ * [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C).
+ *
+ * <b>Default value</b>: Error logging is turned off, and no code is generated for calls
+ * to the macro in the MQTT library on compilation.
+ */
+#ifndef LogError
+    #define LogError( message )
+#endif
+
+/**
+ * @brief Macro that is called in the MQTT library for logging "Warning" level
+ * messages.
+ *
+ * To enable warning level logging in the MQTT library, this macro should be mapped to the
+ * application-specific logging implementation that supports warning logging.
+ *
+ * @note This logging macro is called in the MQTT library with parameters wrapped in
+ * double parentheses to be ISO C89/C90 standard compliant. For a reference
+ * POSIX implementation of the logging macros, refer to core_mqtt_config.h files, and the
+ * logging-stack in demos folder of the
+ * [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
+ *
+ * <b>Default value</b>: Warning logs are turned off, and no code is generated for calls
+ * to the macro in the MQTT library on compilation.
+ */
+#ifndef LogWarn
+    #define LogWarn( message )
+#endif
+
+/**
+ * @brief Macro that is called in the MQTT library for logging "Info" level
+ * messages.
+ *
+ * To enable info level logging in the MQTT library, this macro should be mapped to the
+ * application-specific logging implementation that supports info logging.
+ *
+ * @note This logging macro is called in the MQTT library with parameters wrapped in
+ * double parentheses to be ISO C89/C90 standard compliant. For a reference
+ * POSIX implementation of the logging macros, refer to core_mqtt_config.h files, and the
+ * logging-stack in demos folder of the
+ * [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
+ *
+ * <b>Default value</b>: Info logging is turned off, and no code is generated for calls
+ * to the macro in the MQTT library on compilation.
+ */
+#ifndef LogInfo
+    #define LogInfo( message )
+#endif
+
+/**
+ * @brief Macro that is called in the MQTT library for logging "Debug" level
+ * messages.
+ *
+ * To enable debug level logging from MQTT library, this macro should be mapped to the
+ * application-specific logging implementation that supports debug logging.
+ *
+ * @note This logging macro is called in the MQTT library with parameters wrapped in
+ * double parentheses to be ISO C89/C90 standard compliant. For a reference
+ * POSIX implementation of the logging macros, refer to core_mqtt_config.h files, and the
+ * logging-stack in demos folder of the
+ * [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
+ *
+ * <b>Default value</b>: Debug logging is turned off, and no code is generated for calls
+ * to the macro in the MQTT library on compilation.
+ */
+#ifndef LogDebug
+    #define LogDebug( message )
+#endif
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    }
+#endif
+/* *INDENT-ON* */
+
+#endif /* ifndef CORE_MQTT_DEFAULT_LOGGING_H_ */
diff --git a/project/coreMQTT/coreMQTT/core_mqtt_serializer.c b/project/coreMQTT/coreMQTT/core_mqtt_serializer.c
new file mode 100644
index 0000000..3e2b427
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/core_mqtt_serializer.c
@@ -0,0 +1,2684 @@
+/*
+ * coreMQTT v2.1.1
+ * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_mqtt_serializer.c
+ * @brief Implements the user-facing functions in core_mqtt_serializer.h.
+ */
+#include <string.h>
+#include <assert.h>
+
+#include "core_mqtt_serializer.h"
+
+/* Include config defaults header to get default values of configs. */
+#include "core_mqtt_config_defaults.h"
+
+#include "core_mqtt_default_logging.h"
+
+/**
+ * @brief MQTT protocol version 3.1.1.
+ */
+#define MQTT_VERSION_3_1_1                          ( ( uint8_t ) 4U )
+
+/**
+ * @brief Size of the fixed and variable header of a CONNECT packet.
+ */
+#define MQTT_PACKET_CONNECT_HEADER_SIZE             ( 10UL )
+
+/* MQTT CONNECT flags. */
+#define MQTT_CONNECT_FLAG_CLEAN                     ( 1 ) /**< @brief Clean session. */
+#define MQTT_CONNECT_FLAG_WILL                      ( 2 ) /**< @brief Will present. */
+#define MQTT_CONNECT_FLAG_WILL_QOS1                 ( 3 ) /**< @brief Will QoS 1. */
+#define MQTT_CONNECT_FLAG_WILL_QOS2                 ( 4 ) /**< @brief Will QoS 2. */
+#define MQTT_CONNECT_FLAG_WILL_RETAIN               ( 5 ) /**< @brief Will retain. */
+#define MQTT_CONNECT_FLAG_PASSWORD                  ( 6 ) /**< @brief Password present. */
+#define MQTT_CONNECT_FLAG_USERNAME                  ( 7 ) /**< @brief User name present. */
+
+/*
+ * Positions of each flag in the first byte of an MQTT PUBLISH packet's
+ * fixed header.
+ */
+#define MQTT_PUBLISH_FLAG_RETAIN                    ( 0 ) /**< @brief MQTT PUBLISH retain flag. */
+#define MQTT_PUBLISH_FLAG_QOS1                      ( 1 ) /**< @brief MQTT PUBLISH QoS1 flag. */
+#define MQTT_PUBLISH_FLAG_QOS2                      ( 2 ) /**< @brief MQTT PUBLISH QoS2 flag. */
+#define MQTT_PUBLISH_FLAG_DUP                       ( 3 ) /**< @brief MQTT PUBLISH duplicate flag. */
+
+/**
+ * @brief The size of MQTT DISCONNECT packets, per MQTT spec.
+ */
+#define MQTT_DISCONNECT_PACKET_SIZE                 ( 2UL )
+
+/**
+ * @brief A PINGREQ packet is always 2 bytes in size, defined by MQTT 3.1.1 spec.
+ */
+#define MQTT_PACKET_PINGREQ_SIZE                    ( 2UL )
+
+/**
+ * @brief The Remaining Length field of MQTT disconnect packets, per MQTT spec.
+ */
+#define MQTT_DISCONNECT_REMAINING_LENGTH            ( ( uint8_t ) 0 )
+
+/*
+ * Constants relating to CONNACK packets, defined by MQTT 3.1.1 spec.
+ */
+#define MQTT_PACKET_CONNACK_REMAINING_LENGTH        ( ( uint8_t ) 2U )    /**< @brief A CONNACK packet always has a "Remaining length" of 2. */
+#define MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK    ( ( uint8_t ) 0x01U ) /**< @brief The "Session Present" bit is always the lowest bit. */
+
+/*
+ * UNSUBACK, PUBACK, PUBREC, PUBREL, and PUBCOMP always have a remaining length
+ * of 2.
+ */
+#define MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH     ( ( uint8_t ) 2 ) /**< @brief PUBACK, PUBREC, PUBREl, PUBCOMP, UNSUBACK Remaining length. */
+#define MQTT_PACKET_PINGRESP_REMAINING_LENGTH       ( 0U )            /**< @brief A PINGRESP packet always has a "Remaining length" of 0. */
+
+/**
+ * @brief Per the MQTT 3.1.1 spec, the largest "Remaining Length" of an MQTT
+ * packet is this value, 256 MB.
+ */
+#define MQTT_MAX_REMAINING_LENGTH                   ( 268435455UL )
+
+/**
+ * @brief Set a bit in an 8-bit unsigned integer.
+ */
+#define UINT8_SET_BIT( x, position )      ( ( x ) = ( uint8_t ) ( ( x ) | ( 0x01U << ( position ) ) ) )
+
+/**
+ * @brief Macro for checking if a bit is set in a 1-byte unsigned int.
+ *
+ * @param[in] x The unsigned int to check.
+ * @param[in] position Which bit to check.
+ */
+#define UINT8_CHECK_BIT( x, position )    ( ( ( x ) & ( 0x01U << ( position ) ) ) == ( 0x01U << ( position ) ) )
+
+/**
+ * @brief Get the high byte of a 16-bit unsigned integer.
+ */
+#define UINT16_HIGH_BYTE( x )             ( ( uint8_t ) ( ( x ) >> 8 ) )
+
+/**
+ * @brief Get the low byte of a 16-bit unsigned integer.
+ */
+#define UINT16_LOW_BYTE( x )              ( ( uint8_t ) ( ( x ) & 0x00ffU ) )
+
+/**
+ * @brief Macro for decoding a 2-byte unsigned int from a sequence of bytes.
+ *
+ * @param[in] ptr A uint8_t* that points to the high byte.
+ */
+#define UINT16_DECODE( ptr )                            \
+    ( uint16_t ) ( ( ( ( uint16_t ) ptr[ 0 ] ) << 8 ) | \
+                   ( ( uint16_t ) ptr[ 1 ] ) )
+
+/**
+ * @brief A value that represents an invalid remaining length.
+ *
+ * This value is greater than what is allowed by the MQTT specification.
+ */
+#define MQTT_REMAINING_LENGTH_INVALID             ( ( size_t ) 268435456 )
+
+/**
+ * @brief The minimum remaining length for a QoS 0 PUBLISH.
+ *
+ * Includes two bytes for topic name length and one byte for topic name.
+ */
+#define MQTT_MIN_PUBLISH_REMAINING_LENGTH_QOS0    ( 3U )
+
+/*-----------------------------------------------------------*/
+
+
+/**
+ * @brief MQTT Subscription packet types.
+ */
+typedef enum MQTTSubscriptionType
+{
+    MQTT_SUBSCRIBE,  /**< @brief The type is a SUBSCRIBE packet. */
+    MQTT_UNSUBSCRIBE /**< @brief The type is a UNSUBSCRIBE packet. */
+} MQTTSubscriptionType_t;
+
+/*-----------------------------------------------------------*/
+
+/**
+ * @brief Serializes MQTT PUBLISH packet into the buffer provided.
+ *
+ * This function serializes MQTT PUBLISH packet into #MQTTFixedBuffer_t.pBuffer.
+ * Copy of the payload into the buffer is done as part of the serialization
+ * only if @p serializePayload is true.
+ *
+ * @brief param[in] pPublishInfo Publish information.
+ * @brief param[in] remainingLength Remaining length of the PUBLISH packet.
+ * @brief param[in] packetIdentifier Packet identifier of PUBLISH packet.
+ * @brief param[in, out] pFixedBuffer Buffer to which PUBLISH packet will be
+ * serialized.
+ * @brief param[in] serializePayload Copy payload to the serialized buffer
+ * only if true. Only PUBLISH header will be serialized if false.
+ *
+ * @return Total number of bytes sent; -1 if there is an error.
+ */
+static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo,
+                                    size_t remainingLength,
+                                    uint16_t packetIdentifier,
+                                    const MQTTFixedBuffer_t * pFixedBuffer,
+                                    bool serializePayload );
+
+/**
+ * @brief Calculates the packet size and remaining length of an MQTT
+ * PUBLISH packet.
+ *
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT PUBLISH packet.
+ * @param[out] pPacketSize The total size of the MQTT PUBLISH packet.
+ *
+ * @return false if the packet would exceed the size allowed by the
+ * MQTT spec; true otherwise.
+ */
+static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
+                                        size_t * pRemainingLength,
+                                        size_t * pPacketSize );
+
+/**
+ * @brief Calculates the packet size and remaining length of an MQTT
+ * SUBSCRIBE or UNSUBSCRIBE packet.
+ *
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT SUBSCRIBE or
+ * UNSUBSCRIBE packet.
+ * @param[out] pPacketSize The total size of the MQTT MQTT SUBSCRIBE or
+ * UNSUBSCRIBE packet.
+ * @param[in] subscriptionType #MQTT_SUBSCRIBE or #MQTT_UNSUBSCRIBE.
+ *
+ * #MQTTBadParameter if the packet would exceed the size allowed by the
+ * MQTT spec or a subscription is empty; #MQTTSuccess otherwise.
+ */
+static MQTTStatus_t calculateSubscriptionPacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                                     size_t subscriptionCount,
+                                                     size_t * pRemainingLength,
+                                                     size_t * pPacketSize,
+                                                     MQTTSubscriptionType_t subscriptionType );
+
+/**
+ * @brief Validates parameters of #MQTT_SerializeSubscribe or
+ * #MQTT_SerializeUnsubscribe.
+ *
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[in] packetId Packet identifier.
+ * @param[in] remainingLength Remaining length of the packet.
+ * @param[in] pFixedBuffer Buffer for packet serialization.
+ *
+ * @return #MQTTNoMemory if pBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ */
+static MQTTStatus_t validateSubscriptionSerializeParams( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                                         size_t subscriptionCount,
+                                                         uint16_t packetId,
+                                                         size_t remainingLength,
+                                                         const MQTTFixedBuffer_t * pFixedBuffer );
+
+/**
+ * @brief Serialize an MQTT CONNECT packet in the given buffer.
+ *
+ * @param[in] pConnectInfo MQTT CONNECT packet parameters.
+ * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used.
+ * @param[in] remainingLength Remaining Length of MQTT CONNECT packet.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ */
+static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo,
+                                    const MQTTPublishInfo_t * pWillInfo,
+                                    size_t remainingLength,
+                                    const MQTTFixedBuffer_t * pFixedBuffer );
+
+/**
+ * @brief Prints the appropriate message for the CONNACK response code if logs
+ * are enabled.
+ *
+ * @param[in] responseCode MQTT standard CONNACK response code.
+ */
+static void logConnackResponse( uint8_t responseCode );
+
+/**
+ * @brief Encodes the remaining length of the packet using the variable length
+ * encoding scheme provided in the MQTT v3.1.1 specification.
+ *
+ * @param[out] pDestination The destination buffer to store the encoded remaining
+ * length.
+ * @param[in] length The remaining length to encode.
+ *
+ * @return The location of the byte following the encoded value.
+ */
+static uint8_t * encodeRemainingLength( uint8_t * pDestination,
+                                        size_t length );
+
+/**
+ * @brief Retrieve the size of the remaining length if it were to be encoded.
+ *
+ * @param[in] length The remaining length to be encoded.
+ *
+ * @return The size of the remaining length if it were to be encoded.
+ */
+static size_t remainingLengthEncodedSize( size_t length );
+
+/**
+ * @brief Encode a string whose size is at maximum 16 bits in length.
+ *
+ * @param[out] pDestination Destination buffer for the encoding.
+ * @param[in] pSource The source string to encode.
+ * @param[in] sourceLength The length of the source string to encode.
+ *
+ * @return A pointer to the end of the encoded string.
+ */
+static uint8_t * encodeString( uint8_t * pDestination,
+                               const char * pSource,
+                               uint16_t sourceLength );
+
+/**
+ * @brief Retrieves and decodes the Remaining Length from the network interface
+ * by reading a single byte at a time.
+ *
+ * @param[in] recvFunc Network interface receive function.
+ * @param[in] pNetworkContext Network interface context to the receive function.
+ *
+ * @return The Remaining Length of the incoming packet.
+ */
+static size_t getRemainingLength( TransportRecv_t recvFunc,
+                                  NetworkContext_t * pNetworkContext );
+
+/**
+ * @brief Retrieves, decodes and stores the Remaining Length from the network
+ * interface by reading a single byte at a time.
+ *
+ * @param[in] pBuffer The buffer holding the raw data to be processed
+ * @param[in] pIndex Pointer to the index within the buffer to marking the end of raw data
+ *            available.
+ * @param[in] pIncomingPacket Structure used to hold the fields of the
+ *            incoming packet.
+ *
+ * @return MQTTNeedMoreBytes is returned to show that the incoming
+ *         packet is not yet fully received and decoded. Otherwise, MQTTSuccess
+ *         shows that processing of the packet was successful.
+ */
+static MQTTStatus_t processRemainingLength( const uint8_t * pBuffer,
+                                            const size_t * pIndex,
+                                            MQTTPacketInfo_t * pIncomingPacket );
+
+/**
+ * @brief Check if an incoming packet type is valid.
+ *
+ * @param[in] packetType The packet type to check.
+ *
+ * @return `true` if the packet type is valid; `false` otherwise.
+ */
+static bool incomingPacketValid( uint8_t packetType );
+
+/**
+ * @brief Check the remaining length of an incoming PUBLISH packet against some
+ * value for QoS 0, or for QoS 1 and 2.
+ *
+ * The remaining length for a QoS 1 and 2 packet will always be two greater than
+ * for a QoS 0.
+ *
+ * @param[in] remainingLength Remaining length of the PUBLISH packet.
+ * @param[in] qos The QoS of the PUBLISH.
+ * @param[in] qos0Minimum Minimum possible remaining length for a QoS 0 PUBLISH.
+ *
+ * @return #MQTTSuccess or #MQTTBadResponse.
+ */
+static MQTTStatus_t checkPublishRemainingLength( size_t remainingLength,
+                                                 MQTTQoS_t qos,
+                                                 size_t qos0Minimum );
+
+/**
+ * @brief Process the flags of an incoming PUBLISH packet.
+ *
+ * @param[in] publishFlags Flags of an incoming PUBLISH.
+ * @param[in, out] pPublishInfo Pointer to #MQTTPublishInfo_t struct where
+ * output will be written.
+ *
+ * @return #MQTTSuccess or #MQTTBadResponse.
+ */
+static MQTTStatus_t processPublishFlags( uint8_t publishFlags,
+                                         MQTTPublishInfo_t * pPublishInfo );
+
+/**
+ * @brief Deserialize a CONNACK packet.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t.
+ *
+ * @param[in] pConnack Pointer to an MQTT packet struct representing a
+ * CONNACK.
+ * @param[out] pSessionPresent Whether a previous session was present.
+ *
+ * @return #MQTTSuccess if CONNACK specifies that CONNECT was accepted;
+ * #MQTTServerRefused if CONNACK specifies that CONNECT was rejected;
+ * #MQTTBadResponse if the CONNACK packet doesn't follow MQTT spec.
+ */
+static MQTTStatus_t deserializeConnack( const MQTTPacketInfo_t * pConnack,
+                                        bool * pSessionPresent );
+
+/**
+ * @brief Decode the status bytes of a SUBACK packet to a #MQTTStatus_t.
+ *
+ * @param[in] statusCount Number of status bytes in the SUBACK.
+ * @param[in] pStatusStart The first status byte in the SUBACK.
+ *
+ * @return #MQTTSuccess, #MQTTServerRefused, or #MQTTBadResponse.
+ */
+static MQTTStatus_t readSubackStatus( size_t statusCount,
+                                      const uint8_t * pStatusStart );
+
+/**
+ * @brief Deserialize a SUBACK packet.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts
+ * the packet identifier.
+ *
+ * @param[in] pSuback Pointer to an MQTT packet struct representing a SUBACK.
+ * @param[out] pPacketIdentifier Packet ID of the SUBACK.
+ *
+ * @return #MQTTSuccess if SUBACK is valid; #MQTTBadResponse if SUBACK packet
+ * doesn't follow the MQTT spec.
+ */
+static MQTTStatus_t deserializeSuback( const MQTTPacketInfo_t * pSuback,
+                                       uint16_t * pPacketIdentifier );
+
+/**
+ * @brief Deserialize a PUBLISH packet received from the server.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTPublishInfo_t and
+ * extracts the packet identifier. Also prints out debug log messages about the
+ * packet.
+ *
+ * @param[in] pIncomingPacket Pointer to an MQTT packet struct representing a
+ * PUBLISH.
+ * @param[out] pPacketId Packet identifier of the PUBLISH.
+ * @param[out] pPublishInfo Pointer to #MQTTPublishInfo_t where output is
+ * written.
+ *
+ * @return #MQTTSuccess if PUBLISH is valid; #MQTTBadResponse
+ * if the PUBLISH packet doesn't follow MQTT spec.
+ */
+static MQTTStatus_t deserializePublish( const MQTTPacketInfo_t * pIncomingPacket,
+                                        uint16_t * pPacketId,
+                                        MQTTPublishInfo_t * pPublishInfo );
+
+/**
+ * @brief Deserialize an UNSUBACK, PUBACK, PUBREC, PUBREL, or PUBCOMP packet.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t and extracts
+ * the packet identifier.
+ *
+ * @param[in] pAck Pointer to the MQTT packet structure representing the packet.
+ * @param[out] pPacketIdentifier Packet ID of the ack type packet.
+ *
+ * @return #MQTTSuccess if UNSUBACK, PUBACK, PUBREC, PUBREL, or PUBCOMP is valid;
+ * #MQTTBadResponse if the packet doesn't follow the MQTT spec.
+ */
+static MQTTStatus_t deserializeSimpleAck( const MQTTPacketInfo_t * pAck,
+                                          uint16_t * pPacketIdentifier );
+
+/**
+ * @brief Deserialize a PINGRESP packet.
+ *
+ * Converts the packet from a stream of bytes to an #MQTTStatus_t.
+ *
+ * @param[in] pPingresp Pointer to an MQTT packet struct representing a PINGRESP.
+ *
+ * @return #MQTTSuccess if PINGRESP is valid; #MQTTBadResponse if the PINGRESP
+ * packet doesn't follow MQTT spec.
+ */
+static MQTTStatus_t deserializePingresp( const MQTTPacketInfo_t * pPingresp );
+
+/*-----------------------------------------------------------*/
+
+static size_t remainingLengthEncodedSize( size_t length )
+{
+    size_t encodedSize;
+
+    /* Determine how many bytes are needed to encode length.
+     * The values below are taken from the MQTT 3.1.1 spec. */
+
+    /* 1 byte is needed to encode lengths between 0 and 127. */
+    if( length < 128U )
+    {
+        encodedSize = 1U;
+    }
+    /* 2 bytes are needed to encode lengths between 128 and 16,383. */
+    else if( length < 16384U )
+    {
+        encodedSize = 2U;
+    }
+    /* 3 bytes are needed to encode lengths between 16,384 and 2,097,151. */
+    else if( length < 2097152U )
+    {
+        encodedSize = 3U;
+    }
+    /* 4 bytes are needed to encode lengths between 2,097,152 and 268,435,455. */
+    else
+    {
+        encodedSize = 4U;
+    }
+
+    LogDebug( ( "Encoded size for length %lu is %lu bytes.",
+                ( unsigned long ) length,
+                ( unsigned long ) encodedSize ) );
+
+    return encodedSize;
+}
+
+/*-----------------------------------------------------------*/
+
+static uint8_t * encodeRemainingLength( uint8_t * pDestination,
+                                        size_t length )
+{
+    uint8_t lengthByte;
+    uint8_t * pLengthEnd = NULL;
+    size_t remainingLength = length;
+
+    assert( pDestination != NULL );
+
+    pLengthEnd = pDestination;
+
+    /* This algorithm is copied from the MQTT v3.1.1 spec. */
+    do
+    {
+        lengthByte = ( uint8_t ) ( remainingLength % 128U );
+        remainingLength = remainingLength / 128U;
+
+        /* Set the high bit of this byte, indicating that there's more data. */
+        if( remainingLength > 0U )
+        {
+            UINT8_SET_BIT( lengthByte, 7 );
+        }
+
+        /* Output a single encoded byte. */
+        *pLengthEnd = lengthByte;
+        pLengthEnd++;
+    } while( remainingLength > 0U );
+
+    return pLengthEnd;
+}
+
+/*-----------------------------------------------------------*/
+
+static uint8_t * encodeString( uint8_t * pDestination,
+                               const char * pSource,
+                               uint16_t sourceLength )
+{
+    uint8_t * pBuffer = NULL;
+
+    /* Typecast const char * typed source buffer to const uint8_t *.
+     * This is to use same type buffers in memcpy. */
+    const uint8_t * pSourceBuffer = ( const uint8_t * ) pSource;
+
+    assert( pDestination != NULL );
+
+    pBuffer = pDestination;
+
+    /* The first byte of a UTF-8 string is the high byte of the string length. */
+    *pBuffer = UINT16_HIGH_BYTE( sourceLength );
+    pBuffer++;
+
+    /* The second byte of a UTF-8 string is the low byte of the string length. */
+    *pBuffer = UINT16_LOW_BYTE( sourceLength );
+    pBuffer++;
+
+    /* Copy the string into pBuffer. */
+    if( pSourceBuffer != NULL )
+    {
+        ( void ) memcpy( pBuffer, pSourceBuffer, sourceLength );
+    }
+
+    /* Return the pointer to the end of the encoded string. */
+    pBuffer = &pBuffer[ sourceLength ];
+
+    return pBuffer;
+}
+
+/*-----------------------------------------------------------*/
+
+static bool calculatePublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
+                                        size_t * pRemainingLength,
+                                        size_t * pPacketSize )
+{
+    bool status = true;
+    size_t packetSize = 0, payloadLimit = 0;
+
+    assert( pPublishInfo != NULL );
+    assert( pRemainingLength != NULL );
+    assert( pPacketSize != NULL );
+
+    /* The variable header of a PUBLISH packet always contains the topic name.
+     * The first 2 bytes of UTF-8 string contains length of the string.
+     */
+    packetSize += pPublishInfo->topicNameLength + sizeof( uint16_t );
+
+    /* The variable header of a QoS 1 or 2 PUBLISH packet contains a 2-byte
+     * packet identifier. */
+    if( pPublishInfo->qos > MQTTQoS0 )
+    {
+        packetSize += sizeof( uint16_t );
+    }
+
+    /* Calculate the maximum allowed size of the payload for the given parameters.
+     * This calculation excludes the "Remaining length" encoding, whose size is not
+     * yet known. */
+    payloadLimit = MQTT_MAX_REMAINING_LENGTH - packetSize - 1U;
+
+    /* Ensure that the given payload fits within the calculated limit. */
+    if( pPublishInfo->payloadLength > payloadLimit )
+    {
+        LogError( ( "PUBLISH payload length of %lu cannot exceed "
+                    "%lu so as not to exceed the maximum "
+                    "remaining length of MQTT 3.1.1 packet( %lu ).",
+                    ( unsigned long ) pPublishInfo->payloadLength,
+                    ( unsigned long ) payloadLimit,
+                    MQTT_MAX_REMAINING_LENGTH ) );
+        status = false;
+    }
+    else
+    {
+        /* Add the length of the PUBLISH payload. At this point, the "Remaining length"
+         * has been calculated. */
+        packetSize += pPublishInfo->payloadLength;
+
+        /* Now that the "Remaining length" is known, recalculate the payload limit
+         * based on the size of its encoding. */
+        payloadLimit -= remainingLengthEncodedSize( packetSize );
+
+        /* Check that the given payload fits within the size allowed by MQTT spec. */
+        if( pPublishInfo->payloadLength > payloadLimit )
+        {
+            LogError( ( "PUBLISH payload length of %lu cannot exceed "
+                        "%lu so as not to exceed the maximum "
+                        "remaining length of MQTT 3.1.1 packet( %lu ).",
+                        ( unsigned long ) pPublishInfo->payloadLength,
+                        ( unsigned long ) payloadLimit,
+                        MQTT_MAX_REMAINING_LENGTH ) );
+            status = false;
+        }
+        else
+        {
+            /* Set the "Remaining length" output parameter and calculate the full
+             * size of the PUBLISH packet. */
+            *pRemainingLength = packetSize;
+
+            packetSize += 1U + remainingLengthEncodedSize( packetSize );
+            *pPacketSize = packetSize;
+        }
+    }
+
+    LogDebug( ( "PUBLISH packet remaining length=%lu and packet size=%lu.",
+                ( unsigned long ) *pRemainingLength,
+                ( unsigned long ) *pPacketSize ) );
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * pPublishInfo,
+                                                      size_t remainingLength,
+                                                      uint8_t * pBuffer,
+                                                      size_t * headerSize )
+{
+    size_t headerLength;
+    uint8_t * pIndex;
+    MQTTStatus_t status = MQTTSuccess;
+
+    /* The first byte of a PUBLISH packet contains the packet type and flags. */
+    uint8_t publishFlags = MQTT_PACKET_TYPE_PUBLISH;
+
+    /* Get the start address of the buffer. */
+    pIndex = pBuffer;
+
+    /* Length of serialized packet = First byte
+     *                               + Length of encoded remaining length
+     *                               + Encoded topic length. */
+    headerLength = 1U + remainingLengthEncodedSize( remainingLength ) + 2U;
+
+    if( pPublishInfo->qos == MQTTQoS1 )
+    {
+        LogDebug( ( "Adding QoS as QoS1 in PUBLISH flags." ) );
+        UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 );
+    }
+    else if( pPublishInfo->qos == MQTTQoS2 )
+    {
+        LogDebug( ( "Adding QoS as QoS2 in PUBLISH flags." ) );
+        UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS2 );
+    }
+    else
+    {
+        /* Empty else MISRA 15.7 */
+    }
+
+    if( pPublishInfo->retain == true )
+    {
+        LogDebug( ( "Adding retain bit in PUBLISH flags." ) );
+        UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_RETAIN );
+    }
+
+    if( pPublishInfo->dup == true )
+    {
+        LogDebug( ( "Adding dup bit in PUBLISH flags." ) );
+        UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_DUP );
+    }
+
+    *pIndex = publishFlags;
+    pIndex++;
+
+    /* The "Remaining length" is encoded from the second byte. */
+    pIndex = encodeRemainingLength( pIndex, remainingLength );
+
+    /* The first byte of a UTF-8 string is the high byte of the string length. */
+    *pIndex = UINT16_HIGH_BYTE( pPublishInfo->topicNameLength );
+    pIndex++;
+
+    /* The second byte of a UTF-8 string is the low byte of the string length. */
+    *pIndex = UINT16_LOW_BYTE( pPublishInfo->topicNameLength );
+    pIndex++;
+
+    *headerSize = headerLength;
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static void serializePublishCommon( const MQTTPublishInfo_t * pPublishInfo,
+                                    size_t remainingLength,
+                                    uint16_t packetIdentifier,
+                                    const MQTTFixedBuffer_t * pFixedBuffer,
+                                    bool serializePayload )
+{
+    uint8_t * pIndex = NULL;
+    const uint8_t * pPayloadBuffer = NULL;
+
+    /* The first byte of a PUBLISH packet contains the packet type and flags. */
+    uint8_t publishFlags = MQTT_PACKET_TYPE_PUBLISH;
+
+    assert( pPublishInfo != NULL );
+    assert( pFixedBuffer != NULL );
+    assert( pFixedBuffer->pBuffer != NULL );
+    /* Packet Id should be non zero for Qos 1 and Qos 2. */
+    assert( ( pPublishInfo->qos == MQTTQoS0 ) || ( packetIdentifier != 0U ) );
+    /* Duplicate flag should be set only for Qos 1 or Qos 2. */
+    assert( ( pPublishInfo->dup != true ) || ( pPublishInfo->qos != MQTTQoS0 ) );
+
+    /* Get the start address of the buffer. */
+    pIndex = pFixedBuffer->pBuffer;
+
+    if( pPublishInfo->qos == MQTTQoS1 )
+    {
+        LogDebug( ( "Adding QoS as QoS1 in PUBLISH flags." ) );
+        UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 );
+    }
+    else if( pPublishInfo->qos == MQTTQoS2 )
+    {
+        LogDebug( ( "Adding QoS as QoS2 in PUBLISH flags." ) );
+        UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS2 );
+    }
+    else
+    {
+        /* Empty else MISRA 15.7 */
+    }
+
+    if( pPublishInfo->retain == true )
+    {
+        LogDebug( ( "Adding retain bit in PUBLISH flags." ) );
+        UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_RETAIN );
+    }
+
+    if( pPublishInfo->dup == true )
+    {
+        LogDebug( ( "Adding dup bit in PUBLISH flags." ) );
+        UINT8_SET_BIT( publishFlags, MQTT_PUBLISH_FLAG_DUP );
+    }
+
+    *pIndex = publishFlags;
+    pIndex++;
+
+    /* The "Remaining length" is encoded from the second byte. */
+    pIndex = encodeRemainingLength( pIndex, remainingLength );
+
+    /* The topic name is placed after the "Remaining length". */
+    pIndex = encodeString( pIndex,
+                           pPublishInfo->pTopicName,
+                           pPublishInfo->topicNameLength );
+
+    /* A packet identifier is required for QoS 1 and 2 messages. */
+    if( pPublishInfo->qos > MQTTQoS0 )
+    {
+        LogDebug( ( "Adding packet Id in PUBLISH packet." ) );
+        /* Place the packet identifier into the PUBLISH packet. */
+        *pIndex = UINT16_HIGH_BYTE( packetIdentifier );
+        pIndex[ 1U ] = UINT16_LOW_BYTE( packetIdentifier );
+        pIndex = &pIndex[ 2U ];
+    }
+
+    /* The payload is placed after the packet identifier.
+     * Payload is copied over only if required by the flag serializePayload.
+     * This will help reduce an unnecessary copy of the payload into the buffer.
+     */
+    if( ( pPublishInfo->payloadLength > 0U ) &&
+        ( serializePayload == true ) )
+    {
+        LogDebug( ( "Copying PUBLISH payload of length =%lu to buffer",
+                    ( unsigned long ) pPublishInfo->payloadLength ) );
+
+        /* Typecast const void * typed payload buffer to const uint8_t *.
+         * This is to use same type buffers in memcpy. */
+        pPayloadBuffer = ( const uint8_t * ) pPublishInfo->pPayload;
+
+        ( void ) memcpy( pIndex, pPayloadBuffer, pPublishInfo->payloadLength );
+        /* Move the index to after the payload. */
+        pIndex = &pIndex[ pPublishInfo->payloadLength ];
+    }
+
+    /* Ensure that the difference between the end and beginning of the buffer
+     * is less than the buffer size. */
+    assert( ( ( size_t ) ( pIndex - pFixedBuffer->pBuffer ) ) <= pFixedBuffer->size );
+}
+
+static size_t getRemainingLength( TransportRecv_t recvFunc,
+                                  NetworkContext_t * pNetworkContext )
+{
+    size_t remainingLength = 0, multiplier = 1, bytesDecoded = 0, expectedSize = 0;
+    uint8_t encodedByte = 0;
+    int32_t bytesReceived = 0;
+
+    /* This algorithm is copied from the MQTT v3.1.1 spec. */
+    do
+    {
+        if( multiplier > 2097152U ) /* 128 ^ 3 */
+        {
+            remainingLength = MQTT_REMAINING_LENGTH_INVALID;
+        }
+        else
+        {
+            bytesReceived = recvFunc( pNetworkContext, &encodedByte, 1U );
+
+            if( bytesReceived == 1 )
+            {
+                remainingLength += ( ( size_t ) encodedByte & 0x7FU ) * multiplier;
+                multiplier *= 128U;
+                bytesDecoded++;
+            }
+            else
+            {
+                remainingLength = MQTT_REMAINING_LENGTH_INVALID;
+            }
+        }
+
+        if( remainingLength == MQTT_REMAINING_LENGTH_INVALID )
+        {
+            break;
+        }
+    } while( ( encodedByte & 0x80U ) != 0U );
+
+    /* Check that the decoded remaining length conforms to the MQTT specification. */
+    if( remainingLength != MQTT_REMAINING_LENGTH_INVALID )
+    {
+        expectedSize = remainingLengthEncodedSize( remainingLength );
+
+        if( bytesDecoded != expectedSize )
+        {
+            remainingLength = MQTT_REMAINING_LENGTH_INVALID;
+        }
+    }
+
+    return remainingLength;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t processRemainingLength( const uint8_t * pBuffer,
+                                            const size_t * pIndex,
+                                            MQTTPacketInfo_t * pIncomingPacket )
+{
+    size_t remainingLength = 0;
+    size_t multiplier = 1;
+    size_t bytesDecoded = 0;
+    size_t expectedSize = 0;
+    uint8_t encodedByte = 0;
+    MQTTStatus_t status = MQTTSuccess;
+
+    /* This algorithm is copied from the MQTT v3.1.1 spec. */
+    do
+    {
+        if( multiplier > 2097152U ) /* 128 ^ 3 */
+        {
+            remainingLength = MQTT_REMAINING_LENGTH_INVALID;
+
+            LogError( ( "Invalid remaining length in the packet.\n" ) );
+
+            status = MQTTBadResponse;
+        }
+        else
+        {
+            if( *pIndex > ( bytesDecoded + 1U ) )
+            {
+                /* Get the next byte. It is at the next position after the bytes
+                 * decoded till now since the header of one byte was read before. */
+                encodedByte = pBuffer[ bytesDecoded + 1U ];
+
+                remainingLength += ( ( size_t ) encodedByte & 0x7FU ) * multiplier;
+                multiplier *= 128U;
+                bytesDecoded++;
+            }
+            else
+            {
+                status = MQTTNeedMoreBytes;
+            }
+        }
+
+        /* If the response is incorrect, or no more data is available, then
+         * break out of the loop. */
+        if( ( remainingLength == MQTT_REMAINING_LENGTH_INVALID ) ||
+            ( status != MQTTSuccess ) )
+        {
+            break;
+        }
+    } while( ( encodedByte & 0x80U ) != 0U );
+
+    if( status == MQTTSuccess )
+    {
+        /* Check that the decoded remaining length conforms to the MQTT specification. */
+        expectedSize = remainingLengthEncodedSize( remainingLength );
+
+        if( bytesDecoded != expectedSize )
+        {
+            LogError( ( "Expected and actual length of decoded bytes do not match.\n" ) );
+            status = MQTTBadResponse;
+        }
+        else
+        {
+            pIncomingPacket->remainingLength = remainingLength;
+            pIncomingPacket->headerLength = bytesDecoded + 1U;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static bool incomingPacketValid( uint8_t packetType )
+{
+    bool status = false;
+
+    /* Check packet type. Mask out lower bits to ignore flags. */
+    switch( packetType & 0xF0U )
+    {
+        /* Valid incoming packet types. */
+        case MQTT_PACKET_TYPE_CONNACK:
+        case MQTT_PACKET_TYPE_PUBLISH:
+        case MQTT_PACKET_TYPE_PUBACK:
+        case MQTT_PACKET_TYPE_PUBREC:
+        case MQTT_PACKET_TYPE_PUBCOMP:
+        case MQTT_PACKET_TYPE_SUBACK:
+        case MQTT_PACKET_TYPE_UNSUBACK:
+        case MQTT_PACKET_TYPE_PINGRESP:
+            status = true;
+            break;
+
+        case ( MQTT_PACKET_TYPE_PUBREL & 0xF0U ):
+
+            /* The second bit of a PUBREL must be set. */
+            if( ( packetType & 0x02U ) > 0U )
+            {
+                status = true;
+            }
+
+            break;
+
+        /* Any other packet type is invalid. */
+        default:
+            LogWarn( ( "Incoming packet invalid: Packet type=%u.",
+                       ( unsigned int ) packetType ) );
+            break;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t checkPublishRemainingLength( size_t remainingLength,
+                                                 MQTTQoS_t qos,
+                                                 size_t qos0Minimum )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    /* Sanity checks for "Remaining length". */
+    if( qos == MQTTQoS0 )
+    {
+        /* Check that the "Remaining length" is greater than the minimum. */
+        if( remainingLength < qos0Minimum )
+        {
+            LogError( ( "QoS 0 PUBLISH cannot have a remaining length less than %lu.",
+                        ( unsigned long ) qos0Minimum ) );
+
+            status = MQTTBadResponse;
+        }
+    }
+    else
+    {
+        /* Check that the "Remaining length" is greater than the minimum. For
+         * QoS 1 or 2, this will be two bytes greater than for QoS 0 due to the
+         * packet identifier. */
+        if( remainingLength < ( qos0Minimum + 2U ) )
+        {
+            LogError( ( "QoS 1 or 2 PUBLISH cannot have a remaining length less than %lu.",
+                        ( unsigned long ) ( qos0Minimum + 2U ) ) );
+
+            status = MQTTBadResponse;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t processPublishFlags( uint8_t publishFlags,
+                                         MQTTPublishInfo_t * pPublishInfo )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    assert( pPublishInfo != NULL );
+
+    /* Check for QoS 2. */
+    if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS2 ) )
+    {
+        /* PUBLISH packet is invalid if both QoS 1 and QoS 2 bits are set. */
+        if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 ) )
+        {
+            LogError( ( "Bad QoS: 3." ) );
+
+            status = MQTTBadResponse;
+        }
+        else
+        {
+            pPublishInfo->qos = MQTTQoS2;
+        }
+    }
+    /* Check for QoS 1. */
+    else if( UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_QOS1 ) )
+    {
+        pPublishInfo->qos = MQTTQoS1;
+    }
+    /* If the PUBLISH isn't QoS 1 or 2, then it's QoS 0. */
+    else
+    {
+        pPublishInfo->qos = MQTTQoS0;
+    }
+
+    if( status == MQTTSuccess )
+    {
+        LogDebug( ( "QoS is %d.", ( int ) pPublishInfo->qos ) );
+
+        /* Parse the Retain bit. */
+        pPublishInfo->retain = UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_RETAIN );
+
+        LogDebug( ( "Retain bit is %d.", ( int ) pPublishInfo->retain ) );
+
+        /* Parse the DUP bit. */
+        pPublishInfo->dup = UINT8_CHECK_BIT( publishFlags, MQTT_PUBLISH_FLAG_DUP );
+
+        LogDebug( ( "DUP bit is %d.", ( int ) pPublishInfo->dup ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static void logConnackResponse( uint8_t responseCode )
+{
+    const char * const pConnackResponses[ 6 ] =
+    {
+        "Connection accepted.",                               /* 0 */
+        "Connection refused: unacceptable protocol version.", /* 1 */
+        "Connection refused: identifier rejected.",           /* 2 */
+        "Connection refused: server unavailable",             /* 3 */
+        "Connection refused: bad user name or password.",     /* 4 */
+        "Connection refused: not authorized."                 /* 5 */
+    };
+
+    /* Avoid unused parameter warning when assert and logs are disabled. */
+    ( void ) responseCode;
+    ( void ) pConnackResponses;
+
+    assert( responseCode <= 5U );
+
+    if( responseCode == 0u )
+    {
+        /* Log at Debug level for a success CONNACK response. */
+        LogDebug( ( "%s", pConnackResponses[ 0 ] ) );
+    }
+    else
+    {
+        /* Log an error based on the CONNACK response code. */
+        LogError( ( "%s", pConnackResponses[ responseCode ] ) );
+    }
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t deserializeConnack( const MQTTPacketInfo_t * pConnack,
+                                        bool * pSessionPresent )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    const uint8_t * pRemainingData = NULL;
+
+    assert( pConnack != NULL );
+    assert( pSessionPresent != NULL );
+    pRemainingData = pConnack->pRemainingData;
+
+    /* According to MQTT 3.1.1, the second byte of CONNACK must specify a
+     * "Remaining length" of 2. */
+    if( pConnack->remainingLength != MQTT_PACKET_CONNACK_REMAINING_LENGTH )
+    {
+        LogError( ( "CONNACK does not have remaining length of %u.",
+                    ( unsigned int ) MQTT_PACKET_CONNACK_REMAINING_LENGTH ) );
+
+        status = MQTTBadResponse;
+    }
+
+    /* Check the reserved bits in CONNACK. The high 7 bits of the third byte
+     * in CONNACK must be 0. */
+    else if( ( pRemainingData[ 0 ] | 0x01U ) != 0x01U )
+    {
+        LogError( ( "Reserved bits in CONNACK incorrect." ) );
+
+        status = MQTTBadResponse;
+    }
+    else
+    {
+        /* Determine if the "Session Present" bit is set. This is the lowest bit of
+         * the third byte in CONNACK. */
+        if( ( pRemainingData[ 0 ] & MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )
+            == MQTT_PACKET_CONNACK_SESSION_PRESENT_MASK )
+        {
+            LogDebug( ( "CONNACK session present bit set." ) );
+            *pSessionPresent = true;
+
+            /* MQTT 3.1.1 specifies that the fourth byte in CONNACK must be 0 if the
+             * "Session Present" bit is set. */
+            if( pRemainingData[ 1 ] != 0U )
+            {
+                LogError( ( "Session Present bit is set, but connect return code in CONNACK is %u (nonzero).",
+                            ( unsigned int ) pRemainingData[ 1 ] ) );
+                status = MQTTBadResponse;
+            }
+        }
+        else
+        {
+            LogDebug( ( "CONNACK session present bit not set." ) );
+            *pSessionPresent = false;
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* In MQTT 3.1.1, only values 0 through 5 are valid CONNACK response codes. */
+        if( pRemainingData[ 1 ] > 5U )
+        {
+            LogError( ( "CONNACK response %u is invalid.",
+                        ( unsigned int ) pRemainingData[ 1 ] ) );
+
+            status = MQTTBadResponse;
+        }
+        else
+        {
+            /* Print the appropriate message for the CONNACK response code if logs are
+             * enabled. */
+            logConnackResponse( pRemainingData[ 1 ] );
+
+            /* A nonzero CONNACK response code means the connection was refused. */
+            if( pRemainingData[ 1 ] > 0U )
+            {
+                status = MQTTServerRefused;
+            }
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t calculateSubscriptionPacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                                     size_t subscriptionCount,
+                                                     size_t * pRemainingLength,
+                                                     size_t * pPacketSize,
+                                                     MQTTSubscriptionType_t subscriptionType )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    size_t i = 0, packetSize = 0;
+
+    assert( pSubscriptionList != NULL );
+    assert( subscriptionCount != 0U );
+    assert( pRemainingLength != NULL );
+    assert( pPacketSize != NULL );
+
+    /* The variable header of a subscription packet consists of a 2-byte packet
+     * identifier. */
+    packetSize += sizeof( uint16_t );
+
+    /* Sum the lengths of all subscription topic filters; add 1 byte for each
+     * subscription's QoS if type is MQTT_SUBSCRIBE. */
+    for( i = 0; i < subscriptionCount; i++ )
+    {
+        /* Add the length of the topic filter. MQTT strings are prepended
+         * with 2 byte string length field. Hence 2 bytes are added to size. */
+        packetSize += pSubscriptionList[ i ].topicFilterLength + sizeof( uint16_t );
+
+        /* Only SUBSCRIBE packets include the QoS. */
+        if( subscriptionType == MQTT_SUBSCRIBE )
+        {
+            packetSize += 1U;
+        }
+
+        /* Validate each topic filter. */
+        if( ( pSubscriptionList[ i ].topicFilterLength == 0U ) ||
+            ( pSubscriptionList[ i ].pTopicFilter == NULL ) )
+        {
+            status = MQTTBadParameter;
+            LogError( ( "Subscription #%lu in %sSUBSCRIBE packet cannot be empty.",
+                        ( unsigned long ) i,
+                        ( subscriptionType == MQTT_SUBSCRIBE ) ? "" : "UN" ) );
+            /* It is not necessary to break as an error status has already been set. */
+        }
+    }
+
+    /* At this point, the "Remaining length" has been calculated. Return error
+     * if the "Remaining length" exceeds what is allowed by MQTT 3.1.1. Otherwise,
+     * set the output parameter.*/
+    if( packetSize > MQTT_MAX_REMAINING_LENGTH )
+    {
+        LogError( ( "Subscription packet length of %lu exceeds"
+                    "the MQTT 3.1.1 maximum packet length of %lu.",
+                    ( unsigned long ) packetSize,
+                    MQTT_MAX_REMAINING_LENGTH ) );
+        status = MQTTBadParameter;
+    }
+
+    if( status == MQTTSuccess )
+    {
+        *pRemainingLength = packetSize;
+
+        /* Calculate the full size of the subscription packet by adding
+         * number of bytes required to encode the "Remaining length" field
+         * plus 1 byte for the "Packet type" field. */
+        packetSize += 1U + remainingLengthEncodedSize( packetSize );
+
+        /*Set the pPacketSize output parameter. */
+        *pPacketSize = packetSize;
+    }
+
+    LogDebug( ( "Subscription packet remaining length=%lu and packet size=%lu.",
+                ( unsigned long ) *pRemainingLength,
+                ( unsigned long ) *pPacketSize ) );
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t readSubackStatus( size_t statusCount,
+                                      const uint8_t * pStatusStart )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    uint8_t subscriptionStatus = 0;
+    size_t i = 0;
+
+    assert( pStatusStart != NULL );
+
+    /* Iterate through each status byte in the SUBACK packet. */
+    for( i = 0; i < statusCount; i++ )
+    {
+        /* Read a single status byte in SUBACK. */
+        subscriptionStatus = pStatusStart[ i ];
+
+        /* MQTT 3.1.1 defines the following values as status codes. */
+        switch( subscriptionStatus )
+        {
+            case 0x00:
+            case 0x01:
+            case 0x02:
+
+                LogDebug( ( "Topic filter %lu accepted, max QoS %u.",
+                            ( unsigned long ) i,
+                            ( unsigned int ) subscriptionStatus ) );
+                break;
+
+            case 0x80:
+
+                LogWarn( ( "Topic filter %lu refused.", ( unsigned long ) i ) );
+
+                /* Application should remove subscription from the list */
+                status = MQTTServerRefused;
+
+                break;
+
+            default:
+                LogError( ( "Bad SUBSCRIBE status %u.",
+                            ( unsigned int ) subscriptionStatus ) );
+
+                status = MQTTBadResponse;
+
+                break;
+        }
+
+        /* Stop parsing the subscription statuses if a bad response was received. */
+        if( status == MQTTBadResponse )
+        {
+            break;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t deserializeSuback( const MQTTPacketInfo_t * pSuback,
+                                       uint16_t * pPacketIdentifier )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    size_t remainingLength;
+    const uint8_t * pVariableHeader = NULL;
+
+    assert( pSuback != NULL );
+    assert( pPacketIdentifier != NULL );
+
+    remainingLength = pSuback->remainingLength;
+    pVariableHeader = pSuback->pRemainingData;
+
+    /* A SUBACK must have a remaining length of at least 3 to accommodate the
+     * packet identifier and at least 1 return code. */
+    if( remainingLength < 3U )
+    {
+        LogError( ( "SUBACK cannot have a remaining length less than 3." ) );
+        status = MQTTBadResponse;
+    }
+    else
+    {
+        /* Extract the packet identifier (first 2 bytes of variable header) from SUBACK. */
+        *pPacketIdentifier = UINT16_DECODE( pVariableHeader );
+
+        LogDebug( ( "Packet identifier %hu.",
+                    ( unsigned short ) *pPacketIdentifier ) );
+
+        if( *pPacketIdentifier == 0U )
+        {
+            status = MQTTBadResponse;
+        }
+        else
+        {
+            status = readSubackStatus( remainingLength - sizeof( uint16_t ),
+                                       &pVariableHeader[ sizeof( uint16_t ) ] );
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t validateSubscriptionSerializeParams( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                                         size_t subscriptionCount,
+                                                         uint16_t packetId,
+                                                         size_t remainingLength,
+                                                         const MQTTFixedBuffer_t * pFixedBuffer )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    size_t packetSize = 0;
+
+    /* Validate all the parameters. */
+    if( ( pFixedBuffer == NULL ) || ( pSubscriptionList == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pFixedBuffer=%p, "
+                    "pSubscriptionList=%p.",
+                    ( void * ) pFixedBuffer,
+                    ( void * ) pSubscriptionList ) );
+        status = MQTTBadParameter;
+    }
+    /* A buffer must be configured for serialization. */
+    else if( pFixedBuffer->pBuffer == NULL )
+    {
+        LogError( ( "Argument cannot be NULL: pFixedBuffer->pBuffer is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( subscriptionCount == 0U )
+    {
+        LogError( ( "Subscription count is 0." ) );
+        status = MQTTBadParameter;
+    }
+    else if( packetId == 0U )
+    {
+        LogError( ( "Packet Id for subscription packet is 0." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* The serialized packet size = First byte
+         * + length of encoded size of remaining length
+         * + remaining length. */
+        packetSize = 1U + remainingLengthEncodedSize( remainingLength )
+                     + remainingLength;
+
+        if( packetSize > pFixedBuffer->size )
+        {
+            LogError( ( "Buffer size of %lu is not sufficient to hold "
+                        "serialized packet of size of %lu.",
+                        ( unsigned long ) pFixedBuffer->size,
+                        ( unsigned long ) packetSize ) );
+            status = MQTTNoMemory;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t deserializePublish( const MQTTPacketInfo_t * pIncomingPacket,
+                                        uint16_t * pPacketId,
+                                        MQTTPublishInfo_t * pPublishInfo )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    const uint8_t * pVariableHeader, * pPacketIdentifierHigh = NULL;
+
+    assert( pIncomingPacket != NULL );
+    assert( pPacketId != NULL );
+    assert( pPublishInfo != NULL );
+    assert( pIncomingPacket->pRemainingData != NULL );
+
+    pVariableHeader = pIncomingPacket->pRemainingData;
+    /* The flags are the lower 4 bits of the first byte in PUBLISH. */
+    status = processPublishFlags( ( pIncomingPacket->type & 0x0FU ), pPublishInfo );
+
+    if( status == MQTTSuccess )
+    {
+        /* Sanity checks for "Remaining length". A QoS 0 PUBLISH  must have a remaining
+         * length of at least 3 to accommodate topic name length (2 bytes) and topic
+         * name (at least 1 byte). A QoS 1 or 2 PUBLISH must have a remaining length of
+         * at least 5 for the packet identifier in addition to the topic name length and
+         * topic name. */
+        status = checkPublishRemainingLength( pIncomingPacket->remainingLength,
+                                              pPublishInfo->qos,
+                                              MQTT_MIN_PUBLISH_REMAINING_LENGTH_QOS0 );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Extract the topic name starting from the first byte of the variable header.
+         * The topic name string starts at byte 3 in the variable header. */
+        pPublishInfo->topicNameLength = UINT16_DECODE( pVariableHeader );
+
+        /* Sanity checks for topic name length and "Remaining length". The remaining
+         * length must be at least as large as the variable length header. */
+        status = checkPublishRemainingLength( pIncomingPacket->remainingLength,
+                                              pPublishInfo->qos,
+                                              pPublishInfo->topicNameLength + sizeof( uint16_t ) );
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Parse the topic. */
+        pPublishInfo->pTopicName = ( const char * ) ( &pVariableHeader[ sizeof( uint16_t ) ] );
+        LogDebug( ( "Topic name length: %hu.", ( unsigned short ) pPublishInfo->topicNameLength ) );
+
+        /* Extract the packet identifier for QoS 1 or 2 PUBLISH packets. Packet
+         * identifier starts immediately after the topic name. */
+        pPacketIdentifierHigh = ( const uint8_t * ) ( &pPublishInfo->pTopicName[ pPublishInfo->topicNameLength ] );
+
+        if( pPublishInfo->qos > MQTTQoS0 )
+        {
+            *pPacketId = UINT16_DECODE( pPacketIdentifierHigh );
+
+            LogDebug( ( "Packet identifier %hu.",
+                        ( unsigned short ) *pPacketId ) );
+
+            /* Advance pointer two bytes to start of payload as in the QoS 0 case. */
+            pPacketIdentifierHigh = &pPacketIdentifierHigh[ sizeof( uint16_t ) ];
+
+            /* Packet identifier cannot be 0. */
+            if( *pPacketId == 0U )
+            {
+                LogError( ( "Packet identifier cannot be 0." ) );
+                status = MQTTBadResponse;
+            }
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Calculate the length of the payload. QoS 1 or 2 PUBLISH packets contain
+         * a packet identifier, but QoS 0 PUBLISH packets do not. */
+        pPublishInfo->payloadLength = pIncomingPacket->remainingLength - pPublishInfo->topicNameLength - sizeof( uint16_t );
+
+        if( pPublishInfo->qos != MQTTQoS0 )
+        {
+            /* Two more bytes for the packet identifier. */
+            pPublishInfo->payloadLength -= sizeof( uint16_t );
+        }
+
+        /* Set payload if it exists. */
+        pPublishInfo->pPayload = ( pPublishInfo->payloadLength != 0U ) ? pPacketIdentifierHigh : NULL;
+
+        LogDebug( ( "Payload length %lu.",
+                    ( unsigned long ) pPublishInfo->payloadLength ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t deserializeSimpleAck( const MQTTPacketInfo_t * pAck,
+                                          uint16_t * pPacketIdentifier )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    assert( pAck != NULL );
+    assert( pPacketIdentifier != NULL );
+
+    /* Check that the "Remaining length" of the received ACK is 2. */
+    if( pAck->remainingLength != MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH )
+    {
+        LogError( ( "ACK does not have remaining length of %u.",
+                    ( unsigned int ) MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH ) );
+
+        status = MQTTBadResponse;
+    }
+    else
+    {
+        /* Extract the packet identifier (third and fourth bytes) from ACK. */
+        *pPacketIdentifier = UINT16_DECODE( pAck->pRemainingData );
+
+        LogDebug( ( "Packet identifier %hu.",
+                    ( unsigned short ) *pPacketIdentifier ) );
+
+        /* Packet identifier cannot be 0. */
+        if( *pPacketIdentifier == 0U )
+        {
+            LogError( ( "Packet identifier cannot be 0." ) );
+            status = MQTTBadResponse;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t deserializePingresp( const MQTTPacketInfo_t * pPingresp )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    assert( pPingresp != NULL );
+
+    /* Check the "Remaining length" (second byte) of the received PINGRESP is 0. */
+    if( pPingresp->remainingLength != MQTT_PACKET_PINGRESP_REMAINING_LENGTH )
+    {
+        LogError( ( "PINGRESP does not have remaining length of %u.",
+                    MQTT_PACKET_PINGRESP_REMAINING_LENGTH ) );
+
+        status = MQTTBadResponse;
+    }
+
+    return status;
+}
+
+uint8_t * MQTT_SerializeConnectFixedHeader( uint8_t * pIndex,
+                                            const MQTTConnectInfo_t * pConnectInfo,
+                                            const MQTTPublishInfo_t * pWillInfo,
+                                            size_t remainingLength )
+{
+    uint8_t * pIndexLocal = pIndex;
+    uint8_t connectFlags = 0U;
+
+    /* The first byte in the CONNECT packet is the control packet type. */
+    *pIndexLocal = MQTT_PACKET_TYPE_CONNECT;
+    pIndexLocal++;
+
+    /* The remaining length of the CONNECT packet is encoded starting from the
+     * second byte. The remaining length does not include the length of the fixed
+     * header or the encoding of the remaining length. */
+    pIndexLocal = encodeRemainingLength( pIndexLocal, remainingLength );
+
+    /* The string "MQTT" is placed at the beginning of the CONNECT packet's variable
+     * header. This string is 4 bytes long. */
+    pIndexLocal = encodeString( pIndexLocal, "MQTT", 4 );
+
+    /* The MQTT protocol version is the second field of the variable header. */
+    *pIndexLocal = MQTT_VERSION_3_1_1;
+    pIndexLocal++;
+
+    /* Set the clean session flag if needed. */
+    if( pConnectInfo->cleanSession == true )
+    {
+        UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_CLEAN );
+    }
+
+    /* Set the flags for username and password if provided. */
+    if( pConnectInfo->pUserName != NULL )
+    {
+        UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_USERNAME );
+    }
+
+    if( pConnectInfo->pPassword != NULL )
+    {
+        UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_PASSWORD );
+    }
+
+    /* Set will flag if a Last Will and Testament is provided. */
+    if( pWillInfo != NULL )
+    {
+        UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL );
+
+        /* Flags only need to be changed for Will QoS 1 or 2. */
+        if( pWillInfo->qos == MQTTQoS1 )
+        {
+            UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS1 );
+        }
+        else if( pWillInfo->qos == MQTTQoS2 )
+        {
+            UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_QOS2 );
+        }
+        else
+        {
+            /* Empty else MISRA 15.7 */
+        }
+
+        if( pWillInfo->retain == true )
+        {
+            UINT8_SET_BIT( connectFlags, MQTT_CONNECT_FLAG_WILL_RETAIN );
+        }
+    }
+
+    *pIndexLocal = connectFlags;
+    pIndexLocal++;
+
+    /* Write the 2 bytes of the keep alive interval into the CONNECT packet. */
+    pIndexLocal[ 0 ] = UINT16_HIGH_BYTE( pConnectInfo->keepAliveSeconds );
+    pIndexLocal[ 1 ] = UINT16_LOW_BYTE( pConnectInfo->keepAliveSeconds );
+    pIndexLocal = &pIndexLocal[ 2 ];
+
+    return pIndexLocal;
+}
+/*-----------------------------------------------------------*/
+
+static void serializeConnectPacket( const MQTTConnectInfo_t * pConnectInfo,
+                                    const MQTTPublishInfo_t * pWillInfo,
+                                    size_t remainingLength,
+                                    const MQTTFixedBuffer_t * pFixedBuffer )
+{
+    uint8_t * pIndex = NULL;
+
+    assert( pConnectInfo != NULL );
+    assert( pFixedBuffer != NULL );
+    assert( pFixedBuffer->pBuffer != NULL );
+
+    pIndex = pFixedBuffer->pBuffer;
+
+    /* Serialize the header. */
+    pIndex = MQTT_SerializeConnectFixedHeader( pIndex,
+                                               pConnectInfo,
+                                               pWillInfo,
+                                               remainingLength );
+
+    /* Write the client identifier into the CONNECT packet. */
+    pIndex = encodeString( pIndex,
+                           pConnectInfo->pClientIdentifier,
+                           pConnectInfo->clientIdentifierLength );
+
+    /* Write the will topic name and message into the CONNECT packet if provided. */
+    if( pWillInfo != NULL )
+    {
+        pIndex = encodeString( pIndex,
+                               pWillInfo->pTopicName,
+                               pWillInfo->topicNameLength );
+
+        pIndex = encodeString( pIndex,
+                               pWillInfo->pPayload,
+                               ( uint16_t ) pWillInfo->payloadLength );
+    }
+
+    /* Encode the user name if provided. */
+    if( pConnectInfo->pUserName != NULL )
+    {
+        pIndex = encodeString( pIndex, pConnectInfo->pUserName, pConnectInfo->userNameLength );
+    }
+
+    /* Encode the password if provided. */
+    if( pConnectInfo->pPassword != NULL )
+    {
+        pIndex = encodeString( pIndex, pConnectInfo->pPassword, pConnectInfo->passwordLength );
+    }
+
+    LogDebug( ( "Length of serialized CONNECT packet is %lu.",
+                ( ( unsigned long ) ( pIndex - pFixedBuffer->pBuffer ) ) ) );
+
+    /* Ensure that the difference between the end and beginning of the buffer
+     * is less than the buffer size. */
+    assert( ( ( size_t ) ( pIndex - pFixedBuffer->pBuffer ) ) <= pFixedBuffer->size );
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo,
+                                        const MQTTPublishInfo_t * pWillInfo,
+                                        size_t * pRemainingLength,
+                                        size_t * pPacketSize )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    size_t remainingLength;
+
+    /* The CONNECT packet will always include a 10-byte variable header. */
+    size_t connectPacketSize = MQTT_PACKET_CONNECT_HEADER_SIZE;
+
+    /* Validate arguments. */
+    if( ( pConnectInfo == NULL ) || ( pRemainingLength == NULL ) ||
+        ( pPacketSize == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pConnectInfo=%p, "
+                    "pRemainingLength=%p, pPacketSize=%p.",
+                    ( void * ) pConnectInfo,
+                    ( void * ) pRemainingLength,
+                    ( void * ) pPacketSize ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pConnectInfo->clientIdentifierLength == 0U ) || ( pConnectInfo->pClientIdentifier == NULL ) )
+    {
+        LogError( ( "Mqtt_GetConnectPacketSize() client identifier must be set." ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pWillInfo != NULL ) && ( pWillInfo->payloadLength > ( size_t ) UINT16_MAX ) )
+    {
+        /* The MQTTPublishInfo_t is reused for the will message. The payload
+         * length for any other message could be larger than 65,535, but
+         * the will message length is required to be represented in 2 bytes.
+         * By bounding the payloadLength of the will message, the CONNECT
+         * packet will never be larger than 327699 bytes. */
+        LogError( ( "The Will Message length must not exceed %d. "
+                    "pWillInfo->payloadLength=%lu.",
+                    UINT16_MAX,
+                    ( unsigned long ) pWillInfo->payloadLength ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Add the length of the client identifier. */
+        connectPacketSize += pConnectInfo->clientIdentifierLength + sizeof( uint16_t );
+
+        /* Add the lengths of the will message and topic name if provided. */
+        if( pWillInfo != NULL )
+        {
+            connectPacketSize += pWillInfo->topicNameLength + sizeof( uint16_t ) +
+                                 pWillInfo->payloadLength + sizeof( uint16_t );
+        }
+
+        /* Add the lengths of the user name and password if provided. */
+        if( pConnectInfo->pUserName != NULL )
+        {
+            connectPacketSize += pConnectInfo->userNameLength + sizeof( uint16_t );
+        }
+
+        if( pConnectInfo->pPassword != NULL )
+        {
+            connectPacketSize += pConnectInfo->passwordLength + sizeof( uint16_t );
+        }
+
+        /* At this point, the "Remaining Length" field of the MQTT CONNECT packet has
+         * been calculated. */
+        remainingLength = connectPacketSize;
+
+        /* Calculate the full size of the MQTT CONNECT packet by adding the size of
+         * the "Remaining Length" field plus 1 byte for the "Packet Type" field. */
+        connectPacketSize += 1U + remainingLengthEncodedSize( connectPacketSize );
+
+        /* The connectPacketSize calculated from this function's parameters is
+         * guaranteed to be less than the maximum MQTT CONNECT packet size, which
+         * is 327700. If the maximum client identifier length, the maximum will
+         * message topic length, the maximum will topic payload length, the
+         * maximum username length, and the maximum password length are all present
+         * in the MQTT CONNECT packet, the total size will be calculated to be
+         * 327699:
+         * (variable length header)10 +
+         * (maximum client identifier length) 65535 + (encoded length) 2 +
+         * (maximum will message topic name length) 65535 + (encoded length)2 +
+         * (maximum will message payload length) 65535 + 2 +
+         * (maximum username length) 65535 + (encoded length) 2 +
+         * (maximum password length) 65535 + (encoded length) 2 +
+         * (packet type field length) 1 +
+         * (CONNECT packet encoded length) 3 = 327699 */
+
+        *pRemainingLength = remainingLength;
+        *pPacketSize = connectPacketSize;
+
+        LogDebug( ( "CONNECT packet remaining length=%lu and packet size=%lu.",
+                    ( unsigned long ) *pRemainingLength,
+                    ( unsigned long ) *pPacketSize ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo,
+                                    const MQTTPublishInfo_t * pWillInfo,
+                                    size_t remainingLength,
+                                    const MQTTFixedBuffer_t * pFixedBuffer )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    size_t connectPacketSize = 0;
+
+    /* Validate arguments. */
+    if( ( pConnectInfo == NULL ) || ( pFixedBuffer == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pConnectInfo=%p, "
+                    "pFixedBuffer=%p.",
+                    ( void * ) pConnectInfo,
+                    ( void * ) pFixedBuffer ) );
+        status = MQTTBadParameter;
+    }
+    /* A buffer must be configured for serialization. */
+    else if( pFixedBuffer->pBuffer == NULL )
+    {
+        LogError( ( "Argument cannot be NULL: pFixedBuffer->pBuffer is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pWillInfo != NULL ) && ( pWillInfo->pTopicName == NULL ) )
+    {
+        LogError( ( "pWillInfo->pTopicName cannot be NULL if Will is present." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Calculate CONNECT packet size. Overflow in in this addition is not checked
+         * because it is part of the API contract to call Mqtt_GetConnectPacketSize()
+         * before this function. */
+        connectPacketSize = remainingLength + remainingLengthEncodedSize( remainingLength ) + 1U;
+
+        /* Check that the full packet size fits within the given buffer. */
+        if( connectPacketSize > pFixedBuffer->size )
+        {
+            LogError( ( "Buffer size of %lu is not sufficient to hold "
+                        "serialized CONNECT packet of size of %lu.",
+                        ( unsigned long ) pFixedBuffer->size,
+                        ( unsigned long ) connectPacketSize ) );
+            status = MQTTNoMemory;
+        }
+        else
+        {
+            serializeConnectPacket( pConnectInfo,
+                                    pWillInfo,
+                                    remainingLength,
+                                    pFixedBuffer );
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                          size_t subscriptionCount,
+                                          size_t * pRemainingLength,
+                                          size_t * pPacketSize )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    /* Validate parameters. */
+    if( ( pSubscriptionList == NULL ) || ( pRemainingLength == NULL ) ||
+        ( pPacketSize == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pSubscriptionList=%p, "
+                    "pRemainingLength=%p, pPacketSize=%p.",
+                    ( void * ) pSubscriptionList,
+                    ( void * ) pRemainingLength,
+                    ( void * ) pPacketSize ) );
+        status = MQTTBadParameter;
+    }
+    else if( subscriptionCount == 0U )
+    {
+        LogError( ( "subscriptionCount is 0." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Calculate the MQTT SUBSCRIBE packet size. */
+        status = calculateSubscriptionPacketSize( pSubscriptionList,
+                                                  subscriptionCount,
+                                                  pRemainingLength,
+                                                  pPacketSize,
+                                                  MQTT_SUBSCRIBE );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+uint8_t * MQTT_SerializeSubscribeHeader( size_t remainingLength,
+                                         uint8_t * pIndex,
+                                         uint16_t packetId )
+{
+    uint8_t * pIterator = pIndex;
+
+    /* The first byte in SUBSCRIBE is the packet type. */
+    *pIterator = MQTT_PACKET_TYPE_SUBSCRIBE;
+    pIterator++;
+
+    /* Encode the "Remaining length" starting from the second byte. */
+    pIterator = encodeRemainingLength( pIterator, remainingLength );
+
+    /* Place the packet identifier into the SUBSCRIBE packet. */
+    pIterator[ 0 ] = UINT16_HIGH_BYTE( packetId );
+    pIterator[ 1 ] = UINT16_LOW_BYTE( packetId );
+    /* Advance the pointer. */
+    pIterator = &pIterator[ 2 ];
+
+    return pIterator;
+}
+
+/*-----------------------------------------------------------*/
+
+uint8_t * MQTT_SerializeUnsubscribeHeader( size_t remainingLength,
+                                           uint8_t * pIndex,
+                                           uint16_t packetId )
+{
+    uint8_t * pIterator = pIndex;
+
+    /* The first byte in UNSUBSCRIBE is the packet type. */
+    *pIterator = MQTT_PACKET_TYPE_UNSUBSCRIBE;
+    pIterator++;
+
+    /* Encode the "Remaining length" starting from the second byte. */
+    pIterator = encodeRemainingLength( pIterator, remainingLength );
+
+    /* Place the packet identifier into the SUBSCRIBE packet. */
+    pIterator[ 0 ] = UINT16_HIGH_BYTE( packetId );
+    pIterator[ 1 ] = UINT16_LOW_BYTE( packetId );
+    /* Increment the pointer. */
+    pIterator = &pIterator[ 2 ];
+
+    return pIterator;
+}
+
+MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                      size_t subscriptionCount,
+                                      uint16_t packetId,
+                                      size_t remainingLength,
+                                      const MQTTFixedBuffer_t * pFixedBuffer )
+{
+    size_t i = 0;
+    uint8_t * pIndex = NULL;
+
+    /* Validate all the parameters. */
+    MQTTStatus_t status =
+        validateSubscriptionSerializeParams( pSubscriptionList,
+                                             subscriptionCount,
+                                             packetId,
+                                             remainingLength,
+                                             pFixedBuffer );
+
+    if( status == MQTTSuccess )
+    {
+        pIndex = pFixedBuffer->pBuffer;
+
+        pIndex = MQTT_SerializeSubscribeHeader( remainingLength,
+                                                pIndex,
+                                                packetId );
+
+        /* Serialize each subscription topic filter and QoS. */
+        for( i = 0; i < subscriptionCount; i++ )
+        {
+            pIndex = encodeString( pIndex,
+                                   pSubscriptionList[ i ].pTopicFilter,
+                                   pSubscriptionList[ i ].topicFilterLength );
+
+            /* Place the QoS in the SUBSCRIBE packet. */
+            *pIndex = ( uint8_t ) ( pSubscriptionList[ i ].qos );
+            pIndex++;
+        }
+
+        LogDebug( ( "Length of serialized SUBSCRIBE packet is %lu.",
+                    ( ( unsigned long ) ( pIndex - pFixedBuffer->pBuffer ) ) ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                            size_t subscriptionCount,
+                                            size_t * pRemainingLength,
+                                            size_t * pPacketSize )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    /* Validate parameters. */
+    if( ( pSubscriptionList == NULL ) || ( pRemainingLength == NULL ) ||
+        ( pPacketSize == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pSubscriptionList=%p, "
+                    "pRemainingLength=%p, pPacketSize=%p.",
+                    ( void * ) pSubscriptionList,
+                    ( void * ) pRemainingLength,
+                    ( void * ) pPacketSize ) );
+        status = MQTTBadParameter;
+    }
+    else if( subscriptionCount == 0U )
+    {
+        LogError( ( "Subscription count is 0." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Calculate the MQTT UNSUBSCRIBE packet size. */
+        status = calculateSubscriptionPacketSize( pSubscriptionList,
+                                                  subscriptionCount,
+                                                  pRemainingLength,
+                                                  pPacketSize,
+                                                  MQTT_UNSUBSCRIBE );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                        size_t subscriptionCount,
+                                        uint16_t packetId,
+                                        size_t remainingLength,
+                                        const MQTTFixedBuffer_t * pFixedBuffer )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    size_t i = 0;
+    uint8_t * pIndex = NULL;
+
+    /* Validate all the parameters. */
+    status = validateSubscriptionSerializeParams( pSubscriptionList,
+                                                  subscriptionCount,
+                                                  packetId,
+                                                  remainingLength,
+                                                  pFixedBuffer );
+
+    if( status == MQTTSuccess )
+    {
+        /* Get the start of the buffer to the iterator variable. */
+        pIndex = pFixedBuffer->pBuffer;
+
+        pIndex = MQTT_SerializeUnsubscribeHeader( remainingLength, pIndex, packetId );
+
+        /* Serialize each subscription topic filter. */
+        for( i = 0; i < subscriptionCount; i++ )
+        {
+            pIndex = encodeString( pIndex,
+                                   pSubscriptionList[ i ].pTopicFilter,
+                                   pSubscriptionList[ i ].topicFilterLength );
+        }
+
+        LogDebug( ( "Length of serialized UNSUBSCRIBE packet is %lu.",
+                    ( ( unsigned long ) ( pIndex - pFixedBuffer->pBuffer ) ) ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
+                                        size_t * pRemainingLength,
+                                        size_t * pPacketSize )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( ( pPublishInfo == NULL ) || ( pRemainingLength == NULL ) || ( pPacketSize == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pPublishInfo=%p, "
+                    "pRemainingLength=%p, pPacketSize=%p.",
+                    ( void * ) pPublishInfo,
+                    ( void * ) pRemainingLength,
+                    ( void * ) pPacketSize ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0U ) )
+    {
+        LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, "
+                    "topicNameLength=%hu.",
+                    ( void * ) pPublishInfo->pTopicName,
+                    ( unsigned short ) pPublishInfo->topicNameLength ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Calculate the "Remaining length" field and total packet size. If it exceeds
+         * what is allowed in the MQTT standard, return an error. */
+        if( calculatePublishPacketSize( pPublishInfo, pRemainingLength, pPacketSize ) == false )
+        {
+            LogError( ( "PUBLISH packet remaining length exceeds %lu, which is the "
+                        "maximum size allowed by MQTT 3.1.1.",
+                        MQTT_MAX_REMAINING_LENGTH ) );
+            status = MQTTBadParameter;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo,
+                                    uint16_t packetId,
+                                    size_t remainingLength,
+                                    const MQTTFixedBuffer_t * pFixedBuffer )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    size_t packetSize = 0;
+
+    if( ( pFixedBuffer == NULL ) || ( pPublishInfo == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pFixedBuffer=%p, "
+                    "pPublishInfo=%p.",
+                    ( void * ) pFixedBuffer,
+                    ( void * ) pPublishInfo ) );
+        status = MQTTBadParameter;
+    }
+    /* A buffer must be configured for serialization. */
+    else if( pFixedBuffer->pBuffer == NULL )
+    {
+        LogError( ( "Argument cannot be NULL: pFixedBuffer->pBuffer is NULL." ) );
+        status = MQTTBadParameter;
+    }
+
+    /* For serializing a publish, if there exists a payload, then the buffer
+     * cannot be NULL. */
+    else if( ( pPublishInfo->payloadLength > 0U ) && ( pPublishInfo->pPayload == NULL ) )
+    {
+        LogError( ( "A nonzero payload length requires a non-NULL payload: "
+                    "payloadLength=%lu, pPayload=%p.",
+                    ( unsigned long ) pPublishInfo->payloadLength,
+                    pPublishInfo->pPayload ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0U ) )
+    {
+        LogError( ( "Invalid topic name for PUBLISH: pTopicName=%p, "
+                    "topicNameLength=%hu.",
+                    ( void * ) pPublishInfo->pTopicName,
+                    ( unsigned short ) pPublishInfo->topicNameLength ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pPublishInfo->qos != MQTTQoS0 ) && ( packetId == 0U ) )
+    {
+        LogError( ( "Packet ID is 0 for PUBLISH with QoS=%u.",
+                    ( unsigned int ) pPublishInfo->qos ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pPublishInfo->dup == true ) && ( pPublishInfo->qos == MQTTQoS0 ) )
+    {
+        LogError( ( "Duplicate flag is set for PUBLISH with Qos 0." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Length of serialized packet = First byte
+         *                                + Length of encoded remaining length
+         *                                + Remaining length. */
+        packetSize = 1U + remainingLengthEncodedSize( remainingLength )
+                     + remainingLength;
+    }
+
+    if( ( status == MQTTSuccess ) && ( packetSize > pFixedBuffer->size ) )
+    {
+        LogError( ( "Buffer size of %lu is not sufficient to hold "
+                    "serialized PUBLISH packet of size of %lu.",
+                    ( unsigned long ) pFixedBuffer->size,
+                    ( unsigned long ) packetSize ) );
+        status = MQTTNoMemory;
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Serialize publish with header and payload. */
+        serializePublishCommon( pPublishInfo,
+                                remainingLength,
+                                packetId,
+                                pFixedBuffer,
+                                true );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo,
+                                          uint16_t packetId,
+                                          size_t remainingLength,
+                                          const MQTTFixedBuffer_t * pFixedBuffer,
+                                          size_t * pHeaderSize )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    size_t packetSize = 0;
+
+    if( ( pFixedBuffer == NULL ) || ( pPublishInfo == NULL ) ||
+        ( pHeaderSize == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pFixedBuffer=%p, "
+                    "pPublishInfo=%p, pHeaderSize=%p.",
+                    ( void * ) pFixedBuffer,
+                    ( void * ) pPublishInfo,
+                    ( void * ) pHeaderSize ) );
+        status = MQTTBadParameter;
+    }
+    /* A buffer must be configured for serialization. */
+    else if( pFixedBuffer->pBuffer == NULL )
+    {
+        LogError( ( "Argument cannot be NULL: pFixedBuffer->pBuffer is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pPublishInfo->pTopicName == NULL ) || ( pPublishInfo->topicNameLength == 0U ) )
+    {
+        LogError( ( "Invalid topic name for publish: pTopicName=%p, "
+                    "topicNameLength=%hu.",
+                    ( void * ) pPublishInfo->pTopicName,
+                    ( unsigned short ) pPublishInfo->topicNameLength ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pPublishInfo->qos != MQTTQoS0 ) && ( packetId == 0U ) )
+    {
+        LogError( ( "Packet Id is 0 for publish with QoS=%hu.",
+                    ( unsigned short ) pPublishInfo->qos ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pPublishInfo->dup == true ) && ( pPublishInfo->qos == MQTTQoS0 ) )
+    {
+        LogError( ( "Duplicate flag is set for PUBLISH with Qos 0." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Length of serialized packet = First byte
+         *                               + Length of encoded remaining length
+         *                               + Remaining length
+         *                               - Payload Length.
+         */
+        packetSize = 1U + remainingLengthEncodedSize( remainingLength )
+                     + remainingLength
+                     - pPublishInfo->payloadLength;
+    }
+
+    if( ( status == MQTTSuccess ) && ( packetSize > pFixedBuffer->size ) )
+    {
+        LogError( ( "Buffer size of %lu is not sufficient to hold "
+                    "serialized PUBLISH header packet of size of %lu.",
+                    ( unsigned long ) pFixedBuffer->size,
+                    ( unsigned long ) ( packetSize - pPublishInfo->payloadLength ) ) );
+        status = MQTTNoMemory;
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Serialize publish without copying the payload. */
+        serializePublishCommon( pPublishInfo,
+                                remainingLength,
+                                packetId,
+                                pFixedBuffer,
+                                false );
+
+        /* Header size is the same as calculated packet size. */
+        *pHeaderSize = packetSize;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializeAck( const MQTTFixedBuffer_t * pFixedBuffer,
+                                uint8_t packetType,
+                                uint16_t packetId )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( pFixedBuffer == NULL )
+    {
+        LogError( ( "Provided buffer is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( pFixedBuffer->pBuffer == NULL )
+    {
+        LogError( ( "pFixedBuffer->pBuffer cannot be NULL." ) );
+        status = MQTTBadParameter;
+    }
+    /* The buffer must be able to fit 4 bytes for the packet. */
+    else if( pFixedBuffer->size < MQTT_PUBLISH_ACK_PACKET_SIZE )
+    {
+        LogError( ( "Insufficient memory for packet." ) );
+        status = MQTTNoMemory;
+    }
+    else if( packetId == 0U )
+    {
+        LogError( ( "Packet ID cannot be 0." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        switch( packetType )
+        {
+            /* Only publish acks are serialized by the client. */
+            case MQTT_PACKET_TYPE_PUBACK:
+            case MQTT_PACKET_TYPE_PUBREC:
+            case MQTT_PACKET_TYPE_PUBREL:
+            case MQTT_PACKET_TYPE_PUBCOMP:
+                pFixedBuffer->pBuffer[ 0 ] = packetType;
+                pFixedBuffer->pBuffer[ 1 ] = MQTT_PACKET_SIMPLE_ACK_REMAINING_LENGTH;
+                pFixedBuffer->pBuffer[ 2 ] = UINT16_HIGH_BYTE( packetId );
+                pFixedBuffer->pBuffer[ 3 ] = UINT16_LOW_BYTE( packetId );
+                break;
+
+            default:
+                LogError( ( "Packet type is not a publish ACK: Packet type=%02x",
+                            ( unsigned int ) packetType ) );
+                status = MQTTBadParameter;
+                break;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetDisconnectPacketSize( size_t * pPacketSize )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( pPacketSize == NULL )
+    {
+        LogError( ( "pPacketSize is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* MQTT DISCONNECT packets always have the same size. */
+        *pPacketSize = MQTT_DISCONNECT_PACKET_SIZE;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializeDisconnect( const MQTTFixedBuffer_t * pFixedBuffer )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    /* Validate arguments. */
+    if( pFixedBuffer == NULL )
+    {
+        LogError( ( "pFixedBuffer cannot be NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( pFixedBuffer->pBuffer == NULL )
+    {
+        LogError( ( "pFixedBuffer->pBuffer cannot be NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Empty else MISRA 15.7 */
+    }
+
+    if( status == MQTTSuccess )
+    {
+        if( pFixedBuffer->size < MQTT_DISCONNECT_PACKET_SIZE )
+        {
+            LogError( ( "Buffer size of %lu is not sufficient to hold "
+                        "serialized DISCONNECT packet of size of %lu.",
+                        ( unsigned long ) pFixedBuffer->size,
+                        MQTT_DISCONNECT_PACKET_SIZE ) );
+            status = MQTTNoMemory;
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        pFixedBuffer->pBuffer[ 0 ] = MQTT_PACKET_TYPE_DISCONNECT;
+        pFixedBuffer->pBuffer[ 1 ] = MQTT_DISCONNECT_REMAINING_LENGTH;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetPingreqPacketSize( size_t * pPacketSize )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( pPacketSize == NULL )
+    {
+        LogError( ( "pPacketSize is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* MQTT PINGREQ packets always have the same size. */
+        *pPacketSize = MQTT_PACKET_PINGREQ_SIZE;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_SerializePingreq( const MQTTFixedBuffer_t * pFixedBuffer )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( pFixedBuffer == NULL )
+    {
+        LogError( ( "pFixedBuffer is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( pFixedBuffer->pBuffer == NULL )
+    {
+        LogError( ( "pFixedBuffer->pBuffer cannot be NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Empty else MISRA 15.7 */
+    }
+
+    if( status == MQTTSuccess )
+    {
+        if( pFixedBuffer->size < MQTT_PACKET_PINGREQ_SIZE )
+        {
+            LogError( ( "Buffer size of %lu is not sufficient to hold "
+                        "serialized PINGREQ packet of size of %lu.",
+                        ( unsigned long ) pFixedBuffer->size,
+                        MQTT_PACKET_PINGREQ_SIZE ) );
+            status = MQTTNoMemory;
+        }
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Ping request packets are always the same. */
+        pFixedBuffer->pBuffer[ 0 ] = MQTT_PACKET_TYPE_PINGREQ;
+        pFixedBuffer->pBuffer[ 1 ] = 0x00;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket,
+                                      uint16_t * pPacketId,
+                                      MQTTPublishInfo_t * pPublishInfo )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( ( pIncomingPacket == NULL ) || ( pPacketId == NULL ) || ( pPublishInfo == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pIncomingPacket=%p, "
+                    "pPacketId=%p, pPublishInfo=%p",
+                    ( void * ) pIncomingPacket,
+                    ( void * ) pPacketId,
+                    ( void * ) pPublishInfo ) );
+        status = MQTTBadParameter;
+    }
+    else if( ( pIncomingPacket->type & 0xF0U ) != MQTT_PACKET_TYPE_PUBLISH )
+    {
+        LogError( ( "Packet is not publish. Packet type: %02x.",
+                    ( unsigned int ) pIncomingPacket->type ) );
+        status = MQTTBadParameter;
+    }
+    else if( pIncomingPacket->pRemainingData == NULL )
+    {
+        LogError( ( "Argument cannot be NULL: "
+                    "pIncomingPacket->pRemainingData is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        status = deserializePublish( pIncomingPacket, pPacketId, pPublishInfo );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket,
+                                  uint16_t * pPacketId,
+                                  bool * pSessionPresent )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( pIncomingPacket == NULL )
+    {
+        LogError( ( "pIncomingPacket cannot be NULL." ) );
+        status = MQTTBadParameter;
+    }
+
+    /* Pointer for packet identifier cannot be NULL for packets other than
+     * CONNACK and PINGRESP. */
+    else if( ( pPacketId == NULL ) &&
+             ( ( pIncomingPacket->type != MQTT_PACKET_TYPE_CONNACK ) &&
+               ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) ) )
+    {
+        LogError( ( "pPacketId cannot be NULL for packet type %02x.",
+                    ( unsigned int ) pIncomingPacket->type ) );
+        status = MQTTBadParameter;
+    }
+    /* Pointer for session present cannot be NULL for CONNACK. */
+    else if( ( pSessionPresent == NULL ) &&
+             ( pIncomingPacket->type == MQTT_PACKET_TYPE_CONNACK ) )
+    {
+        LogError( ( "pSessionPresent cannot be NULL for CONNACK packet." ) );
+        status = MQTTBadParameter;
+    }
+
+    /* Pointer for remaining data cannot be NULL for packets other
+     * than PINGRESP. */
+    else if( ( pIncomingPacket->pRemainingData == NULL ) &&
+             ( pIncomingPacket->type != MQTT_PACKET_TYPE_PINGRESP ) )
+    {
+        LogError( ( "Remaining data of incoming packet is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Make sure response packet is a valid ack. */
+        switch( pIncomingPacket->type )
+        {
+            case MQTT_PACKET_TYPE_CONNACK:
+                status = deserializeConnack( pIncomingPacket, pSessionPresent );
+                break;
+
+            case MQTT_PACKET_TYPE_SUBACK:
+                status = deserializeSuback( pIncomingPacket, pPacketId );
+                break;
+
+            case MQTT_PACKET_TYPE_PINGRESP:
+                status = deserializePingresp( pIncomingPacket );
+                break;
+
+            case MQTT_PACKET_TYPE_UNSUBACK:
+            case MQTT_PACKET_TYPE_PUBACK:
+            case MQTT_PACKET_TYPE_PUBREC:
+            case MQTT_PACKET_TYPE_PUBREL:
+            case MQTT_PACKET_TYPE_PUBCOMP:
+                status = deserializeSimpleAck( pIncomingPacket, pPacketId );
+                break;
+
+            /* Any other packet type is invalid. */
+            default:
+                LogError( ( "IotMqtt_DeserializeResponse() called with unknown packet type:(%02x).",
+                            ( unsigned int ) pIncomingPacket->type ) );
+                status = MQTTBadResponse;
+                break;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_GetIncomingPacketTypeAndLength( TransportRecv_t readFunc,
+                                                  NetworkContext_t * pNetworkContext,
+                                                  MQTTPacketInfo_t * pIncomingPacket )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    int32_t bytesReceived = 0;
+
+    if( pIncomingPacket == NULL )
+    {
+        LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Read a single byte. */
+        bytesReceived = readFunc( pNetworkContext,
+                                  &( pIncomingPacket->type ),
+                                  1U );
+    }
+
+    if( bytesReceived == 1 )
+    {
+        /* Check validity. */
+        if( incomingPacketValid( pIncomingPacket->type ) == true )
+        {
+            pIncomingPacket->remainingLength = getRemainingLength( readFunc,
+                                                                   pNetworkContext );
+
+            if( pIncomingPacket->remainingLength == MQTT_REMAINING_LENGTH_INVALID )
+            {
+                LogError( ( "Incoming packet remaining length invalid." ) );
+                status = MQTTBadResponse;
+            }
+        }
+        else
+        {
+            LogError( ( "Incoming packet invalid: Packet type=%u.",
+                        ( unsigned int ) pIncomingPacket->type ) );
+            status = MQTTBadResponse;
+        }
+    }
+    else if( ( status != MQTTBadParameter ) && ( bytesReceived == 0 ) )
+    {
+        status = MQTTNoDataAvailable;
+    }
+
+    /* If the input packet was valid, then any other number of bytes received is
+     * a failure. */
+    else if( status != MQTTBadParameter )
+    {
+        LogError( ( "A single byte was not read from the transport: "
+                    "transportStatus=%ld.",
+                    ( long int ) bytesReceived ) );
+        status = MQTTRecvFailed;
+    }
+    else
+    {
+        /* Empty else MISRA 15.7 */
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_ProcessIncomingPacketTypeAndLength( const uint8_t * pBuffer,
+                                                      const size_t * pIndex,
+                                                      MQTTPacketInfo_t * pIncomingPacket )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( pIncomingPacket == NULL )
+    {
+        LogError( ( "Invalid parameter: pIncomingPacket is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( pIndex == NULL )
+    {
+        LogError( ( "Invalid parameter: pIndex is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    else if( pBuffer == NULL )
+    {
+        LogError( ( "Invalid parameter: pBuffer is NULL." ) );
+        status = MQTTBadParameter;
+    }
+    /* There should be at least one byte in the buffer */
+    else if( *pIndex < 1U )
+    {
+        /* No data is available. There are 0 bytes received from the network
+         * receive function. */
+        status = MQTTNoDataAvailable;
+    }
+    else
+    {
+        /* At least one byte is present which should be deciphered. */
+        pIncomingPacket->type = pBuffer[ 0 ];
+    }
+
+    if( status == MQTTSuccess )
+    {
+        /* Check validity. */
+        if( incomingPacketValid( pIncomingPacket->type ) == true )
+        {
+            status = processRemainingLength( pBuffer,
+                                             pIndex,
+                                             pIncomingPacket );
+        }
+        else
+        {
+            LogError( ( "Incoming packet invalid: Packet type=%u.",
+                        ( unsigned int ) pIncomingPacket->type ) );
+            status = MQTTBadResponse;
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
diff --git a/project/coreMQTT/coreMQTT/core_mqtt_serializer.h b/project/coreMQTT/coreMQTT/core_mqtt_serializer.h
new file mode 100644
index 0000000..d1b179e
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/core_mqtt_serializer.h
@@ -0,0 +1,1306 @@
+/*
+ * coreMQTT v2.1.1
+ * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_mqtt_serializer.h
+ * @brief User-facing functions for serializing and deserializing MQTT 3.1.1
+ * packets. This header should be included for building a lighter weight MQTT
+ * client than the managed CSDK MQTT library API in core_mqtt.h, by using the
+ * serializer and de-serializer functions exposed in this file's API.
+ */
+#ifndef CORE_MQTT_SERIALIZER_H
+#define CORE_MQTT_SERIALIZER_H
+
+#include <stddef.h>
+#include <stdint.h>
+#include <stdbool.h>
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    extern "C" {
+#endif
+/* *INDENT-ON */
+
+/* MQTT_DO_NOT_USE_CUSTOM_CONFIG allows building the MQTT library
+ * without a custom config. If a custom config is provided, the
+ * MQTT_DO_NOT_USE_CUSTOM_CONFIG macro should not be defined. */
+#ifndef MQTT_DO_NOT_USE_CUSTOM_CONFIG
+    /* Include custom config file before other headers. */
+    #include "core_mqtt_config.h"
+#endif
+
+/* Include config defaults header to get default values of configs not
+ * defined in core_mqtt_config.h file. */
+#include "core_mqtt_config_defaults.h"
+
+#include "transport_interface.h"
+
+/* MQTT packet types. */
+
+/**
+ * @addtogroup mqtt_constants
+ * @{
+ */
+#define MQTT_PACKET_TYPE_CONNECT        ( ( uint8_t ) 0x10U )  /**< @brief CONNECT (client-to-server). */
+#define MQTT_PACKET_TYPE_CONNACK        ( ( uint8_t ) 0x20U )  /**< @brief CONNACK (server-to-client). */
+#define MQTT_PACKET_TYPE_PUBLISH        ( ( uint8_t ) 0x30U )  /**< @brief PUBLISH (bidirectional). */
+#define MQTT_PACKET_TYPE_PUBACK         ( ( uint8_t ) 0x40U )  /**< @brief PUBACK (bidirectional). */
+#define MQTT_PACKET_TYPE_PUBREC         ( ( uint8_t ) 0x50U )  /**< @brief PUBREC (bidirectional). */
+#define MQTT_PACKET_TYPE_PUBREL         ( ( uint8_t ) 0x62U )  /**< @brief PUBREL (bidirectional). */
+#define MQTT_PACKET_TYPE_PUBCOMP        ( ( uint8_t ) 0x70U )  /**< @brief PUBCOMP (bidirectional). */
+#define MQTT_PACKET_TYPE_SUBSCRIBE      ( ( uint8_t ) 0x82U )  /**< @brief SUBSCRIBE (client-to-server). */
+#define MQTT_PACKET_TYPE_SUBACK         ( ( uint8_t ) 0x90U )  /**< @brief SUBACK (server-to-client). */
+#define MQTT_PACKET_TYPE_UNSUBSCRIBE    ( ( uint8_t ) 0xA2U )  /**< @brief UNSUBSCRIBE (client-to-server). */
+#define MQTT_PACKET_TYPE_UNSUBACK       ( ( uint8_t ) 0xB0U )  /**< @brief UNSUBACK (server-to-client). */
+#define MQTT_PACKET_TYPE_PINGREQ        ( ( uint8_t ) 0xC0U )  /**< @brief PINGREQ (client-to-server). */
+#define MQTT_PACKET_TYPE_PINGRESP       ( ( uint8_t ) 0xD0U )  /**< @brief PINGRESP (server-to-client). */
+#define MQTT_PACKET_TYPE_DISCONNECT     ( ( uint8_t ) 0xE0U )  /**< @brief DISCONNECT (client-to-server). */
+/** @} */
+
+/**
+ * @ingroup mqtt_constants
+ * @brief The size of MQTT PUBACK, PUBREC, PUBREL, and PUBCOMP packets, per MQTT spec.
+ */
+#define MQTT_PUBLISH_ACK_PACKET_SIZE    ( 4UL )
+
+/* Structures defined in this file. */
+struct MQTTFixedBuffer;
+struct MQTTConnectInfo;
+struct MQTTSubscribeInfo;
+struct MQTTPublishInfo;
+struct MQTTPacketInfo;
+
+/**
+ * @ingroup mqtt_enum_types
+ * @brief Return codes from MQTT functions.
+ */
+typedef enum MQTTStatus
+{
+    MQTTSuccess = 0,      /**< Function completed successfully. */
+    MQTTBadParameter,     /**< At least one parameter was invalid. */
+    MQTTNoMemory,         /**< A provided buffer was too small. */
+    MQTTSendFailed,       /**< The transport send function failed. */
+    MQTTRecvFailed,       /**< The transport receive function failed. */
+    MQTTBadResponse,      /**< An invalid packet was received from the server. */
+    MQTTServerRefused,    /**< The server refused a CONNECT or SUBSCRIBE. */
+    MQTTNoDataAvailable,  /**< No data available from the transport interface. */
+    MQTTIllegalState,     /**< An illegal state in the state record. */
+    MQTTStateCollision,   /**< A collision with an existing state record entry. */
+    MQTTKeepAliveTimeout, /**< Timeout while waiting for PINGRESP. */
+    MQTTNeedMoreBytes     /**< MQTT_ProcessLoop/MQTT_ReceiveLoop has received
+                          incomplete data; it should be called again (probably after
+                          a delay). */
+} MQTTStatus_t;
+
+/**
+ * @ingroup mqtt_enum_types
+ * @brief MQTT Quality of Service values.
+ */
+typedef enum MQTTQoS
+{
+    MQTTQoS0 = 0, /**< Delivery at most once. */
+    MQTTQoS1 = 1, /**< Delivery at least once. */
+    MQTTQoS2 = 2  /**< Delivery exactly once. */
+} MQTTQoS_t;
+
+/**
+ * @ingroup mqtt_struct_types
+ * @brief Buffer passed to MQTT library.
+ *
+ * These buffers are not copied and must remain in scope for the duration of the
+ * MQTT operation.
+ */
+typedef struct MQTTFixedBuffer
+{
+    uint8_t * pBuffer; /**< @brief Pointer to buffer. */
+    size_t size;       /**< @brief Size of buffer. */
+} MQTTFixedBuffer_t;
+
+/**
+ * @ingroup mqtt_struct_types
+ * @brief MQTT CONNECT packet parameters.
+ */
+typedef struct MQTTConnectInfo
+{
+    /**
+     * @brief Whether to establish a new, clean session or resume a previous session.
+     */
+    bool cleanSession;
+
+    /**
+     * @brief MQTT keep alive period.
+     */
+    uint16_t keepAliveSeconds;
+
+    /**
+     * @brief MQTT client identifier. Must be unique per client.
+     */
+    const char * pClientIdentifier;
+
+    /**
+     * @brief Length of the client identifier.
+     */
+    uint16_t clientIdentifierLength;
+
+    /**
+     * @brief MQTT user name. Set to NULL if not used.
+     */
+    const char * pUserName;
+
+    /**
+     * @brief Length of MQTT user name. Set to 0 if not used.
+     */
+    uint16_t userNameLength;
+
+    /**
+     * @brief MQTT password. Set to NULL if not used.
+     */
+    const char * pPassword;
+
+    /**
+     * @brief Length of MQTT password. Set to 0 if not used.
+     */
+    uint16_t passwordLength;
+} MQTTConnectInfo_t;
+
+/**
+ * @ingroup mqtt_struct_types
+ * @brief MQTT SUBSCRIBE packet parameters.
+ */
+typedef struct MQTTSubscribeInfo
+{
+    /**
+     * @brief Quality of Service for subscription.
+     */
+    MQTTQoS_t qos;
+
+    /**
+     * @brief Topic filter to subscribe to.
+     */
+    const char * pTopicFilter;
+
+    /**
+     * @brief Length of subscription topic filter.
+     */
+    uint16_t topicFilterLength;
+} MQTTSubscribeInfo_t;
+
+/**
+ * @ingroup mqtt_struct_types
+ * @brief MQTT PUBLISH packet parameters.
+ */
+typedef struct MQTTPublishInfo
+{
+    /**
+     * @brief Quality of Service for message.
+     */
+    MQTTQoS_t qos;
+
+    /**
+     * @brief Whether this is a retained message.
+     */
+    bool retain;
+
+    /**
+     * @brief Whether this is a duplicate publish message.
+     */
+    bool dup;
+
+    /**
+     * @brief Topic name on which the message is published.
+     */
+    const char * pTopicName;
+
+    /**
+     * @brief Length of topic name.
+     */
+    uint16_t topicNameLength;
+
+    /**
+     * @brief Message payload.
+     */
+    const void * pPayload;
+
+    /**
+     * @brief Message payload length.
+     */
+    size_t payloadLength;
+} MQTTPublishInfo_t;
+
+/**
+ * @ingroup mqtt_struct_types
+ * @brief MQTT incoming packet parameters.
+ */
+typedef struct MQTTPacketInfo
+{
+    /**
+     * @brief Type of incoming MQTT packet.
+     */
+    uint8_t type;
+
+    /**
+     * @brief Remaining serialized data in the MQTT packet.
+     */
+    uint8_t * pRemainingData;
+
+    /**
+     * @brief Length of remaining serialized data.
+     */
+    size_t remainingLength;
+
+    /**
+     * @brief The length of the MQTT header including the type and length.
+     */
+    size_t headerLength;
+} MQTTPacketInfo_t;
+
+/**
+ * @brief Get the size and Remaining Length of an MQTT CONNECT packet.
+ *
+ * This function must be called before #MQTT_SerializeConnect in order to get
+ * the size of the MQTT CONNECT packet that is generated from #MQTTConnectInfo_t
+ * and optional #MQTTPublishInfo_t. The size of the #MQTTFixedBuffer_t supplied
+ * to #MQTT_SerializeConnect must be at least @p pPacketSize. The provided
+ * @p pConnectInfo and @p pWillInfo are valid for serialization with
+ * #MQTT_SerializeConnect only if this function returns #MQTTSuccess. The
+ * remaining length returned in @p pRemainingLength and the packet size returned
+ * in @p pPacketSize are valid only if this function returns #MQTTSuccess.
+ *
+ * @param[in] pConnectInfo MQTT CONNECT packet parameters.
+ * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT CONNECT packet.
+ * @param[out] pPacketSize The total size of the MQTT CONNECT packet.
+ *
+ * @return #MQTTBadParameter if the packet would exceed the size allowed by the
+ * MQTT spec; #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTConnectInfo_t connectInfo = { 0 };
+ * MQTTPublishInfo_t willInfo = { 0 };
+ * size_t remainingLength = 0, packetSize = 0;
+ *
+ * // Initialize the connection info, the details are out of scope for this example.
+ * initializeConnectInfo( &connectInfo );
+ *
+ * // Initialize the optional will info, the details are out of scope for this example.
+ * initializeWillInfo( &willInfo );
+ *
+ * // Get the size requirement for the connect packet.
+ * status = MQTT_GetConnectPacketSize(
+ *      &connectInfo, &willInfo, &remainingLength, &packetSize
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The application should allocate or use a static #MQTTFixedBuffer_t
+ *      // of size >= packetSize to serialize the connect request.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_getconnectpacketsize] */
+MQTTStatus_t MQTT_GetConnectPacketSize( const MQTTConnectInfo_t * pConnectInfo,
+                                        const MQTTPublishInfo_t * pWillInfo,
+                                        size_t * pRemainingLength,
+                                        size_t * pPacketSize );
+/* @[declare_mqtt_getconnectpacketsize] */
+
+/**
+ * @brief Serialize an MQTT CONNECT packet in the given fixed buffer @p pFixedBuffer.
+ *
+ * #MQTT_GetConnectPacketSize should be called with @p pConnectInfo and
+ * @p pWillInfo before invoking this function to get the size of the required
+ * #MQTTFixedBuffer_t and @p remainingLength. The @p remainingLength must be
+ * the same as returned by #MQTT_GetConnectPacketSize. The #MQTTFixedBuffer_t
+ * must be at least as large as the size returned by #MQTT_GetConnectPacketSize.
+ *
+ * @param[in] pConnectInfo MQTT CONNECT packet parameters.
+ * @param[in] pWillInfo Last Will and Testament. Pass NULL if not used.
+ * @param[in] remainingLength Remaining Length provided by #MQTT_GetConnectPacketSize.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTConnectInfo_t connectInfo = { 0 };
+ * MQTTPublishInfo_t willInfo = { 0 };
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ * size_t remainingLength = 0, packetSize = 0;
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ *
+ * // Assume connectInfo and willInfo are initialized. Get the size requirement for
+ * // the connect packet.
+ * status = MQTT_GetConnectPacketSize(
+ *      &connectInfo, &willInfo, &remainingLength, &packetSize
+ * );
+ * assert( status == MQTTSuccess );
+ * assert( packetSize <= BUFFER_SIZE );
+ *
+ * // Serialize the connect packet into the fixed buffer.
+ * status = MQTT_SerializeConnect( &connectInfo, &willInfo, remainingLength, &fixedBuffer );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The connect packet can now be sent to the broker.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_serializeconnect] */
+MQTTStatus_t MQTT_SerializeConnect( const MQTTConnectInfo_t * pConnectInfo,
+                                    const MQTTPublishInfo_t * pWillInfo,
+                                    size_t remainingLength,
+                                    const MQTTFixedBuffer_t * pFixedBuffer );
+/* @[declare_mqtt_serializeconnect] */
+
+/**
+ * @brief Get packet size and Remaining Length of an MQTT SUBSCRIBE packet.
+ *
+ * This function must be called before #MQTT_SerializeSubscribe in order to get
+ * the size of the MQTT SUBSCRIBE packet that is generated from the list of
+ * #MQTTSubscribeInfo_t. The size of the #MQTTFixedBuffer_t supplied
+ * to #MQTT_SerializeSubscribe must be at least @p pPacketSize. The provided
+ * @p pSubscriptionList is valid for serialization with #MQTT_SerializeSubscribe
+ * only if this function returns #MQTTSuccess. The remaining length returned in
+ * @p pRemainingLength and the packet size returned in @p pPacketSize are valid
+ * only if this function returns #MQTTSuccess.
+ *
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT SUBSCRIBE packet.
+ * @param[out] pPacketSize The total size of the MQTT SUBSCRIBE packet.
+ *
+ * @return #MQTTBadParameter if the packet would exceed the size allowed by the
+ * MQTT spec; #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 };
+ * size_t remainingLength = 0, packetSize = 0;
+ * // This is assumed to be a list of filters we want to subscribe to.
+ * const char * filters[ NUMBER_OF_SUBSCRIPTIONS ];
+ *
+ * // Set each subscription.
+ * for( int i = 0; i < NUMBER_OF_SUBSCRIPTIONS; i++ )
+ * {
+ *      subscriptionList[ i ].qos = MQTTQoS0;
+ *      // Each subscription needs a topic filter.
+ *      subscriptionList[ i ].pTopicFilter = filters[ i ];
+ *      subscriptionList[ i ].topicFilterLength = strlen( filters[ i ] );
+ * }
+ *
+ * // Get the size requirement for the subscribe packet.
+ * status = MQTT_GetSubscribePacketSize(
+ *      &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &remainingLength, &packetSize
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The application should allocate or use a static #MQTTFixedBuffer_t
+ *      // of size >= packetSize to serialize the subscribe request.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_getsubscribepacketsize] */
+MQTTStatus_t MQTT_GetSubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                          size_t subscriptionCount,
+                                          size_t * pRemainingLength,
+                                          size_t * pPacketSize );
+/* @[declare_mqtt_getsubscribepacketsize] */
+
+/**
+ * @brief Serialize an MQTT SUBSCRIBE packet in the given buffer.
+ *
+ * #MQTT_GetSubscribePacketSize should be called with @p pSubscriptionList
+ * before invoking this function to get the size of the required
+ * #MQTTFixedBuffer_t and @p remainingLength. The @p remainingLength must be
+ * the same as returned by #MQTT_GetSubscribePacketSize. The #MQTTFixedBuffer_t
+ * must be at least as large as the size returned by #MQTT_GetSubscribePacketSize.
+ *
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[in] packetId packet ID generated by #MQTT_GetPacketId.
+ * @param[in] remainingLength Remaining Length provided by #MQTT_GetSubscribePacketSize.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 };
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ * size_t remainingLength = 0, packetSize = 0;
+ * uint16_t packetId;
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ *
+ * // Function to return a valid, unused packet identifier. The details are out of
+ * // scope for this example.
+ * packetId = getNewPacketId();
+ *
+ * // Assume subscriptionList has been initialized. Get the subscribe packet size.
+ * status = MQTT_GetSubscribePacketSize(
+ *      &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &remainingLength, &packetSize
+ * );
+ * assert( status == MQTTSuccess );
+ * assert( packetSize <= BUFFER_SIZE );
+ *
+ * // Serialize the subscribe packet into the fixed buffer.
+ * status = MQTT_SerializeSubscribe(
+ *      &subscriptionList[ 0 ],
+ *      NUMBER_OF_SUBSCRIPTIONS,
+ *      packetId,
+ *      remainingLength,
+ *      &fixedBuffer
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The subscribe packet can now be sent to the broker.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_serializesubscribe] */
+MQTTStatus_t MQTT_SerializeSubscribe( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                      size_t subscriptionCount,
+                                      uint16_t packetId,
+                                      size_t remainingLength,
+                                      const MQTTFixedBuffer_t * pFixedBuffer );
+/* @[declare_mqtt_serializesubscribe] */
+
+/**
+ * @brief Get packet size and Remaining Length of an MQTT UNSUBSCRIBE packet.
+ *
+ * This function must be called before #MQTT_SerializeUnsubscribe in order to
+ * get the size of the MQTT UNSUBSCRIBE packet that is generated from the list
+ * of #MQTTSubscribeInfo_t. The size of the #MQTTFixedBuffer_t supplied
+ * to #MQTT_SerializeUnsubscribe must be at least @p pPacketSize. The provided
+ * @p pSubscriptionList is valid for serialization with #MQTT_SerializeUnsubscribe
+ * only if this function returns #MQTTSuccess. The remaining length returned in
+ * @p pRemainingLength and the packet size returned in @p pPacketSize are valid
+ * only if this function returns #MQTTSuccess.
+ *
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT UNSUBSCRIBE packet.
+ * @param[out] pPacketSize The total size of the MQTT UNSUBSCRIBE packet.
+ *
+ * @return #MQTTBadParameter if the packet would exceed the size allowed by the
+ * MQTT spec; #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 };
+ * size_t remainingLength = 0, packetSize = 0;
+ *
+ * // Initialize the subscribe info. The details are out of scope for this example.
+ * initializeSubscribeInfo( &subscriptionList[ 0 ] );
+ *
+ * // Get the size requirement for the unsubscribe packet.
+ * status = MQTT_GetUnsubscribePacketSize(
+ *      &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &remainingLength, &packetSize
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The application should allocate or use a static #MQTTFixedBuffer_t
+ *      // of size >= packetSize to serialize the unsubscribe request.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_getunsubscribepacketsize] */
+MQTTStatus_t MQTT_GetUnsubscribePacketSize( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                            size_t subscriptionCount,
+                                            size_t * pRemainingLength,
+                                            size_t * pPacketSize );
+/* @[declare_mqtt_getunsubscribepacketsize] */
+
+/**
+ * @brief Serialize an MQTT UNSUBSCRIBE packet in the given buffer.
+ *
+ * #MQTT_GetUnsubscribePacketSize should be called with @p pSubscriptionList
+ * before invoking this function to get the size of the required
+ * #MQTTFixedBuffer_t and @p remainingLength. The @p remainingLength must be
+ * the same as returned by #MQTT_GetUnsubscribePacketSize. The #MQTTFixedBuffer_t
+ * must be at least as large as the size returned by #MQTT_GetUnsubscribePacketSize.
+ *
+ * @param[in] pSubscriptionList List of MQTT subscription info.
+ * @param[in] subscriptionCount The number of elements in pSubscriptionList.
+ * @param[in] packetId packet ID generated by #MQTT_GetPacketId.
+ * @param[in] remainingLength Remaining Length provided by #MQTT_GetUnsubscribePacketSize.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTSubscribeInfo_t subscriptionList[ NUMBER_OF_SUBSCRIPTIONS ] = { 0 };
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ * size_t remainingLength = 0, packetSize = 0;
+ * uint16_t packetId;
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ *
+ * // Function to return a valid, unused packet identifier. The details are out of
+ * // scope for this example.
+ * packetId = getNewPacketId();
+ *
+ * // Assume subscriptionList has been initialized. Get the unsubscribe packet size.
+ * status = MQTT_GetUnsubscribePacketSize(
+ *      &subscriptionList[ 0 ], NUMBER_OF_SUBSCRIPTIONS, &remainingLength, &packetSize
+ * );
+ * assert( status == MQTTSuccess );
+ * assert( packetSize <= BUFFER_SIZE );
+ *
+ * // Serialize the unsubscribe packet into the fixed buffer.
+ * status = MQTT_SerializeUnsubscribe(
+ *      &subscriptionList[ 0 ],
+ *      NUMBER_OF_SUBSCRIPTIONS,
+ *      packetId,
+ *      remainingLength,
+ *      &fixedBuffer
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The unsubscribe packet can now be sent to the broker.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_serializeunsubscribe] */
+MQTTStatus_t MQTT_SerializeUnsubscribe( const MQTTSubscribeInfo_t * pSubscriptionList,
+                                        size_t subscriptionCount,
+                                        uint16_t packetId,
+                                        size_t remainingLength,
+                                        const MQTTFixedBuffer_t * pFixedBuffer );
+/* @[declare_mqtt_serializeunsubscribe] */
+
+/**
+ * @brief Get the packet size and remaining length of an MQTT PUBLISH packet.
+ *
+ * This function must be called before #MQTT_SerializePublish in order to get
+ * the size of the MQTT PUBLISH packet that is generated from #MQTTPublishInfo_t.
+ * The size of the #MQTTFixedBuffer_t supplied to #MQTT_SerializePublish must be
+ * at least @p pPacketSize. The provided @p pPublishInfo is valid for
+ * serialization with #MQTT_SerializePublish only if this function returns
+ * #MQTTSuccess. The remaining length returned in @p pRemainingLength and the
+ * packet size returned in @p pPacketSize are valid only if this function
+ * returns #MQTTSuccess.
+ *
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[out] pRemainingLength The Remaining Length of the MQTT PUBLISH packet.
+ * @param[out] pPacketSize The total size of the MQTT PUBLISH packet.
+ *
+ * @return #MQTTBadParameter if the packet would exceed the size allowed by the
+ * MQTT spec or if invalid parameters are passed; #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPublishInfo_t publishInfo = { 0 };
+ * size_t remainingLength = 0, packetSize = 0;
+ *
+ * // Initialize the publish info.
+ * publishInfo.qos = MQTTQoS0;
+ * publishInfo.pTopicName = "/some/topic/name";
+ * publishInfo.topicNameLength = strlen( publishInfo.pTopicName );
+ * publishInfo.pPayload = "Hello World!";
+ * publishInfo.payloadLength = strlen( "Hello World!" );
+ *
+ * // Get the size requirement for the publish packet.
+ * status = MQTT_GetPublishPacketSize(
+ *      &publishInfo, &remainingLength, &packetSize
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The application should allocate or use a static #MQTTFixedBuffer_t
+ *      // of size >= packetSize to serialize the publish.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_getpublishpacketsize] */
+MQTTStatus_t MQTT_GetPublishPacketSize( const MQTTPublishInfo_t * pPublishInfo,
+                                        size_t * pRemainingLength,
+                                        size_t * pPacketSize );
+/* @[declare_mqtt_getpublishpacketsize] */
+
+/**
+ * @brief Serialize an MQTT PUBLISH packet in the given buffer.
+ *
+ * This function will serialize complete MQTT PUBLISH packet into
+ * the given buffer. If the PUBLISH payload can be sent separately,
+ * consider using #MQTT_SerializePublishHeader, which will serialize
+ * only the PUBLISH header into the buffer.
+ *
+ * #MQTT_GetPublishPacketSize should be called with @p pPublishInfo before
+ * invoking this function to get the size of the required #MQTTFixedBuffer_t and
+ * @p remainingLength. The @p remainingLength must be the same as returned by
+ * #MQTT_GetPublishPacketSize. The #MQTTFixedBuffer_t must be at least as large
+ * as the size returned by #MQTT_GetPublishPacketSize.
+ *
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[in] packetId packet ID generated by #MQTT_GetPacketId.
+ * @param[in] remainingLength Remaining Length provided by #MQTT_GetPublishPacketSize.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPublishInfo_t publishInfo = { 0 };
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ * size_t remainingLength = 0, packetSize = 0;
+ * uint16_t packetId;
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ *
+ * // A packet identifier is unused for QoS 0 publishes. Otherwise, a valid, unused packet
+ * // identifier must be used.
+ * packetId = 0;
+ *
+ * // Assume publishInfo has been initialized. Get publish packet size.
+ * status = MQTT_GetPublishPacketSize(
+ *      &publishInfo, &remainingLength, &packetSize
+ * );
+ * assert( status == MQTTSuccess );
+ * assert( packetSize <= BUFFER_SIZE );
+ *
+ * // Serialize the publish packet into the fixed buffer.
+ * status = MQTT_SerializePublish(
+ *      &publishInfo,
+ *      packetId,
+ *      remainingLength,
+ *      &fixedBuffer
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The publish packet can now be sent to the broker.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_serializepublish] */
+MQTTStatus_t MQTT_SerializePublish( const MQTTPublishInfo_t * pPublishInfo,
+                                    uint16_t packetId,
+                                    size_t remainingLength,
+                                    const MQTTFixedBuffer_t * pFixedBuffer );
+/* @[declare_mqtt_serializepublish] */
+
+/**
+ * @brief Serialize an MQTT PUBLISH packet header without the topic string in the
+ * given buffer. This function will add the topic string length to the provided
+ * buffer. This helps reduce an unnecessary copy of the topic string into the
+ * buffer.
+ *
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[in] remainingLength Remaining Length provided by #MQTT_GetPublishPacketSize.
+ * @param[out] pBuffer Buffer for packet serialization.
+ * @param[out] headerSize Size of the serialized MQTT PUBLISH header.
+ *
+ * @return #MQTTSuccess if the serialization is successful. Otherwise, #MQTTBadParameter.
+ */
+MQTTStatus_t MQTT_SerializePublishHeaderWithoutTopic( const MQTTPublishInfo_t * pPublishInfo,
+                                                      size_t remainingLength,
+                                                      uint8_t * pBuffer,
+                                                      size_t * headerSize );
+
+/**
+ * @brief Serialize an MQTT PUBLISH packet header in the given buffer.
+ *
+ * This function serializes PUBLISH header in to the given buffer. The payload
+ * for PUBLISH will not be copied over to the buffer. This will help reduce
+ * the memory needed for the buffer and avoid an unwanted copy operation of the
+ * PUBLISH payload into the buffer. If the payload also would need to be part of
+ * the serialized buffer, consider using #MQTT_SerializePublish.
+ *
+ * #MQTT_GetPublishPacketSize should be called with @p pPublishInfo before
+ * invoking this function to get the size of the required #MQTTFixedBuffer_t and
+ * @p remainingLength. The @p remainingLength must be the same as returned by
+ * #MQTT_GetPublishPacketSize. The #MQTTFixedBuffer_t must be at least as large
+ * as the size returned by #MQTT_GetPublishPacketSize.
+ *
+ * @param[in] pPublishInfo MQTT PUBLISH packet parameters.
+ * @param[in] packetId packet ID generated by #MQTT_GetPacketId.
+ * @param[in] remainingLength Remaining Length provided by #MQTT_GetPublishPacketSize.
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ * @param[out] pHeaderSize Size of the serialized MQTT PUBLISH header.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPublishInfo_t publishInfo = { 0 };
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ * size_t remainingLength = 0, packetSize = 0, headerSize = 0;
+ * uint16_t packetId;
+ * int32_t bytesSent;
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ *
+ * // A packet identifier is unused for QoS 0 publishes. Otherwise, a valid, unused packet
+ * // identifier must be used.
+ * packetId = 0;
+ *
+ * // Assume publishInfo has been initialized. Get the publish packet size.
+ * status = MQTT_GetPublishPacketSize(
+ *      &publishInfo, &remainingLength, &packetSize
+ * );
+ * assert( status == MQTTSuccess );
+ * // The payload will not be serialized, so the the fixed buffer does not need to hold it.
+ * assert( ( packetSize - publishInfo.payloadLength ) <= BUFFER_SIZE );
+ *
+ * // Serialize the publish packet header into the fixed buffer.
+ * status = MQTT_SerializePublishHeader(
+ *      &publishInfo,
+ *      packetId,
+ *      remainingLength,
+ *      &fixedBuffer,
+ *      &headerSize
+ * );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The publish header and payload can now be sent to the broker.
+ *      // mqttSocket here is a socket descriptor created and connected to the MQTT
+ *      // broker outside of this function.
+ *      bytesSent = send( mqttSocket, ( void * ) fixedBuffer.pBuffer, headerSize, 0 );
+ *      assert( bytesSent == headerSize );
+ *      bytesSent = send( mqttSocket, publishInfo.pPayload, publishInfo.payloadLength, 0 );
+ *      assert( bytesSent == publishInfo.payloadLength );
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_serializepublishheader] */
+MQTTStatus_t MQTT_SerializePublishHeader( const MQTTPublishInfo_t * pPublishInfo,
+                                          uint16_t packetId,
+                                          size_t remainingLength,
+                                          const MQTTFixedBuffer_t * pFixedBuffer,
+                                          size_t * pHeaderSize );
+/* @[declare_mqtt_serializepublishheader] */
+
+/**
+ * @brief Serialize an MQTT PUBACK, PUBREC, PUBREL, or PUBCOMP into the given
+ * buffer.
+ *
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ * @param[in] packetType Byte of the corresponding packet fixed header per the
+ * MQTT spec.
+ * @param[in] packetId Packet ID of the publish.
+ *
+ * @return #MQTTBadParameter, #MQTTNoMemory, or #MQTTSuccess.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ * uint16_t packetId;
+ * uint8_t packetType;
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ * // The fixed buffer must be large enough to hold 4 bytes.
+ * assert( BUFFER_SIZE >= MQTT_PUBLISH_ACK_PACKET_SIZE );
+ *
+ * // The packet ID must be the same as the original publish packet.
+ * packetId = publishPacketId;
+ *
+ * // The byte representing a packet of type ACK. This function accepts PUBACK, PUBREC, PUBREL, or PUBCOMP.
+ * packetType = MQTT_PACKET_TYPE_PUBACK;
+ *
+ * // Serialize the publish acknowledgment into the fixed buffer.
+ * status = MQTT_SerializeAck( &fixedBuffer, packetType, packetId );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The publish acknowledgment can now be sent to the broker.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_serializeack] */
+MQTTStatus_t MQTT_SerializeAck( const MQTTFixedBuffer_t * pFixedBuffer,
+                                uint8_t packetType,
+                                uint16_t packetId );
+/* @[declare_mqtt_serializeack] */
+
+/**
+ * @brief Get the size of an MQTT DISCONNECT packet.
+ *
+ * @param[out] pPacketSize The size of the MQTT DISCONNECT packet.
+ *
+ * @return #MQTTSuccess, or #MQTTBadParameter if @p pPacketSize is NULL.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * size_t packetSize = 0;
+ *
+ * // Get the size requirement for the disconnect packet.
+ * status = MQTT_GetDisconnectPacketSize( &packetSize );
+ * assert( status == MQTTSuccess );
+ * assert( packetSize == 2 );
+ *
+ * // The application should allocate or use a static #MQTTFixedBuffer_t of
+ * // size >= 2 to serialize the disconnect packet.
+ *
+ * @endcode
+ */
+/* @[declare_mqtt_getdisconnectpacketsize] */
+MQTTStatus_t MQTT_GetDisconnectPacketSize( size_t * pPacketSize );
+/* @[declare_mqtt_getdisconnectpacketsize] */
+
+/**
+ * @brief Serialize an MQTT DISCONNECT packet into the given buffer.
+ *
+ * The input #MQTTFixedBuffer_t.size must be at least as large as the size
+ * returned by #MQTT_GetDisconnectPacketSize.
+ *
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ *
+ * // Get the disconnect packet size.
+ * status = MQTT_GetDisconnectPacketSize( &packetSize );
+ * assert( status == MQTTSuccess );
+ * assert( packetSize <= BUFFER_SIZE );
+ *
+ * // Serialize the disconnect into the fixed buffer.
+ * status = MQTT_SerializeDisconnect( &fixedBuffer );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The disconnect packet can now be sent to the broker.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_serializedisconnect] */
+MQTTStatus_t MQTT_SerializeDisconnect( const MQTTFixedBuffer_t * pFixedBuffer );
+/* @[declare_mqtt_serializedisconnect] */
+
+/**
+ * @brief Get the size of an MQTT PINGREQ packet.
+ *
+ * @param[out] pPacketSize The size of the MQTT PINGREQ packet.
+ *
+ * @return  #MQTTSuccess or #MQTTBadParameter if pPacketSize is NULL.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * size_t packetSize = 0;
+ *
+ * // Get the size requirement for the ping request packet.
+ * status = MQTT_GetPingreqPacketSize( &packetSize );
+ * assert( status == MQTTSuccess );
+ * assert( packetSize == 2 );
+ *
+ * // The application should allocate or use a static #MQTTFixedBuffer_t of
+ * // size >= 2 to serialize the ping request.
+ *
+ * @endcode
+ */
+/* @[declare_mqtt_getpingreqpacketsize] */
+MQTTStatus_t MQTT_GetPingreqPacketSize( size_t * pPacketSize );
+/* @[declare_mqtt_getpingreqpacketsize] */
+
+/**
+ * @brief Serialize an MQTT PINGREQ packet into the given buffer.
+ *
+ * The input #MQTTFixedBuffer_t.size must be at least as large as the size
+ * returned by #MQTT_GetPingreqPacketSize.
+ *
+ * @param[out] pFixedBuffer Buffer for packet serialization.
+ *
+ * @return #MQTTNoMemory if pFixedBuffer is too small to hold the MQTT packet;
+ * #MQTTBadParameter if invalid parameters are passed;
+ * #MQTTSuccess otherwise.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTFixedBuffer_t fixedBuffer;
+ * uint8_t buffer[ BUFFER_SIZE ];
+ *
+ * fixedBuffer.pBuffer = buffer;
+ * fixedBuffer.size = BUFFER_SIZE;
+ *
+ * // Get the ping request packet size.
+ * status = MQTT_GetPingreqPacketSize( &packetSize );
+ * assert( status == MQTTSuccess );
+ * assert( packetSize <= BUFFER_SIZE );
+ *
+ * // Serialize the ping request into the fixed buffer.
+ * status = MQTT_SerializePingreq( &fixedBuffer );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      // The ping request can now be sent to the broker.
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_serializepingreq] */
+MQTTStatus_t MQTT_SerializePingreq( const MQTTFixedBuffer_t * pFixedBuffer );
+/* @[declare_mqtt_serializepingreq] */
+
+/**
+ * @brief Deserialize an MQTT PUBLISH packet.
+ *
+ * @param[in] pIncomingPacket #MQTTPacketInfo_t containing the buffer.
+ * @param[out] pPacketId The packet ID obtained from the buffer.
+ * @param[out] pPublishInfo Struct containing information about the publish.
+ *
+ * @return #MQTTBadParameter, #MQTTBadResponse, or #MQTTSuccess.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // TransportRecv_t function for reading from the network.
+ * int32_t socket_recv(
+ *      NetworkContext_t * pNetworkContext,
+ *      void * pBuffer,
+ *      size_t bytesToRecv
+ * );
+ * // Some context to be used with the above transport receive function.
+ * NetworkContext_t networkContext;
+ *
+ * // Other variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPacketInfo_t incomingPacket;
+ * MQTTPublishInfo_t publishInfo = { 0 };
+ * uint16_t packetId;
+ *
+ * int32_t bytesRecvd;
+ * // A buffer to hold remaining data of the incoming packet.
+ * uint8_t buffer[ BUFFER_SIZE ];
+ *
+ * // Populate all fields of the incoming packet.
+ * status = MQTT_GetIncomingPacketTypeAndLength(
+ *      socket_recv,
+ *      &networkContext,
+ *      &incomingPacket
+ * );
+ * assert( status == MQTTSuccess );
+ * assert( incomingPacket.remainingLength <= BUFFER_SIZE );
+ * bytesRecvd = socket_recv(
+ *      &networkContext,
+ *      ( void * ) buffer,
+ *      incomingPacket.remainingLength
+ * );
+ * incomingPacket.pRemainingData = buffer;
+ *
+ * // Deserialize the publish information if the incoming packet is a publish.
+ * if( ( incomingPacket.type & 0xF0 ) == MQTT_PACKET_TYPE_PUBLISH )
+ * {
+ *      status = MQTT_DeserializePublish( &incomingPacket, &packetId, &publishInfo );
+ *      if( status == MQTTSuccess )
+ *      {
+ *          // The deserialized publish information can now be used from `publishInfo`.
+ *      }
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_deserializepublish] */
+MQTTStatus_t MQTT_DeserializePublish( const MQTTPacketInfo_t * pIncomingPacket,
+                                      uint16_t * pPacketId,
+                                      MQTTPublishInfo_t * pPublishInfo );
+/* @[declare_mqtt_deserializepublish] */
+
+/**
+ * @brief Deserialize an MQTT CONNACK, SUBACK, UNSUBACK, PUBACK, PUBREC, PUBREL,
+ * PUBCOMP, or PINGRESP.
+ *
+ * @param[in] pIncomingPacket #MQTTPacketInfo_t containing the buffer.
+ * @param[out] pPacketId The packet ID of obtained from the buffer. Not used
+ * in CONNACK or PINGRESP.
+ * @param[out] pSessionPresent Boolean flag from a CONNACK indicating present session.
+ *
+ * @return #MQTTBadParameter, #MQTTBadResponse, #MQTTServerRefused, or #MQTTSuccess.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTPacketInfo_t incomingPacket;
+ * // Used for SUBACK, UNSUBACK, PUBACK, PUBREC, PUBREL, and PUBCOMP.
+ * uint16_t packetId;
+ * // Used for CONNACK.
+ * bool sessionPresent;
+ *
+ * // Receive an incoming packet and populate all fields. The details are out of scope
+ * // for this example.
+ * receiveIncomingPacket( &incomingPacket );
+ *
+ * // Deserialize ack information if the incoming packet is not a publish.
+ * if( ( incomingPacket.type & 0xF0 ) != MQTT_PACKET_TYPE_PUBLISH )
+ * {
+ *      status = MQTT_DeserializeAck( &incomingPacket, &packetId, &sessionPresent );
+ *      if( status == MQTTSuccess )
+ *      {
+ *          // The packet ID or session present flag information is available. For
+ *          // ping response packets, the only information is the status code.
+ *      }
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_deserializeack] */
+MQTTStatus_t MQTT_DeserializeAck( const MQTTPacketInfo_t * pIncomingPacket,
+                                  uint16_t * pPacketId,
+                                  bool * pSessionPresent );
+/* @[declare_mqtt_deserializeack] */
+
+/**
+ * @brief Extract the MQTT packet type and length from incoming packet.
+ *
+ * This function must be called for every incoming packet to retrieve the
+ * #MQTTPacketInfo_t.type and #MQTTPacketInfo_t.remainingLength. A
+ * #MQTTPacketInfo_t is not valid until this routine has been invoked.
+ *
+ * @param[in] readFunc Transport layer read function pointer.
+ * @param[in] pNetworkContext The network context pointer provided by the application.
+ * @param[out] pIncomingPacket Pointer to MQTTPacketInfo_t structure. This is
+ * where type, remaining length and packet identifier are stored.
+ *
+ * @return #MQTTSuccess on successful extraction of type and length,
+ * #MQTTBadParameter if @p pIncomingPacket is invalid,
+ * #MQTTRecvFailed on transport receive failure,
+ * #MQTTBadResponse if an invalid packet is read, and
+ * #MQTTNoDataAvailable if there is nothing to read.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // TransportRecv_t function for reading from the network.
+ * int32_t socket_recv(
+ *      NetworkContext_t * pNetworkContext,
+ *      void * pBuffer,
+ *      size_t bytesToRecv
+ * );
+ * // Some context to be used with above transport receive function.
+ * NetworkContext_t networkContext;
+ *
+ * // Struct to hold the incoming packet information.
+ * MQTTPacketInfo_t incomingPacket;
+ * MQTTStatus_t status = MQTTSuccess;
+ * int32_t bytesRecvd;
+ * // Buffer to hold the remaining data of the incoming packet.
+ * uint8_t buffer[ BUFFER_SIZE ];
+ *
+ * // Loop until data is available to be received.
+ * do{
+ *      status = MQTT_GetIncomingPacketTypeAndLength(
+ *          socket_recv,
+ *          &networkContext,
+ *          &incomingPacket
+ *      );
+ * } while( status == MQTTNoDataAvailable );
+ *
+ * assert( status == MQTTSuccess );
+ *
+ * // Receive the rest of the incoming packet.
+ * assert( incomingPacket.remainingLength <= BUFFER_SIZE );
+ * bytesRecvd = socket_recv(
+ *      &networkContext,
+ *      ( void * ) buffer,
+ *      incomingPacket.remainingLength
+ * );
+ *
+ * // Set the remaining data field.
+ * incomingPacket.pRemainingData = buffer;
+ * @endcode
+ */
+/* @[declare_mqtt_getincomingpackettypeandlength] */
+MQTTStatus_t MQTT_GetIncomingPacketTypeAndLength( TransportRecv_t readFunc,
+                                                  NetworkContext_t * pNetworkContext,
+                                                  MQTTPacketInfo_t * pIncomingPacket );
+/* @[declare_mqtt_getincomingpackettypeandlength] */
+
+/**
+ * @brief Extract the MQTT packet type and length from incoming packet.
+ *
+ * This function must be called for every incoming packet to retrieve the
+ * #MQTTPacketInfo_t.type and #MQTTPacketInfo_t.remainingLength. A
+ * #MQTTPacketInfo_t is not valid until this routine has been invoked.
+ *
+ * @param[in] pBuffer The buffer holding the raw data to be processed
+ * @param[in] pIndex Pointer to the index within the buffer to marking the end
+ *            of raw data available.
+ * @param[out] pIncomingPacket Structure used to hold the fields of the
+ *            incoming packet.
+ *
+ * @return #MQTTSuccess on successful extraction of type and length,
+ * #MQTTBadParameter if @p pIncomingPacket is invalid,
+ * #MQTTBadResponse if an invalid packet is read, and
+ * #MQTTNoDataAvailable if there is nothing to read.
+ */
+ /* @[declare_mqtt_processincomingpackettypeandlength] */
+MQTTStatus_t MQTT_ProcessIncomingPacketTypeAndLength( const uint8_t * pBuffer,
+                                                      const size_t * pIndex,
+                                                      MQTTPacketInfo_t * pIncomingPacket );
+/* @[declare_mqtt_processincomingpackettypeandlength] */
+
+/**
+ * @fn uint8_t * MQTT_SerializeConnectFixedHeader( uint8_t * pIndex, const MQTTConnectInfo_t * pConnectInfo, const MQTTPublishInfo_t * pWillInfo, size_t remainingLength );
+ * @brief Serialize the fixed part of the connect packet header.
+ *
+ * @param[out] pIndex Pointer to the buffer where the header is to
+ * be serialized.
+ * @param[in] pConnectInfo The connect information.
+ * @param[in] pWillInfo The last will and testament information.
+ * @param[in] remainingLength The remaining length of the packet to be
+ * serialized.
+ *
+ * @return A pointer to the end of the encoded string.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+uint8_t * MQTT_SerializeConnectFixedHeader( uint8_t * pIndex,
+                                            const MQTTConnectInfo_t * pConnectInfo,
+                                            const MQTTPublishInfo_t * pWillInfo,
+                                            size_t remainingLength );
+/** @endcond */
+
+/**
+ * @fn  uint8_t * MQTT_SerializeSubscribeHeader( size_t remainingLength, uint8_t * pIndex, uint16_t packetId );
+ * @brief Serialize the fixed part of the subscribe packet header.
+ *
+ * @param[in] remainingLength The remaining length of the packet to be
+ * serialized.
+ * @param[in] pIndex Pointer to the buffer where the header is to
+ * be serialized.
+ * @param[in] packetId The packet ID to be serialized.
+ *
+ * @return A pointer to the end of the encoded string.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+uint8_t * MQTT_SerializeSubscribeHeader( size_t remainingLength,
+                                         uint8_t * pIndex,
+                                         uint16_t packetId );
+/** @endcond */
+
+/**
+ * @fn uint8_t * MQTT_SerializeUnsubscribeHeader( size_t remainingLength, uint8_t * pIndex, uint16_t packetId );
+ * @brief Serialize the fixed part of the unsubscribe packet header.
+ *
+ * @param[in] remainingLength The remaining length of the packet to be
+ * serialized.
+ * @param[in] pIndex Pointer to the buffer where the header is to
+ * be serialized.
+ * @param[in] packetId The packet ID to be serialized.
+ *
+ * @return A pointer to the end of the encoded string.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+uint8_t * MQTT_SerializeUnsubscribeHeader( size_t remainingLength,
+                                           uint8_t * pIndex,
+                                           uint16_t packetId );
+/** @endcond */
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    }
+#endif
+/* *INDENT-ON* */
+
+#endif /* ifndef CORE_MQTT_SERIALIZER_H */
diff --git a/project/coreMQTT/coreMQTT/core_mqtt_state.c b/project/coreMQTT/coreMQTT/core_mqtt_state.c
new file mode 100644
index 0000000..8ad3f2b
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/core_mqtt_state.c
@@ -0,0 +1,1214 @@
+/*
+ * coreMQTT v2.1.1
+ * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_mqtt_state.c
+ * @brief Implements the functions in core_mqtt_state.h.
+ */
+#include <assert.h>
+#include <string.h>
+#include "core_mqtt_state.h"
+
+/* Include config defaults header to get default values of configs. */
+#include "core_mqtt_config_defaults.h"
+
+#include "core_mqtt_default_logging.h"
+
+/*-----------------------------------------------------------*/
+
+/**
+ * @brief A global static variable used to generate the macro
+ * #MQTT_INVALID_STATE_COUNT of size_t length.
+ */
+static const size_t ZERO_SIZE_T = 0U;
+
+/**
+ * @brief This macro depicts the invalid value for the state publishes.
+ */
+#define MQTT_INVALID_STATE_COUNT    ( ~ZERO_SIZE_T )
+
+/**
+ * @brief Create a 16-bit bitmap with bit set at specified position.
+ *
+ * @param[in] position The position at which the bit need to be set.
+ */
+#define UINT16_BITMAP_BIT_SET_AT( position )    ( ( uint16_t ) 0x01U << ( ( uint16_t ) position ) )
+
+/**
+ * @brief Set a bit in an 16-bit unsigned integer.
+ *
+ * @param[in] x The 16-bit unsigned integer to set a bit.
+ * @param[in] position The position at which the bit need to be set.
+ */
+#define UINT16_SET_BIT( x, position )           ( ( x ) = ( uint16_t ) ( ( x ) | ( UINT16_BITMAP_BIT_SET_AT( position ) ) ) )
+
+/**
+ * @brief Macro for checking if a bit is set in a 16-bit unsigned integer.
+ *
+ * @param[in] x The unsigned 16-bit integer to check.
+ * @param[in] position Which bit to check.
+ */
+#define UINT16_CHECK_BIT( x, position )         ( ( ( x ) & ( UINT16_BITMAP_BIT_SET_AT( position ) ) ) == ( UINT16_BITMAP_BIT_SET_AT( position ) ) )
+
+/*-----------------------------------------------------------*/
+
+/**
+ * @brief Test if a transition to new state is possible, when dealing with PUBLISHes.
+ *
+ * @param[in] currentState The current state.
+ * @param[in] newState State to transition to.
+ * @param[in] opType Reserve, Send, or Receive.
+ * @param[in] qos 0, 1, or 2.
+ *
+ * @note This function does not validate the current state, or the new state
+ * based on either the operation type or QoS. It assumes the new state is valid
+ * given the opType and QoS, which will be the case if calculated by
+ * MQTT_CalculateStatePublish().
+ *
+ * @return `true` if transition is possible, else `false`
+ */
+static bool validateTransitionPublish( MQTTPublishState_t currentState,
+                                       MQTTPublishState_t newState,
+                                       MQTTStateOperation_t opType,
+                                       MQTTQoS_t qos );
+
+/**
+ * @brief Test if a transition to a new state is possible, when dealing with acks.
+ *
+ * @param[in] currentState The current state.
+ * @param[in] newState State to transition to.
+ *
+ * @return `true` if transition is possible, else `false`.
+ */
+static bool validateTransitionAck( MQTTPublishState_t currentState,
+                                   MQTTPublishState_t newState );
+
+/**
+ * @brief Test if the publish corresponding to an ack is outgoing or incoming.
+ *
+ * @param[in] packetType PUBACK, PUBREC, PUBREL, or PUBCOMP.
+ * @param[in] opType Send, or Receive.
+ *
+ * @return `true` if corresponds to outgoing publish, else `false`.
+ */
+static bool isPublishOutgoing( MQTTPubAckType_t packetType,
+                               MQTTStateOperation_t opType );
+
+/**
+ * @brief Find a packet ID in the state record.
+ *
+ * @param[in] records State record array.
+ * @param[in] recordCount Length of record array.
+ * @param[in] packetId packet ID to search for.
+ * @param[out] pQos QoS retrieved from record.
+ * @param[out] pCurrentState state retrieved from record.
+ *
+ * @return index of the packet id in the record if it exists, else the record length.
+ */
+static size_t findInRecord( const MQTTPubAckInfo_t * records,
+                            size_t recordCount,
+                            uint16_t packetId,
+                            MQTTQoS_t * pQos,
+                            MQTTPublishState_t * pCurrentState );
+
+/**
+ * @brief Compact records.
+ *
+ * Records are arranged in the relative order to maintain message ordering.
+ * This will lead to fragmentation and this function will help in defragmenting
+ * the records array.
+ *
+ * @param[in] records State record array.
+ * @param[in] recordCount Length of record array.
+ */
+static void compactRecords( MQTTPubAckInfo_t * records,
+                            size_t recordCount );
+
+/**
+ * @brief Store a new entry in the state record.
+ *
+ * @param[in] records State record array.
+ * @param[in] recordCount Length of record array.
+ * @param[in] packetId Packet ID of new entry.
+ * @param[in] qos QoS of new entry.
+ * @param[in] publishState State of new entry.
+ *
+ * @return #MQTTSuccess, #MQTTNoMemory, or #MQTTStateCollision.
+ */
+static MQTTStatus_t addRecord( MQTTPubAckInfo_t * records,
+                               size_t recordCount,
+                               uint16_t packetId,
+                               MQTTQoS_t qos,
+                               MQTTPublishState_t publishState );
+
+/**
+ * @brief Update and possibly delete an entry in the state record.
+ *
+ * @param[in] records State record array.
+ * @param[in] recordIndex index of record to update.
+ * @param[in] newState New state to update.
+ * @param[in] shouldDelete Whether an existing entry should be deleted.
+ */
+static void updateRecord( MQTTPubAckInfo_t * records,
+                          size_t recordIndex,
+                          MQTTPublishState_t newState,
+                          bool shouldDelete );
+
+/**
+ * @brief Get the packet ID and index of an outgoing publish in specified
+ * states.
+ *
+ * @param[in] pMqttContext Initialized MQTT context.
+ * @param[in] searchStates The states to search for in 2-byte bit map.
+ * @param[in,out] pCursor Index at which to start searching.
+ *
+ * @return Packet ID of the outgoing publish.
+ */
+static uint16_t stateSelect( const MQTTContext_t * pMqttContext,
+                             uint16_t searchStates,
+                             MQTTStateCursor_t * pCursor );
+
+/**
+ * @brief Update the state records for an ACK after state transition
+ * validations.
+ *
+ * @param[in] records State records pointer.
+ * @param[in] maxRecordCount The maximum number of records.
+ * @param[in] recordIndex Index at which the record is stored.
+ * @param[in] packetId Packet id of the packet.
+ * @param[in] currentState Current state of the publish record.
+ * @param[in] newState New state of the publish.
+ *
+ * @return #MQTTIllegalState, or #MQTTSuccess.
+ */
+static MQTTStatus_t updateStateAck( MQTTPubAckInfo_t * records,
+                                    size_t maxRecordCount,
+                                    size_t recordIndex,
+                                    uint16_t packetId,
+                                    MQTTPublishState_t currentState,
+                                    MQTTPublishState_t newState );
+
+/**
+ * @brief Update the state record for a PUBLISH packet after validating
+ * the state transitions.
+ *
+ * @param[in] pMqttContext Initialized MQTT context.
+ * @param[in] recordIndex Index in state records at which publish record exists.
+ * @param[in] packetId ID of the PUBLISH packet.
+ * @param[in] opType Send or Receive.
+ * @param[in] qos 0, 1, or 2.
+ * @param[in] currentState Current state of the publish record.
+ * @param[in] newState New state of the publish record.
+ *
+ * @return #MQTTIllegalState, #MQTTStateCollision or #MQTTSuccess.
+ */
+static MQTTStatus_t updateStatePublish( const MQTTContext_t * pMqttContext,
+                                        size_t recordIndex,
+                                        uint16_t packetId,
+                                        MQTTStateOperation_t opType,
+                                        MQTTQoS_t qos,
+                                        MQTTPublishState_t currentState,
+                                        MQTTPublishState_t newState );
+
+/*-----------------------------------------------------------*/
+
+static bool validateTransitionPublish( MQTTPublishState_t currentState,
+                                       MQTTPublishState_t newState,
+                                       MQTTStateOperation_t opType,
+                                       MQTTQoS_t qos )
+{
+    bool isValid = false;
+
+    switch( currentState )
+    {
+        case MQTTStateNull:
+
+            /* Transitions from null occur when storing a new entry into the record. */
+            if( opType == MQTT_RECEIVE )
+            {
+                isValid = ( newState == MQTTPubAckSend ) || ( newState == MQTTPubRecSend );
+            }
+
+            break;
+
+        case MQTTPublishSend:
+
+            /* Outgoing publish. All such publishes start in this state due to
+             * the reserve operation. */
+            switch( qos )
+            {
+                case MQTTQoS1:
+                    isValid = newState == MQTTPubAckPending;
+                    break;
+
+                case MQTTQoS2:
+                    isValid = newState == MQTTPubRecPending;
+                    break;
+
+                case MQTTQoS0:
+                default:
+                    /* QoS 0 is checked before calling this function. */
+                    break;
+            }
+
+            break;
+
+        /* Below cases are for validating the resends of publish when a session is
+         * reestablished. */
+        case MQTTPubAckPending:
+
+            /* When a session is reestablished, outgoing QoS1 publishes in state
+             * #MQTTPubAckPending can be resent. The state remains the same. */
+            isValid = newState == MQTTPubAckPending;
+
+            break;
+
+        case MQTTPubRecPending:
+
+            /* When a session is reestablished, outgoing QoS2 publishes in state
+             * #MQTTPubRecPending can be resent. The state remains the same. */
+            isValid = newState == MQTTPubRecPending;
+
+            break;
+
+        case MQTTPubAckSend:
+        case MQTTPubCompPending:
+        case MQTTPubCompSend:
+        case MQTTPubRecSend:
+        case MQTTPubRelPending:
+        case MQTTPubRelSend:
+        case MQTTPublishDone:
+        default:
+            /* For a PUBLISH, we should not start from any other state. */
+            break;
+    }
+
+    return isValid;
+}
+
+/*-----------------------------------------------------------*/
+
+static bool validateTransitionAck( MQTTPublishState_t currentState,
+                                   MQTTPublishState_t newState )
+{
+    bool isValid = false;
+
+    switch( currentState )
+    {
+        case MQTTPubAckSend:
+        /* Incoming publish, QoS 1. */
+        case MQTTPubAckPending:
+            /* Outgoing publish, QoS 1. */
+            isValid = newState == MQTTPublishDone;
+            break;
+
+        case MQTTPubRecSend:
+            /* Incoming publish, QoS 2. */
+            isValid = newState == MQTTPubRelPending;
+            break;
+
+        case MQTTPubRelPending:
+
+            /* Incoming publish, QoS 2.
+             * There are 2 valid transitions possible.
+             * 1. MQTTPubRelPending -> MQTTPubCompSend : A PUBREL ack is received
+             *    when publish record state is MQTTPubRelPending. This is the
+             *    normal state transition without any connection interruptions.
+             * 2. MQTTPubRelPending -> MQTTPubRelPending : Receiving a duplicate
+             *    QoS2 publish can result in a transition to the same state.
+             *    This can happen in the below state transition.
+             *    1. Incoming publish received.
+             *    2. PUBREC ack sent and state is now MQTTPubRelPending.
+             *    3. TCP connection failure and broker didn't receive the PUBREC.
+             *    4. Reestablished MQTT session.
+             *    5. MQTT broker resent the un-acked publish.
+             *    6. Publish is received when publish record state is in
+             *       MQTTPubRelPending.
+             *    7. Sending out a PUBREC will result in this transition
+             *       to the same state. */
+            isValid = ( newState == MQTTPubCompSend ) ||
+                      ( newState == MQTTPubRelPending );
+            break;
+
+        case MQTTPubCompSend:
+
+            /* Incoming publish, QoS 2.
+             * There are 2 valid transitions possible.
+             * 1. MQTTPubCompSend -> MQTTPublishDone : A PUBCOMP ack is sent
+             *    after receiving a PUBREL from broker. This is the
+             *    normal state transition without any connection interruptions.
+             * 2. MQTTPubCompSend -> MQTTPubCompSend : Receiving a duplicate PUBREL
+             *    can result in a transition to the same state.
+             *    This can happen in the below state transition.
+             *    1. A TCP connection failure happened before sending a PUBCOMP
+             *       for an incoming PUBREL.
+             *    2. Reestablished an MQTT session.
+             *    3. MQTT broker resent the un-acked PUBREL.
+             *    4. Receiving the PUBREL again will result in this transition
+             *       to the same state. */
+            isValid = ( newState == MQTTPublishDone ) ||
+                      ( newState == MQTTPubCompSend );
+            break;
+
+        case MQTTPubRecPending:
+            /* Outgoing publish, Qos 2. */
+            isValid = newState == MQTTPubRelSend;
+            break;
+
+        case MQTTPubRelSend:
+            /* Outgoing publish, Qos 2. */
+            isValid = newState == MQTTPubCompPending;
+            break;
+
+        case MQTTPubCompPending:
+
+            /* Outgoing publish, Qos 2.
+             * There are 2 valid transitions possible.
+             * 1. MQTTPubCompPending -> MQTTPublishDone : A PUBCOMP is received.
+             *    This marks the complete state transition for the publish packet.
+             *    This is the normal state transition without any connection
+             *    interruptions.
+             * 2. MQTTPubCompPending -> MQTTPubCompPending : Resending a PUBREL for
+             *    packets in state #MQTTPubCompPending can result in this
+             *    transition to the same state.
+             *    This can happen in the below state transition.
+             *    1. A TCP connection failure happened before receiving a PUBCOMP
+             *       for an outgoing PUBREL.
+             *    2. An MQTT session is reestablished.
+             *    3. Resending the un-acked PUBREL results in this transition
+             *       to the same state. */
+            isValid = ( newState == MQTTPublishDone ) ||
+                      ( newState == MQTTPubCompPending );
+            break;
+
+        case MQTTPublishDone:
+        /* Done state should transition to invalid since it will be removed from the record. */
+        case MQTTPublishSend:
+        /* If an ack was sent/received we shouldn't have been in this state. */
+        case MQTTStateNull:
+        /* If an ack was sent/received the record should exist. */
+        default:
+            /* Invalid. */
+            break;
+    }
+
+    return isValid;
+}
+
+/*-----------------------------------------------------------*/
+
+static bool isPublishOutgoing( MQTTPubAckType_t packetType,
+                               MQTTStateOperation_t opType )
+{
+    bool isOutgoing = false;
+
+    switch( packetType )
+    {
+        case MQTTPuback:
+        case MQTTPubrec:
+        case MQTTPubcomp:
+            isOutgoing = opType == MQTT_RECEIVE;
+            break;
+
+        case MQTTPubrel:
+            isOutgoing = opType == MQTT_SEND;
+            break;
+
+        default:
+            /* No other ack type. */
+            break;
+    }
+
+    return isOutgoing;
+}
+
+/*-----------------------------------------------------------*/
+
+static size_t findInRecord( const MQTTPubAckInfo_t * records,
+                            size_t recordCount,
+                            uint16_t packetId,
+                            MQTTQoS_t * pQos,
+                            MQTTPublishState_t * pCurrentState )
+{
+    size_t index = 0;
+
+    assert( packetId != MQTT_PACKET_ID_INVALID );
+
+    *pCurrentState = MQTTStateNull;
+
+    for( index = 0; index < recordCount; index++ )
+    {
+        if( records[ index ].packetId == packetId )
+        {
+            *pQos = records[ index ].qos;
+            *pCurrentState = records[ index ].publishState;
+            break;
+        }
+    }
+
+    if( index == recordCount )
+    {
+        index = MQTT_INVALID_STATE_COUNT;
+    }
+
+    return index;
+}
+
+/*-----------------------------------------------------------*/
+
+static void compactRecords( MQTTPubAckInfo_t * records,
+                            size_t recordCount )
+{
+    size_t index = 0;
+    size_t emptyIndex = MQTT_INVALID_STATE_COUNT;
+
+    assert( records != NULL );
+
+    /* Find the empty spots and fill those with non empty values. */
+    for( ; index < recordCount; index++ )
+    {
+        /* Find the first empty spot. */
+        if( records[ index ].packetId == MQTT_PACKET_ID_INVALID )
+        {
+            if( emptyIndex == MQTT_INVALID_STATE_COUNT )
+            {
+                emptyIndex = index;
+            }
+        }
+        else
+        {
+            if( emptyIndex != MQTT_INVALID_STATE_COUNT )
+            {
+                /* Copy over the contents at non empty index to empty index. */
+                records[ emptyIndex ].packetId = records[ index ].packetId;
+                records[ emptyIndex ].qos = records[ index ].qos;
+                records[ emptyIndex ].publishState = records[ index ].publishState;
+
+                /* Mark the record at current non empty index as invalid. */
+                records[ index ].packetId = MQTT_PACKET_ID_INVALID;
+                records[ index ].qos = MQTTQoS0;
+                records[ index ].publishState = MQTTStateNull;
+
+                /* Advance the emptyIndex. */
+                emptyIndex++;
+            }
+        }
+    }
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t addRecord( MQTTPubAckInfo_t * records,
+                               size_t recordCount,
+                               uint16_t packetId,
+                               MQTTQoS_t qos,
+                               MQTTPublishState_t publishState )
+{
+    MQTTStatus_t status = MQTTNoMemory;
+    int32_t index = 0;
+    size_t availableIndex = recordCount;
+    bool validEntryFound = false;
+
+    assert( packetId != MQTT_PACKET_ID_INVALID );
+    assert( qos != MQTTQoS0 );
+
+    /* Check if we have to compact the records. This is known by checking if
+     * the last spot in the array is filled. */
+    if( records[ recordCount - 1U ].packetId != MQTT_PACKET_ID_INVALID )
+    {
+        compactRecords( records, recordCount );
+    }
+
+    /* Start from end so first available index will be populated.
+     * Available index is always found after the last element in the records.
+     * This is to make sure the relative order of the records in order to meet
+     * the message ordering requirement of MQTT spec 3.1.1. */
+    for( index = ( ( int32_t ) recordCount - 1 ); index >= 0; index-- )
+    {
+        /* Available index is only found after packet at the highest index. */
+        if( records[ index ].packetId == MQTT_PACKET_ID_INVALID )
+        {
+            if( validEntryFound == false )
+            {
+                availableIndex = ( size_t ) index;
+            }
+        }
+        else
+        {
+            /* A non-empty spot found in the records. */
+            validEntryFound = true;
+
+            if( records[ index ].packetId == packetId )
+            {
+                /* Collision. */
+                LogError( ( "Collision when adding PacketID=%u at index=%d.",
+                            ( unsigned int ) packetId,
+                            ( int ) index ) );
+
+                status = MQTTStateCollision;
+                availableIndex = recordCount;
+                break;
+            }
+        }
+    }
+
+    if( availableIndex < recordCount )
+    {
+        records[ availableIndex ].packetId = packetId;
+        records[ availableIndex ].qos = qos;
+        records[ availableIndex ].publishState = publishState;
+        status = MQTTSuccess;
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static void updateRecord( MQTTPubAckInfo_t * records,
+                          size_t recordIndex,
+                          MQTTPublishState_t newState,
+                          bool shouldDelete )
+{
+    assert( records != NULL );
+
+    if( shouldDelete == true )
+    {
+        /* Mark the record as invalid. */
+        records[ recordIndex ].packetId = MQTT_PACKET_ID_INVALID;
+        records[ recordIndex ].qos = MQTTQoS0;
+        records[ recordIndex ].publishState = MQTTStateNull;
+    }
+    else
+    {
+        records[ recordIndex ].publishState = newState;
+    }
+}
+
+/*-----------------------------------------------------------*/
+
+static uint16_t stateSelect( const MQTTContext_t * pMqttContext,
+                             uint16_t searchStates,
+                             MQTTStateCursor_t * pCursor )
+{
+    uint16_t packetId = MQTT_PACKET_ID_INVALID;
+    uint16_t outgoingStates = 0U;
+    const MQTTPubAckInfo_t * records = NULL;
+    size_t maxCount;
+    bool stateCheck = false;
+
+    assert( pMqttContext != NULL );
+    assert( searchStates != 0U );
+    assert( pCursor != NULL );
+
+    /* Create a bit map with all the outgoing publish states. */
+    UINT16_SET_BIT( outgoingStates, MQTTPublishSend );
+    UINT16_SET_BIT( outgoingStates, MQTTPubAckPending );
+    UINT16_SET_BIT( outgoingStates, MQTTPubRecPending );
+    UINT16_SET_BIT( outgoingStates, MQTTPubRelSend );
+    UINT16_SET_BIT( outgoingStates, MQTTPubCompPending );
+
+    /* Only outgoing publish records need to be searched. */
+    assert( ( outgoingStates & searchStates ) > 0U );
+    assert( ( ~outgoingStates & searchStates ) == 0U );
+
+    records = pMqttContext->outgoingPublishRecords;
+    maxCount = pMqttContext->outgoingPublishRecordMaxCount;
+
+    while( *pCursor < maxCount )
+    {
+        /* Check if any of the search states are present. */
+        stateCheck = UINT16_CHECK_BIT( searchStates, records[ *pCursor ].publishState );
+
+        if( stateCheck == true )
+        {
+            packetId = records[ *pCursor ].packetId;
+            ( *pCursor )++;
+            break;
+        }
+
+        ( *pCursor )++;
+    }
+
+    return packetId;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTPublishState_t MQTT_CalculateStateAck( MQTTPubAckType_t packetType,
+                                           MQTTStateOperation_t opType,
+                                           MQTTQoS_t qos )
+{
+    MQTTPublishState_t calculatedState = MQTTStateNull;
+    /* There are more QoS2 cases than QoS1, so initialize to that. */
+    bool qosValid = qos == MQTTQoS2;
+
+    switch( packetType )
+    {
+        case MQTTPuback:
+            qosValid = qos == MQTTQoS1;
+            calculatedState = MQTTPublishDone;
+            break;
+
+        case MQTTPubrec:
+
+            /* Incoming publish: send PUBREC, PUBREL pending.
+             * Outgoing publish: receive PUBREC, send PUBREL. */
+            calculatedState = ( opType == MQTT_SEND ) ? MQTTPubRelPending : MQTTPubRelSend;
+            break;
+
+        case MQTTPubrel:
+
+            /* Incoming publish: receive PUBREL, send PUBCOMP.
+             * Outgoing publish: send PUBREL, PUBCOMP pending. */
+            calculatedState = ( opType == MQTT_SEND ) ? MQTTPubCompPending : MQTTPubCompSend;
+            break;
+
+        case MQTTPubcomp:
+            calculatedState = MQTTPublishDone;
+            break;
+
+        default:
+            /* No other ack type. */
+            break;
+    }
+
+    /* Sanity check, make sure ack and QoS agree. */
+    if( qosValid == false )
+    {
+        calculatedState = MQTTStateNull;
+    }
+
+    return calculatedState;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t updateStateAck( MQTTPubAckInfo_t * records,
+                                    size_t maxRecordCount,
+                                    size_t recordIndex,
+                                    uint16_t packetId,
+                                    MQTTPublishState_t currentState,
+                                    MQTTPublishState_t newState )
+{
+    MQTTStatus_t status = MQTTIllegalState;
+    bool shouldDeleteRecord = false;
+    bool isTransitionValid = false;
+
+    assert( records != NULL );
+
+    /* Record to be deleted if the state transition is completed or if a PUBREC
+     * is received for an outgoing QoS2 publish. When a PUBREC is received,
+     * record is deleted and added back to the end of the records to maintain
+     * ordering for PUBRELs. */
+    shouldDeleteRecord = ( newState == MQTTPublishDone ) || ( newState == MQTTPubRelSend );
+    isTransitionValid = validateTransitionAck( currentState, newState );
+
+    if( isTransitionValid == true )
+    {
+        status = MQTTSuccess;
+
+        /* Update record for acks. When sending or receiving acks for packets that
+         * are resent during a session reestablishment, the new state and
+         * current state can be the same. No update of record required in that case. */
+        if( currentState != newState )
+        {
+            updateRecord( records,
+                          recordIndex,
+                          newState,
+                          shouldDeleteRecord );
+
+            /* For QoS2 messages, in order to preserve the message ordering, when
+             * a PUBREC is received for an outgoing publish, the record should be
+             * moved to the last. This move will help preserve the order in which
+             * a PUBREL needs to be resent in case of a session reestablishment. */
+            if( newState == MQTTPubRelSend )
+            {
+                status = addRecord( records,
+                                    maxRecordCount,
+                                    packetId,
+                                    MQTTQoS2,
+                                    MQTTPubRelSend );
+            }
+        }
+    }
+    else
+    {
+        /* Invalid state transition. */
+        LogError( ( "Invalid transition from state %s to state %s.",
+                    MQTT_State_strerror( currentState ),
+                    MQTT_State_strerror( newState ) ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+static MQTTStatus_t updateStatePublish( const MQTTContext_t * pMqttContext,
+                                        size_t recordIndex,
+                                        uint16_t packetId,
+                                        MQTTStateOperation_t opType,
+                                        MQTTQoS_t qos,
+                                        MQTTPublishState_t currentState,
+                                        MQTTPublishState_t newState )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    bool isTransitionValid = false;
+
+    assert( pMqttContext != NULL );
+    assert( packetId != MQTT_PACKET_ID_INVALID );
+    assert( qos != MQTTQoS0 );
+
+    /* This will always succeed for an incoming publish. This is due to the fact
+     * that the passed in currentState must be MQTTStateNull, since
+     * #MQTT_UpdateStatePublish does not perform a lookup for receives. */
+    isTransitionValid = validateTransitionPublish( currentState, newState, opType, qos );
+
+    if( isTransitionValid == true )
+    {
+        /* addRecord will check for collisions. */
+        if( opType == MQTT_RECEIVE )
+        {
+            status = addRecord( pMqttContext->incomingPublishRecords,
+                                pMqttContext->incomingPublishRecordMaxCount,
+                                packetId,
+                                qos,
+                                newState );
+        }
+        /* Send operation. */
+        else
+        {
+            /* Skip updating record when publish is resend and no state
+             * update is required. */
+            if( currentState != newState )
+            {
+                updateRecord( pMqttContext->outgoingPublishRecords,
+                              recordIndex,
+                              newState,
+                              false );
+            }
+        }
+    }
+    else
+    {
+        status = MQTTIllegalState;
+        LogError( ( "Invalid transition from state %s to state %s.",
+                    MQTT_State_strerror( currentState ),
+                    MQTT_State_strerror( newState ) ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_ReserveState( const MQTTContext_t * pMqttContext,
+                                uint16_t packetId,
+                                MQTTQoS_t qos )
+{
+    MQTTStatus_t status = MQTTSuccess;
+
+    if( qos == MQTTQoS0 )
+    {
+        status = MQTTSuccess;
+    }
+    else if( ( packetId == MQTT_PACKET_ID_INVALID ) || ( pMqttContext == NULL ) )
+    {
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        /* Collisions are detected when adding the record. */
+        status = addRecord( pMqttContext->outgoingPublishRecords,
+                            pMqttContext->outgoingPublishRecordMaxCount,
+                            packetId,
+                            qos,
+                            MQTTPublishSend );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTPublishState_t MQTT_CalculateStatePublish( MQTTStateOperation_t opType,
+                                               MQTTQoS_t qos )
+{
+    MQTTPublishState_t calculatedState = MQTTStateNull;
+
+    switch( qos )
+    {
+        case MQTTQoS0:
+            calculatedState = MQTTPublishDone;
+            break;
+
+        case MQTTQoS1:
+            calculatedState = ( opType == MQTT_SEND ) ? MQTTPubAckPending : MQTTPubAckSend;
+            break;
+
+        case MQTTQoS2:
+            calculatedState = ( opType == MQTT_SEND ) ? MQTTPubRecPending : MQTTPubRecSend;
+            break;
+
+        default:
+            /* No other QoS values. */
+            break;
+    }
+
+    return calculatedState;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_UpdateStatePublish( const MQTTContext_t * pMqttContext,
+                                      uint16_t packetId,
+                                      MQTTStateOperation_t opType,
+                                      MQTTQoS_t qos,
+                                      MQTTPublishState_t * pNewState )
+{
+    MQTTPublishState_t newState = MQTTStateNull;
+    MQTTPublishState_t currentState = MQTTStateNull;
+    MQTTStatus_t mqttStatus = MQTTSuccess;
+    size_t recordIndex = MQTT_INVALID_STATE_COUNT;
+    MQTTQoS_t foundQoS = MQTTQoS0;
+
+    if( ( pMqttContext == NULL ) || ( pNewState == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pMqttContext=%p, pNewState=%p",
+                    ( void * ) pMqttContext,
+                    ( void * ) pNewState ) );
+
+        mqttStatus = MQTTBadParameter;
+    }
+    else if( qos == MQTTQoS0 )
+    {
+        /* QoS 0 publish. Do nothing. */
+        *pNewState = MQTTPublishDone;
+    }
+    else if( packetId == MQTT_PACKET_ID_INVALID )
+    {
+        /* Publishes > QoS 0 need a valid packet ID. */
+        mqttStatus = MQTTBadParameter;
+    }
+    else if( opType == MQTT_SEND )
+    {
+        /* Search record for entry so we can check QoS. */
+        recordIndex = findInRecord( pMqttContext->outgoingPublishRecords,
+                                    pMqttContext->outgoingPublishRecordMaxCount,
+                                    packetId,
+                                    &foundQoS,
+                                    &currentState );
+
+        if( ( recordIndex == MQTT_INVALID_STATE_COUNT ) || ( foundQoS != qos ) )
+        {
+            /* Entry should match with supplied QoS. */
+            mqttStatus = MQTTBadParameter;
+        }
+    }
+    else
+    {
+        /* QoS 1 or 2 receive. Nothing to be done. */
+    }
+
+    if( ( qos != MQTTQoS0 ) && ( mqttStatus == MQTTSuccess ) )
+    {
+        newState = MQTT_CalculateStatePublish( opType, qos );
+        /* Validate state transition and update state records. */
+        mqttStatus = updateStatePublish( pMqttContext,
+                                         recordIndex,
+                                         packetId,
+                                         opType,
+                                         qos,
+                                         currentState,
+                                         newState );
+
+        /* Update output parameter on success. */
+        if( mqttStatus == MQTTSuccess )
+        {
+            *pNewState = newState;
+        }
+    }
+
+    return mqttStatus;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_RemoveStateRecord( const MQTTContext_t * pMqttContext,
+                                     uint16_t packetId )
+{
+    MQTTStatus_t status = MQTTSuccess;
+    MQTTPubAckInfo_t * records;
+    size_t recordIndex;
+    /* Current state is updated by the findInRecord function. */
+    MQTTPublishState_t currentState;
+    MQTTQoS_t qos = MQTTQoS0;
+
+
+    if( ( pMqttContext == NULL ) || ( ( pMqttContext->outgoingPublishRecords == NULL ) ) )
+    {
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        records = pMqttContext->outgoingPublishRecords;
+
+        recordIndex = findInRecord( records,
+                                    pMqttContext->outgoingPublishRecordMaxCount,
+                                    packetId,
+                                    &qos,
+                                    &currentState );
+
+        if( currentState == MQTTStateNull )
+        {
+            status = MQTTBadParameter;
+        }
+        else if( ( qos != MQTTQoS1 ) && ( qos != MQTTQoS2 ) )
+        {
+            status = MQTTBadParameter;
+        }
+        else
+        {
+            /* Delete the record. */
+            updateRecord( records,
+                          recordIndex,
+                          MQTTStateNull,
+                          true );
+        }
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+MQTTStatus_t MQTT_UpdateStateAck( const MQTTContext_t * pMqttContext,
+                                  uint16_t packetId,
+                                  MQTTPubAckType_t packetType,
+                                  MQTTStateOperation_t opType,
+                                  MQTTPublishState_t * pNewState )
+{
+    MQTTPublishState_t newState = MQTTStateNull;
+    MQTTPublishState_t currentState = MQTTStateNull;
+    bool isOutgoingPublish = isPublishOutgoing( packetType, opType );
+    MQTTQoS_t qos = MQTTQoS0;
+    size_t maxRecordCount = MQTT_INVALID_STATE_COUNT;
+    size_t recordIndex = MQTT_INVALID_STATE_COUNT;
+
+    MQTTPubAckInfo_t * records = NULL;
+    MQTTStatus_t status = MQTTBadResponse;
+
+    if( ( pMqttContext == NULL ) || ( pNewState == NULL ) )
+    {
+        LogError( ( "Argument cannot be NULL: pMqttContext=%p, pNewState=%p.",
+                    ( void * ) pMqttContext,
+                    ( void * ) pNewState ) );
+        status = MQTTBadParameter;
+    }
+    else if( packetId == MQTT_PACKET_ID_INVALID )
+    {
+        LogError( ( "Packet ID must be nonzero." ) );
+        status = MQTTBadParameter;
+    }
+    else if( packetType > MQTTPubcomp )
+    {
+        LogError( ( "Invalid packet type %u.", ( unsigned int ) packetType ) );
+        status = MQTTBadParameter;
+    }
+    else
+    {
+        if( isOutgoingPublish == true )
+        {
+            records = pMqttContext->outgoingPublishRecords;
+            maxRecordCount = pMqttContext->outgoingPublishRecordMaxCount;
+        }
+        else
+        {
+            records = pMqttContext->incomingPublishRecords;
+            maxRecordCount = pMqttContext->incomingPublishRecordMaxCount;
+        }
+
+        recordIndex = findInRecord( records,
+                                    maxRecordCount,
+                                    packetId,
+                                    &qos,
+                                    &currentState );
+    }
+
+    if( recordIndex != MQTT_INVALID_STATE_COUNT )
+    {
+        newState = MQTT_CalculateStateAck( packetType, opType, qos );
+
+        /* Validate state transition and update state record. */
+        status = updateStateAck( records,
+                                 maxRecordCount,
+                                 recordIndex,
+                                 packetId,
+                                 currentState,
+                                 newState );
+
+        /* Update the output parameter. */
+        if( status == MQTTSuccess )
+        {
+            *pNewState = newState;
+        }
+    }
+    else
+    {
+        LogError( ( "No matching record found for publish: PacketId=%u.",
+                    ( unsigned int ) packetId ) );
+    }
+
+    return status;
+}
+
+/*-----------------------------------------------------------*/
+
+uint16_t MQTT_PubrelToResend( const MQTTContext_t * pMqttContext,
+                              MQTTStateCursor_t * pCursor,
+                              MQTTPublishState_t * pState )
+{
+    uint16_t packetId = MQTT_PACKET_ID_INVALID;
+    uint16_t searchStates = 0U;
+
+    /* Validate arguments. */
+    if( ( pMqttContext == NULL ) || ( pCursor == NULL ) || ( pState == NULL ) )
+    {
+        LogError( ( "Arguments cannot be NULL pMqttContext=%p, pCursor=%p"
+                    " pState=%p.",
+                    ( void * ) pMqttContext,
+                    ( void * ) pCursor,
+                    ( void * ) pState ) );
+    }
+    else
+    {
+        /* PUBREL for packets in state #MQTTPubCompPending and #MQTTPubRelSend
+         * would need to be resent when a session is reestablished.*/
+        UINT16_SET_BIT( searchStates, MQTTPubCompPending );
+        UINT16_SET_BIT( searchStates, MQTTPubRelSend );
+        packetId = stateSelect( pMqttContext, searchStates, pCursor );
+
+        /* The state needs to be in #MQTTPubRelSend for sending PUBREL. */
+        if( packetId != MQTT_PACKET_ID_INVALID )
+        {
+            *pState = MQTTPubRelSend;
+        }
+    }
+
+    return packetId;
+}
+
+/*-----------------------------------------------------------*/
+
+uint16_t MQTT_PublishToResend( const MQTTContext_t * pMqttContext,
+                               MQTTStateCursor_t * pCursor )
+{
+    uint16_t packetId = MQTT_PACKET_ID_INVALID;
+    uint16_t searchStates = 0U;
+
+    /* Validate arguments. */
+    if( ( pMqttContext == NULL ) || ( pCursor == NULL ) )
+    {
+        LogError( ( "Arguments cannot be NULL pMqttContext=%p, pCursor=%p",
+                    ( void * ) pMqttContext,
+                    ( void * ) pCursor ) );
+    }
+    else
+    {
+        /* Packets in state #MQTTPublishSend, #MQTTPubAckPending and
+         * #MQTTPubRecPending would need to be resent when a session is
+         * reestablished. */
+        UINT16_SET_BIT( searchStates, MQTTPublishSend );
+        UINT16_SET_BIT( searchStates, MQTTPubAckPending );
+        UINT16_SET_BIT( searchStates, MQTTPubRecPending );
+
+        packetId = stateSelect( pMqttContext, searchStates, pCursor );
+    }
+
+    return packetId;
+}
+
+/*-----------------------------------------------------------*/
+
+const char * MQTT_State_strerror( MQTTPublishState_t state )
+{
+    const char * str = NULL;
+
+    switch( state )
+    {
+        case MQTTStateNull:
+            str = "MQTTStateNull";
+            break;
+
+        case MQTTPublishSend:
+            str = "MQTTPublishSend";
+            break;
+
+        case MQTTPubAckSend:
+            str = "MQTTPubAckSend";
+            break;
+
+        case MQTTPubRecSend:
+            str = "MQTTPubRecSend";
+            break;
+
+        case MQTTPubRelSend:
+            str = "MQTTPubRelSend";
+            break;
+
+        case MQTTPubCompSend:
+            str = "MQTTPubCompSend";
+            break;
+
+        case MQTTPubAckPending:
+            str = "MQTTPubAckPending";
+            break;
+
+        case MQTTPubRecPending:
+            str = "MQTTPubRecPending";
+            break;
+
+        case MQTTPubRelPending:
+            str = "MQTTPubRelPending";
+            break;
+
+        case MQTTPubCompPending:
+            str = "MQTTPubCompPending";
+            break;
+
+        case MQTTPublishDone:
+            str = "MQTTPublishDone";
+            break;
+
+        default:
+            /* Invalid state received. */
+            str = "Invalid MQTT State";
+            break;
+    }
+
+    return str;
+}
+
+/*-----------------------------------------------------------*/
diff --git a/project/coreMQTT/coreMQTT/core_mqtt_state.h b/project/coreMQTT/coreMQTT/core_mqtt_state.h
new file mode 100644
index 0000000..0644489
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/core_mqtt_state.h
@@ -0,0 +1,310 @@
+/*
+ * coreMQTT v2.1.1
+ * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_mqtt_state.h
+ * @brief Function to keep state of MQTT PUBLISH packet deliveries.
+ */
+#ifndef CORE_MQTT_STATE_H
+#define CORE_MQTT_STATE_H
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    extern "C" {
+#endif
+/* *INDENT-ON* */
+
+#include "core_mqtt.h"
+
+/**
+ * @ingroup mqtt_constants
+ * @brief Initializer value for an #MQTTStateCursor_t, indicating a search
+ * should start at the beginning of a state record array
+ */
+#define MQTT_STATE_CURSOR_INITIALIZER    ( ( size_t ) 0 )
+
+/**
+ * @ingroup mqtt_basic_types
+ * @brief Cursor for iterating through state records.
+ */
+typedef size_t MQTTStateCursor_t;
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this section, this enum is private.
+ *
+ * @brief Value indicating either send or receive.
+ */
+typedef enum MQTTStateOperation
+{
+    MQTT_SEND,
+    MQTT_RECEIVE
+} MQTTStateOperation_t;
+/** @endcond */
+
+/**
+ * @fn MQTTStatus_t MQTT_ReserveState( const MQTTContext_t * pMqttContext, uint16_t packetId, MQTTQoS_t qos );
+ * @brief Reserve an entry for an outgoing QoS 1 or Qos 2 publish.
+ *
+ * @param[in] pMqttContext Initialized MQTT context.
+ * @param[in] packetId The ID of the publish packet.
+ * @param[in] qos 1 or 2.
+ *
+ * @return MQTTSuccess, MQTTNoMemory, or MQTTStateCollision.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+MQTTStatus_t MQTT_ReserveState( const MQTTContext_t * pMqttContext,
+                                uint16_t packetId,
+                                MQTTQoS_t qos );
+/** @endcond */
+
+/**
+ * @fn MQTTPublishState_t MQTT_CalculateStatePublish( MQTTStateOperation_t opType, MQTTQoS_t qos )
+ * @brief Calculate the new state for a publish from its qos and operation type.
+ *
+ * @param[in] opType Send or Receive.
+ * @param[in] qos 0, 1, or 2.
+ *
+ * @return The calculated state.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+MQTTPublishState_t MQTT_CalculateStatePublish( MQTTStateOperation_t opType,
+                                               MQTTQoS_t qos );
+/** @endcond */
+
+/**
+ * @fn MQTTStatus_t MQTT_UpdateStatePublish( const MQTTContext_t * pMqttContext, uint16_t packetId, MQTTStateOperation_t opType, MQTTQoS_t qos, MQTTPublishState_t * pNewState );
+ * @brief Update the state record for a PUBLISH packet.
+ *
+ * @param[in] pMqttContext Initialized MQTT context.
+ * @param[in] packetId ID of the PUBLISH packet.
+ * @param[in] opType Send or Receive.
+ * @param[in] qos 0, 1, or 2.
+ * @param[out] pNewState Updated state of the publish.
+ *
+ * @return #MQTTBadParameter, #MQTTIllegalState, #MQTTStateCollision or
+ * #MQTTSuccess.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+MQTTStatus_t MQTT_UpdateStatePublish( const MQTTContext_t * pMqttContext,
+                                      uint16_t packetId,
+                                      MQTTStateOperation_t opType,
+                                      MQTTQoS_t qos,
+                                      MQTTPublishState_t * pNewState );
+/** @endcond */
+
+/**
+ * @fn MQTTStatus_t MQTT_RemoveStateRecord( const MQTTContext_t * pMqttContext, uint16_t packetId );
+ * @brief Remove the state record for a PUBLISH packet.
+ *
+ * @param[in] pMqttContext Initialized MQTT context.
+ * @param[in] packetId ID of the PUBLISH packet.
+ *
+ * @return #MQTTBadParameter or #MQTTSuccess.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+MQTTStatus_t MQTT_RemoveStateRecord( const MQTTContext_t * pMqttContext,
+                                     uint16_t packetId );
+/** @endcond */
+
+/**
+ * @fn MQTTPublishState_t MQTT_CalculateStateAck( MQTTPubAckType_t packetType, MQTTStateOperation_t opType, MQTTQoS_t qos );
+ * @brief Calculate the state from a PUBACK, PUBREC, PUBREL, or PUBCOMP.
+ *
+ * @param[in] packetType PUBACK, PUBREC, PUBREL, or PUBCOMP.
+ * @param[in] opType Send or Receive.
+ * @param[in] qos 1 or 2.
+ *
+ * @return The calculated state.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+MQTTPublishState_t MQTT_CalculateStateAck( MQTTPubAckType_t packetType,
+                                           MQTTStateOperation_t opType,
+                                           MQTTQoS_t qos );
+/** @endcond */
+
+/**
+ * @fn MQTTStatus_t MQTT_UpdateStateAck( const MQTTContext_t * pMqttContext, uint16_t packetId, MQTTPubAckType_t packetType, MQTTStateOperation_t opType, MQTTPublishState_t * pNewState );
+ * @brief Update the state record for an ACKed publish.
+ *
+ * @param[in] pMqttContext Initialized MQTT context.
+ * @param[in] packetId ID of the ack packet.
+ * @param[in] packetType PUBACK, PUBREC, PUBREL, or PUBCOMP.
+ * @param[in] opType Send or Receive.
+ * @param[out] pNewState Updated state of the publish.
+ *
+ * @return #MQTTBadParameter if an invalid parameter is passed;
+ * #MQTTBadResponse if the packet from the network is not found in the records;
+ * #MQTTIllegalState if the requested update would result in an illegal transition;
+ * #MQTTSuccess otherwise.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+MQTTStatus_t MQTT_UpdateStateAck( const MQTTContext_t * pMqttContext,
+                                  uint16_t packetId,
+                                  MQTTPubAckType_t packetType,
+                                  MQTTStateOperation_t opType,
+                                  MQTTPublishState_t * pNewState );
+/** @endcond */
+
+/**
+ * @fn uint16_t MQTT_PubrelToResend( const MQTTContext_t * pMqttContext, MQTTStateCursor_t * pCursor, MQTTPublishState_t * pState );
+ * @brief Get the packet ID of next pending PUBREL ack to be resent.
+ *
+ * This function will need to be called to get the packet for which a PUBREL
+ * need to be sent when a session is reestablished. Calling this function
+ * repeatedly until packet id is 0 will give all the packets for which
+ * a PUBREL need to be resent in the correct order.
+ *
+ * @param[in] pMqttContext Initialized MQTT context.
+ * @param[in,out] pCursor Index at which to start searching.
+ * @param[out] pState State indicating that PUBREL packet need to be sent.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+uint16_t MQTT_PubrelToResend( const MQTTContext_t * pMqttContext,
+                              MQTTStateCursor_t * pCursor,
+                              MQTTPublishState_t * pState );
+/** @endcond */
+
+/**
+ * @brief Get the packet ID of next pending publish to be resent.
+ *
+ * This function will need to be called to get the packet for which a publish
+ * need to be sent when a session is reestablished. Calling this function
+ * repeatedly until packet id is 0 will give all the packets for which
+ * a publish need to be resent in the correct order.
+ *
+ * @param[in] pMqttContext Initialized MQTT context.
+ * @param[in,out] pCursor Index at which to start searching.
+ *
+ * <b>Example</b>
+ * @code{c}
+ *
+ * // For this example assume this function returns an outgoing unacknowledged
+ * // QoS 1 or 2 publish from its packet identifier.
+ * MQTTPublishInfo_t * getPublish( uint16_t packetID );
+ *
+ * // Variables used in this example.
+ * MQTTStatus_t status;
+ * MQTTStateCursor_t cursor = MQTT_STATE_CURSOR_INITIALIZER;
+ * bool sessionPresent;
+ * uint16_t packetID;
+ * MQTTPublishInfo_t * pResendPublish = NULL;
+ * MQTTConnectInfo_t connectInfo = { 0 };
+ *
+ * // This is assumed to have been initialized before the call to MQTT_Connect().
+ * MQTTContext_t * pContext;
+ *
+ * // Set clean session to false to attempt session resumption.
+ * connectInfo.cleanSession = false;
+ * connectInfo.pClientIdentifier = "someClientID";
+ * connectInfo.clientIdentifierLength = strlen( connectInfo.pClientIdentifier );
+ * connectInfo.keepAliveSeconds = 60;
+ * // Optional connect parameters are not relevant to this example.
+ *
+ * // Create an MQTT connection. Use 100 milliseconds as a timeout.
+ * status = MQTT_Connect( pContext, &connectInfo, NULL, 100, &sessionPresent );
+ *
+ * if( status == MQTTSuccess )
+ * {
+ *      if( sessionPresent )
+ *      {
+ *          // Loop while packet ID is nonzero.
+ *          while( ( packetID = MQTT_PublishToResend( pContext, &cursor ) ) != 0 )
+ *          {
+ *              // Assume this function will succeed.
+ *              pResendPublish = getPublish( packetID );
+ *              // Set DUP flag.
+ *              pResendPublish->dup = true;
+ *              status = MQTT_Publish( pContext, pResendPublish, packetID );
+ *
+ *              if( status != MQTTSuccess )
+ *              {
+ *                  // Application can decide how to handle a failure.
+ *              }
+ *          }
+ *      }
+ *      else
+ *      {
+ *          // The broker did not resume a session, so we can clean up the
+ *          // list of outgoing publishes.
+ *      }
+ * }
+ * @endcode
+ */
+/* @[declare_mqtt_publishtoresend] */
+uint16_t MQTT_PublishToResend( const MQTTContext_t * pMqttContext,
+                               MQTTStateCursor_t * pCursor );
+/* @[declare_mqtt_publishtoresend] */
+
+/**
+ * @fn const char * MQTT_State_strerror( MQTTPublishState_t state );
+ * @brief State to string conversion for state engine.
+ *
+ * @param[in] state The state to convert to a string.
+ *
+ * @return The string representation of the state.
+ */
+
+/**
+ * @cond DOXYGEN_IGNORE
+ * Doxygen should ignore this definition, this function is private.
+ */
+const char * MQTT_State_strerror( MQTTPublishState_t state );
+/** @endcond */
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    }
+#endif
+/* *INDENT-ON* */
+
+#endif /* ifndef CORE_MQTT_STATE_H */
diff --git a/project/coreMQTT/coreMQTT/makefile b/project/coreMQTT/coreMQTT/makefile
new file mode 100644
index 0000000..b6ece0e
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/makefile
@@ -0,0 +1,35 @@
+#********************************************************************************
+#      Copyright:  (C) 2023 LingYun IoT System Studio
+#                  All rights reserved.
+#
+#       Filename:  Makefile
+#    Description:  This file used compile all the source code to static library
+#
+#        Version:  1.0.0(11/08/23)
+#         Author:  Guo Wenxue <guowenxue@gmail.com>
+#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+PWD=$(shell pwd )
+
+BUILD_ARCH=$(shell uname -m)
+ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
+    CROSS_COMPILE?=arm-linux-gnueabihf-
+endif
+
+LIBNAME=$(shell basename ${PWD} )
+TOPDIR=$(shell dirname ${PWD} )
+CFLAGS+=-D_GNU_SOURCE
+
+all: clean
+	@rm -f *.o
+	@${CROSS_COMPILE}gcc ${CFLAGS} -I${TOPDIR} -c *.c
+	${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
+
+clean:
+	@rm -f *.o
+	@rm -f *.a
+
+distclean:
+	@make clean
diff --git a/project/coreMQTT/coreMQTT/transport_interface.h b/project/coreMQTT/coreMQTT/transport_interface.h
new file mode 100644
index 0000000..e86443d
--- /dev/null
+++ b/project/coreMQTT/coreMQTT/transport_interface.h
@@ -0,0 +1,316 @@
+/*
+ * coreMQTT v2.1.1
+ * Copyright (C) 2022 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file transport_interface.h
+ * @brief Transport interface definitions to send and receive data over the
+ * network.
+ */
+#ifndef TRANSPORT_INTERFACE_H_
+#define TRANSPORT_INTERFACE_H_
+
+#include <stdint.h>
+#include <stddef.h>
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/**
+ * @transportpage
+ * @brief The transport interface definition.
+ *
+ * @transportsectionoverview
+ *
+ * The transport interface is a set of APIs that must be implemented using an
+ * external transport layer protocol. The transport interface is defined in
+ * @ref transport_interface.h. This interface allows protocols like MQTT and
+ * HTTP to send and receive data over the transport layer. This
+ * interface does not handle connection and disconnection to the server of
+ * interest. The connection, disconnection, and other transport settings, like
+ * timeout and TLS setup, must be handled in the user application.
+ * <br>
+ *
+ * The functions that must be implemented are:<br>
+ * - [Transport Receive](@ref TransportRecv_t)
+ * - [Transport Send](@ref TransportSend_t)
+ *
+ * Each of the functions above take in an opaque context @ref NetworkContext_t.
+ * The functions above and the context are also grouped together in the
+ * @ref TransportInterface_t structure:<br><br>
+ * @snippet this define_transportinterface
+ * <br>
+ *
+ * @transportsectionimplementation
+ *
+ * The following steps give guidance on implementing the transport interface:
+ *
+ * -# Implementing @ref NetworkContext_t<br><br>
+ * @snippet this define_networkcontext
+ * <br>
+ * @ref NetworkContext_t is the incomplete type <b>struct NetworkContext</b>.
+ * The implemented struct NetworkContext must contain all of the information
+ * that is needed to receive and send data with the @ref TransportRecv_t
+ * and the @ref TransportSend_t implementations.<br>
+ * In the case of TLS over TCP, struct NetworkContext is typically implemented
+ * with the TCP socket context and a TLS context.<br><br>
+ * <b>Example code:</b>
+ * @code{c}
+ * struct NetworkContext
+ * {
+ *     struct MyTCPSocketContext tcpSocketContext;
+ *     struct MyTLSContext tlsContext;
+ * };
+ * @endcode
+ * <br>
+ * -# Implementing @ref TransportRecv_t<br><br>
+ * @snippet this define_transportrecv
+ * <br>
+ * This function is expected to populate a buffer, with bytes received from the
+ * transport, and return the number of bytes placed in the buffer.
+ * In the case of TLS over TCP, @ref TransportRecv_t is typically implemented by
+ * calling the TLS layer function to receive data. In case of plaintext TCP
+ * without TLS, it is typically implemented by calling the TCP layer receive
+ * function. @ref TransportRecv_t may be invoked multiple times by the protocol
+ * library, if fewer bytes than were requested to receive are returned.
+ * <br><br>
+ * <b>Example code:</b>
+ * @code{c}
+ * int32_t myNetworkRecvImplementation( NetworkContext_t * pNetworkContext,
+ *                                      void * pBuffer,
+ *                                      size_t bytesToRecv )
+ * {
+ *     int32_t bytesReceived = 0;
+ *     bool callTlsRecvFunc = true;
+ *
+ *     // For a single byte read request, check if data is available on the network.
+ *     if( bytesToRecv == 1 )
+ *     {
+ *        // If no data is available on the network, do not call TLSRecv
+ *        // to avoid blocking for socket timeout.
+ *        if( TLSRecvCount( pNetworkContext->tlsContext ) == 0 )
+ *        {
+ *            callTlsRecvFunc = false;
+ *        }
+ *     }
+ *
+ *     if( callTlsRecvFunc == true )
+ *     {
+ *        bytesReceived = TLSRecv( pNetworkContext->tlsContext,
+ *                                 pBuffer,
+ *                                 bytesToRecv,
+ *                                 MY_SOCKET_TIMEOUT );
+ *        if( bytesReceived < 0 )
+ *        {
+ *           // If the error code represents a timeout, then the return
+ *           // code should be translated to zero so that the caller
+ *           // can retry the read operation.
+ *           if( bytesReceived == MY_SOCKET_ERROR_TIMEOUT )
+ *           {
+ *              bytesReceived = 0;
+ *           }
+ *        }
+ *        // Handle other cases.
+ *     }
+ *     return bytesReceived;
+ * }
+ * @endcode
+ * <br>
+ * -# Implementing @ref TransportSend_t<br><br>
+ * @snippet this define_transportsend
+ * <br>
+ * This function is expected to send the bytes, in the given buffer over the
+ * transport, and return the number of bytes sent.
+ * In the case of TLS over TCP, @ref TransportSend_t is typically implemented by
+ * calling the TLS layer function to send data. In case of plaintext TCP
+ * without TLS, it is typically implemented by calling the TCP layer send
+ * function. @ref TransportSend_t may be invoked multiple times by the protocol
+ * library, if fewer bytes than were requested to send are returned.
+ * <br><br>
+ * <b>Example code:</b>
+ * @code{c}
+ * int32_t myNetworkSendImplementation( NetworkContext_t * pNetworkContext,
+ *                                      const void * pBuffer,
+ *                                      size_t bytesToSend )
+ * {
+ *     int32_t bytesSent = 0;
+ *     bytesSent = TLSSend( pNetworkContext->tlsContext,
+ *                          pBuffer,
+ *                          bytesToSend,
+ *                          MY_SOCKET_TIMEOUT );
+ *
+ *      // If underlying TCP buffer is full, set the return value to zero
+ *      // so that caller can retry the send operation.
+ *     if( bytesSent == MY_SOCKET_ERROR_BUFFER_FULL )
+ *     {
+ *          bytesSent = 0;
+ *     }
+ *     else if( bytesSent < 0 )
+ *     {
+ *         // Handle socket error.
+ *     }
+ *     // Handle other cases.
+ *
+ *     return bytesSent;
+ * }
+ * @endcode
+ */
+
+/**
+ * @transportstruct
+ * @typedef NetworkContext_t
+ * @brief The NetworkContext is an incomplete type. An implementation of this
+ * interface must define struct NetworkContext for the system requirements.
+ * This context is passed into the network interface functions.
+ */
+/* @[define_networkcontext] */
+struct NetworkContext;
+typedef struct NetworkContext NetworkContext_t;
+/* @[define_networkcontext] */
+
+/**
+ * @transportcallback
+ * @brief Transport interface for receiving data on the network.
+ *
+ * @note It is RECOMMENDED that the transport receive implementation
+ * does NOT block when requested to read a single byte. A single byte
+ * read request can be made by the caller to check whether there is a
+ * new frame available on the network for reading.
+ * However, the receive implementation MAY block for a timeout period when
+ * it is requested to read more than 1 byte. This is because once the caller
+ * is aware that a new frame is available to read on the network, then
+ * the likelihood of reading more than one byte over the network becomes high.
+ *
+ * @param[in] pNetworkContext Implementation-defined network context.
+ * @param[in] pBuffer Buffer to receive the data into.
+ * @param[in] bytesToRecv Number of bytes requested from the network.
+ *
+ * @return The number of bytes received or a negative value to indicate
+ * error.
+ *
+ * @note If no data is available on the network to read and no error
+ * has occurred, zero MUST be the return value. A zero return value
+ * SHOULD represent that the read operation can be retried by calling
+ * the API function. Zero MUST NOT be returned if a network disconnection
+ * has occurred.
+ */
+/* @[define_transportrecv] */
+typedef int32_t ( * TransportRecv_t )( NetworkContext_t * pNetworkContext,
+                                       void * pBuffer,
+                                       size_t bytesToRecv );
+/* @[define_transportrecv] */
+
+/**
+ * @transportcallback
+ * @brief Transport interface for sending data over the network.
+ *
+ * @param[in] pNetworkContext Implementation-defined network context.
+ * @param[in] pBuffer Buffer containing the bytes to send over the network stack.
+ * @param[in] bytesToSend Number of bytes to send over the network.
+ *
+ * @return The number of bytes sent or a negative value to indicate error.
+ *
+ * @note If no data is transmitted over the network due to a full TX buffer and
+ * no network error has occurred, this MUST return zero as the return value.
+ * A zero return value SHOULD represent that the send operation can be retried
+ * by calling the API function. Zero MUST NOT be returned if a network disconnection
+ * has occurred.
+ */
+/* @[define_transportsend] */
+typedef int32_t ( * TransportSend_t )( NetworkContext_t * pNetworkContext,
+                                       const void * pBuffer,
+                                       size_t bytesToSend );
+/* @[define_transportsend] */
+
+/**
+ * @brief Transport vector structure for sending multiple messages.
+ */
+typedef struct TransportOutVector
+{
+    /**
+     * @brief Base address of data.
+     */
+    const void * iov_base;
+
+    /**
+     * @brief Length of data in buffer.
+     */
+    size_t iov_len;
+} TransportOutVector_t;
+
+/**
+ * @transportcallback
+ * @brief Transport interface function for "vectored" / scatter-gather based
+ * writes. This function is expected to iterate over the list of vectors pIoVec
+ * having ioVecCount entries containing portions of one MQTT message at a maximum.
+ * If the proper functionality is available, then the data in the list should be
+ * copied to the underlying TCP buffer before flushing the buffer. Implementing it
+ * in this fashion  will lead to sending of fewer TCP packets for all the values
+ * in the list.
+ *
+ * @note If the proper write functionality is not present for a given device/IP-stack,
+ * then there is no strict requirement to implement write. Only the send and recv
+ * interfaces must be defined for the application to work properly.
+ *
+ * @param[in] pNetworkContext Implementation-defined network context.
+ * @param[in] pIoVec An array of TransportIoVector_t structs.
+ * @param[in] ioVecCount Number of TransportIoVector_t in pIoVec.
+ *
+ * @return The number of bytes written or a negative value to indicate error.
+ *
+ * @note If no data is written to the buffer due to the buffer being full this MUST
+ * return zero as the return value.
+ * A zero return value SHOULD represent that the write operation can be retried
+ * by calling the API function. Zero MUST NOT be returned if a network disconnection
+ * has occurred.
+ */
+/* @[define_transportwritev] */
+typedef int32_t ( * TransportWritev_t )( NetworkContext_t * pNetworkContext,
+                                         TransportOutVector_t * pIoVec,
+                                         size_t ioVecCount );
+/* @[define_transportwritev] */
+
+/**
+ * @transportstruct
+ * @brief The transport layer interface.
+ */
+/* @[define_transportinterface] */
+typedef struct TransportInterface
+{
+    TransportRecv_t recv;               /**< Transport receive function pointer. */
+    TransportSend_t send;               /**< Transport send function pointer. */
+    TransportWritev_t writev;           /**< Transport writev function pointer. */
+    NetworkContext_t * pNetworkContext; /**< Implementation-defined network context. */
+} TransportInterface_t;
+/* @[define_transportinterface] */
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    }
+#endif
+/* *INDENT-ON* */
+
+#endif /* ifndef TRANSPORT_INTERFACE_H_ */
diff --git a/project/coreMQTT/coreSNTP/README.md b/project/coreMQTT/coreSNTP/README.md
new file mode 100644
index 0000000..597e271
--- /dev/null
+++ b/project/coreMQTT/coreSNTP/README.md
@@ -0,0 +1,28 @@
+## coreSNTP Library
+
+This repository contains the coreSNTP library, a client library to use Simple Network Time Protocol (SNTP) to synchronize device clocks with internet time. This library implements the SNTPv4 specification defined in [RFC 4330](https://tools.ietf.org/html/rfc4330).
+
+An SNTP client can request time from both NTP and SNTP servers. According to the SNTPv4 specification, "_To an NTP or SNTP server, NTP and SNTP clients are indistinguishable; to an NTP or SNTP client, NTP and SNTP servers are indistinguishable._", thereby, allowing SNTP clients to request time from NTP servers.
+
+**coreSNTP v1.2.0 [source code](https://github.com/FreeRTOS/coreSNTP/tree/v1.2.0/source) is part of the [FreeRTOS 202210.00 LTS](https://github.com/FreeRTOS/FreeRTOS-LTS/tree/202210.00-LTS) release.**
+
+## Documentation
+
+The API reference documentation for the coreSNTP library version released in [FreeRTOS/FreeRTOS](https://github.com/FreeRTOS/FreeRTOS) can be viewed from the [freertos.org website](https://freertos.org/coresntp/index.html).
+
+## Cloning this repository
+This repo uses [Git Submodules](https://git-scm.com/book/en/v2/Git-Tools-Submodules) to bring in dependent components.
+
+To clone using HTTPS:
+```
+git clone https://github.com/FreeRTOS/coreSNTP.git --recurse-submodules
+```
+Using SSH:
+```
+git clone git@github.com:FreeRTOS/coreSNTP.git --recurse-submodules
+```
+
+If you have downloaded the repo without using the `--recurse-submodules` argument, you need to run:
+```
+git submodule update --init --recursive
+```
diff --git a/project/coreMQTT/coreSNTP/core_sntp_client.c b/project/coreMQTT/coreSNTP/core_sntp_client.c
new file mode 100644
index 0000000..6b4d8d9
--- /dev/null
+++ b/project/coreMQTT/coreSNTP/core_sntp_client.c
@@ -0,0 +1,959 @@
+/*
+ * coreSNTP v1.2.0
+ * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_sntp_client.c
+ * @brief Implementation of the client API of the coreSNTP library.
+ */
+
+/* Standard includes. */
+#include <assert.h>
+#include <string.h>
+
+/* SNTP client library API include. */
+#include "core_sntp_client.h"
+
+#include "core_sntp_config_defaults.h"
+
+/**
+ * @brief Utility to convert fractions part of SNTP timestamp to milliseconds.
+ *
+ * @param[in] fractions The fractions value in an SNTP timestamp.
+ */
+#define FRACTIONS_TO_MS( fractions ) \
+    ( fractions / ( SNTP_FRACTION_VALUE_PER_MICROSECOND * 1000U ) )
+
+SntpStatus_t Sntp_Init( SntpContext_t * pContext,
+                        const SntpServerInfo_t * pTimeServers,
+                        size_t numOfServers,
+                        uint32_t serverResponseTimeoutMs,
+                        uint8_t * pNetworkBuffer,
+                        size_t bufferSize,
+                        SntpResolveDns_t resolveDnsFunc,
+                        SntpGetTime_t getSystemTimeFunc,
+                        SntpSetTime_t setSystemTimeFunc,
+                        const UdpTransportInterface_t * pTransportIntf,
+                        const SntpAuthenticationInterface_t * pAuthIntf )
+{
+    SntpStatus_t status = SntpSuccess;
+
+    /* Validate pointer parameters are not NULL. */
+    if( ( pContext == NULL ) || ( pTimeServers == NULL ) ||
+        ( pNetworkBuffer == NULL ) || ( resolveDnsFunc == NULL ) ||
+        ( getSystemTimeFunc == NULL ) || ( setSystemTimeFunc == NULL ) ||
+        ( pTransportIntf == NULL ) )
+    {
+        LogError( ( "Invalid parameter: Pointer parameters (except pAuthIntf) cannot be NULL" ) );
+
+        status = SntpErrorBadParameter;
+    }
+    /* Validate the length of the servers list.*/
+    else if( numOfServers == 0U )
+    {
+        LogError( ( "Invalid parameter: Size of server list cannot be zero" ) );
+        status = SntpErrorBadParameter;
+    }
+    /* Validate that the UDP transport interface functions are valid. */
+    else if( ( pTransportIntf->recvFrom == NULL ) || ( pTransportIntf->sendTo == NULL ) )
+    {
+        LogError( ( "Invalid parameter: Function members of UDP transport interface cannot be NULL" ) );
+        status = SntpErrorBadParameter;
+    }
+
+    /* If an authentication interface is provided, validate that its function pointer
+     * members are valid. */
+    else if( ( pAuthIntf != NULL ) &&
+             ( ( pAuthIntf->generateClientAuth == NULL ) ||
+               ( pAuthIntf->validateServerAuth == NULL ) ) )
+    {
+        LogError( ( "Invalid parameter: Function members of authentication interface cannot be NULL" ) );
+        status = SntpErrorBadParameter;
+    }
+    else if( bufferSize < SNTP_PACKET_BASE_SIZE )
+    {
+        LogError( ( "Cannot initialize context: Passed network buffer size is less than %u bytes: "
+                    "bufferSize=%lu", SNTP_PACKET_BASE_SIZE, ( unsigned long ) bufferSize ) );
+        status = SntpErrorBufferTooSmall;
+    }
+    else
+    {
+        /* Reset the context memory to zero. */
+        ( void ) memset( pContext, 0, sizeof( SntpContext_t ) );
+
+        /* Set the members of the context with passed parameters. */
+        pContext->pTimeServers = pTimeServers;
+        pContext->numOfServers = numOfServers;
+
+        pContext->responseTimeoutMs = serverResponseTimeoutMs;
+
+        pContext->pNetworkBuffer = pNetworkBuffer;
+        pContext->bufferSize = bufferSize;
+
+        pContext->resolveDnsFunc = resolveDnsFunc;
+        pContext->getTimeFunc = getSystemTimeFunc;
+        pContext->setTimeFunc = setSystemTimeFunc;
+
+        /* Copy contents of UDP transport interface to context. */
+        ( void ) memcpy( &pContext->networkIntf, pTransportIntf, sizeof( UdpTransportInterface_t ) );
+
+        /* If authentication interface has been passed, copy its contents to the context. */
+        if( pAuthIntf != NULL )
+        {
+            ( void ) memcpy( &pContext->authIntf, pAuthIntf, sizeof( SntpAuthenticationInterface_t ) );
+        }
+
+        /* Initialize the packet size member to the standard minimum SNTP packet size.*/
+        pContext->sntpPacketSize = SNTP_PACKET_BASE_SIZE;
+    }
+
+    return status;
+}
+
+/**
+ * @brief Utility to calculate the difference in milliseconds between 2
+ * SNTP timestamps.
+ *
+ * @param[in] pCurrentTime The more recent timestamp.
+ * @param[in] pOlderTime The older timestamp.
+ *
+ * @note This functions supports the edge case of SNTP timestamp overflow
+ * when @p pCurrentTime represents time in NTP era 1 (i.e. time since 7 Feb 2036)
+ * and the @p OlderTime represents time in NTP era 0 (i.e. time since 1st Jan 1900).
+ *
+ * @return Returns the calculated time duration between the two timestamps.
+ *
+ * @note This function returns the calculated time difference as unsigned 64 bit
+ * to avoid integer overflow when converting time difference between the seconds part
+ * of the timestamps, which are 32 bits wide, to milliseconds.
+ */
+static uint64_t calculateElapsedTimeMs( const SntpTimestamp_t * pCurrentTime,
+                                        const SntpTimestamp_t * pOlderTime )
+{
+    uint64_t timeDiffMs = 0UL;
+    uint32_t timeDiffSec = 0U;
+
+    assert( pCurrentTime != NULL );
+    assert( pOlderTime != NULL );
+
+    /* Detect if SNTP time has overflown between the 2 timestamps. */
+    if( pCurrentTime->seconds < pOlderTime->seconds )
+    {
+        /* Handle the SNTP time overflow by calculating the actual time
+         * duration from pOlderTime, that exists in NTP era 0, to pCurrentTime,
+         * that exists in NTP era 1. */
+        timeDiffSec = ( UINT32_MAX - pOlderTime->seconds ) + /* Time in NTP era 0. */
+                      1U +                                   /* Epoch time in NTP era 1, i.e. 7 Feb 2036 6h:14m:28s. */
+                      pCurrentTime->seconds;                 /* Time in NTP era 1. */
+
+        timeDiffMs = ( uint64_t ) timeDiffSec * 1000UL;
+    }
+    else
+    {
+        timeDiffSec = ( pCurrentTime->seconds - pOlderTime->seconds );
+        timeDiffMs = ( uint64_t ) timeDiffSec * 1000UL;
+    }
+
+    if( pCurrentTime->fractions > pOlderTime->fractions )
+    {
+        timeDiffMs += ( ( uint64_t ) pCurrentTime->fractions - ( uint64_t ) pOlderTime->fractions ) /
+                      ( SNTP_FRACTION_VALUE_PER_MICROSECOND * 1000UL );
+    }
+    else
+    {
+        timeDiffMs -= ( ( uint64_t ) pOlderTime->fractions - ( uint64_t ) pCurrentTime->fractions ) /
+                      ( SNTP_FRACTION_VALUE_PER_MICROSECOND * 1000UL );
+    }
+
+    return timeDiffMs;
+}
+
+/**
+ * @brief Validates the content of the SNTP context passed to the APIs to
+ * check whether it represents an initialized context.
+ *
+ * @param[in] pContext The SNTP context to validate.
+ *
+ * @return Returns one of the following:
+ * - #SntpSuccess if the context is verified to be initialized.
+ * - #SntpErrorBadParameter if the context is NULL.
+ * - #SntpErrorContextNotInitialized if the context is validated to be initialized.
+ */
+static SntpStatus_t validateContext( const SntpContext_t * pContext )
+{
+    SntpStatus_t status = SntpSuccess;
+
+    /* Check if the context parameter is invalid. */
+    if( pContext == NULL )
+    {
+        status = SntpErrorBadParameter;
+        LogError( ( "Invalid context parameter: Context is NULL" ) );
+    }
+
+    /* Validate pointer parameters are not NULL. */
+    else if( ( pContext->pTimeServers == NULL ) || ( pContext->pNetworkBuffer == NULL ) ||
+             ( pContext->resolveDnsFunc == NULL ) ||
+             ( pContext->getTimeFunc == NULL ) || ( pContext->setTimeFunc == NULL ) )
+    {
+        status = SntpErrorContextNotInitialized;
+    }
+
+    /* Validate the size of the configured servers list, network buffer size and the state
+     * variable for the SNTP packet size.*/
+    else if( ( pContext->numOfServers == 0U ) || ( pContext->bufferSize < SNTP_PACKET_BASE_SIZE ) ||
+             ( pContext->sntpPacketSize < SNTP_PACKET_BASE_SIZE ) )
+    {
+        status = SntpErrorContextNotInitialized;
+    }
+    /* Validate that the UDP transport interface functions are valid. */
+    else if( ( pContext->networkIntf.recvFrom == NULL ) || ( pContext->networkIntf.sendTo == NULL ) )
+    {
+        status = SntpErrorContextNotInitialized;
+    }
+
+    /* If an authentication interface is provided, validate that both its function pointer
+     * members are valid. */
+    else if( ( ( pContext->authIntf.generateClientAuth != NULL ) && ( pContext->authIntf.validateServerAuth == NULL ) ) ||
+             ( ( pContext->authIntf.generateClientAuth == NULL ) && ( pContext->authIntf.validateServerAuth != NULL ) ) )
+    {
+        status = SntpErrorContextNotInitialized;
+    }
+    else
+    {
+        status = SntpSuccess;
+    }
+
+    if( status == SntpErrorContextNotInitialized )
+    {
+        LogError( ( "Invalid context parameter: Context is not initialized with Sntp_Init" ) );
+    }
+
+    return status;
+}
+
+/**
+ * @brief Sends SNTP request packet to the passed server over the network
+ * using transport interface's send function.
+ *
+ * @note For the case of zero byte transmissions over the network, this function
+ * repeatedly retries the send operation by calling the transport interface
+ * until either:
+ * 1. The requested number of bytes @p packetSize have been sent.
+ *                    OR
+ * 2. There is an error in sending data over the network.
+ *
+ * @note This function treats partial data transmissions as error as UDP
+ * transport protocol does not support partial sends.
+ *
+ * @param[in] pNetworkIntf The UDP transport interface to use for
+ * sending data over the network.
+ * @param[in] timeServer The IPv4 address of the server to send the
+ * SNTP request packet to.
+ * @param[in] serverPort The port of the @p timeServer to send the
+ * request to.
+ * @param[in] getTimeFunc The function to query system time for
+ * tracking retry time period of no data transmissions.
+ * @param[in] pPacket The buffer containing the SNTP packet data
+ * to send over the network.
+ * @param[in] packetSize The size of data in the SNTP request packet.
+ * @param[in] timeoutMs The timeout period for retry attempts of sending
+ * SNTP request packet over the network.
+ *
+ * @return Returns #SntpSuccess on successful transmission of the entire
+ * SNTP request packet over the network; #SntpErrorNetworkFailure
+ * to indicate failure from transport interface; #SntpErrorSendTimeout if
+ * time request could not be sent over the network within the @p timeoutMs
+ * duration.
+ */
+static SntpStatus_t sendSntpPacket( const UdpTransportInterface_t * pNetworkIntf,
+                                    uint32_t timeServer,
+                                    uint16_t serverPort,
+                                    SntpGetTime_t getTimeFunc,
+                                    const uint8_t * pPacket,
+                                    uint16_t packetSize,
+                                    uint32_t timeoutMs )
+{
+    const uint8_t * pIndex = pPacket;
+    int32_t bytesSent = 0;
+    SntpTimestamp_t lastSendTime;
+    bool shouldRetry = false;
+    SntpStatus_t status = SntpErrorSendTimeout;
+
+    assert( pPacket != NULL );
+    assert( getTimeFunc != NULL );
+    assert( pNetworkIntf != NULL );
+    assert( packetSize >= SNTP_PACKET_BASE_SIZE );
+
+    /* Record the starting time of attempting to send data. This begins the retry timeout
+     * window. */
+    getTimeFunc( &lastSendTime );
+
+    /* Loop until the entire packet is sent. */
+    do
+    {
+        /* Reset flag for retrying send operation for the iteration. If request packet cannot be
+         * sent and timeout has not occurred, the flag will be set later for the next iteration. */
+        shouldRetry = false;
+
+        bytesSent = pNetworkIntf->sendTo( pNetworkIntf->pUserContext,
+                                          timeServer,
+                                          serverPort,
+                                          pIndex,
+                                          packetSize );
+
+        if( bytesSent < 0 )
+        {
+            LogError( ( "Unable to send request packet: Transport send failed. "
+                        "ErrorCode=%ld.", ( long int ) bytesSent ) );
+            status = SntpErrorNetworkFailure;
+        }
+        else if( bytesSent == 0 )
+        {
+            /* No bytes were sent over the network. Retry send if we have not timed out. */
+
+            SntpTimestamp_t currentTime;
+            uint64_t elapsedTimeMs;
+
+            getTimeFunc( &currentTime );
+
+            /* Calculate time elapsed since last data was sent over network. */
+            elapsedTimeMs = calculateElapsedTimeMs( &currentTime, &lastSendTime );
+
+            /* Check for timeout if we have been waiting to send any data over the network. */
+            if( elapsedTimeMs >= timeoutMs )
+            {
+                LogError( ( "Unable to send request packet: Timed out retrying send: "
+                            "SendRetryTimeout=%ums", timeoutMs ) );
+                status = SntpErrorSendTimeout;
+            }
+            else
+            {
+                shouldRetry = true;
+            }
+        }
+
+        /* Partial sends are not supported by UDP, which only supports sending the entire datagram as a whole.
+         * Thus, if the transport send function returns status representing partial send, it will be treated as failure. */
+        else if( bytesSent != ( int32_t ) packetSize )
+        {
+            LogError( ( "Unable to send request packet: Transport send returned unexpected bytes sent. "
+                        "ReturnCode=%ld, ExpectedCode=%u", ( long int ) bytesSent, packetSize ) );
+
+            status = SntpErrorNetworkFailure;
+        }
+        else
+        {
+            /* The time request packet has been sent over the network. */
+            status = SntpSuccess;
+        }
+    } while( shouldRetry == true );
+
+    return status;
+}
+
+/**
+ * @brief Adds client authentication data to SNTP request packet by calling the
+ * authentication interface.
+ *
+ * @param[in] pContext The SNTP context.
+ *
+ * @return Returns one of the following:
+ * - #SntpSuccess if the interface function successfully appends client
+ * authentication data.
+ * - #SntpErrorAuthFailure when the interface returns either an error OR an
+ * incorrect size of the client authentication data.
+ */
+static SntpStatus_t addClientAuthentication( SntpContext_t * pContext )
+{
+    SntpStatus_t status = SntpSuccess;
+    uint16_t authDataSize = 0U;
+
+    assert( pContext != NULL );
+    assert( pContext->authIntf.generateClientAuth != NULL );
+    assert( pContext->currentServerIndex <= pContext->numOfServers );
+
+    status = pContext->authIntf.generateClientAuth( pContext->authIntf.pAuthContext,
+                                                    &pContext->pTimeServers[ pContext->currentServerIndex ],
+                                                    pContext->pNetworkBuffer,
+                                                    pContext->bufferSize,
+                                                    &authDataSize );
+
+    if( status != SntpSuccess )
+    {
+        LogError( ( "Unable to send time request: Client authentication function failed: "
+                    "RetStatus=%s", Sntp_StatusToStr( status ) ) );
+    }
+
+    /* Sanity check that the returned authentication data size fits in the remaining space
+     * of the request buffer besides the first #SNTP_PACKET_BASE_SIZE bytes. */
+    else if( authDataSize > ( pContext->bufferSize - SNTP_PACKET_BASE_SIZE ) )
+    {
+        LogError( ( "Unable to send time request: Invalid authentication code size: "
+                    "AuthCodeSize=%lu, NetworkBufferSize=%lu",
+                    ( unsigned long ) authDataSize, ( unsigned long ) pContext->bufferSize ) );
+        status = SntpErrorAuthFailure;
+    }
+    else
+    {
+        /* With the authentication data added. calculate total SNTP request packet size. The same
+         * size would be expected in the SNTP response from server. */
+        pContext->sntpPacketSize = SNTP_PACKET_BASE_SIZE + authDataSize;
+
+        LogInfo( ( "Appended client authentication code to SNTP request packet:"
+                   " AuthCodeSize=%lu, TotalPacketSize=%lu",
+                   ( unsigned long ) authDataSize,
+                   ( unsigned long ) pContext->sntpPacketSize ) );
+    }
+
+    return status;
+}
+
+SntpStatus_t Sntp_SendTimeRequest( SntpContext_t * pContext,
+                                   uint32_t randomNumber,
+                                   uint32_t blockTimeMs )
+{
+    SntpStatus_t status = SntpSuccess;
+
+    /* Validate the context parameter. */
+    status = validateContext( pContext );
+
+    if( status == SntpSuccess )
+    {
+        const SntpServerInfo_t * pServer = NULL;
+
+        /* Set local variable for the currently indexed server to use for time
+         * query. */
+        pServer = &pContext->pTimeServers[ pContext->currentServerIndex ];
+
+        LogDebug( ( "Using server %.*s for time query", ( int ) pServer->serverNameLen, pServer->pServerName ) );
+
+        /* Perform DNS resolution of the currently indexed server in the list
+         * of configured servers. */
+        if( pContext->resolveDnsFunc( pServer, &pContext->currentServerAddr ) == false )
+        {
+            LogError( ( "Unable to send time request: DNS resolution failed: Server=%.*s",
+                        ( int ) pServer->serverNameLen, pServer->pServerName ) );
+
+            status = SntpErrorDnsFailure;
+        }
+        else
+        {
+            LogDebug( ( "Server DNS resolved: Address=0x%08X", pContext->currentServerAddr ) );
+        }
+
+        if( status == SntpSuccess )
+        {
+            /* Obtain current system time to generate SNTP request packet. */
+            pContext->getTimeFunc( &pContext->lastRequestTime );
+
+            LogDebug( ( "Obtained current time for SNTP request packet: Time=%us %ums",
+                        pContext->lastRequestTime.seconds, FRACTIONS_TO_MS( pContext->lastRequestTime.fractions ) ) );
+
+            /* Generate SNTP request packet with the current system time and
+             * the passed random number. */
+            status = Sntp_SerializeRequest( &pContext->lastRequestTime,
+                                            randomNumber,
+                                            pContext->pNetworkBuffer,
+                                            pContext->bufferSize );
+
+            /* The serialization should be successful as all parameter validation has
+             * been done before. */
+            assert( status == SntpSuccess );
+        }
+
+        /* If an authentication interface has been configured, call the function to append client
+         * authentication data to SNTP request buffer. */
+        if( ( status == SntpSuccess ) && ( pContext->authIntf.generateClientAuth != NULL ) )
+        {
+            status = addClientAuthentication( pContext );
+        }
+
+        if( status == SntpSuccess )
+        {
+            LogInfo( ( "Sending serialized SNTP request packet to the server: Addr=%u, Port=%u",
+                       pContext->currentServerAddr,
+                       pContext->pTimeServers[ pContext->currentServerIndex ].port ) );
+
+            /* Send the request packet over the network to the time server. */
+            status = sendSntpPacket( &pContext->networkIntf,
+                                     pContext->currentServerAddr,
+                                     pContext->pTimeServers[ pContext->currentServerIndex ].port,
+                                     pContext->getTimeFunc,
+                                     pContext->pNetworkBuffer,
+                                     pContext->sntpPacketSize,
+                                     blockTimeMs );
+        }
+    }
+
+    return status;
+}
+
+/**
+ * @brief Utility to update the SNTP context to rotate the server of use for subsequent
+ * time request(s).
+ *
+ * @note If there is no next server remaining, after the current server's index, in the list of
+ * configured servers, the server rotation algorithm wraps around to the first server in the list.
+ * The wrap around is done so that an application using the library for a long-running SNTP client
+ * functionality (like a daemon task) does not become dysfunctional after all configured time
+ * servers have been used up. Time synchronization can be a critical functionality for a system
+ * and the wrap around logic ensures that the SNTP client continues to function in such a case.
+ *
+ * @note Server rotation is performed ONLY when either of:
+ * - The current server responds with a rejection for time request.
+ *                         OR
+ * - The current server response wait has timed out.
+ */
+static void rotateServerForNextTimeQuery( SntpContext_t * pContext )
+{
+    size_t nextServerIndex = ( pContext->currentServerIndex + 1U ) % pContext->numOfServers;
+
+    LogInfo( ( "Rotating server for next time query: PreviousServer=%.*s, NextServer=%.*s",
+               ( int ) pContext->pTimeServers[ pContext->currentServerIndex ].serverNameLen,
+               pContext->pTimeServers[ pContext->currentServerIndex ].pServerName,
+               ( int ) pContext->pTimeServers[ nextServerIndex ].serverNameLen,
+               pContext->pTimeServers[ nextServerIndex ].pServerName ) );
+
+    pContext->currentServerIndex = nextServerIndex;
+}
+
+
+/**
+ * @brief This function attempts to receive the SNTP response packet from a server.
+ *
+ * @note This function treats reads of data sizes less than the expected server response packet,
+ * as an error as UDP does not support partial reads. Such a scenario can exist either due:
+ * - An error in the server sending its response with smaller packet size than the request packet OR
+ * - A malicious attacker spoofing or modifying server response OR
+ * - An error in the UDP transport interface implementation for read operation.
+ *
+ * @param[in] pTransportIntf The UDP transport interface to use for receiving data from
+ * the network.
+ * @param[in] timeServer The server to read the response from the network.
+ * @param[in] serverPort The port of the server to read the response from.
+ * @param[in, out] pBuffer This will be filled with the server response read from the
+ * network.
+ * @param[in] responseSize The size of server response to read from the network.
+ *
+ * @return It returns one of the following:
+ * - #SntpSuccess if an SNTP response packet is received from the network.
+ * - #SntpNoResponseReceived if a server response is not received from the network.
+ * - #SntpErrorNetworkFailure if there is an internal failure in reading from the network
+ * in the user-defined transport interface.
+ */
+static SntpStatus_t receiveSntpResponse( const UdpTransportInterface_t * pTransportIntf,
+                                         uint32_t timeServer,
+                                         uint16_t serverPort,
+                                         uint8_t * pBuffer,
+                                         uint16_t responseSize )
+{
+    SntpStatus_t status = SntpNoResponseReceived;
+    int32_t bytesRead = 0;
+
+    assert( pTransportIntf != NULL );
+    assert( pTransportIntf->recvFrom != NULL );
+    assert( pBuffer != NULL );
+    assert( responseSize >= SNTP_PACKET_BASE_SIZE );
+
+    bytesRead = pTransportIntf->recvFrom( pTransportIntf->pUserContext,
+                                          timeServer,
+                                          serverPort,
+                                          pBuffer,
+                                          responseSize );
+
+    /* Negative return code indicates error. */
+    if( bytesRead < 0 )
+    {
+        status = SntpErrorNetworkFailure;
+        LogError( ( "Unable to receive server response: Transport receive failed: Code=%ld",
+                    ( long int ) bytesRead ) );
+    }
+    /* If the packet was not available on the network, check whether we can retry. */
+    else if( bytesRead == 0 )
+    {
+        status = SntpNoResponseReceived;
+    }
+
+    /* Partial reads are not supported by UDP, which only supports receiving the entire datagram as a whole.
+     * Thus, if the transport receive function returns reception of partial data, it will be treated as failure. */
+    else if( bytesRead != ( int32_t ) responseSize )
+    {
+        LogError( ( "Failed to receive server response: Transport recv returned less than expected bytes."
+                    "ExpectedBytes=%u, ReadBytes=%ld", responseSize, ( long int ) bytesRead ) );
+        status = SntpErrorNetworkFailure;
+    }
+    else
+    {
+        LogDebug( ( "Received server response: PacketSize=%ld", ( long int ) bytesRead ) );
+        status = SntpSuccess;
+    }
+
+    return status;
+}
+
+/**
+ * @brief Processes the response from a server by de-serializing the SNTP packet to
+ * validate the server (if an authentication interface has been configured), determine
+ * whether server has accepted or rejected the time request, and update the system clock
+ * if the server responded positively with time.
+ *
+ * @param[in] pContext The SNTP context representing the SNTP client.
+ * @param[in] pResponseRxTime The time of receiving the server response from the network.
+ *
+ * @return It returns one of the following:
+ * - #SntpSuccess if the server response is successfully de-serialized and system clock
+ * updated.
+ * - #SntpErrorAuthFailure if there is internal failure in user-defined authentication
+ * interface when validating server from the response.
+ * - #SntpServerNotAuthenticated if the server failed authenticated check in the user-defined
+ * interface.
+ * - #SntpRejectedResponse if the server has rejected the time request in its response.
+ * - #SntpInvalidResponse if the server response failed sanity checks.
+ */
+static SntpStatus_t processServerResponse( SntpContext_t * pContext,
+                                           const SntpTimestamp_t * pResponseRxTime )
+{
+    SntpStatus_t status = SntpSuccess;
+    const SntpServerInfo_t * pServer = &pContext->pTimeServers[ pContext->currentServerIndex ];
+
+    assert( pContext != NULL );
+    assert( pResponseRxTime != NULL );
+
+    if( pContext->authIntf.validateServerAuth != NULL )
+    {
+        /* Verify the server from the authentication data in the SNTP response packet. */
+        status = pContext->authIntf.validateServerAuth( pContext->authIntf.pAuthContext,
+                                                        pServer,
+                                                        pContext->pNetworkBuffer,
+                                                        pContext->sntpPacketSize );
+        assert( ( status == SntpSuccess ) || ( status == SntpErrorAuthFailure ) ||
+                ( status == SntpServerNotAuthenticated ) );
+
+        if( status != SntpSuccess )
+        {
+            LogError( ( "Unable to use server response: Server authentication function failed: "
+                        "ReturnStatus=%s", Sntp_StatusToStr( status ) ) );
+        }
+        else
+        {
+            LogDebug( ( "Server response has been validated: Server=%.*s", ( int ) pServer->serverNameLen, pServer->pServerName ) );
+        }
+    }
+
+    if( status == SntpSuccess )
+    {
+        SntpResponseData_t parsedResponse;
+
+        /* De-serialize response packet to determine whether the server accepted or rejected
+         * the request for time. Also, calculate the system clock offset if the server responded
+         * with time. */
+        status = Sntp_DeserializeResponse( &pContext->lastRequestTime,
+                                           pResponseRxTime,
+                                           pContext->pNetworkBuffer,
+                                           pContext->sntpPacketSize,
+                                           &parsedResponse );
+
+        /* We do not expect the following errors to be returned as the context
+         * has been validated in the Sntp_ReceiveTimeResponse API. */
+        assert( status != SntpErrorBadParameter );
+        assert( status != SntpErrorBufferTooSmall );
+
+        if( ( status == SntpRejectedResponseChangeServer ) ||
+            ( status == SntpRejectedResponseRetryWithBackoff ) ||
+            ( status == SntpRejectedResponseOtherCode ) )
+        {
+            /* Server has rejected the time request. Thus, we will rotate to the next time server
+             * in the list. */
+            rotateServerForNextTimeQuery( pContext );
+
+            LogError( ( "Unable to use server response: Server has rejected request for time: RejectionCode=%.*s",
+                        ( int ) SNTP_KISS_OF_DEATH_CODE_LENGTH, ( char * ) &parsedResponse.rejectedResponseCode ) );
+            status = SntpRejectedResponse;
+        }
+        else if( status == SntpInvalidResponse )
+        {
+            LogError( ( "Unable to use server response: Server response failed sanity checks." ) );
+        }
+        else
+        {
+            /* Server has responded successfully with time, and we have calculated the clock offset
+             * of system clock relative to the server.*/
+            LogDebug( ( "Updating system time: ServerTime=%u %ums ClockOffset=%lums",
+                        parsedResponse.serverTime.seconds, FRACTIONS_TO_MS( parsedResponse.serverTime.fractions ),
+                        parsedResponse.clockOffsetMs ) );
+
+            /* Update the system clock with the calculated offset. */
+            pContext->setTimeFunc( pServer, &parsedResponse.serverTime,
+                                   parsedResponse.clockOffsetMs, parsedResponse.leapSecondType );
+
+            status = SntpSuccess;
+        }
+    }
+
+    /* Reset the last request time state in context to protect against replay attacks.
+     * Note: The last request time is not cleared when a rejection response packet is received and the client does
+     * has not authenticated server from the response. This is because clearing of the state causes the coreSNTP
+     * library to discard any subsequent server response packets (as the "originate timestamp" of those packets will
+     * not match the last request time value of the context), and thus, an attacker can cause Denial of Service
+     * attacks by spoofing server response before the actual server is able to respond.
+     */
+    if( ( status == SntpSuccess ) ||
+        ( ( pContext->authIntf.validateServerAuth != NULL ) && ( status == SntpRejectedResponse ) ) )
+    {
+        /* In the attack of SNTP request packet being replayed, the replayed request packet is serviced by
+         * SNTP/NTP server with SNTP response (as servers are stateless) and client receives the response
+         * containing new values of server timestamps but the stale value of "originate timestamp".
+         * To prevent the coreSNTP library from servicing such a server response (associated with the replayed
+         * SNTP request packet), the last request timestamp state is cleared in the context after receiving the
+         * first valid server response. Therefore, any subsequent server response(s) from replayed client request
+         * packets can be invalidated due to the "originate timestamp" not matching the last request time stored
+         * in the context.
+         * Note: If an attacker spoofs a server response with a zero "originate timestamp" after the coreSNTP
+         * library (i.e. the SNTP client) has cleared the internal state to zero, the spoofed packet will be
+         * discarded as the coreSNTP serializer does not accept server responses with zero value for timestamps.
+         */
+        pContext->lastRequestTime.seconds = 0U;
+        pContext->lastRequestTime.fractions = 0U;
+    }
+
+    return status;
+}
+
+/**
+ * @brief Determines whether a retry attempt should be made to receive server response packet from the network
+ * depending on the timing constraints of server response timeout, @p responseTimeoutMs, and the block time
+ * period, @p blockTimeMs, passed. If neither of the time windows have expired, the function determines that the
+ * read operation can be re-tried.
+ *
+ * @param[in] pCurrentTime The current time in the system used for calculating elapsed time windows.
+ * @param[in] pReadStartTime The time of the first read attempt in the current set of read tries occurring
+ * from the Sntp_ReceiveTimeRequest API call by the application. This time is used for calculating the elapsed
+ * time to determine whether the block time has expired.
+ * @param[in] pRequestTime The time of sending the SNTP request to the server for which the response is
+ * awaited. This time is used for calculating total elapsed elapsed time of waiting for server response to
+ * determine if a server response timeout has occurred.
+ * @param[in] responseTimeoutMs The server response timeout configuration.
+ * @param[in] blockTimeMs The maximum block time of waiting for server response across read tries in the current
+ * call made by application to Sntp_ReceiveTimeResponse API.
+ * @param[out] pHasResponseTimedOut This will be populated with state to indicate whether the wait for server
+ * response has timed out.
+ *
+ * @return Returns true for retrying read operation of server response; false on either server response timeout
+ * OR completion of block time window.
+ */
+static bool decideAboutReadRetry( const SntpTimestamp_t * pCurrentTime,
+                                  const SntpTimestamp_t * pReadStartTime,
+                                  const SntpTimestamp_t * pRequestTime,
+                                  uint32_t responseTimeoutMs,
+                                  uint32_t blockTimeMs,
+                                  bool * pHasResponseTimedOut )
+{
+    uint64_t timeSinceRequestMs = 0UL;
+    uint64_t timeElapsedInReadAttempts = 0UL;
+    bool shouldRetry = false;
+
+    assert( pCurrentTime != NULL );
+    assert( pReadStartTime != NULL );
+    assert( pRequestTime != NULL );
+    assert( pHasResponseTimedOut != NULL );
+
+    /* Calculate time elapsed since the time request was sent to the server
+     * to determine whether the server response has timed out. */
+    timeSinceRequestMs = calculateElapsedTimeMs( pCurrentTime, pRequestTime );
+
+    /* Calculate the time elapsed across all the read attempts so far to determine
+     * whether the block time window for reading server response has expired. */
+    timeElapsedInReadAttempts = calculateElapsedTimeMs( pCurrentTime, pReadStartTime );
+
+    /* Check whether a response timeout has occurred to inform whether we should
+     * wait for server response anymore. */
+    if( timeSinceRequestMs >= ( uint64_t ) responseTimeoutMs )
+    {
+        shouldRetry = false;
+        *pHasResponseTimedOut = true;
+
+        LogError( ( "Unable to receive response: Server response has timed out: "
+                    "RequestTime=%us %ums, TimeoutDuration=%ums, ElapsedTime=%lu",
+                    pRequestTime->seconds, FRACTIONS_TO_MS( pRequestTime->fractions ),
+                    responseTimeoutMs, timeSinceRequestMs ) );
+    }
+    /* Check whether the block time window has expired to determine whether read can be retried. */
+    else if( timeElapsedInReadAttempts >= ( uint64_t ) blockTimeMs )
+    {
+        shouldRetry = false;
+        LogDebug( ( "Did not receive server response: Read block time has expired: "
+                    "BlockTime=%ums, ResponseWaitElapsedTime=%lums",
+                    blockTimeMs, timeSinceRequestMs ) );
+    }
+    else
+    {
+        shouldRetry = true;
+        LogDebug( ( "Did not receive server response: Retrying read: "
+                    "BlockTime=%ums, ResponseWaitElapsedTime=%lums, ResponseTimeout=%u",
+                    blockTimeMs, timeSinceRequestMs, responseTimeoutMs ) );
+    }
+
+    return shouldRetry;
+}
+
+SntpStatus_t Sntp_ReceiveTimeResponse( SntpContext_t * pContext,
+                                       uint32_t blockTimeMs )
+{
+    SntpStatus_t status = SntpNoResponseReceived;
+    bool hasResponseTimedOut = false;
+
+    /* Validate the context parameter. */
+    status = validateContext( pContext );
+
+    if( status == SntpSuccess )
+    {
+        SntpTimestamp_t startTime, currentTime;
+        const SntpTimestamp_t * pRequestTime = &pContext->lastRequestTime;
+        bool shouldRetry = false;
+
+        /* Record time before read attempts so that it can be used as base time for
+         * for tracking the block time window across read retries. */
+        pContext->getTimeFunc( &startTime );
+
+        do
+        {
+            /* Reset the retry read operation flag. If the server response is not received in the current iteration's read
+             * attempt and the wait has not timed out, the flag will be set to perform a retry. */
+            shouldRetry = false;
+
+            /* Make an attempt to read the server response from the network. */
+            status = receiveSntpResponse( &pContext->networkIntf,
+                                          pContext->currentServerAddr,
+                                          pContext->pTimeServers[ pContext->currentServerIndex ].port,
+                                          pContext->pNetworkBuffer,
+                                          pContext->sntpPacketSize );
+
+            /* If the server response is received, deserialize it, validate the server
+             * (if authentication interface is provided), and update system time with
+             * the calculated clock offset. */
+            if( status == SntpSuccess )
+            {
+                /* Get current time to de-serialize the receive server response packet. */
+                pContext->getTimeFunc( &currentTime );
+
+                status = processServerResponse( pContext, &currentTime );
+            }
+            else if( status == SntpNoResponseReceived )
+            {
+                /* Get current time to determine whether another attempt for reading the packet can
+                 * be made. */
+                pContext->getTimeFunc( &currentTime );
+
+                /* Set the flag to retry read of server response from the network. */
+                shouldRetry = decideAboutReadRetry( &currentTime,
+                                                    &startTime,
+                                                    pRequestTime,
+                                                    pContext->responseTimeoutMs,
+                                                    blockTimeMs,
+                                                    &hasResponseTimedOut );
+            }
+            else
+            {
+                /* Empty else marker. */
+            }
+        } while( shouldRetry == true );
+
+        /* If the wait for server response to the time request has timed out, rotate the server of use in the
+         * context for subsequent time request(s). Also, update the return status to indicate response timeout. */
+        if( hasResponseTimedOut == true )
+        {
+            status = SntpErrorResponseTimeout;
+
+            /* Rotate server to the next in the list of configured servers in the context. */
+            rotateServerForNextTimeQuery( pContext );
+        }
+    }
+
+    return status;
+}
+
+const char * Sntp_StatusToStr( SntpStatus_t status )
+{
+    const char * pString = NULL;
+
+    switch( status )
+    {
+        case SntpSuccess:
+            pString = "SntpSuccess";
+            break;
+
+        case SntpErrorBadParameter:
+            pString = "SntpErrorBadParameter";
+            break;
+
+        case SntpRejectedResponseChangeServer:
+            pString = "SntpRejectedResponseChangeServer";
+            break;
+
+        case SntpRejectedResponseRetryWithBackoff:
+            pString = "SntpRejectedResponseRetryWithBackoff";
+            break;
+
+        case SntpRejectedResponseOtherCode:
+            pString = "SntpRejectedResponseOtherCode";
+            break;
+
+        case SntpErrorBufferTooSmall:
+            pString = "SntpErrorBufferTooSmall";
+            break;
+
+        case SntpInvalidResponse:
+            pString = "SntpInvalidResponse";
+            break;
+
+        case SntpZeroPollInterval:
+            pString = "SntpZeroPollInterval";
+            break;
+
+        case SntpErrorTimeNotSupported:
+            pString = "SntpErrorTimeNotSupported";
+            break;
+
+        case SntpErrorDnsFailure:
+            pString = "SntpErrorDnsFailure";
+            break;
+
+        case SntpErrorNetworkFailure:
+            pString = "SntpErrorNetworkFailure";
+            break;
+
+        case SntpServerNotAuthenticated:
+            pString = "SntpServerNotAuthenticated";
+            break;
+
+        case SntpErrorAuthFailure:
+            pString = "SntpErrorAuthFailure";
+            break;
+
+        default:
+            pString = "Invalid status code!";
+            break;
+    }
+
+    return pString;
+}
diff --git a/project/coreMQTT/coreSNTP/core_sntp_client.h b/project/coreMQTT/coreSNTP/core_sntp_client.h
new file mode 100644
index 0000000..829aed5
--- /dev/null
+++ b/project/coreMQTT/coreSNTP/core_sntp_client.h
@@ -0,0 +1,655 @@
+/*
+ * coreSNTP v1.2.0
+ * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_sntp_client.h
+ * @brief API of an SNTPv4 client library that can send time requests and receive time response to/from
+ * SNTP/NTP servers. The library follows the Best Practices suggested in the the SNTPv4 specification,
+ * [RFC 4330](https://tools.ietf.org/html/rfc4330).
+ * The library can be used to run an SNTP client in a dedicated deamon task to periodically synchronize
+ * time from the Internet.
+ */
+
+#ifndef CORE_SNTP_CLIENT_H_
+#define CORE_SNTP_CLIENT_H_
+
+/* Standard include. */
+#include <stdint.h>
+#include <stddef.h>
+#include <stdbool.h>
+
+/* Include coreSNTP Serializer header. */
+#include "core_sntp_serializer.h"
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/**
+ * @ingroup sntp_constants
+ * @brief The default UDP port supported by SNTP/NTP servers for client-server
+ * communication.
+ *
+ * @note It is possible for a server to use a different port number than
+ * the default port when using the Network Time Security protocol as the security
+ * mechanism for SNTP communication. For more information, refer to Section 4.1.8
+ * of [RFC 8915](https://tools.ietf.org/html/rfc8915).
+ */
+#define SNTP_DEFAULT_SERVER_PORT    ( 123U )
+
+/**
+ * @ingroup sntp_struct_types
+ * @brief Structure representing information for a time server.
+ */
+typedef struct SntpServerInfo
+{
+    const char * pServerName; /**<@brief The time server name. */
+    size_t serverNameLen;     /**<@brief The length of the server name.*/
+    uint16_t port;            /**<@brief The UDP port supported by the server
+                               * for SNTP/NTP communication. */
+} SntpServerInfo_t;
+
+/**
+ * @ingroup sntp_callback_types
+ * @brief Interface for user-defined function to resolve time server domain-name
+ * to an IPv4 address.
+ * The SNTP client library attempts to resolve the DNS of the time-server being
+ * used every time the @ref Sntp_SendTimeRequest API is called.
+ *
+ * @param[in] pServerAddr The time-server whose IPv4 address is to be resolved.
+ * @param[out] pIpV4Addr This should be filled with the resolved IPv4 address.
+ * of @p pTimeServer.
+ *
+ * @return `true` if DNS resolution is successful; otherwise `false` to represent
+ * failure.
+ */
+typedef bool ( * SntpResolveDns_t )( const SntpServerInfo_t * pServerAddr,
+                                     uint32_t * pIpV4Addr );
+
+/**
+ * @ingroup sntp_callback_types
+ * @brief Interface for user-defined function to obtain the current system time
+ * in SNTP timestamp format.
+ *
+ * @note If your platform follows UNIX representation of time, the
+ * #SNTP_TIME_AT_UNIX_EPOCH_SECS and #SNTP_FRACTION_VALUE_PER_MICROSECOND macros
+ * can be used to convert UNIX time to SNTP timestamp.
+ *
+ * @param[out] pCurrentTime This should be filled with the current system time
+ * in SNTP timestamp format.
+ *
+ * @return `true` if obtaining system time is successful; otherwise `false` to
+ * represent failure.
+ */
+typedef void ( * SntpGetTime_t )( SntpTimestamp_t * pCurrentTime );
+
+/**
+ * @ingroup sntp_callback_types
+ * @brief Interface for user-defined function to update the system clock time
+ * so that it is synchronized the time server used for getting current time.
+ *
+ * @param[in] pTimeServer The time server used to request time.
+ * @param[in] pServerTime The current time returned by the @p pTimeServer.
+ * @param[in] clockOffsetMs The calculated clock offset (in milliseconds) of the
+ * system relative to the server time.
+ * @param[in] leapSecondInfo Information about whether there is about an upcoming
+ * leap second adjustment of insertion or deletion in the last minute before midnight
+ * on the last day of the current month. For more information on leap seconds, refer
+ * to https://www.nist.gov/pml/time-and-frequency-division/leap-seconds-faqs. Depending
+ * on the accuracy requirements of the system clock, the user can choose to account
+ * for the leap second or ignore it in their system clock update logic.
+ *
+ * @note If the @p clockOffsetMs is positive, then the system time is BEHIND the server time,
+ * and if the @p clockOffsetMs, the system time is AHEAD of the server time. To correct the
+ * system time, the user can use either of "step", "slew" OR combination of the two clock
+ * discipline methodologies depending on the application needs.
+ * If the application requires a smooth time continuum of system time, then the "slew"
+ * discipline methodology can be used with the clock offset value, @p clockOffSetMs, to correct
+ * the system clock gradually with a "slew rate".
+ * If the application can accept sudden jump in time (forward or backward), then
+ * the "step" discipline methodology can be used to directly update the system
+ * clock with the current server time, @p pServerTime, every time the coreSNTP library
+ * calls the interface.
+ */
+typedef void ( * SntpSetTime_t )( const SntpServerInfo_t * pTimeServer,
+                                  const SntpTimestamp_t * pServerTime,
+                                  int64_t clockOffsetMs,
+                                  SntpLeapSecondInfo_t leapSecondInfo );
+
+/**
+ * @ingroup sntp_struct_types
+ * @typedef NetworkContext_t
+ * @brief A user-defined type for context that is passed to the transport interface functions.
+ * It MUST be defined by the user to use the library.
+ * It is of incomplete type to allow user to define to the needs of their transport
+ * interface implementation.
+ */
+struct NetworkContext;
+typedef struct NetworkContext NetworkContext_t;
+
+/**
+ * @ingroup sntp_callback_types
+ * @brief Interface for user-defined function to send time request as a single datagram
+ * to server on the network over User Datagram Protocol (UDP).
+ *
+ * @note It is RECOMMENDED that the send operation is non-blocking, i.e. it
+ * SHOULD immediately return when the entire UDP data cannot be sent over the
+ * network. In such a case, the coreSNTP library will re-try send operation
+ * for a maximum period of blocking time passed to the @ref Sntp_SendTimeRequest API.
+ *
+ * @note If the size of the SNTP packet exceeds the Maximum Transmission Unit (MTU)
+ * supported by the network interface of the device, the user-defined implementation
+ * MAY either support fragmentation of UDP packets OR use a size for authentication data
+ * that allows the SNTP packet to fit within the MTU required size threshold. (Note that
+ * the size of SNTP packet is #SNTP_PACKET_BASE_SIZE + authentication data.)
+ *
+ * @param[in,out] pNetworkContext The user defined NetworkContext_t which
+ * is opaque to the coreSNTP library.
+ * @param[in] serverAddr The IPv4 address of the time server to send the data to.
+ * @param[in] serverPort The port of the server to send data to.
+ * @param[in] pBuffer The buffer containing the data to send over the network.
+ * @param[in] bytesToSend The size of data in @p pBuffer to send.
+ *
+ * @return The function SHOULD return one of the following integer codes:
+ * - @p bytesToSend when all requested data is successfully transmitted over the
+ * network.
+ * - 0 when no data could be sent over the network (due to network buffer being
+ * full, for example), and the send operation can be retried.
+ * - < 0 when the send operation failed to send any data due to an internal error,
+ * and operation cannot be retried.
+ */
+typedef int32_t ( * UdpTransportSendTo_t )( NetworkContext_t * pNetworkContext,
+                                            uint32_t serverAddr,
+                                            uint16_t serverPort,
+                                            const void * pBuffer,
+                                            uint16_t bytesToSend );
+
+/**
+ * @ingroup sntp_callback_types
+ * @brief Interface for user-defined function to receive the server response, to a time
+ * request (sent through the @ref UdpTransportSendTo_t function), from the network over
+ * User Datagram Protocol (UDP).
+ *
+ * @note It is RECOMMENDED that the read operation is non-blocking, i.e. it
+ * SHOULD immediately return when no data is available on the network.
+ * In such a case, the coreSNTP library will re-try send operation for a maximum period
+ * of blocking time passed through the @ref Sntp_ReceiveTimeResponse API.
+ *
+ * @note If the size of the SNTP response packet from the server exceeds the
+ * Maximum Transmission Unit (MTU) supported by the network interface of the device,
+ * the user-defined implementation of the interface MAY either support receiving and
+ * assembling fragmented UDP packets OR use an authentication data size that allows
+ * SNTP packet to fit within the MTU required packet size threshold. (Note that
+ * the size of SNTP packet is #SNTP_PACKET_BASE_SIZE + authentication data.)
+ *
+ * @param[in,out] pNetworkContext The user defined NetworkContext_t which
+ * is opaque to the coreSNTP library.
+ * @param[in] serverAddr The IPv4 address of the time server to receive data from.
+ * @param[in] serverPort The port of the server to receive data from.
+ * @param[out] pBuffer This SHOULD be filled with data received from the network.
+ * @param[in] bytesToRecv The expected number of bytes to receive from the
+ * server.
+ *
+ * @return The function SHOULD return one of the following integer codes:
+ * - @p bytesToRecv value if all the requested number of bytes are received
+ * from the network.
+ * - ZERO when no data is available on the network, and the operation can be
+ * retried.
+ * - < 0 when the read operation failed due to internal error, and operation cannot
+ * be retried.
+ */
+typedef int32_t ( * UdpTransportRecvFrom_t )( NetworkContext_t * pNetworkContext,
+                                              uint32_t serverAddr,
+                                              uint16_t serverPort,
+                                              void * pBuffer,
+                                              uint16_t bytesToRecv );
+
+/**
+ * @ingroup sntp_struct_types
+ * @brief Struct representing the UDP transport interface for user-defined functions
+ * that coreSNTP library depends on for performing read/write network operations.
+ */
+typedef struct UdpTransportIntf
+{
+    NetworkContext_t * pUserContext; /**<@brief The user-defined context for storing
+                                      * network socket information. */
+    UdpTransportSendTo_t sendTo;     /**<@brief The user-defined UDP send function. */
+    UdpTransportRecvFrom_t recvFrom; /**<@brief The user-defined UDP receive function. */
+} UdpTransportInterface_t;
+
+/**
+ * @ingroup sntp_struct_types
+ * @typedef SntpAuthContext_t
+ * @brief A user-defined type for context that is passed to the authentication interface functions.
+ * It MUST be defined by the user to use the library.
+ * It is of incomplete type to allow user to defined to the the needs of their authentication
+ * interface implementation.
+ */
+struct SntpAuthContext;
+typedef struct SntpAuthContext SntpAuthContext_t;
+
+/**
+ * @ingroup sntp_callback_types
+ * @brief Interface for user-defined function to generate and append
+ * authentication code in an SNTP request buffer for the SNTP client to be
+ * authenticated by the time server, if a security mechanism is used.
+ *
+ * The user can choose to implement with any security mechanism, symmetric
+ * key-based (like AES-CMAC) or asymmetric key-based (like Network Time Security),
+ * depending on the security mechanism supported by the time server being used
+ * to synchronize time with.
+ *
+ * @note The function SHOULD generate the authentication data for the first
+ * #SNTP_PACKET_BASE_SIZE bytes of SNTP request packet present in the passed buffer
+ * @p pBuffer, and fill the generated authentication data after #SNTP_PACKET_BASE_SIZE
+ * bytes in the buffer.
+ *
+ * @param[in,out] pContext The user defined NetworkContext_t which
+ * is opaque to the coreSNTP library.
+ * @param[in] pTimeServer The time server being used to request time from.
+ * This parameter is useful to choose the security mechanism when multiple time
+ * servers are configured in the library, and they require different security
+ * mechanisms or authentication credentials to use.
+ * @param[in, out] pBuffer This buffer SHOULD be filled with the authentication
+ * code generated from the #SNTP_PACKET_BASE_SIZE bytes of SNTP request data
+ * present in it.
+ * @param[in] bufferSize The maximum amount of data that can be held by the buffer,
+ * @p pBuffer.
+ * @param[out] pAuthCodeSize This should be filled with size of the authentication
+ * data appended to the SNTP request buffer, @p pBuffer. This value plus
+ * #SNTP_PACKET_BASE_SIZE should not exceed the buffer size, @p bufferSize.
+ *
+ * @return The function SHOULD return one of the following integer codes:
+ * - #SntpSuccess when the authentication data is successfully appended to @p pBuffer.
+ * - #SntpErrorBufferTooSmall when the user-supplied buffer (to the SntpContext_t through
+ * @ref Sntp_Init) is not large enough to hold authentication data.
+ * - #SntpErrorAuthFailure for failure to generate authentication data due to internal
+ * error.
+ */
+typedef SntpStatus_t (* SntpGenerateAuthCode_t )( SntpAuthContext_t * pContext,
+                                                  const SntpServerInfo_t * pTimeServer,
+                                                  void * pBuffer,
+                                                  size_t bufferSize,
+                                                  uint16_t * pAuthCodeSize );
+
+/**
+ * @ingroup sntp_callback_types
+ * @brief Interface for user-defined function to authenticate server by validating
+ * the authentication code present in its SNTP response to a time request, if
+ * a security mechanism is supported by the server.
+ *
+ * The user can choose to implement with any security mechanism, symmetric
+ * key-based (like AES-CMAC) or asymmetric key-based (like Network Time Security),
+ * depending on the security mechanism supported by the time server being used
+ * to synchronize time with.
+ *
+ * @note In an SNTP response, the authentication code is present only after the
+ * first #SNTP_PACKET_BASE_SIZE bytes. Depending on the security mechanism used,
+ * the first #SNTP_PACKET_BASE_SIZE bytes MAY be used in validating the
+ * authentication data sent by the server.
+ *
+ * @param[in,out] pContext The user defined NetworkContext_t which
+ * is opaque to the coreSNTP library.
+ * @param[in] pTimeServer The time server that has to be authenticated from its
+ * SNTP response.
+ * This parameter is useful to choose the security mechanism when multiple time
+ * servers are configured in the library, and they require different security
+ * mechanisms or authentication credentials to use.
+ * @param[in] pResponseData The SNTP response from the server that contains the
+ * authentication code after the first #SNTP_PACKET_BASE_SIZE bytes.
+ * @param[in] responseSize The total size of the response from the server.
+ *
+ * @return The function SHOULD return one of the following integer codes:
+ * - #SntpSuccess when the server is successfully authenticated.
+ * - #SntpServerNotAuthenticated when server could not be authenticated.
+ * - #SntpErrorAuthFailure for failure to authenticate server due to internal
+ * error.
+ */
+typedef SntpStatus_t (* SntpValidateServerAuth_t )( SntpAuthContext_t * pContext,
+                                                    const SntpServerInfo_t * pTimeServer,
+                                                    const void * pResponseData,
+                                                    uint16_t responseSize );
+
+/**
+ * @ingroup sntp_struct_types
+ * @brief Struct representing the authentication interface for securely
+ * communicating with time servers.
+ *
+ * @note Using a security mechanism is OPTIONAL for using the coreSNTP
+ * library i.e. a user does not need to define the authentication interface
+ * if they are not using a security mechanism for SNTP communication.
+ */
+typedef struct SntpAuthenticationIntf
+{
+    /**
+     *@brief The user-defined context for storing information like
+     * key credentials required for cryptographic operations in the
+     * security mechanism used for communicating with server.
+     */
+    SntpAuthContext_t * pAuthContext;
+
+    /**
+     * @brief The user-defined function for appending client authentication data.
+     * */
+    SntpGenerateAuthCode_t generateClientAuth;
+
+    /**
+     * @brief The user-defined function for authenticating server from its SNTP
+     * response.
+     */
+    SntpValidateServerAuth_t validateServerAuth;
+} SntpAuthenticationInterface_t;
+
+/**
+ * @ingroup sntp_struct_types
+ * @brief Structure for a context that stores state for managing a long-running
+ * SNTP client that periodically polls time and synchronizes system clock.
+ */
+typedef struct SntpContext
+{
+    /**
+     * @brief List of time servers in decreasing priority order configured
+     * for the SNTP client.
+     * Only a single server is configured for use at a time across polling
+     * attempts until the server rejects a time request or there is a response
+     * timeout, after which, the next server in the list is used for subsequent
+     * polling requests.
+     */
+    const SntpServerInfo_t * pTimeServers;
+
+    /**
+     * @brief Number of servers configured for use.
+     */
+    size_t numOfServers;
+
+    /**
+     * @brief The index for the currently configured time server for time querying
+     * from the list of time servers in @ref pTimeServers.
+     */
+    size_t currentServerIndex;
+
+    /**
+     * @brief The user-supplied buffer for storing network data of both SNTP requests
+     * and SNTP response.
+     */
+    uint8_t * pNetworkBuffer;
+
+    /**
+     * @brief The size of the network buffer.
+     */
+    size_t bufferSize;
+
+    /**
+     * @brief The user-supplied function for resolving DNS name of time servers.
+     */
+    SntpResolveDns_t resolveDnsFunc;
+
+    /**
+     * @brief The user-supplied function for obtaining the current system time.
+     */
+    SntpGetTime_t getTimeFunc;
+
+    /**
+     * @brief The user-supplied function for correcting system time after receiving
+     * time from a server.
+     */
+    SntpSetTime_t setTimeFunc;
+
+    /**
+     * @brief The user-defined interface for performing User Datagram Protocol (UDP)
+     * send and receive network operations.
+     */
+    UdpTransportInterface_t networkIntf;
+
+    /**
+     * @brief The user-defined interface for incorporating security mechanism of
+     * adding client authentication in SNTP request as well as authenticating server
+     * from SNTP response.
+     *
+     * @note If the application will not use security mechanism for any of the
+     * configured servers, then this interface can be undefined.
+     */
+    SntpAuthenticationInterface_t authIntf;
+
+    /**
+     * @brief Cache of the resolved Ipv4 address of the current server being used for
+     * time synchronization.
+     * As a Best Practice functionality, the client library attempts to resolve the
+     * DNS of the time-server every time the @ref Sntp_SendTimeRequest API is called.
+     */
+    uint32_t currentServerAddr;
+
+    /**
+     * @brief Cache of the timestamp of sending the last time request to a server
+     * for replay attack protection by checking that the server response contains
+     * the same timestamp in its "originate timestamp" field.
+     */
+    SntpTimestamp_t lastRequestTime;
+
+    /**
+     * @brief State member for storing the size of the SNTP packet that includes
+     * both #SNTP_PACKET_BASE_SIZE bytes plus any authentication data, if a security
+     * mechanism is used.
+     * This value is used for expecting the same size for an SNTP response
+     * from the server.
+     */
+    uint16_t sntpPacketSize;
+
+    /**
+     * @brief The timeout duration (in milliseconds) for receiving a response, through
+     * @ref Sntp_ReceiveTimeResponse API, from a server after the request for time is
+     * sent to it through @ref Sntp_SendTimeRequest API.
+     */
+    uint32_t responseTimeoutMs;
+} SntpContext_t;
+
+/**
+ * @brief Initializes a context for SNTP client communication with SNTP/NTP
+ * servers.
+ *
+ * @param[out] pContext The user-supplied memory for the context that will be
+ * initialized to represent an SNTP client.
+ * @param[in] pTimeServers The list of decreasing order of priority of time
+ * servers that should be used by the SNTP client. This list MUST stay in
+ * scope for all the time of use of the context.
+ * @param[in] numOfServers The number of servers in the list, @p pTimeServers.
+ * @param[in] serverResponseTimeoutMs The timeout duration (in milliseconds) for
+ * receiving server response for time requests. The same timeout value is used for
+ * each server in the @p pTimeServers list.
+ * @param[in] pNetworkBuffer The user-supplied memory that will be used for
+ * storing network data for SNTP client-server communication. The buffer
+ * MUST stay in scope for all the time of use of the context.
+ * @param[in] bufferSize The size of the passed buffer @p pNetworkBuffer. The buffer
+ * SHOULD be appropriately sized for storing an entire SNTP packet which includes
+ * both #SNTP_PACKET_BASE_SIZE bytes of standard SNTP packet size, and space for
+ * authentication data, if security mechanism is used to communicate with any of
+ * the time servers configured for use.
+ * @param[in] resolveDnsFunc The user-defined function for DNS resolution of time
+ * server.
+ * @param[in] getSystemTimeFunc The user-defined function for querying system
+ * time.
+ * @param[in] setSystemTimeFunc The user-defined function for correcting system
+ * time for every successful time response received from a server.
+ * @param[in] pTransportIntf The user-defined function for performing network
+ * send/recv operations over UDP.
+ * @param[in] pAuthIntf The user-defined interface for generating client authentication
+ * in SNTP requests and authenticating servers in SNTP responses, if security mechanism
+ * is used in SNTP communication with server(s). If security mechanism is not used in
+ * communication with any of the configured servers (in @p pTimeServers), then the
+ * @ref SntpAuthenticationInterface_t does not need to be defined and this parameter
+ * can be NULL.
+ *
+ * @return This function returns one of the following:
+ * - #SntpSuccess if the context is initialized.
+ * - #SntpErrorBadParameter if any of the passed parameters in invalid.
+ * - #SntpErrorBufferTooSmall if the buffer does not have the minimum size
+ * required for a valid SNTP response packet.
+ */
+/* @[define_sntp_init] */
+SntpStatus_t Sntp_Init( SntpContext_t * pContext,
+                        const SntpServerInfo_t * pTimeServers,
+                        size_t numOfServers,
+                        uint32_t serverResponseTimeoutMs,
+                        uint8_t * pNetworkBuffer,
+                        size_t bufferSize,
+                        SntpResolveDns_t resolveDnsFunc,
+                        SntpGetTime_t getSystemTimeFunc,
+                        SntpSetTime_t setSystemTimeFunc,
+                        const UdpTransportInterface_t * pTransportIntf,
+                        const SntpAuthenticationInterface_t * pAuthIntf );
+/* @[define_sntp_init] */
+
+
+/**
+ * @brief Sends a request for time from the currently configured server (in the
+ * context).
+ * If the user has provided an authentication interface, the client
+ * authentication code is appended to the request before sending over the network
+ * by calling the @ref SntpGenerateAuthCode_t function of the
+ * @ref SntpAuthenticationInterface_t.
+ *
+ * @note This function will ONLY send a request if there is a server available
+ * in the configured list that has not rejected an earlier request for time.
+ * This adheres to the Best Practice functionality specified in [Section 10 Point 8
+ * of SNTPv4 specification](https://tools.ietf.org/html/rfc4330#section-10).
+ *
+ * @param[in] pContext The context representing an SNTPv4 client.
+ * @param[in] randomNumber A random number serializing the SNTP request packet
+ * to protect against spoofing attacks by attackers that are off the network path
+ * of the SNTP client-server communication. This mechanism is suggested by SNTPv4
+ * specification in [RFC 4330 Section 3](https://tools.ietf.org/html/rfc4330#section-3).
+ * @param[in] blockTimeMs The maximum duration of time (in milliseconds) the function will
+ * block on attempting to send time request to the server over the network. If a zero
+ * block time value is provided, then the function will attempt to send the packet ONLY
+ * once.
+ *
+ * @note It is RECOMMENDED that a True Random Number Generator is used to generate the
+ * random number by using a hardware module, like Hardware Security Module (HSM), Secure Element,
+ * etc, as the entropy source.
+ *
+ * @return The API function returns one of the following:
+ *  - #SntpSuccess if a time request is successfully sent to the currently configured
+ * time server in the context.
+ *  - #SntpErrorBadParameter if an invalid context is passed to the function.
+ *  - #SntpErrorContextNotInitialized if an uninitialized or invalid context is passed
+ * to the function.
+ *  - #SntpErrorDnsFailure if there is failure in the user-defined function for
+ * DNS resolution of the time server.
+ *  - #SntpErrorNetworkFailure if the SNTP request could not be sent over the network
+ * through the user-defined transport interface.
+ *  - #SntpErrorAuthFailure if there was a failure in generating the client
+ * authentication code in the user-defined authentication interface.
+ *  - #SntpErrorSendTimeout if the time request packet could not be sent over the
+ * network for the entire @p blockTimeMs duration.
+ */
+/* @[define_sntp_sendtimerequest] */
+SntpStatus_t Sntp_SendTimeRequest( SntpContext_t * pContext,
+                                   uint32_t randomNumber,
+                                   uint32_t blockTimeMs );
+/* @[define_sntp_sendtimerequest] */
+
+
+/**
+ * @brief Receives a time response from the server that has been requested for time with
+ * the @ref Sntp_SendTimeRequest API function.
+ * Once an accepted response containing time from server is received, this function calls
+ * the user-defined @ref SntpSetTime_t function to update the system time.
+ *
+ * @note If the user has provided an authentication interface to the client
+ * (through @ref Sntp_Init), the server response is authenticated by calling the
+ * @ref SntpValidateServerAuth_t function of the @ref SntpAuthenticationInterface_t interface.
+ *
+ * @note On receiving a successful server response containing server time,
+ * this API calculates the clock offset value of the system clock relative to the
+ * server before calling the user-defined @ref SntpSetTime_t function for updating
+ * the system time.
+ *
+ * @note For correct calculation of clock-offset, the server and client times MUST be within
+ * ~68 years (or 2^31 seconds) of each other. In the special case when the server and client
+ * times are exactly 2^31 seconds apart, the library ASSUMES that the server time is ahead
+ * of the client time, and returns the positive clock-offset value of INT32_MAX seconds.
+ *
+ * @note This API will rotate the server of use in the library for the next time request
+ * (through the @ref Sntp_SendTimeRequest) if either of following events occur:
+ *  - The server has responded with a rejection for the time request.
+ *                         OR
+ *  - The server response wait has timed out.
+ * If all the servers configured in the context have been used, the API will rotate server for
+ * time query back to the first server in the list which will be used in next time request.
+ *
+ * @param[in] pContext The context representing an SNTPv4 client.
+ * @param[in] blockTimeMs The maximum duration of time (in milliseconds) the function will
+ * block on receiving a response from the server unless either the response is received
+ * OR a response timeout occurs.
+ *
+ * @note This function can be called multiple times with zero or small blocking times
+ * to poll whether server response is received until either the response response is
+ * received from the server OR a response timeout has occurred.
+ *
+ * @return This API functions returns one of the following:
+ *  - #SntpSuccess if a successful server response is received.
+ *  - #SntpErrorContextNotInitialized if an uninitialized or invalid context is passed
+ * to the function.
+ *  - #SntpErrorBadParameter if an invalid context is passed to the function.
+ *  - #SntpErrorNetworkFailure if there is a failure in the user-defined transport
+ *  - #SntpErrorAuthFailure if an internal error occurs in the user-defined
+ * authentication interface when validating the server response.
+ *  - #SntpServerNotAuthenticated when the server could not be authenticated from
+ * its response with the user-defined authentication interface.
+ *  - #SntpInvalidResponse if the server response fails sanity checks expected in an
+ * SNTP response packet.
+ *  - #SntpErrorResponseTimeout if a timeout has occurred in receiving response from
+ * the server.
+ *  - #SntpRejectedResponse if the server responded with a rejection for the time
+ * request.
+ */
+/* @[define_sntp_receivetimeresponse] */
+SntpStatus_t Sntp_ReceiveTimeResponse( SntpContext_t * pContext,
+                                       uint32_t blockTimeMs );
+/* @[define_sntp_receivetimeresponse] */
+
+/**
+ * @brief Converts @ref SntpStatus_t to its equivalent
+ * string.
+ *
+ * @note The returned string MUST NOT be modified.
+ *
+ * @param[in] status The status to convert to a string.
+ *
+ * @return The string representation of the status
+ * code.
+ */
+/* @[define_sntp_statustostr] */
+const char * Sntp_StatusToStr( SntpStatus_t status );
+/* @[define_sntp_statustostr] */
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    }
+#endif
+/* *INDENT-ON* */
+
+#endif /* ifndef CORE_SNTP_CLIENT_H_ */
diff --git a/project/coreMQTT/coreSNTP/core_sntp_config_defaults.h b/project/coreMQTT/coreSNTP/core_sntp_config_defaults.h
new file mode 100644
index 0000000..162a966
--- /dev/null
+++ b/project/coreMQTT/coreSNTP/core_sntp_config_defaults.h
@@ -0,0 +1,144 @@
+/*
+ * coreSNTP v1.2.0
+ * Copyright (C) 2020 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_sntp_config_defaults.h
+ * @brief This file represents the default values for the configuration macros
+ * of the coreSNTP library.
+ *
+ * @note This file SHOULD NOT be modified. If custom values are needed for
+ * any configuration macro, a core_sntp_config.h file should be provided to
+ * the SNTP library to override the default values defined in this file.
+ * To build the library with the core_sntp_config.h file, make sure to
+ * not set the SNTP_DO_NOT_USE_CUSTOM_CONFIG preprocessor macro.
+ */
+
+#ifndef CORE_SNTP_CONFIG_DEFAULTS_H_
+#define CORE_SNTP_CONFIG_DEFAULTS_H_
+
+/* The macro definition for SNTP_DO_NOT_USE_CUSTOM_CONFIG is for Doxygen
+ * documentation only. */
+
+/**
+ * @brief Define this macro to build the SNTP library without the custom config
+ * file core_sntp_config.h.
+ *
+ * Without the custom config, the SNTP library builds with
+ * default values of config macros defined in core_sntp_config_defaults.h file.
+ *
+ * If a custom config is provided, then SNTP_DO_NOT_USE_CUSTOM_CONFIG should not
+ * be defined.
+ */
+#ifdef DOXYGEN
+    #define SNTP_DO_NOT_USE_CUSTOM_CONFIG
+#endif
+
+/* SNTP_DO_NOT_USE_CUSTOM_CONFIG allows building the SNTP library
+ * without a custom config. If a custom config is provided, the
+ * SNTP_DO_NOT_USE_CUSTOM_CONFIG macro should not be defined. */
+#ifndef SNTP_DO_NOT_USE_CUSTOM_CONFIG
+    #include "core_sntp_config.h"
+#endif
+
+/**
+ * @brief Macro that is called in the SNTP library for logging "Error" level
+ * messages.
+ *
+ * To enable error level logging in the SNTP library, this macro should be mapped to the
+ * application-specific logging implementation that supports error logging.
+ *
+ * @note This logging macro is called in the SNTP library with parameters wrapped in
+ * double parentheses to be ISO C89/C90 standard compliant. For a reference
+ * POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
+ * logging-stack in demos folder of the
+ * [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C).
+ *
+ * <b>Default value</b>: Error logging is turned off, and no code is generated for calls
+ * to the macro in the SNTP library on compilation.
+ */
+#ifndef LogError
+    #define LogError( message )
+#endif
+
+/**
+ * @brief Macro that is called in the SNTP library for logging "Warning" level
+ * messages.
+ *
+ * To enable warning level logging in the SNTP library, this macro should be mapped to the
+ * application-specific logging implementation that supports warning logging.
+ *
+ * @note This logging macro is called in the SNTP library with parameters wrapped in
+ * double parentheses to be ISO C89/C90 standard compliant. For a reference
+ * POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
+ * logging-stack in demos folder of the
+ * [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
+ *
+ * <b>Default value</b>: Warning logs are turned off, and no code is generated for calls
+ * to the macro in the SNTP library on compilation.
+ */
+#ifndef LogWarn
+    #define LogWarn( message )
+#endif
+
+/**
+ * @brief Macro that is called in the SNTP library for logging "Info" level
+ * messages.
+ *
+ * To enable info level logging in the SNTP library, this macro should be mapped to the
+ * application-specific logging implementation that supports info logging.
+ *
+ * @note This logging macro is called in the SNTP library with parameters wrapped in
+ * double parentheses to be ISO C89/C90 standard compliant. For a reference
+ * POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
+ * logging-stack in demos folder of the
+ * [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
+ *
+ * <b>Default value</b>: Info logging is turned off, and no code is generated for calls
+ * to the macro in the SNTP library on compilation.
+ */
+#ifndef LogInfo
+    #define LogInfo( message )
+#endif
+
+/**
+ * @brief Macro that is called in the SNTP library for logging "Debug" level
+ * messages.
+ *
+ * To enable debug level logging from SNTP library, this macro should be mapped to the
+ * application-specific logging implementation that supports debug logging.
+ *
+ * @note This logging macro is called in the SNTP library with parameters wrapped in
+ * double parentheses to be ISO C89/C90 standard compliant. For a reference
+ * POSIX implementation of the logging macros, refer to core_sntp_config.h files, and the
+ * logging-stack in demos folder of the
+ * [AWS IoT Embedded C SDK repository](https://github.com/aws/aws-iot-device-sdk-embedded-C/).
+ *
+ * <b>Default value</b>: Debug logging is turned off, and no code is generated for calls
+ * to the macro in the SNTP library on compilation.
+ */
+#ifndef LogDebug
+    #define LogDebug( message )
+#endif
+
+#endif /* ifndef CORE_SNTP_CONFIG_DEFAULTS_H_ */
diff --git a/project/coreMQTT/coreSNTP/core_sntp_serializer.c b/project/coreMQTT/coreSNTP/core_sntp_serializer.c
new file mode 100644
index 0000000..14fffed
--- /dev/null
+++ b/project/coreMQTT/coreSNTP/core_sntp_serializer.c
@@ -0,0 +1,853 @@
+/*
+ * coreSNTP v1.2.0
+ * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_sntp_serializer.c
+ * @brief Implementation of the Serializer API of the coreSNTP library.
+ */
+
+/* Standard includes. */
+#include <string.h>
+#include <stdbool.h>
+#include <assert.h>
+
+/* Include API header. */
+#include "core_sntp_serializer.h"
+
+#include "core_sntp_config_defaults.h"
+
+/**
+ * @brief The version of SNTP supported by the coreSNTP library by complying
+ * with the SNTPv4 specification defined in [RFC 4330](https://tools.ietf.org/html/rfc4330).
+ */
+#define SNTP_VERSION                        ( 4U )
+
+/**
+ * @brief The bit mask for the "Mode" information in the first byte of an SNTP packet.
+ * The "Mode" field occupies bits 0-2 of the byte.
+ * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
+ * for more information.
+ */
+#define SNTP_MODE_BITS_MASK                 ( 0x07U )
+
+/**
+ * @brief The value indicating a "client" in the "Mode" field of an SNTP packet.
+ * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
+ * for more information.
+ */
+#define SNTP_MODE_CLIENT                    ( 3U )
+
+/**
+ * @brief The value indicating a "server" in the "Mode" field of an SNTP packet.
+ * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
+ * for more information.
+ */
+#define SNTP_MODE_SERVER                    ( 4U )
+
+/**
+ * @brief The position of the least significant bit of the "Leap Indicator" field
+ * in first byte of an SNTP packet. The "Leap Indicator" field occupies bits 6-7 of the byte.
+ * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
+ * for more information.
+ */
+#define SNTP_LEAP_INDICATOR_LSB_POSITION    ( 6 )
+
+/**
+ * @brief Value of Stratum field in SNTP packet representing a Kiss-o'-Death message
+ * from server.
+ */
+#define SNTP_KISS_OF_DEATH_STRATUM          ( 0U )
+
+/**
+ * @brief The position of least significant bit of the "Version" information
+ * in the first byte of an SNTP packet. "Version" field occupies bits 3-5 of
+ * the byte.
+ * @note Refer to the [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4)
+ * for more information.
+ */
+#define SNTP_VERSION_LSB_POSITION           ( 3 )
+
+/**
+ * @brief The integer value of the Kiss-o'-Death ASCII code, "DENY", used
+ * for comparison with data in an SNTP response.
+ * @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
+ * for more information.
+ */
+#define KOD_CODE_DENY_UINT_VALUE            ( 0x44454e59U )
+
+/**
+ * @brief The integer value of the Kiss-o'-Death ASCII code, "RSTR", used
+ * for comparison with data in an SNTP response.
+ * @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
+ * for more information.
+ */
+#define KOD_CODE_RSTR_UINT_VALUE            ( 0x52535452U )
+
+/**
+ * @brief The integer value of the Kiss-o'-Death ASCII code, "RATE", used
+ * for comparison with data in an SNTP response.
+ * @note Refer to [RFC 4330 Section 8](https://tools.ietf.org/html/rfc4330#section-8)
+ * for more information.
+ */
+#define KOD_CODE_RATE_UINT_VALUE            ( 0x52415445U )
+
+/**
+ * @brief Macro to represent exactly half of the total number of seconds in an NTP era.
+ * As 32 bits are used to represent the "seconds" part of an SNTP timestamp, the half of
+ * the total range of seconds in an NTP era is 2^31 seconds, which represents ~68 years.
+ *
+ * @note This macro represents the edge case of calculating the client system clock-offset
+ * relative to server time as the library ASSUMES that the client and server times are within
+ * 2^31 seconds apart in duration. This is done to support calculating clock-offset for the
+ * cases when server and client systems are in adjacent NTP eras, which can occur when NTP time
+ * wraps around in 2036, and the relative NTP era presence of client and server times is
+ * determined based on comparing first order difference values between all possible NTP era
+ * configurations of the systems.
+ */
+#define CLOCK_OFFSET_MAX_TIME_DIFFERENCE    ( ( ( ( int64_t ) INT32_MAX + 1 ) * 1000 ) )
+
+/**
+ * @brief Macro to represent the total number of milliseconds that are represented in an
+ * NTP era period. This macro represents a duration of ~136 years.
+ *
+ * @note Rationale for calculation: The "seconds" part of an NTP timestamp is represented in
+ * unsigned 32 bit width, thus, the total range of seconds it represents is 2^32,
+ * i.e. (UINT32_MAX + 1).
+ */
+#define TOTAL_MILLISECONDS_IN_NTP_ERA       ( ( ( int64_t ) UINT32_MAX + 1 ) * 1000 )
+
+/**
+ * @brief Structure representing an SNTP packet header.
+ * For more information on SNTP packet format, refer to
+ * [RFC 4330 Section 4](https://tools.ietf.org/html/rfc4330#section-4).
+ *
+ * @note This does not include extension fields for authentication data
+ * for secure SNTP communication. Authentication data follows the
+ * packet header represented by this structure.
+ */
+typedef struct SntpPacket
+{
+    /**
+     * @brief Bits 6-7 leap indicator, bits 3-5 are version number, bits 0-2 are mode
+     */
+    uint8_t leapVersionMode;
+
+    /**
+     * @brief stratum
+     */
+    uint8_t stratum;
+
+    /**
+     * @brief poll interval
+     */
+    uint8_t poll;
+
+    /**
+     * @brief precision
+     */
+    uint8_t precision;
+
+    /**
+     * @brief root delay
+     */
+    uint32_t rootDelay;
+
+    /**
+     * @brief root dispersion
+     */
+    uint32_t rootDispersion;
+
+    /**
+     * @brief reference ID
+     */
+    uint32_t refId;
+
+    /**
+     * @brief reference time
+     */
+    SntpTimestamp_t refTime;
+
+    /**
+     * @brief origin timestamp
+     */
+    SntpTimestamp_t originTime;
+
+    /**
+     * @brief receive timestamp
+     */
+    SntpTimestamp_t receiveTime;
+
+    /**
+     * @brief transmit timestamp
+     */
+    SntpTimestamp_t transmitTime;
+} SntpPacket_t;
+
+/**
+ * @brief Utility macro to fill 32-bit integer in word-sized
+ * memory in network byte (or Big Endian) order.
+ *
+ * @param[out] pWordMemory Pointer to the word-sized memory in which
+ * the 32-bit integer will be filled.
+ * @param[in] data The 32-bit integer to fill in the @p wordMemory
+ * in network byte order.
+ *
+ * @note This utility ensures that data is filled in memory
+ * in expected network byte order, as an assignment operation
+ * (like *pWordMemory = word) can cause undesired side-effect
+ * of network-byte ordering getting reversed on Little Endian platforms.
+ */
+static void fillWordMemoryInNetworkOrder( uint32_t * pWordMemory,
+                                          uint32_t data )
+{
+    assert( pWordMemory != NULL );
+
+    *( ( uint8_t * ) pWordMemory ) = ( uint8_t ) ( data >> 24 );
+    *( ( uint8_t * ) pWordMemory + 1 ) = ( uint8_t ) ( ( data >> 16 ) & 0x000000FFU );
+    *( ( uint8_t * ) pWordMemory + 2 ) = ( uint8_t ) ( ( data >> 8 ) & 0x000000FFU );
+    *( ( uint8_t * ) pWordMemory + 3 ) = ( uint8_t ) ( ( data ) & 0x000000FFU );
+}
+
+/**
+ * @brief Utility macro to generate a 32-bit integer from memory containing
+ * integer in network (or Big Endian) byte order.
+ * @param[in] ptr Pointer to the memory containing 32-bit integer in network
+ * byte order.
+ *
+ * @return The host representation of the 32-bit integer in the passed word
+ * memory.
+ */
+static uint32_t readWordFromNetworkByteOrderMemory( const uint32_t * ptr )
+{
+    const uint8_t * pMemStartByte = ( const uint8_t * ) ptr;
+
+    assert( ptr != NULL );
+
+    return ( uint32_t ) ( ( ( uint32_t ) *( pMemStartByte ) << 24 ) |
+                          ( 0x00FF0000U & ( ( uint32_t ) *( pMemStartByte + 1 ) << 16 ) ) |
+                          ( 0x0000FF00U & ( ( uint32_t ) *( pMemStartByte + 2 ) << 8 ) ) |
+                          ( ( uint32_t ) *( pMemStartByte + 3 ) ) );
+}
+
+/**
+ * @brief Utility to return absolute (or positively signed) value of an signed
+ * 64 bit integer.
+ *
+ * @param[in] value The integer to return the absolute value of.
+ *
+ * @return The absolute value of @p value.
+ */
+static int64_t absoluteOf( int64_t value )
+{
+    return ( value >= 0 ) ? value : ( ( int64_t ) 0 - value );
+}
+
+/**
+ * @brief Utility to determine whether a timestamp represents a zero
+ * timestamp value.
+ *
+ * @note This utility is used to determine whether a timestamp value is
+ * invalid. According to the SNTPv4 specification, a zero timestamp value
+ * is considered invalid.
+ *
+ * @param[in] pTime The timestamp whose value is to be inspected for
+ * zero value.
+ *
+ * @return `true` if the timestamp is zero; otherwise `false`.
+ */
+static bool isZeroTimestamp( const SntpTimestamp_t * pTime )
+{
+    bool isSecondsZero = ( pTime->seconds == 0U ) ? true : false;
+    bool isFractionsZero = ( pTime->fractions == 0U ) ? true : false;
+
+    return( isSecondsZero && isFractionsZero );
+}
+
+/**
+ * @brief Utility to convert the "fractions" part of an SNTP timestamp to milliseconds
+ * duration of time.
+ * @param[in] fractions The fractions value.
+ *
+ * @return The milliseconds equivalent of the @p fractions value.
+ */
+static uint32_t fractionsToMs( uint32_t fractions )
+{
+    return( fractions / ( 1000U * SNTP_FRACTION_VALUE_PER_MICROSECOND ) );
+}
+
+/**
+ * @brief Utility to safely calculate difference between server and client timestamps and
+ * return the difference in the resolution of milliseconds as a signed 64 bit integer.
+ * The calculated value represents the effective subtraction as ( @p serverTimeSec - @p clientTimeSec ).
+ *
+ * @note This utility SUPPORTS the cases of server and client timestamps being in different NTP eras,
+ * and ASSUMES that the server and client systems are within 68 years of each other.
+ * To handle the case of different NTP eras, this function calculates difference values for all
+ * possible combinations of NTP eras of server and client times (i.e. 1. both timestamps in same era,
+ * 2. server timestamp one era ahead, and 3. client timestamp being one era ahead), and determines
+ * the NTP era configuration by choosing the difference value of the smallest absolute value.
+ *
+ * @param[in] pServerTime The server timestamp.
+ * @param[in] pClientTime The client timestamp.
+ *
+ * @return The calculated difference between server and client times as a signed 64 bit integer.
+ */
+static int64_t safeTimeDifference( const SntpTimestamp_t * pServerTime,
+                                   const SntpTimestamp_t * pClientTime )
+{
+    int64_t eraAdjustedDiff = 0;
+
+    /* Convert the timestamps into 64 bit signed integer values of milliseconds. */
+    int64_t serverTime = ( ( int64_t ) pServerTime->seconds * 1000 ) + ( int64_t ) fractionsToMs( pServerTime->fractions );
+    int64_t clientTime = ( ( int64_t ) pClientTime->seconds * 1000 ) + ( int64_t ) fractionsToMs( pClientTime->fractions );
+
+    /* The difference between the 2 timestamps is calculated by determining the whether the timestamps
+     * are present in the same NTP era or adjacent NTP eras (i.e. the NTP timestamp overflow case). */
+
+    /* First, calculate the first order time difference assuming that server and client times
+     * are in the same NTP era. */
+    int64_t diffWithNoEraAdjustment = serverTime - clientTime;
+
+    /* Store the absolute value of the time difference which will be used for comparison with
+     * different cases of relative NTP era configuration of client and server times. */
+    int64_t absSameEraDiff = absoluteOf( diffWithNoEraAdjustment );
+
+    /* If the absolute difference value is 2^31 seconds, it means that the server and client times are
+     * away by exactly half the range of SNTP timestamp "second" values representable in unsigned 32 bits.
+     * In such a case, irrespective of whether the 2 systems exist in the same or adjacent NTP eras, the
+     * time difference calculated between the systems will ALWAYS yield the same value when viewed from
+     * all NTP era configurations of the times.
+     * For such a case, we will ASSUME that the server time is AHEAD of client time, and thus, generate
+     * a positive clock-offset value.
+     */
+    if( absSameEraDiff == CLOCK_OFFSET_MAX_TIME_DIFFERENCE )
+    {
+        /* It does not matter whether server and client are in the same era for this
+         * special case as the difference value for both same and adjacent eras will yield
+         * the same absolute value of 2^31.*/
+        eraAdjustedDiff = CLOCK_OFFSET_MAX_TIME_DIFFERENCE;
+    }
+    else
+    {
+        /* Determine if server time belongs to an NTP era different than the client time, and accordingly
+         * choose the 64 bit representation of the first order difference to account for the era.
+         * The logic for determining the relative era presence of the timestamps is by calculating the
+         * first order difference (of "Server Time - Client Time") for all the 3 different era combinations
+         * (1. both timestamps in same era, 2. server time one era ahead, 3. client time one era ahead)
+         * and choosing the NTP era configuration that has the smallest first order difference value.
+         */
+        int64_t diffWithServerEraAdjustment = serverTime + TOTAL_MILLISECONDS_IN_NTP_ERA -
+                                              clientTime;                                     /* This helps determine whether server is an
+                                                                                               * era ahead of client time. */
+        int64_t diffWithClientEraAdjustment = serverTime -
+                                              ( TOTAL_MILLISECONDS_IN_NTP_ERA + clientTime ); /* This helps determine whether server is an
+                                                                                               * era behind of client time. */
+
+        /* Store the absolute value equivalents of all the time difference configurations
+         * for easier comparison to smallest value from them. */
+        int64_t absServerEraAheadDiff = absoluteOf( diffWithServerEraAdjustment );
+        int64_t absClientEraAheadDiff = absoluteOf( diffWithClientEraAdjustment );
+
+        /* Determine the correct relative era of client and server times by checking which era
+         * configuration of difference value represents the least difference. */
+        if( ( absSameEraDiff <= absServerEraAheadDiff ) && ( absSameEraDiff <= absClientEraAheadDiff ) )
+        {
+            /* Both server and client times are in the same era. */
+            eraAdjustedDiff = diffWithNoEraAdjustment;
+        }
+        /* Check if server time is an NTP era ahead of client time. */
+        else if( absServerEraAheadDiff < absSameEraDiff )
+        {
+            /* Server time is in NTP era 1 while client time is in NTP era 0. */
+            eraAdjustedDiff = diffWithServerEraAdjustment;
+        }
+        /* Now, we know that the client time is an era ahead of server time. */
+        else
+        {
+            /* Server time is in NTP era 0 while client time is in NTP era 1. */
+            eraAdjustedDiff = diffWithClientEraAdjustment;
+        }
+    }
+
+    return eraAdjustedDiff;
+}
+
+/**
+ * @brief Utility to calculate clock offset of system relative to the
+ * server using the on-wire protocol specified in the NTPv4 specification.
+ * For more information on on-wire protocol, refer to
+ * [RFC 5905 Section 8](https://tools.ietf.org/html/rfc5905#section-8).
+ *
+ * @note The following diagram explains the calculation of the clock
+ * offset:
+ *
+ *                 T2      T3
+ *      ---------------------------------   <-----   *SNTP/NTP server*
+ *               /\         \
+ *               /           \
+ *     Request* /             \ *Response*
+ *             /              \/
+ *      ---------------------------------   <-----   *SNTP client*
+ *           T1                T4
+ *
+ *  The four most recent timestamps, T1 through T4, are used to compute
+ *  the clock offset of SNTP client relative to the server where:
+ *
+ *     T1 = Client Request Transmit Time
+ *     T2 = Server Receive Time (of client request)
+ *     T3 = Server Response Transmit Time
+ *     T4 = Client Receive Time (of server response)
+ *
+ *  Clock Offset = T(NTP/SNTP server) - T(SNTP client)
+ *               = [( T2 - T1 ) + ( T3 - T4 )]
+ *                 ---------------------------
+ *                              2
+ *
+ * @note Both NTPv4 and SNTPv4 specifications suggest calculating the
+ * clock offset value, if possible. As the timestamp format uses 64 bit
+ * integer and there exist 2 orders of arithmetic calculations on the
+ * timestamp values (subtraction followed by addition as shown in the
+ * diagram above), the clock offset for the system can be calculated
+ * ONLY if the value can be represented in 62 significant bits and 2 sign
+ * bits i.e. if the system clock is within 34 years (in the future or past)
+ * of the server time.
+ *
+ * @param[in] pClientTxTime The system time of sending the SNTP request.
+ * This is the same as "T1" in the above diagram.
+ * @param[in] pServerRxTime The server time of receiving the SNTP request
+ * packet from the client. This is the same as "T2" in the above diagram.
+ * @param[in] pServerTxTime The server time of sending the SNTP response
+ * packet. This is the same as "T3" in the above diagram.
+ * @param[in] pClientRxTime The system time of receiving the SNTP response
+ * from the server. This is the same as "T4" in the above diagram.
+ * @param[out] pClockOffset This will be filled with the calculated offset value
+ * of the system clock relative to the server time with the assumption that the
+ * system clock is within 68 years of server time.
+ */
+static void calculateClockOffset( const SntpTimestamp_t * pClientTxTime,
+                                  const SntpTimestamp_t * pServerRxTime,
+                                  const SntpTimestamp_t * pServerTxTime,
+                                  const SntpTimestamp_t * pClientRxTime,
+                                  int64_t * pClockOffset )
+{
+    /* Variable for storing the first-order difference between timestamps. */
+    int64_t firstOrderDiffSend = 0;
+    int64_t firstOrderDiffRecv = 0;
+
+    assert( pClientTxTime != NULL );
+    assert( pServerRxTime != NULL );
+    assert( pServerTxTime != NULL );
+    assert( pClientRxTime != NULL );
+    assert( pClockOffset != NULL );
+
+    /* Perform first order difference of timestamps on the network send path i.e. T2 - T1.
+     * Note: The calculated difference value will always represent years in the range of
+     *[-68 years, +68 years]. */
+    firstOrderDiffSend = safeTimeDifference( pServerRxTime, pClientTxTime );
+
+    /* Perform first order difference of timestamps on the network receive path i.e. T3 - T4.
+     * Note: The calculated difference value will always represent years in the range of
+     *[-68 years, +68 years]. */
+    firstOrderDiffRecv = safeTimeDifference( pServerTxTime, pClientRxTime );
+
+    /* Now calculate the system clock-offset relative to server time as the average of the
+     * first order difference of timestamps in both directions of network path.
+     * Note: This will ALWAYS represent offset in the range of [-68 years, +68 years]. */
+    *pClockOffset = ( firstOrderDiffSend + firstOrderDiffRecv ) / 2;
+}
+
+/**
+ * @brief Parse a SNTP response packet by determining whether it is a rejected
+ * or accepted response to an SNTP request, and accordingly, populate the
+ * @p pParsedResponse parameter with the parsed data.
+ *
+ * @note If the server has rejected the request with the a Kiss-o'-Death message,
+ * then this function will set the associated rejection code in the output parameter
+ * while setting the remaining members to zero.
+ * If the server has accepted the time request, then the function will set the
+ * rejectedResponseCode member of the output parameter to #SNTP_KISS_OF_DEATH_CODE_NONE,
+ * and set the other the members with appropriate data extracted from the response
+ * packet.
+ *
+ * @param[in] pResponsePacket The SNTP response packet from server to parse.
+ * @param[in] pRequestTxTime The system time (in SNTP timestamp format) of
+ * sending the SNTP request to server.
+ * @param[in] pResponseRxTime The system time (in SNTP timestamp format) of
+ * receiving the SNTP response from server.
+ * @param[out] pParsedResponse The parameter that will be populated with data
+ * parsed from the response packet, @p pResponsePacket.
+ *
+ * @return This function returns one of the following:
+ * - #SntpSuccess if the server response does not represent a Kiss-o'-Death
+ * message.
+ * - #SntpRejectedResponseChangeServer if the server rejected with a code
+ * indicating that client cannot be retry requests to it.
+ * - #SntpRejectedResponseRetryWithBackoff if the server rejected with a code
+ * indicating that client should back-off before retrying request.
+ * - #SntpRejectedResponseOtherCode if the server rejected with a code
+ * other than "DENY", "RSTR" and "RATE".
+ */
+static SntpStatus_t parseValidSntpResponse( const SntpPacket_t * pResponsePacket,
+                                            const SntpTimestamp_t * pRequestTxTime,
+                                            const SntpTimestamp_t * pResponseRxTime,
+                                            SntpResponseData_t * pParsedResponse )
+{
+    SntpStatus_t status = SntpSuccess;
+
+    assert( pResponsePacket != NULL );
+    assert( pResponseRxTime != NULL );
+    assert( pParsedResponse != NULL );
+
+    /* Clear the output parameter memory to zero. */
+    ( void ) memset( pParsedResponse, 0, sizeof( *pParsedResponse ) );
+
+    /* Determine if the server has accepted or rejected the request for time. */
+    if( pResponsePacket->stratum == SNTP_KISS_OF_DEATH_STRATUM )
+    {
+        /* Server has sent a Kiss-o'-Death message i.e. rejected the request. */
+
+        /* Extract the kiss-code sent by the server from the "Reference ID" field
+         * of the SNTP packet. */
+        pParsedResponse->rejectedResponseCode =
+            readWordFromNetworkByteOrderMemory( &pResponsePacket->refId );
+
+        /* Determine the return code based on the Kiss-o'-Death code. */
+        switch( pParsedResponse->rejectedResponseCode )
+        {
+            case KOD_CODE_DENY_UINT_VALUE:
+            case KOD_CODE_RSTR_UINT_VALUE:
+                status = SntpRejectedResponseChangeServer;
+                break;
+
+            case KOD_CODE_RATE_UINT_VALUE:
+                status = SntpRejectedResponseRetryWithBackoff;
+                break;
+
+            default:
+                status = SntpRejectedResponseOtherCode;
+                break;
+        }
+    }
+    else
+    {
+        /* Server has responded successfully to the time request. */
+
+        SntpTimestamp_t serverRxTime;
+
+        /* Map of integer value to SntpLeapSecondInfo_t enumeration type for the
+         * "Leap Indicator" field in the first byte of an SNTP packet.
+         * Note: This map is used to not violate MISRA Rule 10.5 when directly
+         * converting an integer to enumeration type.
+         */
+        const SntpLeapSecondInfo_t leapIndicatorTypeMap[] =
+        {
+            NoLeapSecond,
+            LastMinuteHas61Seconds,
+            LastMinuteHas59Seconds,
+            AlarmServerNotSynchronized
+        };
+
+        /* Set the Kiss-o'-Death code value to NULL as server has responded favorably
+         * to the time request. */
+        pParsedResponse->rejectedResponseCode = SNTP_KISS_OF_DEATH_CODE_NONE;
+
+        /* Fill the output parameter with the server time which is the
+         * "transmit" time in the response packet. */
+        pParsedResponse->serverTime.seconds =
+            readWordFromNetworkByteOrderMemory( &pResponsePacket->transmitTime.seconds );
+        pParsedResponse->serverTime.fractions =
+            readWordFromNetworkByteOrderMemory( &pResponsePacket->transmitTime.fractions );
+
+        /* Extract information of any upcoming leap second from the response. */
+        pParsedResponse->leapSecondType = leapIndicatorTypeMap[
+            ( pResponsePacket->leapVersionMode >> SNTP_LEAP_INDICATOR_LSB_POSITION ) ];
+
+        /* Store the "receive" time in SNTP response packet in host order. */
+        serverRxTime.seconds =
+            readWordFromNetworkByteOrderMemory( &pResponsePacket->receiveTime.seconds );
+        serverRxTime.fractions =
+            readWordFromNetworkByteOrderMemory( &pResponsePacket->receiveTime.fractions );
+
+        /* Calculate system clock offset relative to server time, if possible, within
+         * the 64 bit integer width of the SNTP timestamp. */
+        calculateClockOffset( pRequestTxTime,
+                              &serverRxTime,
+                              &pParsedResponse->serverTime,
+                              pResponseRxTime,
+                              &pParsedResponse->clockOffsetMs );
+    }
+
+    return status;
+}
+
+
+SntpStatus_t Sntp_SerializeRequest( SntpTimestamp_t * pRequestTime,
+                                    uint32_t randomNumber,
+                                    void * pBuffer,
+                                    size_t bufferSize )
+{
+    SntpStatus_t status = SntpSuccess;
+
+    if( pRequestTime == NULL )
+    {
+        status = SntpErrorBadParameter;
+    }
+    else if( pBuffer == NULL )
+    {
+        status = SntpErrorBadParameter;
+    }
+    else if( bufferSize < SNTP_PACKET_BASE_SIZE )
+    {
+        status = SntpErrorBufferTooSmall;
+    }
+
+    /* Zero timestamps for client request time is not allowed to protect against
+     * attack spoofing server response containing zero value for "originate timestamp".
+     * Note: In SNTP/NTP communication, the "originate timestamp" of a valid server response
+     * matches the "transmit timestamp" in corresponding client request packet. */
+    else if( isZeroTimestamp( pRequestTime ) == true )
+    {
+        status = SntpErrorBadParameter;
+    }
+    else
+    {
+        /* MISRA Ref 11.5.1 [Void pointer assignment] */
+        /* More details at: https://github.com/FreeRTOS/coreSNTP/blob/main/MISRA.md#rule-115 */
+        /* coverity[misra_c_2012_rule_11_5_violation] */
+        SntpPacket_t * pRequestPacket = ( SntpPacket_t * ) pBuffer;
+
+        /* Fill the buffer with zero as most fields are zero for a standard SNTP
+         * request packet.*/
+        ( void ) memset( pBuffer, 0, sizeof( SntpPacket_t ) );
+
+        /* Set the first byte of the request packet for "Version" and "Mode" fields */
+        pRequestPacket->leapVersionMode = 0U /* Leap Indicator */ |
+                                          ( SNTP_VERSION << SNTP_VERSION_LSB_POSITION ) /* Version Number */ |
+                                          SNTP_MODE_CLIENT /* Mode */;
+
+
+        /* Add passed random number to non-significant bits of the fractions part
+         * of the transmit timestamp.
+         * This is suggested by the SNTPv4 (and NTPv4) specification(s)
+         * to protect against replay attacks. Refer to RFC 4330 Section 3 for
+         * more information.
+         * Adding random bits to the least significant 16 bits of the fractions
+         * part of the timestamp affects only ~15 microseconds of information
+         * (calculated as 0xFFFF * 232 picoseconds).
+         */
+        pRequestTime->fractions = ( pRequestTime->fractions
+                                    | ( randomNumber >> 16 ) );
+
+        /* Update the request buffer with request timestamp in network byte order. */
+        fillWordMemoryInNetworkOrder( &pRequestPacket->transmitTime.seconds,
+                                      pRequestTime->seconds );
+        fillWordMemoryInNetworkOrder( &pRequestPacket->transmitTime.fractions,
+                                      pRequestTime->fractions );
+    }
+
+    return status;
+}
+
+
+SntpStatus_t Sntp_DeserializeResponse( const SntpTimestamp_t * pRequestTime,
+                                       const SntpTimestamp_t * pResponseRxTime,
+                                       const void * pResponseBuffer,
+                                       size_t bufferSize,
+                                       SntpResponseData_t * pParsedResponse )
+{
+    SntpStatus_t status = SntpSuccess;
+    /* MISRA Ref 11.5.1 [Void pointer assignment] */
+    /* More details at: https://github.com/FreeRTOS/coreSNTP/blob/main/MISRA.md#rule-115 */
+    /* coverity[misra_c_2012_rule_11_5_violation] */
+    const SntpPacket_t * pResponsePacket = ( const SntpPacket_t * ) pResponseBuffer;
+
+    if( ( pRequestTime == NULL ) || ( pResponseRxTime == NULL ) ||
+        ( pResponseBuffer == NULL ) || ( pParsedResponse == NULL ) )
+    {
+        status = SntpErrorBadParameter;
+    }
+    else if( bufferSize < SNTP_PACKET_BASE_SIZE )
+    {
+        status = SntpErrorBufferTooSmall;
+    }
+
+    /* Zero timestamps for client request time is not allowed to protect against
+     * attack spoofing server response containing zero value for "originate timestamp".
+     * Note: In SNTP/NTP communication, the "originate timestamp" of a valid server response
+     * matches the "transmit timestamp" in corresponding client request packet. */
+    else if( isZeroTimestamp( pRequestTime ) == true )
+    {
+        status = SntpErrorBadParameter;
+    }
+    /* Check if the packet represents a server in the "Mode" field. */
+    else if( ( pResponsePacket->leapVersionMode & SNTP_MODE_BITS_MASK ) != SNTP_MODE_SERVER )
+    {
+        status = SntpInvalidResponse;
+    }
+
+    /* Check if any of the timestamps in the response packet are zero, which is invalid.
+     * Note: This is done to protect against a nuanced server spoofing attack where if the
+     * SNTP client resets its internal state of "Client transmit timestamp" (OR "originate
+     * timestamp" from server perspective) to zero as a protection against replay attack, an
+     * an attacker with this knowledge of the client operation can spoof a server response
+     * containing the "originate timestamp" as zero. Thus, to protect against such attack,
+     * a server response packet with any zero timestamp is rejected. */
+    else if( ( isZeroTimestamp( &pResponsePacket->originTime ) == true ) ||
+             ( isZeroTimestamp( &pResponsePacket->receiveTime ) == true ) ||
+             ( isZeroTimestamp( &pResponsePacket->transmitTime ) == true ) )
+    {
+        status = SntpInvalidResponse;
+    }
+
+
+    /* Validate that the server has sent the client's request timestamp in the
+     * "originate" timestamp field of the response. */
+    else if( ( pRequestTime->seconds !=
+               readWordFromNetworkByteOrderMemory( &pResponsePacket->originTime.seconds ) ) ||
+             ( pRequestTime->fractions !=
+               readWordFromNetworkByteOrderMemory( &pResponsePacket->originTime.fractions ) ) )
+
+    {
+        status = SntpInvalidResponse;
+    }
+    else
+    {
+        /* As the response packet is valid, parse more information from it and
+         * populate the output parameter. */
+
+        status = parseValidSntpResponse( pResponsePacket,
+                                         pRequestTime,
+                                         pResponseRxTime,
+                                         pParsedResponse );
+    }
+
+    return status;
+}
+
+SntpStatus_t Sntp_CalculatePollInterval( uint16_t clockFreqTolerance,
+                                         uint16_t desiredAccuracy,
+                                         uint32_t * pPollInterval )
+{
+    SntpStatus_t status = SntpSuccess;
+
+    if( ( clockFreqTolerance == 0U ) || ( desiredAccuracy == 0U ) || ( pPollInterval == NULL ) )
+    {
+        status = SntpErrorBadParameter;
+    }
+    else
+    {
+        uint32_t exactIntervalForAccuracy = 0U;
+        uint8_t log2PollInterval = 0U;
+
+        /* Calculate the poll interval required for achieving the exact desired clock accuracy
+         * with the following formulae:
+         *
+         * System Clock Drift Rate ( microseconds / second ) = Clock Frequency Tolerance (in PPM )
+         * Maximum Clock Drift ( milliseconds ) = Desired Accuracy ( milliseconds )
+         *
+         * Poll Interval ( seconds ) =     Maximum Clock Drift
+         *                              ---------------------------
+         *                                System Clock Drift Rate
+         *
+         *                           =  Maximum Drift ( milliseconds ) * 1000 ( microseconds / millisecond )
+         *                             ------------------------------------------------------------------------
+         *                                        System Clock Drift Rate ( microseconds / second )
+         *
+         *                           =    Desired Accuracy * 1000
+         *                             ------------------------------
+         *                               Clock Frequency Tolerance
+         */
+        exactIntervalForAccuracy = ( ( uint32_t ) desiredAccuracy * 1000U ) / clockFreqTolerance;
+
+        /* Check if calculated poll interval value falls in the supported range of seconds. */
+        if( exactIntervalForAccuracy == 0U )
+        {
+            /* Poll interval value is less than 1 second, and is not supported by the function. */
+            status = SntpZeroPollInterval;
+        }
+        else
+        {
+            /* To calculate the log 2 value of the exact poll interval value, first determine the highest
+             * bit set in the value. */
+            while( exactIntervalForAccuracy != 0U )
+            {
+                log2PollInterval++;
+                exactIntervalForAccuracy /= 2U;
+            }
+
+            /* Convert the highest bit in the exact poll interval value to the nearest integer
+             * value lower or equal to the log2 of the exact poll interval value. */
+            log2PollInterval--;
+
+            /* Calculate the poll interval as the closest exponent of 2 value that achieves
+             * equal or higher accuracy than the desired accuracy. */
+            *pPollInterval = ( ( ( uint32_t ) 1U ) << log2PollInterval );
+        }
+    }
+
+    return status;
+}
+
+SntpStatus_t Sntp_ConvertToUnixTime( const SntpTimestamp_t * pSntpTime,
+                                     uint32_t * pUnixTimeSecs,
+                                     uint32_t * pUnixTimeMicrosecs )
+{
+    SntpStatus_t status = SntpSuccess;
+
+    if( ( pSntpTime == NULL ) || ( pUnixTimeSecs == NULL ) || ( pUnixTimeMicrosecs == NULL ) )
+    {
+        status = SntpErrorBadParameter;
+    }
+    /* Check if passed time does not lie in the [UNIX epoch in 1970, UNIX time overflow in 2038] time range. */
+    else if( ( pSntpTime->seconds > SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS ) &&
+             ( pSntpTime->seconds < SNTP_TIME_AT_UNIX_EPOCH_SECS ) )
+    {
+        /* The SNTP timestamp is outside the supported time range for conversion. */
+        status = SntpErrorTimeNotSupported;
+    }
+    else
+    {
+        /* Handle case when timestamp represents date in SNTP era 1
+         * (i.e. time from 7 Feb 2036 6:28:16 UTC onwards). */
+        if( pSntpTime->seconds <= SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS )
+        {
+            /* Unix Time ( seconds ) = Seconds Duration in
+             *                         [UNIX epoch, SNTP Era 1 Epoch Time]
+             *                                        +
+             *                           Sntp Time since Era 1 Epoch
+             */
+            *pUnixTimeSecs = UNIX_TIME_SECS_AT_SNTP_ERA_1_SMALLEST_TIME + pSntpTime->seconds;
+        }
+        /* Handle case when SNTP timestamp is in SNTP era 1 time range. */
+        else
+        {
+            *pUnixTimeSecs = pSntpTime->seconds - SNTP_TIME_AT_UNIX_EPOCH_SECS;
+        }
+
+        /* Convert SNTP fractions to microseconds for UNIX time. */
+        *pUnixTimeMicrosecs = pSntpTime->fractions / SNTP_FRACTION_VALUE_PER_MICROSECOND;
+    }
+
+    return status;
+}
diff --git a/project/coreMQTT/coreSNTP/core_sntp_serializer.h b/project/coreMQTT/coreSNTP/core_sntp_serializer.h
new file mode 100644
index 0000000..55215f1
--- /dev/null
+++ b/project/coreMQTT/coreSNTP/core_sntp_serializer.h
@@ -0,0 +1,535 @@
+/*
+ * coreSNTP v1.2.0
+ * Copyright (C) 2021 Amazon.com, Inc. or its affiliates.  All Rights Reserved.
+ *
+ * SPDX-License-Identifier: MIT
+ *
+ * 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.
+ */
+
+/**
+ * @file core_sntp_serializer.h
+ * @brief API for serializing SNTP request packets and, and de-serializing SNTP
+ * response packets.
+ * This API layer adheres to the SNTPv4 specification defined in
+ * [RFC 4330](https://tools.ietf.org/html/rfc4330).
+ */
+
+#ifndef CORE_SNTP_SERIALIZER_H_
+#define CORE_SNTP_SERIALIZER_H_
+
+/* Standard include. */
+#include <stdint.h>
+#include <stddef.h>
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    extern "C" {
+#endif
+/* *INDENT-ON* */
+
+/**
+ * @ingroup sntp_constants
+ * @brief The base packet size of request and response of the (S)NTP protocol.
+ * @note This is the packet size without any authentication headers for security
+ * mechanism. If the application uses a security mechanism for communicating with
+ * an (S)NTP server, it can add authentication data after the SNTP packet is
+ * serialized with the @ref Sntp_SerializeRequest API function.
+ */
+#define SNTP_PACKET_BASE_SIZE                         ( 48U )
+
+/**
+ * @ingroup sntp_constants
+ * @brief Number of SNTP timestamp fractions in 1 microsecond.
+ *
+ * The fraction's part of an SNTP timestamp is 32-bits wide, thereby, giving a
+ * resolution of 2^(-32) seconds ~ 232 picoseconds.
+ *
+ * @note The application can use this value to convert microseconds part of system
+ * time into SNTP timestamp fractions. For example, if the microseconds
+ * part of system time is n microseconds, the fractions value to be used for the
+ * SNTP timestamp part will be n * SNTP_FRACTION_VALUE_PER_MICROSECOND.
+ */
+#define SNTP_FRACTION_VALUE_PER_MICROSECOND           ( 4295U )
+
+/**
+ * @ingroup sntp_constants
+ * @brief The seconds part of SNTP time at the UNIX epoch time, which represents
+ * an offset of 70 years (in seconds) between SNTP epoch and UNIX epoch time.
+ * SNTP uses 1st Jan 1900 UTC as the epoch time, whereas UNIX standard uses
+ * 1st Jan 1970 UTC as the epoch time, thereby, causing an offset of 70 years
+ * between them.
+ *
+ *  Difference of 70 years = ((70 * 365) + 17 leap days) * 24 * 3600 seconds
+ *
+ * @note If your system follows UNIX time, the application can use this value to
+ * convert seconds part of a system time to seconds part of the equivalent SNTP
+ * time. For example, if the seconds part of system time is n seconds, the seconds
+ * value to be used for the SNTP timestamp will be n + SNTP_TO_UNIX_OFFSET_SECS.
+ */
+#define SNTP_TIME_AT_UNIX_EPOCH_SECS                  ( 2208988800U )
+
+/**
+ * @ingroup sntp_constants
+ * @brief The seconds value of SNTP timestamp for the largest UNIX time when
+ * using signed 32-bit integer for seconds.
+ * The largest time representable with a 32-bit signed integer in UNIX time
+ * is 19 Jan 2038 3h 14m 7s UTC. However, as the SNTP time overflows at
+ * 7 Feb 2036 6h 28m 16s UTC, therefore, the SNTP time for the largest UNIX time
+ * represents the time duration between the 2 timestamps.
+ *
+ * SNTP Time at Largest       Time Duration in the range
+ * Signed 32-bit UNIX time =  [7 Feb 2036 6:28:16, 19 Jan 2038 3:14:07]
+ */
+#define SNTP_TIME_AT_LARGEST_UNIX_TIME_SECS           ( 61505151U )
+
+/**
+ * @ingroup sntp_constants
+ * @brief The UNIX time (in seconds) at the smallest SNTP time in era 1,
+ * i.e. UNIX time at 7 Feb 2036 6:28:16 UTC/
+ *
+ * Time Duration = 7 Feb 6:28:16 UTC (SNTP Era 1 Epoch) -
+ *                 1 Jan 1970 0:0:0 UTC (UNIX epoch)
+ *               = 66 years, 37 days, 6 hours, 28 minutes and 16 seconds
+ *               = ((66 * 365) + 16 leap days) * 24 * 3600) + (6 * 3600)
+ *                 + (28 * 60) + 16
+ */
+#define UNIX_TIME_SECS_AT_SNTP_ERA_1_SMALLEST_TIME    ( 2085978496U )
+
+/**
+ * @ingroup sntp_constants
+ * @brief The fixed-length of any Kiss-o'-Death message ASCII code sent
+ * in an SNTP server response.
+ * @note An SNTP server sends a Kiss-o'-Death message to reject a time request
+ * from the client. For more information on the Kiss-o'-Death codes, refer to the
+ * [SNTPv4 specification Section 8](https://tools.ietf.org/html/rfc4330#section-8).
+ */
+#define SNTP_KISS_OF_DEATH_CODE_LENGTH                ( 4U )
+
+/**
+ * @ingroup sntp_constants
+ * @brief The value for the #SntpResponseData_t.rejectedResponseCode member
+ * when that the server response packet does not contain a Kiss-o'-Death
+ * message, and therefore, does not have a "kiss code".
+ * The server sends a "kiss-code" only when it rejects an SNTP request
+ * with a Kiss-o'-Death message.
+ */
+#define SNTP_KISS_OF_DEATH_CODE_NONE                  ( 0U )
+
+/**
+ * @ingroup sntp_enum_types
+ * @brief Enumeration of status codes that can be returned
+ * by the coreSNTP Library API.
+ */
+typedef enum SntpStatus
+{
+    /**
+     * @brief Successful operation of an SNTP API.
+     */
+    SntpSuccess,
+
+    /**
+     * @brief Invalid parameter passed to an API function.
+     */
+    SntpErrorBadParameter,
+
+    /**
+     * @brief Server sent a Kiss-o'-Death message to reject the request for time.
+     * This status can be returned by the @ref Sntp_ReceiveTimeResponse API.
+     */
+    SntpRejectedResponse,
+
+    /**
+     * @brief Server sent a Kiss-o'-Death message with non-retryable code (i.e. DENY or RSTR).
+     */
+    SntpRejectedResponseChangeServer,
+
+    /**
+     * @brief Server sent a Kiss-o'-Death message with a RATE code, which means that
+     * client should back-off before retrying.
+     */
+    SntpRejectedResponseRetryWithBackoff,
+
+    /**
+     * @brief Server sent a Kiss-o'-Death message with a code, specific to the server.
+     * Application can inspect the ASCII kiss-code from @ref Sntp_DeserializeResponse API.
+     */
+    SntpRejectedResponseOtherCode,
+
+    /**
+     * @brief Application provided insufficient buffer space for serializing
+     * or de-serializing an SNTP packet.
+     * The minimum size of an SNTP packet is #SNTP_PACKET_BASE_SIZE
+     * bytes. */
+    SntpErrorBufferTooSmall,
+
+    /**
+     * @brief Server response failed validation checks for expected data in SNTP packet.
+     */
+    SntpInvalidResponse,
+
+    /**
+     * @brief Poll interval value is under 1 second which cannot be calculated
+     *  by @ref Sntp_CalculatePollInterval.
+     */
+    SntpZeroPollInterval,
+
+    /**
+     * @brief SNTP timestamp cannot be converted to UNIX time as time does not lie
+     * in time range supported by Sntp_ConvertToUnixTime.
+     */
+    SntpErrorTimeNotSupported,
+
+    /**
+     * @brief The user-defined DNS resolution interface, @ref SntpResolveDns_t, failed to resolve
+     * address for a time server. This status is returned by the @ref Sntp_SendTimeRequest API.
+     */
+    SntpErrorDnsFailure,
+
+    /**
+     * @brief Networking operation of sending or receiving SNTP packet through the user-defined UDP
+     * transport interface, @ref UdpTransportInterface_t, failed.
+     * This status is returned by either of @ref Sntp_SendTimeRequest OR @ref Sntp_ReceiveTimeResponse
+     * APIs.
+     */
+    SntpErrorNetworkFailure,
+
+    /**
+     * @brief Time server is not authenticated from the authentication data in its response.
+     * This status can be returned by the user-supplied definition of the
+     * @ref SntpValidateServerAuth_t authentication interface.
+     */
+    SntpServerNotAuthenticated,
+
+    /**
+     * @brief Failure from the user-supplied authentication interface, @ref SntpAuthenticationInterface_t,
+     * in either generating authentication data for SNTP request OR validating the authentication
+     * data in SNTP response from server.
+     */
+    SntpErrorAuthFailure,
+
+    /**
+     * @brief A timeout occurred in sending time request packet over the network to a server through the
+     * @ref Sntp_SendTimeRequest API.
+     */
+    SntpErrorSendTimeout,
+
+    /**
+     * @brief A timeout has occurred in receiving server response with the @ref Sntp_ReceiveTimeResponse
+     * API.
+     */
+    SntpErrorResponseTimeout,
+
+    /**
+     * @brief No SNTP packet for server response is received from the network by the
+     * @ref Sntp_ReceiveTimeResponse API.
+     */
+    SntpNoResponseReceived,
+
+    /**
+     * @brief The SNTP context passed to @ref Sntp_SendTimeRequest or @ref Sntp_ReceiveTimeResponse APIs is
+     * is uninitialized.
+     */
+    SntpErrorContextNotInitialized
+} SntpStatus_t;
+
+/**
+ * @ingroup sntp_enum_types
+ * @brief Enumeration for leap second information that an SNTP server can
+ * send its response to a time request. An SNTP server sends information about
+ * whether there is an upcoming leap second adjustment in the last day of the
+ * current month.
+ *
+ * @note A leap second is an adjustment made in atomic clock time because Earth's rotation
+ * can be inconsistent. Leap seconds are usually incorporated as an extra second insertion
+ * or second deletion in the last minute before midnight i.e. in the minute of 23h:59m UTC
+ * on the last day of June or December. For more information on leap seconds, refer to
+ * https://www.nist.gov/pml/time-and-frequency-division/leap-seconds-faqs.
+ */
+typedef enum SntpLeapSecondInfo
+{
+    NoLeapSecond = 0x00,              /** <@brief There is no upcoming leap second adjustment. */
+    LastMinuteHas61Seconds = 0x01,    /** <@brief A leap second should be inserted in the last minute before midnight. */
+    LastMinuteHas59Seconds = 0x02,    /** <@brief A leap second should be deleted from the last minute before midnight. */
+    AlarmServerNotSynchronized = 0x03 /** <@brief An alarm condition meaning that server's time is not synchronized
+                                       * to an upstream NTP (or SNTP) server. */
+} SntpLeapSecondInfo_t;
+
+/**
+ * @ingroup sntp_struct_types
+ * @brief Structure representing an SNTP timestamp.
+ *
+ * @note The SNTP timestamp uses 1st January 1900 0h 0m 0s Coordinated Universal Time (UTC)
+ * as the primary epoch i.e. the timestamp represents current time as the amount of time since
+ * the epoch time.
+ * Refer to the [SNTPv4 specification](https://tools.ietf.org/html/rfc4330#section-3) for more
+ * information of the SNTP timestamp format.
+ */
+typedef struct SntpTimestamp
+{
+    uint32_t seconds;   /**< @brief Number of seconds since epoch time. */
+    uint32_t fractions; /**< @brief The fractions part of the SNTP timestamp with resolution
+                         *   of 2^(-32) ~ 232 picoseconds. */
+} SntpTimestamp_t;
+
+/**
+ * @ingroup sntp_struct_types
+ * @brief Structure representing data parsed from an SNTP response from server
+ * as well as data of arithmetic calculations derived from the response.
+ */
+typedef struct SntpResponse
+{
+    /**
+     * @brief The timestamp sent by the server.
+     */
+    SntpTimestamp_t serverTime;
+
+    /**
+     * @brief The information of an upcoming leap second in the
+     * server response.
+     */
+    SntpLeapSecondInfo_t leapSecondType;
+
+    /**
+     * @brief If a server responded with Kiss-o'-Death message to reject
+     * time request, this is the fixed length ASCII code sequence for the
+     * rejection.
+     *
+     * The Kiss-o'-Death code is always #SNTP_KISS_OF_DEATH_CODE_LENGTH
+     * bytes long.
+     *
+     * @note If the server does not send a Kiss-o'-Death message in its
+     * response, this value will be #SNTP_KISS_OF_DEATH_CODE_NONE.
+     */
+    uint32_t rejectedResponseCode;
+
+    /**
+     * @brief The offset (in milliseconds) of the system clock relative to the server time
+     * calculated from timestamps in the client SNTP request and server SNTP response packets.
+     * If the the system time is BEHIND the server time, then the clock-offset value is > 0.
+     * If the system time is AHEAD of the server time, then the clock-offset value is < 0.
+     *
+     * @note This information can be used to synchronize the system clock with a "slew",
+     * "step" OR combination of the two clock correction methodologies depending on the degree
+     *  of system clock drift (represented by the clock-offset) and the application's
+     * tolerance for system clock error.
+     *
+     * @note The library calculates the clock-offset value using the On-Wire
+     * protocol suggested by the NTPv4 specification. For more information,
+     * refer to https://tools.ietf.org/html/rfc5905#section-8.
+     *
+     * @note The library ASSUMES that the server and client systems are within
+     * ~68 years of each other clock, whether in the same NTP era or across adjacent
+     * NTP eras. Thus, the client and system times MUST be within ~68 years (or
+     * 2^31 seconds exactly) of each other for correct calculation of clock-offset.
+     *
+     * @note When the server and client times are exactly 2^31 (or INT32_MAX + 1 )
+     * seconds apart, the library ASSUMES that the server time is ahead of the client
+     * time, and return the clock-offset value of INT32_MAX.
+     */
+    int64_t clockOffsetMs;
+} SntpResponseData_t;
+
+
+/**
+ * @brief Serializes an SNTP request packet to use for querying a
+ * time server.
+ *
+ * This function will fill only #SNTP_PACKET_BASE_SIZE bytes of data in the
+ * passed buffer.
+ *
+ * @param[in, out] pRequestTime The current time of the system, expressed as time
+ * since the SNTP epoch (i.e. 0h of 1st Jan 1900 ). This time will be serialized
+ * in the SNTP request packet. The function will use this parameter to return the
+ * timestamp serialized in the SNTP request. To protect against attacks spoofing
+ * server responses, the timestamp MUST NOT be zero in value.
+ * @param[in] randomNumber A random number (generated by a True Random Generator)
+ * for use in the SNTP request packet to protect against replay attacks as suggested
+ * by SNTPv4 specification. For more information, refer to
+ * [RFC 4330 Section 3](https://tools.ietf.org/html/rfc4330#section-3).
+ * @param[out] pBuffer The buffer that will be populated with the serialized
+ * SNTP request packet.
+ * @param[in] bufferSize The size of the @p pBuffer buffer. It should be at least
+ * #SNTP_PACKET_BASE_SIZE bytes in size.
+ *
+ * @note It is recommended to use a True Random Generator (TRNG) to generate
+ * the random number.
+ * @note The application MUST save the @p pRequestTime value for de-serializing
+ * the server response with @ref Sntp_DeserializeResponse API.
+ *
+ * @return This function returns one of the following:
+ * - #SntpSuccess when serialization operation is successful.
+ * - #SntpErrorBadParameter if an invalid parameter is passed.
+ * - #SntpErrorBufferTooSmall if the buffer does not have the minimum size
+ * for serializing an SNTP request packet.
+ */
+/* @[define_sntp_serializerequest] */
+SntpStatus_t Sntp_SerializeRequest( SntpTimestamp_t * pRequestTime,
+                                    uint32_t randomNumber,
+                                    void * pBuffer,
+                                    size_t bufferSize );
+/* @[define_sntp_serializerequest] */
+
+/**
+ * @brief De-serializes an SNTP packet received from a server as a response
+ * to a SNTP request.
+ *
+ * This function will parse only the #SNTP_PACKET_BASE_SIZE bytes of data
+ * in the passed buffer.
+ *
+ * @note If the server has sent a Kiss-o'-Death message to reject the associated
+ * time request, the API function will return the appropriate return code and,
+ * also, provide the ASCII code (of fixed length, #SNTP_KISS_OF_DEATH_CODE_LENGTH bytes)
+ * in the #SntpResponseData_t.rejectedResponseCode member of @p pParsedResponse parameter,
+ * parsed from the response packet.
+ * The application SHOULD respect the server rejection and take appropriate action
+ * based on the rejection code.
+ * If the server response represents an accepted SNTP client request, then the API
+ * function will set the #SntpResponseData_t.rejectedResponseCode member of
+ * @p pParsedResponse parameter to #SNTP_KISS_OF_DEATH_CODE_NONE.
+ *
+ * @note If the server has positively responded with its clock time, then this API
+ * function will calculate the clock-offset. For the clock-offset to be correctly
+ * calculated, the system clock MUST be within ~68 years (or 2^31 seconds) of the server
+ * time mentioned. This function supports clock-offset calculation when server and client
+ * timestamps are in adjacent NTP eras, with one system is in NTP era 0 (i.e. before 7 Feb 2036
+ * 6h:28m:14s UTC) and another system in NTP era 1 (on or after 7 Feb 2036 6h:28m:14s UTC).
+ *
+ * @note In the special case when the server and client times are exactly 2^31 seconds apart,
+ * the library ASSUMES that the server time is ahead of the client time, and returns the
+ * positive clock-offset value of INT32_MAX seconds.
+ *
+ * @param[in] pRequestTime The system time used in the SNTP request packet
+ * that is associated with the server response. This MUST be the same as the
+ * time returned by the @ref Sntp_SerializeRequest API. To protect against attacks
+ * spoofing server responses, this timestamp MUST NOT be zero in value.
+ * @param[in] pResponseRxTime The time of the system, expressed as time since the
+ * SNTP epoch (i.e. 0h of 1st Jan 1900 ), at receiving SNTP response from server.
+ * This time will be used to calculate system clock offset relative to server.
+ * @param[in] pResponseBuffer The buffer containing the SNTP response from the
+ * server.
+ * @param[in] bufferSize The size of the @p pResponseBuffer containing the SNTP
+ * response. It MUST be at least #SNTP_PACKET_BASE_SIZE bytes
+ * long for a valid SNTP response.
+ * @param[out] pParsedResponse The information parsed from the SNTP response packet.
+ * If possible to calculate without overflow, it also contains the system clock
+ * offset relative to the server time.
+ *
+ * @return This function returns one of the following:
+ * - #SntpSuccess if the de-serialization operation is successful.
+ * - #SntpErrorBadParameter if an invalid parameter is passed.
+ * - #SntpErrorBufferTooSmall if the buffer does not have the minimum size
+ * required for a valid SNTP response packet.
+ * - #SntpInvalidResponse if the response fails sanity checks expected in an
+ * SNTP response.
+ * - #SntpRejectedResponseChangeServer if the server rejected with a code
+ * indicating that client cannot be retry requests to it.
+ * - #SntpRejectedResponseRetryWithBackoff if the server rejected with a code
+ * indicating that client should back-off before retrying request.
+ * - #SntpRejectedResponseOtherCode if the server rejected with a code that
+ * application can inspect in the @p pParsedResponse parameter.
+ */
+/* @[define_sntp_deserializeresponse] */
+SntpStatus_t Sntp_DeserializeResponse( const SntpTimestamp_t * pRequestTime,
+                                       const SntpTimestamp_t * pResponseRxTime,
+                                       const void * pResponseBuffer,
+                                       size_t bufferSize,
+                                       SntpResponseData_t * pParsedResponse );
+/* @[define_sntp_deserializeresponse] */
+
+/**
+ * @brief Utility to calculate the poll interval of sending periodic time queries
+ * to servers to achieve a desired system clock accuracy for a given
+ * frequency tolerance of the system clock.
+ *
+ * For example, from the SNTPv4 specification, "if the frequency tolerance
+ * is 200 parts per million (PPM) and the required accuracy is one minute,
+ * the maximum timeout is about 3.5 days". In this example, the system
+ * clock frequency tolerance is 200 PPM and the desired accuracy is
+ * 60000 milliseconds (or 1 minute) for which this API function
+ * will return the maximum poll interval value as 2^18 seconds (or ~3 days).
+ *
+ * @note The poll interval returned is a power of 2, which is the
+ * standard way to represent the value. According to the SNTPv4 specification
+ * Best Practices, an SNTP client SHOULD NOT have a poll interval less than 15 seconds.
+ * https://tools.ietf.org/html/rfc4330#section-10. This API function DOES NOT
+ * support poll interval calculation less than 1 second.
+ *
+ * @param[in] clockFreqTolerance The frequency tolerance of system clock
+ * in parts per million (PPM) units. This parameter MUST be non-zero.
+ * @param[in] desiredAccuracy The acceptable maximum drift, in milliseconds,
+ * for the system clock. The maximum value (0xFFFF) represents ~1 minute of
+ * desired clock accuracy. This parameter MUST be non-zero.
+ * @param[out] pPollInterval This is filled with the poll interval, in seconds
+ * calculated as the closest power of 2 value that will achieve either the
+ * exact desired or higher clock accuracy @p desiredAccuracy, for the given clock
+ * frequency tolerance, @p clockFreqTolerance.
+ *
+ * @return Returns one of the following:
+ *  - #SntpSuccess if calculation is successful.
+ *  - #SntpErrorBadParameter for an invalid parameter passed to the function.
+ *  - #SntpZeroPollInterval if calculated poll interval is less than 1 second.
+ */
+/* @[define_sntp_calculatepollinterval] */
+SntpStatus_t Sntp_CalculatePollInterval( uint16_t clockFreqTolerance,
+                                         uint16_t desiredAccuracy,
+                                         uint32_t * pPollInterval );
+/* @[define_sntp_calculatepollinterval] */
+
+
+/**
+ * @brief Utility to convert SNTP timestamp (that uses 1st Jan 1900 as the epoch) to
+ * UNIX timestamp (that uses 1st Jan 1970 as the epoch).
+ *
+ * @note This function can ONLY handle conversions of SNTP timestamps that lie in the
+ * range from 1st Jan 1970 0h 0m 0s, the UNIX epoch time, to 19th Jan 2038 3h 14m 7s,
+ * the maximum UNIX time that can be represented in a signed 32 bit integer. (The
+ * limitation is to support systems that use signed 32-bit integer to represent the
+ * seconds part of the UNIX time.)
+ *
+ * @note This function supports overflow of the SNTP timestamp (from the 7 Feb 2036
+ * 6h 28m 16s time, i.e. SNTP era 1) by treating the timestamps with seconds part
+ * in the range [0, 61,505,152] seconds where the upper limit represents the UNIX
+ * overflow time (i.e. 19 Jan 2038 3h 14m 7s) for systems that use signed 32-bit
+ * integer to represent time.
+ *
+ * @param[in] pSntpTime The SNTP timestamp to convert to UNIX time.
+ * @param[out] pUnixTimeSecs This will be filled with the seconds part of the
+ * UNIX time equivalent of the SNTP time, @p pSntpTime.
+ * @param[out] pUnixTimeMicrosecs This will be filled with the microseconds part
+ * of the UNIX time equivalent of the SNTP time, @p pSntpTime.
+ *
+ * @return Returns one of the following:
+ *  - #SntpSuccess if conversion to UNIX time is successful
+ *  - #SntpErrorBadParameter if any of the passed parameters are NULL.
+ *  - #SntpErrorTimeNotSupported if the passed SNTP time does not lie in the
+ * supported time range.
+ */
+/* @[define_sntp_converttounixtime] */
+SntpStatus_t Sntp_ConvertToUnixTime( const SntpTimestamp_t * pSntpTime,
+                                     uint32_t * pUnixTimeSecs,
+                                     uint32_t * pUnixTimeMicrosecs );
+/* @[define_sntp_converttounixtime] */
+
+/* *INDENT-OFF* */
+#ifdef __cplusplus
+    }
+#endif
+/* *INDENT-ON* */
+
+#endif /* ifndef CORE_SNTP_SERIALIZER_H_ */
diff --git a/project/coreMQTT/coreSNTP/makefile b/project/coreMQTT/coreSNTP/makefile
new file mode 100644
index 0000000..b6ece0e
--- /dev/null
+++ b/project/coreMQTT/coreSNTP/makefile
@@ -0,0 +1,35 @@
+#********************************************************************************
+#      Copyright:  (C) 2023 LingYun IoT System Studio
+#                  All rights reserved.
+#
+#       Filename:  Makefile
+#    Description:  This file used compile all the source code to static library
+#
+#        Version:  1.0.0(11/08/23)
+#         Author:  Guo Wenxue <guowenxue@gmail.com>
+#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+PWD=$(shell pwd )
+
+BUILD_ARCH=$(shell uname -m)
+ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
+    CROSS_COMPILE?=arm-linux-gnueabihf-
+endif
+
+LIBNAME=$(shell basename ${PWD} )
+TOPDIR=$(shell dirname ${PWD} )
+CFLAGS+=-D_GNU_SOURCE
+
+all: clean
+	@rm -f *.o
+	@${CROSS_COMPILE}gcc ${CFLAGS} -I${TOPDIR} -c *.c
+	${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
+
+clean:
+	@rm -f *.o
+	@rm -f *.a
+
+distclean:
+	@make clean
diff --git a/project/coreMQTT/etc/mqttd.conf b/project/coreMQTT/etc/mqttd.conf
new file mode 100644
index 0000000..50fcffa
--- /dev/null
+++ b/project/coreMQTT/etc/mqttd.conf
@@ -0,0 +1,69 @@
+[common]
+devid="RPi3B#01"
+
+# 树莓派连接的外设信息,0:禁用或未连接  其他: 使能或相关硬件连接的Pin管脚(wPI模式)
+[hardware]
+
+# 是否使能继电器模块,0:禁用  1:使能
+relay=1
+
+# 是否使能 PWM 蜂鸣器模块,0:禁用  N:Beep 次数
+beep=3
+
+# 是否使能 RGB 三色灯模块,0:禁用  1:使能
+rgbled=1
+
+# 是否使能 DS18b20 温度传感器模块,0:禁用  1:使能
+ds18b20=1
+
+# 是否使能 SHT2X 温湿度传感器模块,0:禁用  1:使能
+sht2x=1
+
+# 是否使能 TSL2561 光强传感器模块,0:禁用  1:使能
+lux=1
+
+[logger]
+
+# 日志记录文件
+file=/tmp/mqttd.log
+
+# 日志级别: 0:ERROR 1:WARN 2:INFO 3:DEBUG 4:TRACE 
+level=2
+
+# 日志回滚大小
+size=1024
+
+
+[broker]
+
+# broker 服务器地址和端口号
+hostname="main.iot-yun.club"
+port=10883
+
+# broker 认证连接的用户名和密码
+username="lingyun"
+password="lingyun"
+
+# broker给subsciber和publisher发送PING报文保持 keepalive 的时间周期,单位是秒
+keepalive=30
+
+# Qos0: 发送者只发送一次消息,不进行重试,Broker不会返回确认消息。在Qos0情况下,Broker可能没有接受到消息
+# Qos1: 发送者最少发送一次消息,确保消息到达Broker,Broker需要返回确认消息PUBACK。在Qos1情况下,Broker可能接受到重复消息
+# Qos2: Qos2使用两阶段确认来保证消息的不丢失和不重复。在Qos2情况下,Broker肯定会收到消息,且只收到一次
+
+[publisher]
+
+# mosquitto_sub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Uplink
+pubTopic="$Sys/Studio/Uplink"
+pubQos=0
+
+# Publisher上报传感器数据的周期,单位是秒
+interval=60
+
+[subsciber]
+
+# mosquitto_pub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Downlink -m '{"id":"RPi3B#01", "light":"on", "buzzer":"on"}'
+# mosquitto_pub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Downlink -m '{"id":"RPi3B#01", "leds":[{"red":"on","green":"off","blue":"on"}]}'
+subTopic="$Sys/Studio/Downlink"
+subQos=0
+
diff --git a/project/coreMQTT/main.c b/project/coreMQTT/main.c
new file mode 100644
index 0000000..31d8fd7
--- /dev/null
+++ b/project/coreMQTT/main.c
@@ -0,0 +1,458 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  main.c
+ *    Description:  This file
+ *
+ *        Version:  1.0.0(29/01/19)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "29/01/19 15:34:41"
+ *
+ ********************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <string.h>
+#include <errno.h>
+
+#include <mosquitto.h>
+#include <cjson/cJSON.h>
+
+#include "logger.h"
+#include "util_proc.h"
+#include "modules.h"
+#include "conf.h"
+
+#define PROG_VERSION               "v1.0.0"
+#define DAEMON_PIDFILE             "/tmp/.mqtt.pid"
+
+void *mqtt_sub_worker(void *args);
+void *mqtt_pub_worker(void *args);
+
+static void program_usage(char *progname)
+{
+
+    printf("Usage: %s [OPTION]...\n", progname);
+    printf(" %s is LingYun studio MQTT daemon program running on RaspberryPi\n", progname);
+
+    printf("\nMandatory arguments to long options are mandatory for short options too:\n");
+    printf(" -d[debug   ]  Running in debug mode\n");
+    printf(" -c[conf    ]  Specify configure file\n");
+    printf(" -h[help    ]  Display this help information\n");
+    printf(" -v[version ]  Display the program version\n");
+
+    printf("\n%s version %s\n", progname, PROG_VERSION);
+    return;
+}
+
+int main (int argc, char **argv)
+{
+    int                daemon = 1;
+    pthread_t          tid;
+    mqtt_ctx_t         ctx;
+    char               *conf_file=NULL;
+    int                debug = 0;
+    int                opt;
+    char              *progname=NULL;
+
+    struct option long_options[] = {
+        {"conf", required_argument, NULL, 'c'},
+        {"debug", no_argument, NULL, 'd'},
+        {"version", no_argument, NULL, 'v'},
+        {"help", no_argument, NULL, 'h'},
+        {NULL, 0, NULL, 0}
+    };
+
+    progname = (char *)basename(argv[0]);
+
+    /* Parser the command line parameters */
+    while ((opt = getopt_long(argc, argv, "c:dvh", long_options, NULL)) != -1)
+    {
+        switch (opt)
+        {
+            case 'c': /* Set configure file */
+                conf_file = optarg;
+                break;
+
+            case 'd': /* Set debug running */
+                daemon = 0;
+                debug = 1;
+                break;
+
+            case 'v':  /* Get software version */
+                printf("%s version %s\n", progname, PROG_VERSION);
+                return 0;
+
+            case 'h':  /* Get help information */
+                program_usage(progname);
+                return 0;
+
+            default:
+                break;
+        }
+
+    }
+
+    if( !conf_file )
+        debug = 1;
+
+    if( mqttd_parser_conf(conf_file, &ctx, debug)<0 )
+    {
+        fprintf(stderr, "Parser mqtted configure file failure\n");
+        return -2;
+    }
+
+    install_default_signal();
+
+    if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 )
+        goto cleanup;
+
+    mosquitto_lib_init();
+
+    if( thread_start(&tid, mqtt_sub_worker, &ctx ) < 0 )
+    {
+        log_error("Start MQTT subsciber worker thread failure\n");
+        goto cleanup;
+    }
+    log_info("Start MQTT subsciber worker thread ok\n");
+
+    if( thread_start(&tid, mqtt_pub_worker, &ctx) < 0 )
+    {
+        log_error("Start MQTT publisher worker thread failure\n");
+        goto cleanup;
+    }
+    log_info("Start MQTT publisher worker thread ok\n");
+
+    while( ! g_signal.stop )
+    {
+        msleep(1000);
+    }
+
+cleanup:
+    mosquitto_lib_cleanup();
+    log_close();
+
+    return 0;
+} /* ----- End of main() ----- */
+
+void pub_connect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+    int                     rv = 0;
+    char                    msg[128];
+    float                   temp = 0.0; /* temperature */
+    float                   rh = 0.0;   /* relative humidity */
+    int                     retain = 0;
+
+    if( result )
+    {
+        log_error("Publisher connect to broker server[%s:%d] failed, rv=%d\n", ctx->host, ctx->port, result);
+        return ;
+    }
+
+    log_info("Publisher connect to broker server[%s:%d] successfully\n", ctx->host, ctx->port);
+    log_debug("Publish topic '%s'\n", ctx->pubTopic);
+
+    if( ctx->hwconf.ds18b20 )
+    {
+        memset(msg, 0, sizeof(msg));
+
+        log_debug("DS18B20 temperature sensor enabled, start broadcast it\n");
+
+        if( ds18b20_get_temperature(&temp) < 0 )
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"error\" }", ctx->devid);
+        else
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"%.2f\" }", ctx->devid, temp);
+
+        rv = mosquitto_publish(mosq, NULL, ctx->pubTopic, strlen(msg), msg, ctx->pubQos, retain);
+        if( rv )
+        {
+            log_error("Publisher broadcast message '%s' failure: %d\n", msg, rv);
+        }
+        else
+        {
+            log_info("Publisher broadcast message '%s' ok\n", msg);
+        }
+    }
+
+    if( ctx->hwconf.sht2x )
+    {
+        memset(msg, 0, sizeof(msg));
+
+        log_debug("SHT2X temperature and humidity sensor enabled, start broadcast it\n");
+
+        if( sht2x_get_temp_humidity(&temp, &rh) < 0 )
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"error\", \"RH\":\"error\" }", ctx->devid);
+        else
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"%.2f\", \"RH\":\"%.2f\" }", ctx->devid, temp, rh);
+
+        rv = mosquitto_publish(mosq, NULL, ctx->pubTopic, strlen(msg), msg, ctx->pubQos, retain);
+        if( rv )
+        {
+            log_error("Publisher broadcast message '%s' failure: %d\n", msg, rv);
+        }
+        else
+        {
+            log_info("Publisher broadcast message '%s' ok\n", msg);
+        }
+    }
+
+    log_info("Publisher broadcast over and disconnect broker now\n", ctx->host, ctx->port);
+    mosquitto_disconnect(mosq);
+
+    return ;
+}
+
+
+void *mqtt_pub_worker(void *args)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)args;
+    struct mosquitto       *mosq;
+    bool                    session = true;
+
+
+    mosq = mosquitto_new(NULL, session, ctx);
+    if( !mosq )
+    {
+        log_error("mosquitto_new failure\n");
+        return NULL;
+    }
+
+    /* set connnect to broker username and password  */
+    if( strlen(ctx->uid)> 0  && strlen(ctx->pwd)> 0 )
+        mosquitto_username_pw_set(mosq, ctx->uid, ctx->pwd);
+
+    /* set callback functions */
+    mosquitto_connect_callback_set(mosq, pub_connect_callback);
+
+    while( !g_signal.stop )
+    {
+        /* connect to MQTT broker  */
+        if( mosquitto_connect(mosq, ctx->host, ctx->port, ctx->keepalive) )
+        {
+            log_error("Publisher connect to broker[%s:%d] failure: %s\n", ctx->host, ctx->port, strerror(errno));
+            msleep(1000);
+            continue;
+        }
+
+        /* -1: use default timeout 1000ms  1: unused */
+        mosquitto_loop_forever(mosq, -1, 1);
+
+        /* Publisher broadcast sensors data message interval time, unit seconds */
+        sleep( ctx->interval );
+    }
+
+    mosquitto_destroy(mosq);
+    return NULL;
+}
+
+void sub_connect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+
+    if( result )
+    {
+        log_error("Subscriber connect to broker server failed, rv=%d\n", result);
+        return ;
+    }
+
+    log_info("Subscriber connect to broker server[%s:%d] successfully\n", ctx->host, ctx->port);
+    mosquitto_subscribe(mosq, NULL, ctx->subTopic, ctx->subQos);
+}
+
+void sub_disconnect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+
+    log_warn("Subscriber disconnect to broker server[%s:%d], reason=%d\n", ctx->host, ctx->port, result);
+}
+
+static inline void mqtt_turn_led(int which, char *cmd)
+{
+    if( strcasestr(cmd, "on") )
+        turn_led(which, ON);
+    else if( strcasestr(cmd, "off") )
+        turn_led(which, OFF);
+}
+
+void proc_json_items(cJSON *root, mqtt_ctx_t *ctx)
+{
+    int                    i;
+    char                  *value;
+    cJSON                 *item;
+    cJSON                 *array;
+    hwconf_t              *hwconf = &ctx->hwconf;
+
+    if( !root )
+    {
+        log_error("Invalid input arguments $root\n");
+        return ;
+    }
+
+    for( i=0; i<cJSON_GetArraySize(root); i++ )
+    {
+        item = cJSON_GetArrayItem(root, i);
+        if( !item )
+            break;
+
+        /* if item is cJSON_Object, then recursive call proc_json */
+        if( cJSON_Object == item->type )
+        {
+            proc_json_items(item, ctx);
+        }
+        else if( cJSON_Array == item->type )
+        {
+            /* RGB colors led control: {"id":"RPi3B#01", "leds":[{"red":"on","green":"off","blue":"on"}]} */
+            if( hwconf->led && !strcasecmp(item->string, "leds") )
+            {
+                array = cJSON_GetArrayItem(item, 0);
+                if( NULL != array )
+                {
+                    cJSON        *led_item;
+
+                    if( NULL != (led_item=cJSON_GetObjectItem(array , "red")) )
+                    {
+                        log_info("turn red led '%s'\n", led_item->valuestring);
+                        mqtt_turn_led(LED_R, led_item->valuestring);
+                    }
+
+                    if( NULL != (led_item=cJSON_GetObjectItem(array , "green")) )
+                    {
+                        log_info("turn green led '%s'\n", led_item->valuestring);
+                        mqtt_turn_led(LED_G, led_item->valuestring);
+                    }
+
+                    if( NULL != (led_item=cJSON_GetObjectItem(array , "blue")) )
+                    {
+                        log_info("turn blue led '%s'\n", led_item->valuestring);
+                        mqtt_turn_led(LED_B, led_item->valuestring);
+                    }
+                }
+            }
+        }
+        else
+        {
+            value = cJSON_Print(item);
+
+            /* light controled by relay: {"id":"RPi3B#01", "light":"on"} */
+            if( hwconf->relay && !strcasecmp(item->string, "light") )
+            {
+                if( strcasestr(value, "on") )
+                {
+                    log_info("Turn light on\n");
+                    turn_relay(RELAY1, ON);
+                }
+                else if( strcasestr(value, "off") )
+                {
+                    log_info("Turn light off\n");
+                    turn_relay(RELAY1, OFF);
+                }
+            }
+
+            /* buzzer controled : {"id":"RPi3B#01", "buzzer":"on"} */
+            if( hwconf->beeper && !strcasecmp(item->string, "buzzer") )
+            {
+                if( strcasestr(value, "on") )
+                {
+                    turn_beep(3);
+                }
+            }
+
+            free(value); /* must free it, or it will result memory leak */
+        }
+    }
+
+}
+
+void sub_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+
+    cJSON                  *root = NULL;
+    cJSON                  *item;
+    char                   *value;
+
+    if ( !message->payloadlen )
+    {
+        log_error("%s (null)\n", message->topic);
+        return ;
+    }
+
+    log_debug("Subscriber receive message: '%s'\n", message->payload);
+
+    root = cJSON_Parse(message->payload);
+    if( !root )
+    {
+        log_error("cJSON_Parse parser failure: %s\n", cJSON_GetErrorPtr());
+        return ;
+    }
+
+    item = cJSON_GetObjectItem(root, "id");
+    if( !item )
+    {
+        log_error("cJSON_Parse get ID failure: %s\n", cJSON_GetErrorPtr());
+        goto cleanup;
+    }
+
+    value = cJSON_PrintUnformatted(item);
+    if( strcasecmp(value, ctx->devid) )
+    {
+        free(value);
+        goto cleanup;
+    }
+
+    free(value);
+    log_info("Subscriber receive message: '%s'\n", message->payload);
+
+    proc_json_items(root, ctx);
+
+cleanup:
+    cJSON_Delete(root); /* must delete it, or it will result memory leak */
+    return ;
+}
+
+
+void *mqtt_sub_worker(void *args)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)args;
+    struct mosquitto       *mosq;
+    bool                    session = true;
+
+    mosq = mosquitto_new(NULL, session, ctx);
+    if( !mosq )
+    {
+        log_error("mosquitto_new failure\n");
+        return NULL;
+    }
+
+    /* set connnect to broker username and password  */
+    if( strlen(ctx->uid)> 0  && strlen(ctx->pwd)> 0 )
+        mosquitto_username_pw_set(mosq, ctx->uid, ctx->pwd);
+
+    /* set callback functions */
+    mosquitto_connect_callback_set(mosq, sub_connect_callback);
+    mosquitto_disconnect_callback_set(mosq, sub_disconnect_callback);
+    mosquitto_message_callback_set(mosq, sub_message_callback);
+
+    while( !g_signal.stop )
+    {
+        /* connect to MQTT broker  */
+        if( mosquitto_connect(mosq, ctx->host, ctx->port, ctx->keepalive) )
+        {
+            log_error("Subscriber connect to broker[%s:%d] failure: %s\n", ctx->host, ctx->port, strerror(errno));
+            msleep(1000);
+            continue;
+        }
+
+        /* -1: use default timeout 1000ms  1: unused */
+        mosquitto_loop_forever(mosq, -1, 1);
+    }
+
+    mosquitto_destroy(mosq);
+    return NULL;
+}
+
diff --git a/project/coreMQTT/makefile b/project/coreMQTT/makefile
new file mode 100644
index 0000000..49a790f
--- /dev/null
+++ b/project/coreMQTT/makefile
@@ -0,0 +1,69 @@
+#********************************************************************************
+#      Copyright:  (C) 2023 LingYun IoT System Studio
+#                  All rights reserved.
+#
+#       Filename:  Makefile
+#    Description:  This file is the project top Makefie
+#
+#        Version:  1.0.0(11/08/23)
+#         Author:  Guo Wenxue <guowenxue@gmail.com>
+#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+PRJ_PATH=$(shell pwd)
+APP_NAME = mqttd
+
+BUILD_ARCH=$(shell uname -m)
+ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
+    CROSS_COMPILE=arm-linux-gnueabihf-
+endif
+
+# C source files in top-level directory
+SRCFILES = $(wildcard *.c)
+
+# common CFLAGS for our source code
+CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized -D_GNU_SOURCE
+
+# C source file in sub-directory
+SRCS=booster modules coreJSON  coreMQTT  coreSNTP
+SRCS_PATH=$(patsubst %,${PRJ_PATH}/%,$(SRCS))
+CFLAGS+=$(patsubst %,-I%,$(SRCS_PATH))
+LDFLAGS+=$(patsubst %,-L%,$(SRCS_PATH))
+LIBS=$(patsubst %,-l%,$(SRCS))
+LDFLAGS+=${LIBS}
+
+# Open source libraries
+CFLAGS+=-I ${PRJ_PATH}/openlibs/install/include
+LDFLAGS+=-L ${PRJ_PATH}/openlibs/install/lib
+
+# libraries
+libs=openlibs ${SRCS}
+LDFLAGS+=-lmosquitto -lcjson -lssl -lcrypto -lgpiod
+
+LDFLAGS+=-lpthread
+
+all: entry subdir
+	${CROSS_COMPILE}gcc ${CFLAGS} ${SRCFILES} -o ${APP_NAME} ${LDFLAGS}
+	@make install
+
+entry:
+	@echo "Building ${APP_NAME} on ${BUILD_ARCH}"
+
+subdir:
+	@for dir in ${libs} ; do if [ ! -e $${dir} ] ; then ln -s ../$${dir}; fi; done
+	@for dir in ${libs} ;  do make -C $${dir} ; done
+
+install:
+	cp ${APP_NAME} /tftp
+
+clean:
+	@for dir in ${SRCS} ; do if [ -e $${dir} ] ; then make clean -C $${dir}; fi; done
+	@rm -f ${APP_NAME}
+
+distclean:
+	@for dir in ${libs} ; do if [ -e $${dir} ] ; then make clean -C $${dir}; fi; done
+	@for dir in ${libs} ; do if [ -e $${dir} ] ; then unlink $${dir}; fi; done
+	@rm -f ${APP_NAME}
+	@rm -f cscope.* tags
+
diff --git a/project/gpsd/gpsd.c b/project/gpsd/gpsd.c
new file mode 100644
index 0000000..61356f1
--- /dev/null
+++ b/project/gpsd/gpsd.c
@@ -0,0 +1,245 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  gpsd.c
+ *    Description:  This file is GPS location parser program
+ *
+ *        Version:  1.0.0(07/10/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "07/10/23 09:49:55"
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "logger.h"
+#include "comport.h"
+#include "util_proc.h"
+
+typedef struct gps_fix_s
+{
+    char       time[32];  /* GPS UTC time, formt: hhmmss */
+    char       status;    /* A: Valid  V:Invalid */
+    float      latitude;  /* Latitude */
+    char       lat;       /* N: North  S: South */
+    float      longitude; /* Longitude */
+    char       lon;       /* E: East   W: West */
+    float      speed;     /* Speed over ground, meters/sec */
+    float      track;     /* Course made good (relative to true north) */
+} gps_fix_t;
+
+
+int proc_gprmc(char *buf, int size, gps_fix_t *info);
+
+static inline void banner(const char *progname)
+{
+    printf("%s program Version v1.0.0 Build on (%s)\n", progname, __DATE__);
+    printf("Copyright (C) 2023 LingYun IoT System Studio.\n");
+}
+
+static void program_usage(const char *progname)
+{
+    banner(progname);
+
+    printf("Usage: %s [OPTION]...\n", progname);
+    printf(" %s is a MQTT subscribe daemon program to control relay. \n", progname);
+
+    printf("\nMandatory arguments to long options are mandatory for short options too:\n");
+    printf(" -D[device  ]  Set GPS connect serial port device\n");
+    printf(" -d[debug   ]  Running in debug mode\n");
+    printf(" -l[level   ]  Set the log level as [0..%d]\n", LOG_LEVEL_MAX-1);
+    printf(" -h[help    ]  Display this help information\n");
+    printf(" -v[version ]  Display the program version\n");
+
+    return;
+}
+
+int main (int argc, char **argv)
+{
+    const char             *progname=NULL;
+    int                     opt = 0;
+    int                     rv = 0;
+    int                     debug = 0;
+    char                    pid_file[64] = { 0 }; /*   The file used to record the PID */
+    char                   *log_file = "/tmp/gpsd.log";
+    int                     log_level = LOG_LEVEL_INFO;
+    int                     log_size = 10;
+
+    char                    buf[4096];
+    char                   *dev=NULL;
+    comport_t               comport;
+    gps_fix_t               info;
+
+    struct option long_options[] = {
+        {"device", required_argument, NULL, 'D'},
+        {"debug", no_argument, NULL, 'd'},
+        {"level", required_argument, NULL, 'l'},
+        {"version", no_argument, NULL, 'v'},
+        {"help", no_argument, NULL, 'h'},
+        {NULL, 0, NULL, 0}
+    };
+
+    progname = basename(argv[0]);
+    /*  Parser the command line parameters */
+    while ((opt = getopt_long(argc, argv, "D:dl:vh", long_options, NULL)) != -1)
+    {
+        switch (opt)
+        {
+            case 'D':  /*  Set serial port device */
+                dev = optarg;
+                break;
+
+            case 'd': /*  Set debug running */
+                debug = 1;
+                log_file = "console";
+                log_level = LOG_LEVEL_DEBUG;
+                log_size = ROLLBACK_NONE;
+                break;
+
+            case 'l': /*  Set the log level */
+                rv = atoi(optarg);
+                log_level = rv>LOG_LEVEL_MAX ? LOG_LEVEL_MAX-1 : rv;
+                break;
+
+            case 'v':  /*  Get software version */
+                banner(progname); /*  Defined in version.h */
+                return EXIT_SUCCESS;
+
+            case 'h':  /*  Get help information */
+                program_usage(progname);
+                return 0;
+
+            default:
+                break;
+        } /*   end of "switch(opt)" */
+    }
+
+    if( !dev )
+    {
+        program_usage(progname);
+        return 0;
+    }
+
+    if( (rv=log_open(log_file, log_level, log_size, LOG_LOCK_DISABLE)) < 0 )
+    {
+        fprintf(stderr, "open logger failed, rv=%d\n", rv);
+        return 1;
+    }
+
+    if( !debug )
+    {
+        snprintf(pid_file, sizeof(pid_file), "/var/run/%s.pid", progname);
+        log_info("check program running in daemon or not by pidfile [%s]\n", pid_file);
+        if( check_daemon_running(pid_file) )
+        {
+            printf("Programe already running, exit now.\n");
+            return 3;
+        }
+
+        if( set_daemon_running(pid_file) < 0 )
+        {
+            printf("Programe already running, exit now.\n");
+            return 3;
+        }
+    }
+
+    if( (rv=comport_open(&comport, dev, 4800, "8N1N")) < 0 )
+    {
+        log_error("Open serial port \"%s\" failed, rv=%d\n", dev, rv);
+        return 2;
+    }
+    log_info("Open serial port \"%s\" successfully\n", dev);
+
+    while(1)
+    {
+        memset(buf, 0, sizeof(buf));
+        rv = comport_recv(&comport, buf, sizeof(buf), 3000);
+        if( rv > 0 )
+        {
+            proc_gprmc(buf, strlen(buf), &info);
+        }
+    }
+
+    comport_close(&comport);
+    return 0;
+}
+
+/*
+ * $GPRMC,024216.000,A,3029.6651,N,11423.6251,E,0.08, 156.95,100723,,,A*65
+ * 1     024216.000    UTC time format: HHMMSS, 10:42:16(BeiJing)
+ * 2     A             Status of Fix:
+ *                      A = Autonomous, valid;
+ *                      D = Differential, valid;
+ *                      V = invalid
+ *
+ * 3,4   3029.6651,N   Latitude format: ddmm.mmmm, Latitude 30 deg. 29.6651 min North
+ * 5,6   11423.6251,E  Longitude: dddmm.mmmm, Longitude 114 deg. 23.6251 min East
+ * 7     0.08          Speed over ground, Knots
+ * 8     156.95        Course Made Good, True north
+ * 9     100723        Date of fix ddmmyy. 2023-07-10
+ * 10,11 ,,            Magnetic variation
+ * 12    A             FAA mode indicator (NMEA 2.3 and later)
+ *                      A=autonomous,
+ *                      D=differential,
+ *                      E=Estimated,
+ *                      M=Manual input mode,
+ *                      N=not valid,
+ *                      S=Simulator,
+ *                      V=Valid
+ * 13   *68            mandatory nmea_checksum
+ */
+
+#define DD(s)   ((int)((s)[0]-'0')*10+(int)((s)[1]-'0'))
+int proc_gprmc(char *buf, int size, gps_fix_t *info)
+{
+    char           *ptr_start=NULL;
+    char           *ptr_end=NULL;
+    char            hhmmss[12]={0x0};
+    char            ddmmyy[12]={0x0};
+
+    if( !buf || size<=0 || !info )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    log_trace("GPS receive raw data\n:%s\n", buf);
+
+    if( !(ptr_start=strstr(buf, "$GPRMC")) )
+    {
+        log_warn("$GPRMC keywords not matched\n");
+        return -2;
+    }
+
+    if( !(ptr_end=strchr(ptr_start, 0x0D)) )
+    {
+        log_warn("$GPRMC data not integrated\n");
+    }
+
+    *ptr_end = '\0';
+
+    memset(info, 0, sizeof(*info));
+    sscanf(ptr_start, "$GPRMC,%[^,],%c,%f,%c,%f,%c,%f,%f,%[^,]",
+            hhmmss, &info->status, &info->latitude, &info->lat,
+            &info->longitude, &info->lon, &info->speed, &info->track, ddmmyy);
+
+    snprintf(info->time, sizeof(info->time), "%04d-%02d-%02d %02d:%02d:%02d",
+            DD(ddmmyy+4)+2000, DD(ddmmyy+2), DD(ddmmyy),
+            DD(hhmmss)+8, DD(hhmmss+2), DD(hhmmss+4));
+
+    if( info->status == 'A' )
+    {
+        log_info("NMEA0183: %s lat=%.2f(%c) lon=%.2f(%c) speed=%.2f, track=%.2f\n",
+                info->time, info->latitude, info->lat,
+                info->longitude, info->lon, info->speed, info->track);
+    }
+    else if( info->status == 'V' )
+    {
+        log_info("NMEA0183: Invalid data\n");
+    }
+
+    return 0;
+}
diff --git a/project/gpsd/makefile b/project/gpsd/makefile
new file mode 100644
index 0000000..6e48675
--- /dev/null
+++ b/project/gpsd/makefile
@@ -0,0 +1,62 @@
+#********************************************************************************
+#      Copyright:  (C) 2023 LingYun IoT System Studio
+#                  All rights reserved.
+#
+#       Filename:  Makefile
+#    Description:  This file is the project top Makefie
+#
+#        Version:  1.0.0(11/08/23)
+#         Author:  Guo Wenxue <guowenxue@gmail.com>
+#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+PRJ_PATH=$(shell pwd)
+APP_NAME = gpsd
+INST_PATH= /tftp
+
+BUILD_ARCH=$(shell uname -m)
+ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
+	CROSS_COMPILE=arm-linux-gnueabihf-
+endif
+
+# C source files in top-level directory
+SRCFILES = $(wildcard *.c)
+
+# common CFLAGS for our source code
+CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized -D_GNU_SOURCE
+
+# C source file in sub-directory
+DIRS= booster
+DIRS_PATH=$(patsubst %,${PRJ_PATH}/%,$(DIRS))
+CFLAGS+=$(patsubst %,-I%,$(DIRS_PATH))
+LDFLAGS+=$(patsubst %,-L%,$(DIRS_PATH))
+LIBS=$(patsubst %,-l%,$(DIRS))
+LDFLAGS+=${LIBS}
+
+LDFLAGS+=-lpthread
+
+.PHONY:libs
+all: entry modules binary
+
+entry:
+	@echo "Building ${APP_NAME} on ${BUILD_ARCH}"
+	@if [ ! -e booster ] ; then ln -s ../booster; fi
+
+modules:
+	@set -e; for d in ${DIRS}; do $(MAKE) CROSS_COMPILE=${CROSS_COMPILE} CFLAGS="${CFLAGS}" -C $${d}; done
+
+binary:  ${SRCFILES}
+	$(CROSS_COMPILE)gcc $(CFLAGS) -o ${APP_NAME} $^ ${LDFLAGS}
+	@echo " Compile over"
+
+clean:
+	set -e; for d in ${DIRS}; do $(MAKE) clean -C $${d}; done
+	@rm -f *.o $(APP_NAME)
+
+distclean: clean
+	@rm -rf cscope* tags
+	@rm -f booster
+
+install:
+	cp ${APP_NAME} ${INST_PATH}
diff --git a/project/modules/ds18b20.c b/project/modules/ds18b20.c
new file mode 100644
index 0000000..5f279fe
--- /dev/null
+++ b/project/modules/ds18b20.c
@@ -0,0 +1,119 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  ds18b20.c
+ *    Description:  This file is temperature sensor DS18B20 code
+ *
+ *        Version:  1.0.0(2023/8/10)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2023/8/10 12:13:26"
+ *
+ * Pin connection:
+ *
+ *               DS18B20 Module          Raspberry Pi Board
+ *                   VCC      <----->      #Pin1(3.3V)
+ *                   DQ       <----->      #Pin7(BCM GPIO4)
+ *                   GND      <----->      GND
+ *
+ * /boot/config.txt:
+ *
+ *          dtoverlay=w1-gpio-pullup,gpiopin=4
+ *
+ ********************************************************************************/
+
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+
+#include "logger.h"
+
+/* File Content:
+   pi@raspberrypi:~/guowenxue $ cat /sys/bus/w1/devices/28-041731f7c0ff/w1_slave
+   3a 01 4b 46 7f ff 0c 10 a5 : crc=a5 YES
+   3a 01 4b 46 7f ff 0c 10 a5 t=19625
+   */
+
+int ds18b20_get_temperature(float *temp)
+{
+    char            w1_path[50] = "/sys/bus/w1/devices/";
+    char            chip[20];
+    char            buf[128];
+    DIR            *dirp;
+    struct dirent  *direntp;
+    int             fd =-1;
+    char           *ptr;
+    float           value;
+    int             found = 0;
+
+    if( !temp )
+    {
+        return -1;
+    }
+
+    /*+-------------------------------------------------------------------+
+     *|  open dierectory /sys/bus/w1/devices to get chipset Serial Number |
+     *+-------------------------------------------------------------------+*/
+    if((dirp = opendir(w1_path)) == NULL)
+    {
+        log_error("opendir error: %s\n", strerror(errno));
+        return -2;
+    }
+
+    while((direntp = readdir(dirp)) != NULL)
+    {
+        if(strstr(direntp->d_name,"28-"))
+        {
+            /* find and get the chipset SN filename */
+            strcpy(chip,direntp->d_name);
+            found = 1;
+            break;
+        }
+    }
+    closedir(dirp);
+
+    if( !found )
+    {
+        log_error("Can not find ds18b20 in %s\n", w1_path);
+        return -3;
+    }
+
+    /* get DS18B20 sample file full path: /sys/bus/w1/devices/28-xxxx/w1_slave */
+    strncat(w1_path, chip, sizeof(w1_path)-strlen(w1_path));
+    strncat(w1_path, "/w1_slave", sizeof(w1_path)-strlen(w1_path));
+
+    /* open file /sys/bus/w1/devices/28-xxxx/w1_slave to get temperature */
+    if( (fd=open(w1_path, O_RDONLY)) < 0 )
+    {
+        log_error("open %s error: %s\n", w1_path, strerror(errno));
+        return -4;
+    }
+
+    if(read(fd, buf, sizeof(buf)) < 0)
+    {
+        log_error("read %s error: %s\n", w1_path, strerror(errno));
+        return -5;
+    }
+
+    ptr = strstr(buf, "t=");
+    if( !ptr )
+    {
+        log_error("ERROR: Can not get temperature\n");
+        return -6;
+    }
+
+    ptr+=2;
+
+    /* convert string value to float value */
+    *temp = atof(ptr)/1000;
+
+    close(fd);
+
+    return 0;
+}
diff --git a/project/modules/ds18b20.h b/project/modules/ds18b20.h
new file mode 100644
index 0000000..e756134
--- /dev/null
+++ b/project/modules/ds18b20.h
@@ -0,0 +1,31 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  ds18b20.h
+ *    Description:  This file is temperature sensor DS18B20 code
+ *
+ *        Version:  1.0.0(2023/8/10)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2023/8/10 12:13:26"
+ *
+ * Pin connection:
+ *
+ *               DS18B20 Module          Raspberry Pi Board
+ *                   VCC      <----->      #Pin1(3.3V)
+ *                   DQ       <----->      #Pin7(BCM GPIO4)
+ *                   GND      <----->      GND
+ *
+ * /boot/config.txt:
+ *
+ *          dtoverlay=w1-gpio-pullup,gpiopin=4
+ *
+ ********************************************************************************/
+
+#ifndef  _DS18B20_H_
+#define  _DS18B20_H_
+
+extern int ds18b20_get_temperature(float *temp);
+
+#endif   /* ----- #ifndef _DS18B20_H_  ----- */
+
diff --git a/project/modules/leds.c b/project/modules/leds.c
new file mode 100644
index 0000000..2e46182
--- /dev/null
+++ b/project/modules/leds.c
@@ -0,0 +1,162 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  led.c
+ *    Description:  This file is used to control RGB 3-colors LED
+ *                  compatible with libgpiod-1.x.x, not compatible with 2.x.x
+ *
+ *
+ * Pin connection:
+ *               RGB Led Module           Raspberry Pi Board
+ *                   R        <----->      #Pin33(BCM GPIO13)
+ *                   G        <----->      #Pin35(BCM GPIO19)
+ *                   B        <----->      #Pin37(BCM GPIO26)
+ *                  GND       <----->      GND
+ *
+ * System install:
+ *                  sudo apt install -y libgpiod-dev gpiod
+ *
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+
+#include <gpiod.h>
+#include "logger.h"
+#include "leds.h"
+
+#define DELAY     500
+
+static led_info_t    leds_info[LED_CNT] =
+{
+    {"red",   13, 1, NULL },
+    {"green", 19, 1, NULL },
+    {"blue",  26, 1, NULL },
+};
+
+int init_led(led_ctx_t *ctx, int which)
+{
+    int            i, rv;
+    led_info_t    *led;
+
+    if( !ctx )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    ctx->leds = leds_info;
+    ctx->count = LED_CNT;
+
+    ctx->chip = gpiod_chip_open_by_name("gpiochip0");
+    if( !ctx->chip )
+    {
+        log_error("open gpiochip failure, maybe you need running as root\n");
+        return -2;
+    }
+
+	for(i=0; i<ctx->count; i++)
+	{
+		if( which == i )
+		{
+			led = &ctx->leds[i];
+
+			led->line = gpiod_chip_get_line(ctx->chip, led->gpio);
+			if( !led->line )
+			{
+				log_error("open gpioline for %s[%d] failed\n", led->name, led->gpio);
+				rv = -3;
+				goto failed;
+			}
+
+			rv  = gpiod_line_request_output(led->line, led->name, !led->active);
+			if( rv )
+			{
+				log_error("request gpio output for %5s[%d] failed: %s\n", led->name, led->gpio, strerror(errno));
+				rv = -4;
+				goto failed;
+			}
+
+			log_debug("request %5s led[%d] for gpio output okay\n", led->name, led->gpio);
+		}
+	}
+
+    return 0;
+
+failed:
+    term_led(ctx, which);
+    return rv;
+}
+
+int term_led(led_ctx_t *ctx, int which)
+{
+    int            i;
+    led_info_t    *led;
+
+    log_warn("terminate RGB Led gpios\n");
+
+    if( !ctx )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( !ctx->chip )
+        return 0;
+
+    for(i=0; i<ctx->count; i++)
+	{
+		if( which == i )
+		{
+			led = &ctx->leds[i];
+
+			if( led->line )
+				gpiod_line_release(led->line);
+		}
+	}
+
+    gpiod_chip_close(ctx->chip);
+    return 0;
+}
+
+int turn_led(int which, int cmd)
+{
+    int            rv = 0;
+    led_ctx_t      ctx;
+    led_info_t    *led;
+
+    if( which<0 || which>=LED_CNT )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( (rv=init_led(&ctx, which)) < 0 )
+    {
+        log_error("Initial RGB leds failure, rv=%d\n", rv);
+        return -2;
+    }
+
+    led = &ctx.leds[which];
+
+    if( OFF == cmd )
+    {
+        gpiod_line_set_value(led->line, !led->active);
+    }
+    else
+    {
+        gpiod_line_set_value(led->line, led->active);
+    }
+
+    term_led(&ctx, which);
+    return 0;
+}
diff --git a/project/modules/leds.h b/project/modules/leds.h
new file mode 100644
index 0000000..ddf1518
--- /dev/null
+++ b/project/modules/leds.h
@@ -0,0 +1,58 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  led.h
+ *    Description:  This file is used to control RGB 3-colors LED
+ *
+ *
+ * Pin connection:
+ *               RGB Led Module           Raspberry Pi Board
+ *                   R        <----->      #Pin33(BCM GPIO13)
+ *                   G        <----->      #Pin35(BCM GPIO19)
+ *                   B        <----->      #Pin37(BCM GPIO26)
+ *                  GND       <----->      GND
+ *
+ * System install:
+ *                  sudo apt install -y libgpiod-dev gpiod
+ *
+ *
+ ********************************************************************************/
+
+#ifndef  _LEDS_H_
+#define  _LEDS_H_
+
+#define ON        1
+#define OFF       0
+
+/* Three LEDs code */
+enum
+{
+    LED_R = 0,
+    LED_G,
+    LED_B,
+    LED_CNT,
+};
+
+/* Three LEDs hardware information */
+typedef struct led_info_s
+{
+    const char         *name;  /* RGB 3-color LED name  */
+    int                 gpio;  /* RGB 3-color LED BCM pin number */
+    int                 active;/* RGB 3-color LED active GPIO level: 0->low 1->high */
+    struct gpiod_line  *line;  /* libgpiod line */
+} led_info_t;
+
+/* Three LEDs API context */
+typedef struct led_ctx_s
+{
+    struct gpiod_chip   *chip;
+    led_info_t          *leds;
+    int                  count;
+} led_ctx_t;
+
+extern int init_led(led_ctx_t *ctx, int which);
+extern int term_led(led_ctx_t *ctx, int which);
+extern int turn_led(int which, int cmd);
+
+#endif   /* ----- #ifndef _LEDS_H_  ----- */
diff --git a/project/modules/makefile b/project/modules/makefile
new file mode 100644
index 0000000..1b2a503
--- /dev/null
+++ b/project/modules/makefile
@@ -0,0 +1,38 @@
+#********************************************************************************
+#      Copyright:  (C) 2023 LingYun IoT System Studio
+#                  All rights reserved.
+#
+#       Filename:  Makefile
+#    Description:  This file used compile all the source code to static library
+#
+#        Version:  1.0.0(11/08/23)
+#         Author:  Guo Wenxue <guowenxue@gmail.com>
+#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+PWD=$(shell pwd)
+LIBNAME=$(shell basename ${PWD} )
+TOPDIR=$(shell dirname ${PWD} )
+
+BUILD_ARCH=$(shell uname -m)
+ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
+    CROSS_COMPILE?=arm-linux-gnueabihf-
+endif
+
+OPENLIBS_INCPATH=${TOPDIR}/openlibs/install/include
+OPENLIBS_LIBPATH=${TOPDIR}/openlibs/install/lib
+
+CFLAGS+=-I${OPENLIBS_INCPATH} -I${TOPDIR}/booster
+
+all: clean
+	@rm -f *.o
+	${CROSS_COMPILE}gcc ${CFLAGS} -c *.c
+	${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
+
+clean:
+	@rm -f *.o
+	@rm -f *.a
+
+distclean:
+	@make clean
diff --git a/project/modules/modules.h b/project/modules/modules.h
new file mode 100644
index 0000000..2e2c32c
--- /dev/null
+++ b/project/modules/modules.h
@@ -0,0 +1,24 @@
+/********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  modules.h
+ *    Description:  This file 
+ *
+ *        Version:  1.0.0(08/17/2023)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "08/17/2023 09:27:25 PM"
+ *                 
+ ********************************************************************************/
+
+#ifndef  _MODULES_H_
+#define  _MODULES_H_
+
+#include "ds18b20.h"
+#include "leds.h"
+#include "pwm.h"
+#include "relay.h"
+#include "sht20.h"
+#include "tsl2561.h"
+
+#endif   /* ----- #ifndef _MODULES_H_  ----- */
diff --git a/project/modules/pwm.c b/project/modules/pwm.c
new file mode 100644
index 0000000..922ccbf
--- /dev/null
+++ b/project/modules/pwm.c
@@ -0,0 +1,213 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  pwm.c
+ *    Description:  This file is used to control PWM buzzer/Led
+ *
+ * Pin connection:
+ *               PWM Module              Raspberry Pi Board
+ *                  VCC       <----->      5V
+ *                 buzzer     <----->      #Pin32(BCM GPIO12)
+ *                  Led       <----->      #Pin33(BCM GPIO13)
+ *                  GND       <----->      GND
+ *
+ * /boot/config.txt:
+ *
+ *          dtoverlay=pwm,pin=12,func=4 (Buzzer)
+ *          dtoverlay=pwm,pin=13,func=4 (Led)
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include "logger.h"
+#include "util_proc.h"
+#include "pwm.h"
+
+/* check PWM $channel export or not */
+int check_pwm(int channel)
+{
+	char          path[256];
+
+	/* check /sys/class/pwm/pwmchip0/pwmN exist or not */
+	snprintf(path, sizeof(path), "%s/pwm%d", PWMCHIP_PATH, channel);
+	return access(path, F_OK) ? 0 : 1;
+}
+
+/* export($export=1)/unexport($export=0) PWM $channel */
+int export_pwm(int channel, int export)
+{
+	int           fd;
+	char          buf[32];
+	char          path[256];
+
+	if( export && check_pwm(channel) )
+		return 0; /* export already when export  */
+	else if( !export && !check_pwm(channel) )
+		return 0; /* unexport already when unexport  */
+
+	/* export PWM channel by echo N > /sys/class/pwm/pwmchip0/export */
+	snprintf(path, sizeof(path), "%s/%s", PWMCHIP_PATH, export?"export":"unexport");
+	if( (fd=open(path, O_WRONLY)) < 0 )
+	{
+		log_error("open '%s' failed: %s\n", path, strerror(errno));
+		return -3;
+	}
+
+	snprintf(buf, sizeof(buf), "%d", channel);
+	write(fd, buf, strlen(buf));
+
+	msleep(100);
+
+	if( export && check_pwm(channel) )
+		return 0; /* export already when export  */
+	else if( !export && !check_pwm(channel) )
+		return 0; /* unexport already when unexport  */
+
+	return -4;
+}
+
+/* configure PWM $channel */
+int config_pwm(int channel, int freq, int duty) 
+{
+	int           fd;
+	char          buf[32];
+	char          path[256];
+	int           period;
+	int           duty_cycle;
+
+	if( !check_pwm(channel) )
+		return -2;
+
+	/* open PWM period file and write period in ns */
+	snprintf(path, sizeof(path), "%s/pwm%d/period", PWMCHIP_PATH, channel);
+	if( (fd=open(path, O_WRONLY)) < 0 )
+	{
+		log_error("open '%s' failed: %s\n", path, strerror(errno));
+		return -3;
+	}
+	period = 1000000000/freq;
+	snprintf(buf, sizeof(buf), "%d", period);
+	write(fd, buf, strlen(buf));
+
+	/* open PWM duty_cycle file and write duty */
+	snprintf(path, sizeof(path), "%s/pwm%d/duty_cycle", PWMCHIP_PATH, channel);
+	if( (fd=open(path, O_WRONLY)) < 0 )
+	{
+		log_error("open '%s' failed: %s\n", path, strerror(errno));
+		return -3;
+	}
+	duty_cycle = (period*duty) / 100;
+	snprintf(buf, sizeof(buf), "%d", duty_cycle);
+	write(fd, buf, strlen(buf));
+
+	return 0;
+}
+
+int init_pwm(int channel, int freq, int duty)
+{
+	int           rv;
+	char          buf[32];
+	char          path[256];
+
+	if( (rv=export_pwm(channel, 1)) )
+	{
+		log_error("export PWM channel[%d] failed, rv=%d\n", channel, rv);
+		return rv; 
+	}
+
+	if( (rv=config_pwm(channel, freq, duty)) )
+	{
+		log_error("config PWM channel[%d] failed, rv=%d\n", channel, rv);
+		return rv;
+	}
+
+	log_debug("config pwm%d with freq[%d] duty[%d] okay\n", channel, freq, duty);
+
+	return 0;
+}
+
+int turn_pwm(int channel, int status)
+{
+	int           fd;
+	char          buf[32];
+	char          path[256];
+
+	if( !check_pwm(channel) )
+		return -1;
+
+	/* open PWM enable file and enable(1)/disable(0) it */
+	snprintf(path, sizeof(path), "%s/pwm%d/enable", PWMCHIP_PATH, channel);
+	if( (fd=open(path, O_WRONLY)) < 0 )
+	{
+		log_error("open '%s' failed: %s\n", path, strerror(errno));
+		return -3;
+	}
+	snprintf(buf, sizeof(buf), "%d", status?1:0);
+	write(fd, buf, strlen(buf));
+
+	log_debug("pwm[%d] %s\n", channel, status?"enable":"disable");
+
+	return 0;
+}
+
+int term_pwm(int channel)
+{
+	if( !check_pwm(channel) )
+		return 0;
+
+	turn_pwm(channel, DISABLE);
+	export_pwm(channel, 0);
+
+	return 0;
+}
+
+int turn_beep(int times)
+{
+	int           rv;
+
+	/* stop beeper beep */
+	if(times == 0)
+	{
+		log_debug("Stop beeper\n");
+		term_pwm(CHN_BEEPER);
+		return 0;
+	}
+
+	rv = init_pwm(CHN_BEEPER, FRQ_BEEPER, 50);
+	if(rv < 0)
+	{
+		log_error("Initial beeper pwm[%d] failed, rv=%d\n", CHN_BEEPER);
+		return -2;
+	}
+
+	/* turn beeper beep ceaselessly */
+	if( times < 0)
+	{
+		turn_pwm(CHN_BEEPER, ENABLE);
+		return 0;
+	}
+
+	while( times-- )
+	{
+		turn_pwm(CHN_BEEPER, ENABLE);
+		msleep(800);
+
+		turn_pwm(CHN_BEEPER, DISABLE);
+		msleep(800);
+	}
+	
+	term_pwm(CHN_BEEPER);
+	return 0;
+}
diff --git a/project/modules/pwm.h b/project/modules/pwm.h
new file mode 100644
index 0000000..1e07f6a
--- /dev/null
+++ b/project/modules/pwm.h
@@ -0,0 +1,44 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  pwm.h
+ *    Description:  This file is used to control PWM buzzer/Led
+ *
+ * Pin connection:
+ *               PWM Module              Raspberry Pi Board
+ *                  VCC       <----->      5V
+ *                 buzzer     <----->      #Pin32(BCM GPIO12)
+ *                  Led       <----->      #Pin33(BCM GPIO13)
+ *                  GND       <----->      GND
+ *
+ * /boot/config.txt:
+ *
+ *          dtoverlay=pwm,pin=12,func=4 (Buzzer)
+ *          dtoverlay=pwm,pin=13,func=4 (Led)
+ *
+ ********************************************************************************/
+
+
+#ifndef  _PWM_H_
+#define  _PWM_H_
+
+#define PWMCHIP_PATH      "/sys/class/pwm/pwmchip0"
+
+#define ENABLE            1
+#define DISABLE           0
+
+#define CHN_BEEPER        0
+#define FRQ_BEEPER        2700
+
+#define CHN_RGBLED        1
+#define FRQ_RGBLED        100
+
+extern int init_pwm(int channel, int freq, int duty);
+extern int turn_pwm(int channel, int status);
+extern int term_pwm(int channel);
+
+extern int turn_beep(int times);
+
+#endif   /* ----- #ifndef _PWM_H_  ----- */
+
diff --git a/project/modules/relay.c b/project/modules/relay.c
new file mode 100644
index 0000000..35695ae
--- /dev/null
+++ b/project/modules/relay.c
@@ -0,0 +1,156 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  relay.c
+ *    Description:  This file is used to control Relay
+ *
+ *
+ * Pin connection:
+ *                 Relay Module           Raspberry Pi Board
+ *                  VCC       <----->      5V
+ *                   I        <----->      #Pin16(BCM GPIO23)
+ *                  GND       <----->      GND
+ *
+ * System install:
+ *                  sudo apt install -y libgpiod-dev gpiod
+ *
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <dirent.h>
+#include <string.h>
+#include <time.h>
+#include <errno.h>
+#include <signal.h>
+#include <getopt.h>
+#include <libgen.h>
+
+#include <gpiod.h>
+#include "logger.h"
+#include "relay.h"
+
+#define DELAY     500
+
+static relay_info_t    relay_info[RELAY_CNT] =
+{
+    {"relay1",  23, 1, NULL },
+};
+
+int init_relay(relay_ctx_t *ctx)
+{
+    int            i, rv;
+    relay_info_t  *relay;
+
+    if( !ctx )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+	ctx->relay = relay_info;
+	ctx->count = RELAY_CNT;
+
+    ctx->chip = gpiod_chip_open_by_name("gpiochip0");
+    if( !ctx->chip )
+    {
+        log_error("open gpiochip failure, maybe you need running as root\n");
+        return -2;
+    }
+
+
+    for(i=0; i<ctx->count; i++)
+    {
+        relay = &ctx->relay[i];
+
+        relay->line = gpiod_chip_get_line(ctx->chip, relay->gpio);
+        if( !relay->line )
+        {
+            log_error("open gpioline for %s[%d] failed\n", relay->name, relay->gpio);
+            rv = -3;
+            goto failed;
+        }
+
+        rv  = gpiod_line_request_output(relay->line, relay->name, !relay->active);
+        if( rv )
+        {
+            log_error("request gpio output for %5s[%d] failed\n", relay->name, relay->gpio);
+            rv = -4;
+            goto failed;
+        }
+
+        log_debug("request %s[%d] for gpio output okay\n", relay->name, relay->gpio);
+    }
+
+    return 0;
+
+failed:
+    term_relay(ctx);
+    return rv;
+}
+
+int term_relay(relay_ctx_t *ctx)
+{
+    int            i;
+    relay_info_t  *relay;
+
+    if( !ctx )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( !ctx->chip )
+        return 0;
+
+    for(i=0; i<ctx->count; i++)
+    {
+        relay = &ctx->relay[i];
+
+        if( relay->line )
+            gpiod_line_release(relay->line);
+    }
+
+    gpiod_chip_close(ctx->chip);
+    return 0;
+}
+
+
+int turn_relay(int which, int cmd)
+{
+    int             rv = 0;
+    relay_info_t   *relay;
+    relay_ctx_t     ctx;
+
+    if( which<0 || which>=RELAY_CNT )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+	if( (rv=init_relay(&ctx)) < 0 )
+	{
+		log_error("Initial relay failure, rv=%d\n", rv);
+		return -2;
+	}
+
+    relay = &ctx.relay[which];
+
+    if( OFF == cmd )
+    {
+        gpiod_line_set_value(relay->line, !relay->active);
+    }
+    else
+    {
+        gpiod_line_set_value(relay->line, relay->active);
+    }
+
+	term_relay(&ctx);
+
+    return 0;
+}
+
diff --git a/project/modules/relay.h b/project/modules/relay.h
new file mode 100644
index 0000000..c1d8c1d
--- /dev/null
+++ b/project/modules/relay.h
@@ -0,0 +1,56 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  relay.c
+ *    Description:  This file is used to control Relay
+ *
+ *
+ * Pin connection:
+ *                 Relay Module           Raspberry Pi Board
+ *                  VCC       <----->      5V
+ *                   I        <----->      #Pin16(BCM GPIO23)
+ *                  GND       <----->      GND
+ *
+ * System install:
+ *                  sudo apt install -y libgpiod-dev gpiod
+ *
+ *
+ ********************************************************************************/
+
+#ifndef  _RELAY_H_
+#define  _RELAY_H_
+
+#define ON        1
+#define OFF       0
+
+/* relay code */
+enum
+{
+    RELAY1 = 0,
+    RELAY_CNT,
+};
+
+/* Relay hardware information */
+typedef struct relay_info_s
+{
+    const char         *name;  /* Relay name  */
+    int                 gpio;  /* Relay BCM pin number */
+    int                 active;/* Relay active GPIO level: 0->low 1->high */
+    struct gpiod_line  *line;  /* libgpiod line */
+} relay_info_t;
+
+/* Relay API context */
+typedef struct relay_ctx_s
+{
+    struct gpiod_chip   *chip;
+    relay_info_t        *relay;
+    int                  count;
+} relay_ctx_t;
+
+extern int init_relay(relay_ctx_t *ctx);
+extern int term_relay(relay_ctx_t *ctx);
+extern int turn_relay(int which, int cmd);
+
+#endif   /* ----- #ifndef _RELAY_H_  ----- */
+
diff --git a/project/modules/sht20.c b/project/modules/sht20.c
new file mode 100644
index 0000000..22a2ac6
--- /dev/null
+++ b/project/modules/sht20.c
@@ -0,0 +1,244 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  sht20.c
+ *    Description:  This file is temperature and relative humidity sensor SHT20 code
+ *
+ *        Version:  1.0.0(10/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
+ *
+ * Pin connection:
+ *               STH20 Module            Raspberry Pi Board
+ *                   VCC      <----->      #Pin1(3.3V)
+ *                   SDA1     <----->      #Pin3(SDA, BCM GPIO2)
+ *                   SCL1     <----->      #Pin5(SCL, BCM GPIO3)
+ *                   GND      <----->      GND
+ *
+ * /boot/config.txt:
+ *                  dtoverlay=i2c1,pins_2_3
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdint.h>
+#include <string.h>
+#include <unistd.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <time.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <linux/types.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+
+#include "logger.h"
+#include "util_proc.h"
+#include "sht20.h"
+
+int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len);
+int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size);
+int sht20_checksum(uint8_t *data, int len, int8_t checksum);
+
+int sht2x_get_temp_humidity(float *temp, float *rh)
+{
+    int             fd;
+	int             rv = 0;
+    char           *dev = SHT20_I2CDEV;
+    uint8_t         buf[4];
+
+	if( !temp || !rh )
+	{
+		log_error("Invalid input arguments\n");
+		return -1;
+	}
+
+    /*+--------------------------------+
+     *|     open /dev/i2c-x device     |
+     *+--------------------------------+*/
+    if( (fd=open(dev, O_RDWR)) < 0)
+    {
+        log_error("i2c device '%s' open failed: %s\n", dev, strerror(errno));
+        return -2;
+    }
+
+    /*+--------------------------------+
+     *|   software reset SHT20 sensor  |
+     *+--------------------------------+*/
+
+    buf[0] = 0xFE;
+    i2c_write(fd, SHT20_I2CADDR, buf, 1);
+
+    msleep(50);
+
+
+    /*+--------------------------------+
+     *|   trigger temperature measure  |
+     *+--------------------------------+*/
+
+    buf[0] = 0xF3;
+    i2c_write(fd, SHT20_I2CADDR, buf, 1);
+
+    msleep(85); /* datasheet: typ=66, max=85 */
+
+    memset(buf, 0, sizeof(buf));
+    i2c_read(fd, SHT20_I2CADDR, buf, 3);
+    log_dump(LOG_LEVEL_DEBUG, "Temperature sample data: ", buf, 3);
+
+    if( !sht20_checksum(buf, 2, buf[2]) )
+    {
+		rv = -3;
+        log_error("Temperature sample data CRC checksum failure.\n");
+        goto cleanup;
+    }
+
+    *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
+
+
+    /*+--------------------------------+
+     *|    trigger humidity measure    |
+     *+--------------------------------+*/
+
+    buf[0] = 0xF5;
+    i2c_write(fd, SHT20_I2CADDR, buf, 1);
+
+    msleep(29); /* datasheet: typ=22, max=29 */
+
+    memset(buf, 0, sizeof(buf));
+    i2c_read(fd, SHT20_I2CADDR, buf, 3);
+    log_dump(LOG_LEVEL_DEBUG, "Relative humidity sample data: ", buf, 3);
+
+    if( !sht20_checksum(buf, 2, buf[2]) )
+    {
+		rv = -4;
+        log_error("Relative humidity sample data CRC checksum failure.\n");
+        goto cleanup;
+    }
+
+    *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
+
+    /*+--------------------------------+
+     *|    print the measure result    |
+     *+--------------------------------+*/
+
+    log_debug("Temperature=%lf 'C relative humidity=%lf%%\n", *temp, *rh);
+
+cleanup:
+    close(fd);
+    return rv;
+}
+
+int sht20_checksum(uint8_t *data, int len, int8_t checksum)
+{
+    int8_t crc = 0;
+    int8_t bit;
+    int8_t byteCtr;
+
+    //calculates 8-Bit checksum with given polynomial: x^8 + x^5 + x^4 + 1
+    for (byteCtr = 0; byteCtr < len; ++byteCtr)
+    {
+        crc ^= (data[byteCtr]);
+        for ( bit = 8; bit > 0; --bit)
+        {
+            /* x^8 + x^5 + x^4 + 1 = 0001 0011 0001 = 0x131 */
+            if (crc & 0x80) crc = (crc << 1) ^ 0x131;
+            else crc = (crc << 1);
+        }
+    }
+
+    if (crc != checksum)
+        return 0;
+    else
+        return 1;
+}
+
+int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len)
+{
+    struct i2c_rdwr_ioctl_data i2cdata;
+    int rv = 0;
+
+    if( !data || len<= 0)
+    {
+        log_error("%s() invalid input arguments!\n", __func__);
+        return -1;
+    }
+
+    i2cdata.nmsgs = 1;
+    i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs );
+    if ( !i2cdata.msgs )
+    {
+        log_error("%s() msgs malloc failed!\n", __func__);
+        return -2;
+    }
+
+    i2cdata.msgs[0].addr = slave_addr;
+    i2cdata.msgs[0].flags = 0; //write
+    i2cdata.msgs[0].len = len;
+    i2cdata.msgs[0].buf = malloc(len);
+    if( !i2cdata.msgs[0].buf )
+    {
+        log_error("%s() msgs malloc failed!\n", __func__);
+        rv = -3;
+        goto cleanup;
+    }
+    memcpy(i2cdata.msgs[0].buf, data, len);
+
+
+    if( ioctl(fd, I2C_RDWR, &i2cdata)<0 )
+    {
+        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        rv = -4;
+        goto cleanup;
+    }
+
+cleanup:
+    if( i2cdata.msgs[0].buf )
+        free(i2cdata.msgs[0].buf);
+
+    if( i2cdata.msgs )
+        free(i2cdata.msgs);
+
+    return rv;
+}
+
+int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size)
+{
+    struct i2c_rdwr_ioctl_data i2cdata;
+    int rv = 0;
+
+    if( !buf || size<= 0)
+    {
+        log_error("%s() invalid input arguments!\n", __func__);
+        return -1;
+    }
+
+    i2cdata.nmsgs = 1;
+    i2cdata.msgs = malloc( sizeof(struct i2c_msg)*i2cdata.nmsgs );
+    if ( !i2cdata.msgs )
+    {
+        log_error("%s() msgs malloc failed!\n", __func__);
+        return -2;
+    }
+
+    i2cdata.msgs[0].addr = slave_addr;
+    i2cdata.msgs[0].flags = I2C_M_RD; //read
+    i2cdata.msgs[0].len = size;
+    i2cdata.msgs[0].buf = buf;
+    memset(buf, 0, size);
+
+    if( ioctl(fd, I2C_RDWR, &i2cdata)<0 )
+    {
+        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        rv = -4;
+    }
+
+    free( i2cdata.msgs );
+    return rv;
+}
diff --git a/project/modules/sht20.h b/project/modules/sht20.h
new file mode 100644
index 0000000..6c9f9f9
--- /dev/null
+++ b/project/modules/sht20.h
@@ -0,0 +1,34 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  sht20.c
+ *    Description:  This file is temperature and relative humidity sensor SHT20 code
+ *
+ *        Version:  1.0.0(10/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
+ *
+ * Pin connection:
+ *               STH20 Module            Raspberry Pi Board
+ *                   VCC      <----->      #Pin1(3.3V)
+ *                   SDA      <----->      #Pin3(SDA, BCM GPIO2)
+ *                   SCL      <----->      #Pin5(SCL, BCM GPIO3)
+ *                   GND      <----->      GND
+ *
+ * /boot/config.txt:
+ *                  dtoverlay=i2c1,pins_2_3
+ *
+ ********************************************************************************/
+
+
+#ifndef  _SHT20_H_
+#define  _SHT20_H_
+
+#define SHT20_I2CDEV           "/dev/i2c-1"
+#define SHT20_I2CADDR          0x40
+
+extern int sht2x_get_temp_humidity(float *temp, float *rh);
+
+#endif   /* ----- #ifndef _SHT20_H_  ----- */
+
diff --git a/project/modules/tsl2561.c b/project/modules/tsl2561.c
new file mode 100644
index 0000000..4148d74
--- /dev/null
+++ b/project/modules/tsl2561.c
@@ -0,0 +1,209 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  tsl2561.c
+ *    Description:  This file is the Lux sensor TSL2561 code
+ *
+ *        Version:  1.0.0(10/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
+ *
+ * Pin connection:
+ *               STH20 Module            Raspberry Pi Board
+ *                   VCC      <----->      #Pin1(3.3V)
+ *                   SDA0     <----->      #Pin27(SDA, BCM GPIO0)
+ *                   SCL0     <----->      #Pin28(SCL, BCM GPIO1)
+ *                   GND      <----->      GND
+ *
+ * /boot/config.txt:
+ *                  dtoverlay=i2c0,pins_0_1
+ *
+ ********************************************************************************/
+
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <time.h>
+#include <errno.h>
+#include <libgen.h>
+#include <getopt.h>
+#include <fcntl.h>
+#include <sys/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "util_proc.h"
+#include "logger.h"
+#include "tsl2561.h"
+
+
+#define CONTROL_REG                     0x80
+#define REG_COUNT                       4
+
+#define POWER_UP                        0x03
+#define POWER_DOWN                      0x00
+
+#define OFF                             0
+#define ON                              1
+
+/* Register Address  */
+enum
+{
+    /* Channel_0 = DATA0HIGH<<8 + DATA0LOW */
+    DATA0LOW = 0x8C,
+    DATA0HIGH,
+
+    /* Channel_1 = DATA1HIGH<<8 + DATA1LOW */
+    DATA1LOW,
+    DATA1HIGH,
+};
+
+static const int  regs_addr[REG_COUNT]={DATA0LOW, DATA0HIGH, DATA1LOW, DATA1HIGH};
+
+void tsl2561_power(int fd, int cmd)
+{
+    struct i2c_msg               msg;
+    struct i2c_rdwr_ioctl_data   data;
+    unsigned char                buf[2];
+
+    msg.addr= TSL2561_I2CADDR;
+    msg.flags=0;  /* write */
+    msg.len= 1;
+    msg.buf= buf;
+
+    data.nmsgs= 1;
+    data.msgs= &msg;
+
+    msg.buf[0]=CONTROL_REG;
+    if( ioctl(fd, I2C_RDWR, &data) < 0 )
+    {
+        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return ;
+    }
+
+
+    if( cmd )
+        msg.buf[0]=POWER_UP;
+    else
+        msg.buf[0]=POWER_DOWN;
+
+    if( ioctl(fd, I2C_RDWR, &data) < 0 )
+    {
+        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return ;
+    }
+
+    return ;
+}
+
+int tsl2561_readreg(int fd, unsigned char regaddr, unsigned char *regval)
+{
+    struct i2c_msg               msg;
+    struct i2c_rdwr_ioctl_data   data;
+    unsigned char                buf[2];
+
+    msg.addr= TSL2561_I2CADDR;
+    msg.flags=0;  /* write */
+    msg.len= 1;
+    msg.buf= buf;
+    msg.buf[0] = regaddr;
+
+    data.nmsgs= 1;
+    data.msgs= &msg;
+
+    if( ioctl(fd, I2C_RDWR, &data) < 0 )
+    {
+        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return -1;
+    }
+
+    memset(buf, 0, sizeof(buf));
+
+    msg.addr= TSL2561_I2CADDR;
+    msg.flags=I2C_M_RD;  /* read */
+    msg.len= 1;
+    msg.buf= buf;
+
+    data.nmsgs= 1;
+    data.msgs= &msg;
+
+    if( ioctl(fd, I2C_RDWR, &data) < 0 )
+    {
+        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return -1;
+    }
+
+    *regval = msg.buf[0];
+    return 0;
+}
+
+int tsl2561_get_lux(float *lux)
+{
+    int                 i, fd;
+	int                 rv = 0;
+    char               *dev = TSL2561_I2CDEV;
+    float               div = 0.0;
+
+    unsigned char       reg_data[REG_COUNT];
+    unsigned char       buf;
+    int                 chn0_data = 0;
+    int                 chn1_data = 0;
+
+	if( !lux )
+	{
+		log_error("Invalid input arguments\n");
+		return -1;
+	}
+
+    if( (fd=open(dev, O_RDWR)) < 0)
+    {
+        log_error("i2c device '%s' open failed: %s\n", dev, strerror(errno));
+        return -2;
+    }
+
+    tsl2561_power(fd, ON);
+
+    msleep(410);  /* t(CONV) MAX 400ms */
+
+    /* Read register Channel0 and channel1 data from register */
+    for(i=0; i<REG_COUNT; i++)
+    {
+        tsl2561_readreg(fd, regs_addr[i], &reg_data[i]);
+    }
+
+    chn0_data = reg_data[1]*256 + reg_data[0]; /* Channel0 = DATA0HIGH<<8 + DATA0LOW  */
+    chn1_data = reg_data[3]*256 + reg_data[2]; /* channel1 = DATA1HIGH<<8 +  DATA1LOW */
+
+    if( chn0_data<=0 || chn1_data<0 )
+    {
+		rv = -2;
+        goto OUT;
+    }
+
+    div = (float)chn1_data / (float)chn0_data;
+
+    if( div>0 && div<=0.5 )
+        *lux = 0.304*chn0_data-0.062*chn0_data*pow(div,1.4);
+
+    else if( div>0.5 && div<=0.61 )
+        *lux = 0.0224*chn0_data-0.031*chn1_data;
+
+    else if( div>0.61 && div<=0.8 )
+        *lux = 0.0128*chn0_data-0.0153*chn1_data;
+
+    else if( div>0.8 && div<=1.3 )
+        *lux = 0.00146*chn0_data-0.00112*chn1_data;
+
+    else if( div>1.3 )
+        *lux = 0.0;
+
+OUT:
+    tsl2561_power(fd, OFF);
+    return rv;
+}
diff --git a/project/modules/tsl2561.h b/project/modules/tsl2561.h
new file mode 100644
index 0000000..b73ff4a
--- /dev/null
+++ b/project/modules/tsl2561.h
@@ -0,0 +1,35 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2023 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  tsl2561.h
+ *    Description:  This file is the Lux sensor TSL2561 code
+ *
+ *        Version:  1.0.0(10/08/23)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
+ *
+ * Pin connection:
+ *               STH20 Module            Raspberry Pi Board
+ *                   VCC      <----->      #Pin1(3.3V)
+ *                   SDA0     <----->      #Pin27(SDA, BCM GPIO0)
+ *                   SCL0     <----->      #Pin28(SCL, BCM GPIO1)
+ *                   GND      <----->      GND
+ *
+ * /boot/config.txt:
+ *                  dtoverlay=i2c0,pins_0_1
+ *
+ ********************************************************************************/
+
+
+#ifndef  _TSL2561_H_
+#define  _TSL2561_H_
+
+#define TSL2561_I2CDEV                 "/dev/i2c-0"
+#define TSL2561_I2CADDR                 0x39
+
+extern int tsl2561_get_lux(float *lux);
+
+#endif   /* ----- #ifndef _TSL2561_H_  ----- */
+
+
diff --git a/project/mosquitto/.gitignore b/project/mosquitto/.gitignore
new file mode 100644
index 0000000..d2ecef2
--- /dev/null
+++ b/project/mosquitto/.gitignore
@@ -0,0 +1,9 @@
+# git ignore files/folders in the list
+
+# ignore folders
+install/
+
+# ignore files
+*.so*
+*.o
+*.a
diff --git a/project/mosquitto/conf.c b/project/mosquitto/conf.c
new file mode 100644
index 0000000..4c0ee20
--- /dev/null
+++ b/project/mosquitto/conf.c
@@ -0,0 +1,200 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  conf.c
+ *    Description:  This file is mqttd configure file parser function
+ *                 
+ *        Version:  1.0.0(2019年06月25日)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2019年06月25日 22时23分55秒"
+ *                 
+ ********************************************************************************/
+#include "conf.h"
+#include "logger.h"
+#include "iniparser.h"
+
+
+int mqttd_parser_conf(const char *conf_file, mqtt_ctx_t *ctx, int debug)
+{
+    dictionary          *ini;
+    const char          *str;
+    int                  val; 
+    int                  rv = 0; 
+
+    if( !conf_file || !ctx )
+    { 
+        fprintf(stderr, "%s() Invalid input arguments\n", __func__);
+        return -1;
+    }
+
+    memset(ctx, 0, sizeof(*ctx));
+
+    ini = iniparser_load(conf_file);
+    if( !ini )
+    {
+        fprintf(stderr, "ERROR: Configure file '%s' load failed\n", conf_file);
+        return -2;
+    }
+
+    /*+------------------------------------------------------+
+     *|    parser logger settings and start logger system    |
+     *+------------------------------------------------------+*/
+    if( !debug ) 
+    {
+        str = iniparser_getstring(ini, "logger:file", "/tmp/mqttd.log");
+        strncpy(ctx->logfile, str, sizeof(ctx->logfile));
+        ctx->logsize = iniparser_getint(ini, "logger:size", 1024);
+        ctx->loglevel = iniparser_getint(ini, "logger:level", LOG_LEVEL_INFO);
+    }
+    else
+    {
+        strncpy(ctx->logfile, "console", sizeof(ctx->logfile));
+        ctx->loglevel = LOG_LEVEL_DEBUG;
+        ctx->logsize = 0;
+    }
+
+    if( log_open(ctx->logfile, ctx->loglevel, ctx->logsize, LOG_LOCK_DISABLE) < 0 ) 
+    {
+        fprintf(stderr, "Logger system initialise failure\n");
+        return -2;
+    }
+
+    log_info("Logger system initialise ok\n");
+
+
+    /*+------------------------------------------------------+
+     *|               parser production ID                   |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "common:devid", NULL)) )
+    {
+        log_error("ERROR: Parser device ID failure\n");
+        rv = -3;
+        goto cleanup;
+    }
+    /* cJSON parser ID will get ""  */
+    snprintf(ctx->devid, sizeof(ctx->devid), "\"%s\"", str);
+    log_info("Parser device ID [%s]\n", ctx->devid);
+
+
+    /*+------------------------------------------------------+
+     *|       parser hardware module configuration           |
+     *+------------------------------------------------------+*/
+
+    /* relay */
+    ctx->hwconf.relay=iniparser_getint(ini, "hardware:relay", 0);
+    if( !ctx->hwconf.relay )
+        log_warn("Parser relay module disabled\n");
+    else
+        log_info("Parser relay module enabled\n");
+
+    /* RGB 3-colors LED */
+    ctx->hwconf.led=iniparser_getint(ini, "hardware:rgbled", 0);
+    if( !ctx->hwconf.led )
+        log_warn("Parser RGB 3-colors Led module disabled\n");
+    else
+        log_info("Parser RGB 3-colors Led module enabled\n");
+
+    /* beeper */
+    ctx->hwconf.beeper=iniparser_getint(ini, "hardware:beep", 0);
+    if( !ctx->hwconf.beeper )
+        log_warn("Parser beeper module disabled\n");
+    else
+        log_info("Parser beeper module enabled\n");
+
+    /* DS18B20 temperature module */
+    ctx->hwconf.ds18b20=iniparser_getint(ini, "hardware:ds18b20", 0);
+    if( !ctx->hwconf.ds18b20 )
+        log_warn("Parser DS18B20 temperature module disabled\n");
+    else
+        log_info("Parser DS18B20 temperature module enabled\n");
+
+    /* SHT20 temperature and hummidity module */
+    ctx->hwconf.sht2x=iniparser_getint(ini, "hardware:sht2x", 0);
+    if( !ctx->hwconf.sht2x )
+        log_warn("Parser SHT2X temperature and hummidity module disabled\n");
+    else
+        log_info("Parser SHT2X temperature and hummidity module enabled\n");
+
+    /* TSL2561 light intensity sensor module */
+    ctx->hwconf.tsl2561=iniparser_getint(ini, "hardware:tsl2561", 0);
+    if( !ctx->hwconf.tsl2561 )
+        log_warn("Parser TSL2561 light intensity sensor module disabled\n");
+    else
+        log_info("Parser TSL2561 light intensity sensor module enabled\n");
+
+    /*+------------------------------------------------------+
+     *|              parser broker settings                  |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "broker:hostname", NULL)) )
+    {
+        log_error("ERROR: Parser MQTT broker server hostname failure\n");
+        rv = -4;
+        goto cleanup;
+    }
+    strncpy(ctx->host, str, sizeof(ctx->host) );
+
+    if( (val=iniparser_getint(ini, "broker:port", -1)) < 0 ) 
+    {
+        log_error("ERROR: Parser MQTT broker server port failure\n");
+        rv = -5;
+        goto cleanup;
+    }
+    ctx->port = val;
+    log_info("Parser MQTT broker server [%s:%d]\n", ctx->host, ctx->port);
+
+    str=iniparser_getstring(ini, "broker:username", NULL);
+    strncpy(ctx->uid, str, sizeof(ctx->uid) );
+
+    str=iniparser_getstring(ini, "broker:password", NULL);
+    strncpy(ctx->pwd, str, sizeof(ctx->pwd) );
+
+    if( ctx->uid && ctx->pwd )
+        log_info("Parser broker author by [%s:%s]\n", ctx->uid, ctx->pwd);
+
+    ctx->keepalive = iniparser_getint(ini, "broker:keepalive", DEF_KEEPALIVE);
+    log_info("Parser broker keepalive timeout [%d] seconds\n", ctx->keepalive);
+
+    /*+------------------------------------------------------+
+     *|             parser publisher settings                |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "publisher:pubTopic", NULL)) )
+    {
+        log_error("ERROR: Parser MQTT broker publisher topic failure\n");
+        rv = -6;
+        goto cleanup;
+    }
+    strncpy(ctx->pubTopic, str, sizeof(ctx->pubTopic) );
+
+    ctx->pubQos = iniparser_getint(ini, "publisher:pubQos", DEF_QOS);
+    ctx->interval = iniparser_getint(ini, "publisher:interval", DEF_PUBINTERVAL);
+    log_info("Parser publisher topic \"%s\" with Qos[%d] interval[%d]\n", ctx->pubTopic, ctx->pubQos, ctx->interval);
+
+    /*+------------------------------------------------------+
+     *|             parser subscriber settings               |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "subsciber:subTopic", NULL)) )
+    {
+        log_error("ERROR: Parser MQTT broker publisher topic failure\n");
+        rv = -7;
+        goto cleanup;
+    }
+    strncpy(ctx->subTopic, str, sizeof(ctx->subTopic) );
+
+    ctx->subQos = iniparser_getint(ini, "subsciber:subQos", DEF_QOS);
+    log_info("Parser subscriber topic \"%s\" with Qos[%d]\n", ctx->subTopic, ctx->subQos);
+
+cleanup:
+    if( ini )
+        iniparser_freedict(ini);
+
+    if( rv )
+        log_close();
+
+    return rv;
+}
+
diff --git a/project/mosquitto/conf.h b/project/mosquitto/conf.h
new file mode 100644
index 0000000..89d3a66
--- /dev/null
+++ b/project/mosquitto/conf.h
@@ -0,0 +1,71 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  conf.h
+ *    Description:  This file is mqttd configure file parser function
+ *                 
+ *        Version:  1.0.0(2019年06月25日)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2019年06月25日 22时23分55秒"
+ *                 
+ ********************************************************************************/
+#ifndef  __CONF_H_
+#define  __CONF_H_
+
+enum
+{
+    Qos0, /* 发送者只发送一次消息,不进行重试,Broker不会返回确认消息。在Qos0情况下,Broker可能没有接受到消息 */
+    Qos1, /* 发送者最少发送一次消息,确保消息到达Broker,Broker需要返回确认消息PUBACK。在Qos1情况下,Broker可能接受到重复消息 */
+    Qos2, /* Qos2使用两阶段确认来保证消息的不丢失和不重复。在Qos2情况下,Broker肯定会收到消息,且只收到一次  */
+};
+
+#define DEF_KEEPALIVE       30
+#define DEF_PUBINTERVAL     120
+#define DEF_QOS             Qos2
+
+typedef struct hwconf_s 
+{
+    int            relay;   /* relay aviable or not.   0:Disable  1: Enable */
+    int            led;     /* RGB led aviable or not. 0:Disable  1: Enable */
+    int            beeper;  /* beeper aviable or not.  0:Disable  1: Enable */
+    int            ds18b20; /* DS1B820 aviable or not. 0:Disable  1: Enable */
+    int            sht2x;   /* SHT20  aviable or not.  0:Disable  1: Enable */
+    int            tsl2561; /* TSL2561 aviable or not.  0:Disable  1: Enable */
+} hwconf_t;  
+
+
+typedef struct mqtt_ctx_s
+{
+    char          devid[32];    /*  device ID */
+
+    /* hardware configuration */
+    hwconf_t      hwconf;
+
+    /* logger settings */
+    char          logfile[128]; /* logger record file */
+    int           loglevel;     /* logger level  */
+    int           logsize;      /* logger file maxsize, oversize will rollback */
+
+    /* Broker settings  */
+    char          host[128];  /* MQTT broker server name  */
+    int           port;       /* MQTT broker listen port  */
+    char          uid[64];    /* username */
+    char          pwd[64];    /* password */
+    int           keepalive;  /* MQTT broker send PING message to subsciber/publisher keepalive timeout<seconds> */
+
+    /* Publisher settings */
+    char          pubTopic[256]; /* Publisher topic  */
+    int           pubQos;        /* Publisher Qos */
+    int           interval ;     /* Publish sensor data interval time, unit seconds */
+
+    /* Subscriber settings */
+    char          subTopic[256]; /* Subscriber topic */
+    int           subQos;        /* Subscriber Qos  */
+} mqtt_ctx_t;
+
+
+extern int mqttd_parser_conf(const char *conf_file, mqtt_ctx_t *ctx, int debug);
+
+#endif   /* ----- #ifndef _CONF_H_  ----- */
+
diff --git a/project/mosquitto/etc/mqttd.conf b/project/mosquitto/etc/mqttd.conf
new file mode 100644
index 0000000..50fcffa
--- /dev/null
+++ b/project/mosquitto/etc/mqttd.conf
@@ -0,0 +1,69 @@
+[common]
+devid="RPi3B#01"
+
+# 树莓派连接的外设信息,0:禁用或未连接  其他: 使能或相关硬件连接的Pin管脚(wPI模式)
+[hardware]
+
+# 是否使能继电器模块,0:禁用  1:使能
+relay=1
+
+# 是否使能 PWM 蜂鸣器模块,0:禁用  N:Beep 次数
+beep=3
+
+# 是否使能 RGB 三色灯模块,0:禁用  1:使能
+rgbled=1
+
+# 是否使能 DS18b20 温度传感器模块,0:禁用  1:使能
+ds18b20=1
+
+# 是否使能 SHT2X 温湿度传感器模块,0:禁用  1:使能
+sht2x=1
+
+# 是否使能 TSL2561 光强传感器模块,0:禁用  1:使能
+lux=1
+
+[logger]
+
+# 日志记录文件
+file=/tmp/mqttd.log
+
+# 日志级别: 0:ERROR 1:WARN 2:INFO 3:DEBUG 4:TRACE 
+level=2
+
+# 日志回滚大小
+size=1024
+
+
+[broker]
+
+# broker 服务器地址和端口号
+hostname="main.iot-yun.club"
+port=10883
+
+# broker 认证连接的用户名和密码
+username="lingyun"
+password="lingyun"
+
+# broker给subsciber和publisher发送PING报文保持 keepalive 的时间周期,单位是秒
+keepalive=30
+
+# Qos0: 发送者只发送一次消息,不进行重试,Broker不会返回确认消息。在Qos0情况下,Broker可能没有接受到消息
+# Qos1: 发送者最少发送一次消息,确保消息到达Broker,Broker需要返回确认消息PUBACK。在Qos1情况下,Broker可能接受到重复消息
+# Qos2: Qos2使用两阶段确认来保证消息的不丢失和不重复。在Qos2情况下,Broker肯定会收到消息,且只收到一次
+
+[publisher]
+
+# mosquitto_sub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Uplink
+pubTopic="$Sys/Studio/Uplink"
+pubQos=0
+
+# Publisher上报传感器数据的周期,单位是秒
+interval=60
+
+[subsciber]
+
+# mosquitto_pub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Downlink -m '{"id":"RPi3B#01", "light":"on", "buzzer":"on"}'
+# mosquitto_pub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Downlink -m '{"id":"RPi3B#01", "leds":[{"red":"on","green":"off","blue":"on"}]}'
+subTopic="$Sys/Studio/Downlink"
+subQos=0
+
diff --git a/project/mosquitto/main.c b/project/mosquitto/main.c
new file mode 100644
index 0000000..31d8fd7
--- /dev/null
+++ b/project/mosquitto/main.c
@@ -0,0 +1,458 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  main.c
+ *    Description:  This file
+ *
+ *        Version:  1.0.0(29/01/19)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "29/01/19 15:34:41"
+ *
+ ********************************************************************************/
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <time.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <string.h>
+#include <errno.h>
+
+#include <mosquitto.h>
+#include <cjson/cJSON.h>
+
+#include "logger.h"
+#include "util_proc.h"
+#include "modules.h"
+#include "conf.h"
+
+#define PROG_VERSION               "v1.0.0"
+#define DAEMON_PIDFILE             "/tmp/.mqtt.pid"
+
+void *mqtt_sub_worker(void *args);
+void *mqtt_pub_worker(void *args);
+
+static void program_usage(char *progname)
+{
+
+    printf("Usage: %s [OPTION]...\n", progname);
+    printf(" %s is LingYun studio MQTT daemon program running on RaspberryPi\n", progname);
+
+    printf("\nMandatory arguments to long options are mandatory for short options too:\n");
+    printf(" -d[debug   ]  Running in debug mode\n");
+    printf(" -c[conf    ]  Specify configure file\n");
+    printf(" -h[help    ]  Display this help information\n");
+    printf(" -v[version ]  Display the program version\n");
+
+    printf("\n%s version %s\n", progname, PROG_VERSION);
+    return;
+}
+
+int main (int argc, char **argv)
+{
+    int                daemon = 1;
+    pthread_t          tid;
+    mqtt_ctx_t         ctx;
+    char               *conf_file=NULL;
+    int                debug = 0;
+    int                opt;
+    char              *progname=NULL;
+
+    struct option long_options[] = {
+        {"conf", required_argument, NULL, 'c'},
+        {"debug", no_argument, NULL, 'd'},
+        {"version", no_argument, NULL, 'v'},
+        {"help", no_argument, NULL, 'h'},
+        {NULL, 0, NULL, 0}
+    };
+
+    progname = (char *)basename(argv[0]);
+
+    /* Parser the command line parameters */
+    while ((opt = getopt_long(argc, argv, "c:dvh", long_options, NULL)) != -1)
+    {
+        switch (opt)
+        {
+            case 'c': /* Set configure file */
+                conf_file = optarg;
+                break;
+
+            case 'd': /* Set debug running */
+                daemon = 0;
+                debug = 1;
+                break;
+
+            case 'v':  /* Get software version */
+                printf("%s version %s\n", progname, PROG_VERSION);
+                return 0;
+
+            case 'h':  /* Get help information */
+                program_usage(progname);
+                return 0;
+
+            default:
+                break;
+        }
+
+    }
+
+    if( !conf_file )
+        debug = 1;
+
+    if( mqttd_parser_conf(conf_file, &ctx, debug)<0 )
+    {
+        fprintf(stderr, "Parser mqtted configure file failure\n");
+        return -2;
+    }
+
+    install_default_signal();
+
+    if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 )
+        goto cleanup;
+
+    mosquitto_lib_init();
+
+    if( thread_start(&tid, mqtt_sub_worker, &ctx ) < 0 )
+    {
+        log_error("Start MQTT subsciber worker thread failure\n");
+        goto cleanup;
+    }
+    log_info("Start MQTT subsciber worker thread ok\n");
+
+    if( thread_start(&tid, mqtt_pub_worker, &ctx) < 0 )
+    {
+        log_error("Start MQTT publisher worker thread failure\n");
+        goto cleanup;
+    }
+    log_info("Start MQTT publisher worker thread ok\n");
+
+    while( ! g_signal.stop )
+    {
+        msleep(1000);
+    }
+
+cleanup:
+    mosquitto_lib_cleanup();
+    log_close();
+
+    return 0;
+} /* ----- End of main() ----- */
+
+void pub_connect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+    int                     rv = 0;
+    char                    msg[128];
+    float                   temp = 0.0; /* temperature */
+    float                   rh = 0.0;   /* relative humidity */
+    int                     retain = 0;
+
+    if( result )
+    {
+        log_error("Publisher connect to broker server[%s:%d] failed, rv=%d\n", ctx->host, ctx->port, result);
+        return ;
+    }
+
+    log_info("Publisher connect to broker server[%s:%d] successfully\n", ctx->host, ctx->port);
+    log_debug("Publish topic '%s'\n", ctx->pubTopic);
+
+    if( ctx->hwconf.ds18b20 )
+    {
+        memset(msg, 0, sizeof(msg));
+
+        log_debug("DS18B20 temperature sensor enabled, start broadcast it\n");
+
+        if( ds18b20_get_temperature(&temp) < 0 )
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"error\" }", ctx->devid);
+        else
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"%.2f\" }", ctx->devid, temp);
+
+        rv = mosquitto_publish(mosq, NULL, ctx->pubTopic, strlen(msg), msg, ctx->pubQos, retain);
+        if( rv )
+        {
+            log_error("Publisher broadcast message '%s' failure: %d\n", msg, rv);
+        }
+        else
+        {
+            log_info("Publisher broadcast message '%s' ok\n", msg);
+        }
+    }
+
+    if( ctx->hwconf.sht2x )
+    {
+        memset(msg, 0, sizeof(msg));
+
+        log_debug("SHT2X temperature and humidity sensor enabled, start broadcast it\n");
+
+        if( sht2x_get_temp_humidity(&temp, &rh) < 0 )
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"error\", \"RH\":\"error\" }", ctx->devid);
+        else
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"%.2f\", \"RH\":\"%.2f\" }", ctx->devid, temp, rh);
+
+        rv = mosquitto_publish(mosq, NULL, ctx->pubTopic, strlen(msg), msg, ctx->pubQos, retain);
+        if( rv )
+        {
+            log_error("Publisher broadcast message '%s' failure: %d\n", msg, rv);
+        }
+        else
+        {
+            log_info("Publisher broadcast message '%s' ok\n", msg);
+        }
+    }
+
+    log_info("Publisher broadcast over and disconnect broker now\n", ctx->host, ctx->port);
+    mosquitto_disconnect(mosq);
+
+    return ;
+}
+
+
+void *mqtt_pub_worker(void *args)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)args;
+    struct mosquitto       *mosq;
+    bool                    session = true;
+
+
+    mosq = mosquitto_new(NULL, session, ctx);
+    if( !mosq )
+    {
+        log_error("mosquitto_new failure\n");
+        return NULL;
+    }
+
+    /* set connnect to broker username and password  */
+    if( strlen(ctx->uid)> 0  && strlen(ctx->pwd)> 0 )
+        mosquitto_username_pw_set(mosq, ctx->uid, ctx->pwd);
+
+    /* set callback functions */
+    mosquitto_connect_callback_set(mosq, pub_connect_callback);
+
+    while( !g_signal.stop )
+    {
+        /* connect to MQTT broker  */
+        if( mosquitto_connect(mosq, ctx->host, ctx->port, ctx->keepalive) )
+        {
+            log_error("Publisher connect to broker[%s:%d] failure: %s\n", ctx->host, ctx->port, strerror(errno));
+            msleep(1000);
+            continue;
+        }
+
+        /* -1: use default timeout 1000ms  1: unused */
+        mosquitto_loop_forever(mosq, -1, 1);
+
+        /* Publisher broadcast sensors data message interval time, unit seconds */
+        sleep( ctx->interval );
+    }
+
+    mosquitto_destroy(mosq);
+    return NULL;
+}
+
+void sub_connect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+
+    if( result )
+    {
+        log_error("Subscriber connect to broker server failed, rv=%d\n", result);
+        return ;
+    }
+
+    log_info("Subscriber connect to broker server[%s:%d] successfully\n", ctx->host, ctx->port);
+    mosquitto_subscribe(mosq, NULL, ctx->subTopic, ctx->subQos);
+}
+
+void sub_disconnect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+
+    log_warn("Subscriber disconnect to broker server[%s:%d], reason=%d\n", ctx->host, ctx->port, result);
+}
+
+static inline void mqtt_turn_led(int which, char *cmd)
+{
+    if( strcasestr(cmd, "on") )
+        turn_led(which, ON);
+    else if( strcasestr(cmd, "off") )
+        turn_led(which, OFF);
+}
+
+void proc_json_items(cJSON *root, mqtt_ctx_t *ctx)
+{
+    int                    i;
+    char                  *value;
+    cJSON                 *item;
+    cJSON                 *array;
+    hwconf_t              *hwconf = &ctx->hwconf;
+
+    if( !root )
+    {
+        log_error("Invalid input arguments $root\n");
+        return ;
+    }
+
+    for( i=0; i<cJSON_GetArraySize(root); i++ )
+    {
+        item = cJSON_GetArrayItem(root, i);
+        if( !item )
+            break;
+
+        /* if item is cJSON_Object, then recursive call proc_json */
+        if( cJSON_Object == item->type )
+        {
+            proc_json_items(item, ctx);
+        }
+        else if( cJSON_Array == item->type )
+        {
+            /* RGB colors led control: {"id":"RPi3B#01", "leds":[{"red":"on","green":"off","blue":"on"}]} */
+            if( hwconf->led && !strcasecmp(item->string, "leds") )
+            {
+                array = cJSON_GetArrayItem(item, 0);
+                if( NULL != array )
+                {
+                    cJSON        *led_item;
+
+                    if( NULL != (led_item=cJSON_GetObjectItem(array , "red")) )
+                    {
+                        log_info("turn red led '%s'\n", led_item->valuestring);
+                        mqtt_turn_led(LED_R, led_item->valuestring);
+                    }
+
+                    if( NULL != (led_item=cJSON_GetObjectItem(array , "green")) )
+                    {
+                        log_info("turn green led '%s'\n", led_item->valuestring);
+                        mqtt_turn_led(LED_G, led_item->valuestring);
+                    }
+
+                    if( NULL != (led_item=cJSON_GetObjectItem(array , "blue")) )
+                    {
+                        log_info("turn blue led '%s'\n", led_item->valuestring);
+                        mqtt_turn_led(LED_B, led_item->valuestring);
+                    }
+                }
+            }
+        }
+        else
+        {
+            value = cJSON_Print(item);
+
+            /* light controled by relay: {"id":"RPi3B#01", "light":"on"} */
+            if( hwconf->relay && !strcasecmp(item->string, "light") )
+            {
+                if( strcasestr(value, "on") )
+                {
+                    log_info("Turn light on\n");
+                    turn_relay(RELAY1, ON);
+                }
+                else if( strcasestr(value, "off") )
+                {
+                    log_info("Turn light off\n");
+                    turn_relay(RELAY1, OFF);
+                }
+            }
+
+            /* buzzer controled : {"id":"RPi3B#01", "buzzer":"on"} */
+            if( hwconf->beeper && !strcasecmp(item->string, "buzzer") )
+            {
+                if( strcasestr(value, "on") )
+                {
+                    turn_beep(3);
+                }
+            }
+
+            free(value); /* must free it, or it will result memory leak */
+        }
+    }
+
+}
+
+void sub_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+
+    cJSON                  *root = NULL;
+    cJSON                  *item;
+    char                   *value;
+
+    if ( !message->payloadlen )
+    {
+        log_error("%s (null)\n", message->topic);
+        return ;
+    }
+
+    log_debug("Subscriber receive message: '%s'\n", message->payload);
+
+    root = cJSON_Parse(message->payload);
+    if( !root )
+    {
+        log_error("cJSON_Parse parser failure: %s\n", cJSON_GetErrorPtr());
+        return ;
+    }
+
+    item = cJSON_GetObjectItem(root, "id");
+    if( !item )
+    {
+        log_error("cJSON_Parse get ID failure: %s\n", cJSON_GetErrorPtr());
+        goto cleanup;
+    }
+
+    value = cJSON_PrintUnformatted(item);
+    if( strcasecmp(value, ctx->devid) )
+    {
+        free(value);
+        goto cleanup;
+    }
+
+    free(value);
+    log_info("Subscriber receive message: '%s'\n", message->payload);
+
+    proc_json_items(root, ctx);
+
+cleanup:
+    cJSON_Delete(root); /* must delete it, or it will result memory leak */
+    return ;
+}
+
+
+void *mqtt_sub_worker(void *args)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)args;
+    struct mosquitto       *mosq;
+    bool                    session = true;
+
+    mosq = mosquitto_new(NULL, session, ctx);
+    if( !mosq )
+    {
+        log_error("mosquitto_new failure\n");
+        return NULL;
+    }
+
+    /* set connnect to broker username and password  */
+    if( strlen(ctx->uid)> 0  && strlen(ctx->pwd)> 0 )
+        mosquitto_username_pw_set(mosq, ctx->uid, ctx->pwd);
+
+    /* set callback functions */
+    mosquitto_connect_callback_set(mosq, sub_connect_callback);
+    mosquitto_disconnect_callback_set(mosq, sub_disconnect_callback);
+    mosquitto_message_callback_set(mosq, sub_message_callback);
+
+    while( !g_signal.stop )
+    {
+        /* connect to MQTT broker  */
+        if( mosquitto_connect(mosq, ctx->host, ctx->port, ctx->keepalive) )
+        {
+            log_error("Subscriber connect to broker[%s:%d] failure: %s\n", ctx->host, ctx->port, strerror(errno));
+            msleep(1000);
+            continue;
+        }
+
+        /* -1: use default timeout 1000ms  1: unused */
+        mosquitto_loop_forever(mosq, -1, 1);
+    }
+
+    mosquitto_destroy(mosq);
+    return NULL;
+}
+
diff --git a/project/mosquitto/makefile b/project/mosquitto/makefile
new file mode 100644
index 0000000..c716ebb
--- /dev/null
+++ b/project/mosquitto/makefile
@@ -0,0 +1,68 @@
+#********************************************************************************
+#      Copyright:  (C) 2023 LingYun IoT System Studio
+#                  All rights reserved.
+#
+#       Filename:  Makefile
+#    Description:  This file is the project top Makefie
+#
+#        Version:  1.0.0(11/08/23)
+#         Author:  Guo Wenxue <guowenxue@gmail.com>
+#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
+#
+#*******************************************************************************
+
+PRJ_PATH=$(shell pwd)
+APP_NAME = mqttd
+
+BUILD_ARCH=$(shell uname -m)
+ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
+    CROSS_COMPILE=arm-linux-gnueabihf-
+endif
+
+# C source files in top-level directory
+SRCFILES = $(wildcard *.c)
+
+# common CFLAGS for our source code
+CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized -D_GNU_SOURCE
+
+# C source file in sub-directory
+SRCS=booster modules
+SRCS_PATH=$(patsubst %,${PRJ_PATH}/%,$(SRCS))
+CFLAGS+=$(patsubst %,-I%,$(SRCS_PATH))
+LDFLAGS+=$(patsubst %,-L%,$(SRCS_PATH))
+LIBS=$(patsubst %,-l%,$(SRCS))
+LDFLAGS+=${LIBS}
+
+# Open source libraries
+CFLAGS+=-I ${PRJ_PATH}/openlibs/install/include
+LDFLAGS+=-L ${PRJ_PATH}/openlibs/install/lib
+
+# libraries
+libs=openlibs ${SRCS}
+LDFLAGS+=-lmosquitto -lcjson -lssl -lcrypto -lgpiod
+
+LDFLAGS+=-lpthread
+
+all: entry subdir
+	${CROSS_COMPILE}gcc ${CFLAGS} ${SRCFILES} -o ${APP_NAME} ${LDFLAGS}
+
+entry:
+	@echo "Building ${APP_NAME} on ${BUILD_ARCH}"
+
+subdir:
+	@for dir in ${libs} ; do if [ ! -e $${dir} ] ; then ln -s ../$${dir}; fi; done
+	@for dir in ${libs} ;  do make -C $${dir} ; done
+
+install:
+	cp ${APP_NAME} /tftp
+
+clean:
+	@for dir in ${SRCS} ; do if [ -e $${dir} ] ; then make clean -C $${dir}; fi; done
+	@rm -f ${APP_NAME}
+
+distclean:
+	@for dir in ${libs} ; do if [ -e $${dir} ] ; then make clean -C $${dir}; fi; done
+	@for dir in ${libs} ; do if [ -e $${dir} ] ; then unlink $${dir}; fi; done
+	@rm -f ${APP_NAME}
+	@rm -f cscope.* tags
+
diff --git a/project/openlibs/cjson/build.sh b/project/openlibs/cjson/build.sh
new file mode 100755
index 0000000..1666b33
--- /dev/null
+++ b/project/openlibs/cjson/build.sh
@@ -0,0 +1,182 @@
+#!/bin/bash
+
+# library name and version
+# Official: https://github.com/DaveGamble/cJSON
+LIB_NAME=cJSON-1.7.15
+PACK_SUFIX=tar.gz
+
+# LingYun source code FTP server
+LY_FTP=http://main.iot-yun.club:2211/src/
+
+# library download URL address
+LIB_URL=$LY_FTP
+
+# Cross compiler for cross compile on Linux server
+CROSS_COMPILE=arm-linux-gnueabihf-
+
+# compile jobs
+JOBS=`cat /proc/cpuinfo |grep "processor"|wc -l`
+
+# this project absolute path
+PRJ_PATH=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
+
+# top project absolute path
+TOP_PATH=$(realpath $PRJ_PATH/..)
+
+# binaries install path
+PREFIX_PATH=$TOP_PATH/install
+BIN_PATH=$PREFIX_PATH/bin
+LIB_PATH=$PREFIX_PATH/lib
+INC_PATH=$PREFIX_PATH/include
+
+# check installed or not file
+INST_FILE=$PREFIX_PATH/lib/libcjson.so
+
+# shell script will exit once get command error
+set -e
+
+#+-------------------------+
+#| Shell script functions  |
+#+-------------------------+
+
+function pr_error() {
+	echo -e "\033[40;31m $1 \033[0m"
+}
+
+function pr_warn() {
+	echo -e "\033[40;33m $1 \033[0m"
+}
+
+function pr_info() {
+	echo -e "\033[40;32m $1 \033[0m"
+}
+
+function check_result()
+{
+	if [ $? != 0 ] ; then
+		pr_error $1
+	fi
+}
+# decompress a packet to destination path
+function do_unpack()
+{
+	tarball=$1
+	dstpath=`pwd`
+
+	if [[ $# == 2 ]] ; then
+		dstpath=$2
+	fi
+
+	pr_info "decompress $tarball => $dstpath"
+
+	mkdir -p $dstpath
+	case $tarball in
+		*.tar.gz)
+			tar -xzf $tarball -C $dstpath
+			;;
+
+		*.tar.bz2)
+			tar -xjf $tarball -C $dstpath
+			;;
+
+		*.tar.xz)
+			tar -xJf $tarball -C $dstpath
+			;;
+
+		*.tar.zst)
+			tar -I zstd -xf $tarball -C $dstpath
+			;;
+
+		*.tar)
+			tar -xf $tarball -C $dstpath
+			;;
+
+		*.zip)
+			unzip -qo $tarball -d $dstpath
+			;;
+
+		*)
+			pr_error "decompress Unsupport packet: $tarball"
+			return 1;
+			;;
+	esac
+}
+
+function do_export()
+{
+	BUILD_ARCH=$(uname -m)
+	if [[ $BUILD_ARCH =~ "arm" ]] ; then
+		pr_warn "local($BUILD_ARCH) compile $LIB_NAME"
+		return ;
+	fi
+
+	pr_warn "cross(${CROSS_COMPILE}) compile $LIB_NAME"
+
+	# export cross toolchain
+	export CC=${CROSS_COMPILE}gcc
+	export CXX=${CROSS_COMPILE}g++
+	export AS=${CROSS_COMPILE}as
+	export AR=${CROSS_COMPILE}ar
+	export LD=${CROSS_COMPILE}ld
+	export NM=${CROSS_COMPILE}nm
+	export RANLIB=${CROSS_COMPILE}ranlib
+	export OBJDUMP=${CROSS_COMPILE}objdump
+	export STRIP=${CROSS_COMPILE}strip
+
+	# export cross configure
+	export CONFIG_CROSS=" --build=i686-pc-linux --host=arm-linux "
+
+	# Clear LDFLAGS and CFLAGS
+	export LDFLAGS=
+	export CFLAGS=
+}
+
+function do_fetch()
+{
+	if [ -e ${INST_FILE} ] ; then
+		pr_warn "$LIB_NAME compile and installed alredy"
+		exit ;
+	fi
+
+	if [ -d $LIB_NAME ] ; then
+		pr_warn "$LIB_NAME fetch already"
+		return ;
+	fi
+
+	if [ ! -f ${LIB_NAME}.${PACK_SUFIX} ] ; then
+		wget ${LIB_URL}/${LIB_NAME}.${PACK_SUFIX}
+		check_result "ERROR: download ${LIB_NAME} failure"
+	fi
+
+	do_unpack ${LIB_NAME}.${PACK_SUFIX}
+}
+
+function do_build()
+{
+	cd $LIB_NAME
+
+	do_export
+
+	sed -i "s|^CC =.*|CC = ${CROSS_COMPILE}gcc -std=c89|" Makefile
+
+	make && make PREFIX=$PREFIX_PATH install
+	check_result "ERROR: compile ${LIB_NAME} failure"
+
+	cp libcjson.a $PREFIX_PATH/lib
+}
+
+function do_clean()
+{
+	rm -rf *${LIB_NAME}*
+}
+
+if [[ $# == 1 && $1 == -c ]] ;then
+	pr_warn "start clean ${LIB_NAME}"
+	do_clean
+	exit;
+fi
+
+do_fetch
+
+do_build
+
diff --git a/project/openlibs/libevent/build.sh b/project/openlibs/libevent/build.sh
new file mode 100755
index 0000000..55e452d
--- /dev/null
+++ b/project/openlibs/libevent/build.sh
@@ -0,0 +1,182 @@
+#!/bin/bash
+
+# library name and version
+# Official: https://github.com/libevent/libevent/releases/download/
+LIB_NAME=libevent-2.1.11-stable
+PACK_SUFIX=tar.gz
+
+# LingYun source code FTP server
+LY_FTP=http://main.iot-yun.club:2211/src/
+
+# library download URL address
+LIB_URL=$LY_FTP
+
+# Cross compiler for cross compile on Linux server
+CROSS_COMPILE=arm-linux-gnueabihf-
+
+# compile jobs
+JOBS=`cat /proc/cpuinfo |grep "processor"|wc -l`
+
+# this project absolute path
+PRJ_PATH=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
+
+# top project absolute path
+TOP_PATH=$(realpath $PRJ_PATH/..)
+
+# binaries install path
+PREFIX_PATH=$TOP_PATH/install
+BIN_PATH=$PREFIX_PATH/bin
+LIB_PATH=$PREFIX_PATH/lib
+INC_PATH=$PREFIX_PATH/include
+
+# check installed or not file
+INST_FILE=$PREFIX_PATH/lib/libevent.so
+
+# shell script will exit once get command error
+set -e
+
+#+-------------------------+
+#| Shell script functions  |
+#+-------------------------+
+
+function pr_error() {
+	echo -e "\033[40;31m $1 \033[0m"
+}
+
+function pr_warn() {
+	echo -e "\033[40;33m $1 \033[0m"
+}
+
+function pr_info() {
+	echo -e "\033[40;32m $1 \033[0m"
+}
+
+function check_result()
+{
+	if [ $? != 0 ] ; then
+		pr_error $1
+	fi
+}
+# decompress a packet to destination path
+function do_unpack()
+{
+	tarball=$1
+	dstpath=`pwd`
+
+	if [[ $# == 2 ]] ; then
+		dstpath=$2
+	fi
+
+	pr_info "decompress $tarball => $dstpath"
+
+	mkdir -p $dstpath
+	case $tarball in
+		*.tar.gz)
+			tar -xzf $tarball -C $dstpath
+			;;
+
+		*.tar.bz2)
+			tar -xjf $tarball -C $dstpath
+			;;
+
+		*.tar.xz)
+			tar -xJf $tarball -C $dstpath
+			;;
+
+		*.tar.zst)
+			tar -I zstd -xf $tarball -C $dstpath
+			;;
+
+		*.tar)
+			tar -xf $tarball -C $dstpath
+			;;
+
+		*.zip)
+			unzip -qo $tarball -d $dstpath
+			;;
+
+		*)
+			pr_error "decompress Unsupport packet: $tarball"
+			return 1;
+			;;
+	esac
+}
+
+function do_export()
+{
+	BUILD_ARCH=$(uname -m)
+	if [[ $BUILD_ARCH =~ "arm" ]] ; then
+		pr_warn "local($BUILD_ARCH) compile $LIB_NAME"
+		return ;
+	fi
+
+	pr_warn "cross(${CROSS_COMPILE}) compile $LIB_NAME"
+
+	# export cross toolchain
+	export CC=${CROSS_COMPILE}gcc
+	export CXX=${CROSS_COMPILE}g++
+	export AS=${CROSS_COMPILE}as
+	export AR=${CROSS_COMPILE}ar
+	export LD=${CROSS_COMPILE}ld
+	export NM=${CROSS_COMPILE}nm
+	export RANLIB=${CROSS_COMPILE}ranlib
+	export OBJDUMP=${CROSS_COMPILE}objdump
+	export STRIP=${CROSS_COMPILE}strip
+
+	# export cross configure
+	export CONFIG_CROSS=" --build=i686-pc-linux --host=arm-linux "
+
+	# Clear LDFLAGS and CFLAGS
+	export LDFLAGS=
+	export CFLAGS=
+}
+
+function do_fetch()
+{
+	if [ -e ${INST_FILE} ] ; then
+		pr_warn "$LIB_NAME compile and installed alredy"
+		exit ;
+	fi
+
+	if [ -d $LIB_NAME ] ; then
+		pr_warn "$LIB_NAME fetch already"
+		return ;
+	fi
+
+	if [ ! -f ${LIB_NAME}.${PACK_SUFIX} ] ; then
+		wget ${LIB_URL}/${LIB_NAME}.${PACK_SUFIX}
+		check_result "ERROR: download ${LIB_NAME} failure"
+	fi
+
+	do_unpack ${LIB_NAME}.${PACK_SUFIX}
+}
+
+function do_build()
+{
+	cd $LIB_NAME
+
+	do_export
+
+	./configure --prefix=${PREFIX_PATH} ${CONFIG_CROSS} --enable-static \
+		--enable-thread-support --enable-openssl --enable-function-sections
+	check_result "ERROR: configure ${LIB_NAME} failure"
+
+	make && make install
+	check_result "ERROR: compile ${LIB_NAME} failure"
+}
+
+function do_clean()
+{
+	rm -rf *${LIB_NAME}*
+}
+
+if [[ $# == 1 && $1 == -c ]] ;then
+	pr_warn "start clean ${LIB_NAME}"
+	do_clean
+	exit;
+fi
+
+do_fetch
+
+do_build
+
diff --git a/project/openlibs/libgpiod/build.sh b/project/openlibs/libgpiod/build.sh
new file mode 100755
index 0000000..d703b78
--- /dev/null
+++ b/project/openlibs/libgpiod/build.sh
@@ -0,0 +1,185 @@
+#!/bin/bash
+
+# library name and version
+# Official: https://git.kernel.org/pub/scm/libs/libgpiod/libgpiod.git
+LIB_NAME=libgpiod-1.6.4
+PACK_SUFIX=tar.gz
+
+# LingYun source code FTP server
+LY_FTP=http://main.iot-yun.club:2211/src/
+
+# library download URL address
+LIB_URL=$LY_FTP
+
+# Cross compiler for cross compile on Linux server
+CROSS_COMPILE=arm-linux-gnueabihf-
+
+# compile jobs
+JOBS=`cat /proc/cpuinfo |grep "processor"|wc -l`
+
+# this project absolute path
+PRJ_PATH=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
+
+# top project absolute path
+TOP_PATH=$(realpath $PRJ_PATH/..)
+
+# binaries install path
+PREFIX_PATH=$TOP_PATH/install
+BIN_PATH=$PREFIX_PATH/bin
+LIB_PATH=$PREFIX_PATH/lib
+INC_PATH=$PREFIX_PATH/include
+
+# check installed or not file
+INST_FILE=$PREFIX_PATH/lib/libgpiod.so
+
+# shell script will exit once get command error
+set -e
+
+#+-------------------------+
+#| Shell script functions  |
+#+-------------------------+
+
+function pr_error() {
+	echo -e "\033[40;31m $1 \033[0m"
+}
+
+function pr_warn() {
+	echo -e "\033[40;33m $1 \033[0m"
+}
+
+function pr_info() {
+	echo -e "\033[40;32m $1 \033[0m"
+}
+
+function check_result()
+{
+	if [ $? != 0 ] ; then
+		pr_error $1
+	fi
+}
+# decompress a packet to destination path
+function do_unpack()
+{
+	tarball=$1
+	dstpath=`pwd`
+
+	if [[ $# == 2 ]] ; then
+		dstpath=$2
+	fi
+
+	pr_info "decompress $tarball => $dstpath"
+
+	mkdir -p $dstpath
+	case $tarball in
+		*.tar.gz)
+			tar -xzf $tarball -C $dstpath
+			;;
+
+		*.tar.bz2)
+			tar -xjf $tarball -C $dstpath
+			;;
+
+		*.tar.xz)
+			tar -xJf $tarball -C $dstpath
+			;;
+
+		*.tar.zst)
+			tar -I zstd -xf $tarball -C $dstpath
+			;;
+
+		*.tar)
+			tar -xf $tarball -C $dstpath
+			;;
+
+		*.zip)
+			unzip -qo $tarball -d $dstpath
+			;;
+
+		*)
+			pr_error "decompress Unsupport packet: $tarball"
+			return 1;
+			;;
+	esac
+}
+
+function do_export()
+{
+	BUILD_ARCH=$(uname -m)
+	if [[ $BUILD_ARCH =~ "arm" ]] ; then
+		pr_warn "local($BUILD_ARCH) compile $LIB_NAME"
+		return ;
+	fi
+
+	pr_warn "cross(${CROSS_COMPILE}) compile $LIB_NAME"
+
+	# export cross toolchain
+	export CC=${CROSS_COMPILE}gcc
+	export CXX=${CROSS_COMPILE}g++
+	export AS=${CROSS_COMPILE}as
+	export AR=${CROSS_COMPILE}ar
+	export LD=${CROSS_COMPILE}ld
+	export NM=${CROSS_COMPILE}nm
+	export RANLIB=${CROSS_COMPILE}ranlib
+	export OBJDUMP=${CROSS_COMPILE}objdump
+	export STRIP=${CROSS_COMPILE}strip
+
+	# export cross configure
+	export CONFIG_CROSS=" --build=i686-pc-linux --host=arm-linux "
+
+	# Clear LDFLAGS and CFLAGS
+	export LDFLAGS=
+	export CFLAGS=
+}
+
+function do_fetch()
+{
+	if [ -e ${INST_FILE} ] ; then
+		pr_warn "$LIB_NAME compile and installed alredy"
+		exit ;
+	fi
+
+	if [ -d $LIB_NAME ] ; then
+		pr_warn "$LIB_NAME fetch already"
+		return ;
+	fi
+
+	if [ ! -f ${LIB_NAME}.${PACK_SUFIX} ] ; then
+		wget ${LIB_URL}/${LIB_NAME}.${PACK_SUFIX}
+		check_result "ERROR: download ${LIB_NAME} failure"
+	fi
+
+	do_unpack ${LIB_NAME}.${PACK_SUFIX}
+}
+
+function do_build()
+{
+	cd $LIB_NAME
+
+	./autogen.sh
+
+	do_export
+
+	echo "ac_cv_func_malloc_0_nonnull=yes" > arm-linux.cache
+	./configure --prefix=${PREFIX_PATH} ${CONFIG_CROSS} --enable-static \
+		--cache-file=arm-linux.cache --enable-tools
+	check_result "ERROR: configure ${LIB_NAME} failure"
+
+	make -j ${JOBS} && make install
+	check_result "ERROR: compile ${LIB_NAME} failure"
+}
+
+function do_clean()
+{
+	rm -rf *${LIB_NAME}*
+}
+
+if [[ $# == 1 && $1 == -c ]] ;then
+	pr_warn "start clean ${LIB_NAME}"
+	do_clean
+	exit;
+fi
+
+do_fetch
+
+do_build
+
diff --git a/project/openlibs/makefile b/project/openlibs/makefile
new file mode 100644
index 0000000..7f782e8
--- /dev/null
+++ b/project/openlibs/makefile
@@ -0,0 +1,10 @@
+
+PRJ_PATH=$(shell pwd)
+libs=libgpiod cjson openssl mosquitto libevent
+
+all:
+	for dir in ${libs} ;  do cd ${PRJ_PATH}/$${dir} && ./build.sh ; done
+
+clean:
+	rm -rf install
+	for dir in ${libs} ;  do cd ${PRJ_PATH}/$${dir} && ./build.sh -c ; done
diff --git a/project/openlibs/mosquitto/build.sh b/project/openlibs/mosquitto/build.sh
new file mode 100755
index 0000000..d42acea
--- /dev/null
+++ b/project/openlibs/mosquitto/build.sh
@@ -0,0 +1,188 @@
+#!/bin/bash
+
+# library name and version
+# Official: https://mosquitto.org/
+LIB_NAME=mosquitto-2.0.15
+PACK_SUFIX=tar.gz
+
+# LingYun source code FTP server
+LY_FTP=http://main.iot-yun.club:2211/src/
+
+# library download URL address
+LIB_URL=$LY_FTP
+
+# Cross compiler for cross compile on Linux server
+CROSS_COMPILE=arm-linux-gnueabihf-
+
+# compile jobs
+JOBS=`cat /proc/cpuinfo |grep "processor"|wc -l`
+
+# this project absolute path
+PRJ_PATH=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
+
+# top project absolute path
+TOP_PATH=$(realpath $PRJ_PATH/..)
+
+# binaries install path
+PREFIX_PATH=$TOP_PATH/install
+BIN_PATH=$PREFIX_PATH/bin
+LIB_PATH=$PREFIX_PATH/lib
+INC_PATH=$PREFIX_PATH/include
+
+# check installed or not file
+INST_FILE=$PREFIX_PATH/bin/mosquitto
+
+# shell script will exit once get command error
+set -e
+
+#+-------------------------+
+#| Shell script functions  |
+#+-------------------------+
+
+function pr_error() {
+	echo -e "\033[40;31m $1 \033[0m"
+}
+
+function pr_warn() {
+	echo -e "\033[40;33m $1 \033[0m"
+}
+
+function pr_info() {
+	echo -e "\033[40;32m $1 \033[0m"
+}
+
+function check_result()
+{
+	if [ $? != 0 ] ; then
+		pr_error $1
+	fi
+}
+# decompress a packet to destination path
+function do_unpack()
+{
+	tarball=$1
+	dstpath=`pwd`
+
+	if [[ $# == 2 ]] ; then
+		dstpath=$2
+	fi
+
+	pr_info "decompress $tarball => $dstpath"
+
+	mkdir -p $dstpath
+	case $tarball in
+		*.tar.gz)
+			tar -xzf $tarball -C $dstpath
+			;;
+
+		*.tar.bz2)
+			tar -xjf $tarball -C $dstpath
+			;;
+
+		*.tar.xz)
+			tar -xJf $tarball -C $dstpath
+			;;
+
+		*.tar.zst)
+			tar -I zstd -xf $tarball -C $dstpath
+			;;
+
+		*.tar)
+			tar -xf $tarball -C $dstpath
+			;;
+
+		*.zip)
+			unzip -qo $tarball -d $dstpath
+			;;
+
+		*)
+			pr_error "decompress Unsupport packet: $tarball"
+			return 1;
+			;;
+	esac
+}
+
+function do_export()
+{
+	BUILD_ARCH=$(uname -m)
+	if [[ $BUILD_ARCH =~ "arm" ]] ; then
+		pr_warn "local($BUILD_ARCH) compile $LIB_NAME"
+		return ;
+	fi
+
+	pr_warn "cross(${CROSS_COMPILE}) compile $LIB_NAME"
+
+	# export cross toolchain
+	export CC=${CROSS_COMPILE}gcc
+	export CXX=${CROSS_COMPILE}g++
+	export AS=${CROSS_COMPILE}as
+	export AR=${CROSS_COMPILE}ar
+	export LD=${CROSS_COMPILE}ld
+	export NM=${CROSS_COMPILE}nm
+	export RANLIB=${CROSS_COMPILE}ranlib
+	export OBJDUMP=${CROSS_COMPILE}objdump
+	export STRIP=${CROSS_COMPILE}strip
+
+	# export cross configure
+	export CONFIG_CROSS=" --build=i686-pc-linux --host=arm-linux "
+
+	# Clear LDFLAGS and CFLAGS
+	export LDFLAGS=
+	export CFLAGS=
+}
+
+function do_fetch()
+{
+	if [ -e ${INST_FILE} ] ; then
+		pr_warn "$LIB_NAME compile and installed alredy"
+		exit ;
+	fi
+
+	if [ -d $LIB_NAME ] ; then
+		pr_warn "$LIB_NAME fetch already"
+		return ;
+	fi
+
+	if [ ! -f ${LIB_NAME}.${PACK_SUFIX} ] ; then
+		wget ${LIB_URL}/${LIB_NAME}.${PACK_SUFIX}
+		check_result "ERROR: download ${LIB_NAME} failure"
+	fi
+
+	do_unpack ${LIB_NAME}.${PACK_SUFIX}
+}
+
+function do_build()
+{
+	cd $LIB_NAME
+
+	do_export
+
+	export CFLAGS=-I${PREFIX_PATH}/include
+	export LDFLAGS="-L${PREFIX_PATH}/lib -lcrypto -lssl -ldl -lpthread"
+	export DESTDIR=${PREFIX_PATH}
+
+	make WITH_UUID=no WITH_STATIC_LIBRARIES=yes
+	check_result "ERROR: compile ${LIB_NAME} failure"
+
+	make DESTDIR=${PREFIX_PATH} prefix=/ install
+	check_result "ERROR: compile ${LIB_NAME} failure"
+
+	install -m 755 src/mosquitto $BIN_PATH
+	install -m 644 lib/libmosquitto.a $LIB_PATH
+}
+
+function do_clean()
+{
+	rm -rf *${LIB_NAME}*
+}
+
+if [[ $# == 1 && $1 == -c ]] ;then
+	pr_warn "start clean ${LIB_NAME}"
+	do_clean
+	exit;
+fi
+
+do_fetch
+
+do_build
+
diff --git a/project/openlibs/openssl/build.sh b/project/openlibs/openssl/build.sh
new file mode 100755
index 0000000..244da93
--- /dev/null
+++ b/project/openlibs/openssl/build.sh
@@ -0,0 +1,182 @@
+#!/bin/bash
+
+# library name and version
+# Official: https://www.openssl.org/
+LIB_NAME=openssl-1.1.1v
+PACK_SUFIX=tar.gz
+
+# LingYun source code FTP server
+LY_FTP=http://main.iot-yun.club:2211/src/
+
+# library download URL address
+LIB_URL=$LY_FTP
+
+# Cross compiler for cross compile on Linux server
+CROSS_COMPILE=arm-linux-gnueabihf-
+
+# compile jobs
+JOBS=`cat /proc/cpuinfo |grep "processor"|wc -l`
+
+# this project absolute path
+PRJ_PATH=$(cd $(dirname "${BASH_SOURCE[0]}") && pwd)
+
+# top project absolute path
+TOP_PATH=$(realpath $PRJ_PATH/..)
+
+# binaries install path
+PREFIX_PATH=$TOP_PATH/install
+BIN_PATH=$PREFIX_PATH/bin
+LIB_PATH=$PREFIX_PATH/lib
+INC_PATH=$PREFIX_PATH/include
+
+# check installed or not file
+INST_FILE=$PREFIX_PATH/bin/openssl
+
+# shell script will exit once get command error
+set -e
+
+#+-------------------------+
+#| Shell script functions  |
+#+-------------------------+
+
+function pr_error() {
+	echo -e "\033[40;31m $1 \033[0m"
+}
+
+function pr_warn() {
+	echo -e "\033[40;33m $1 \033[0m"
+}
+
+function pr_info() {
+	echo -e "\033[40;32m $1 \033[0m"
+}
+
+function check_result()
+{
+	if [ $? != 0 ] ; then
+		pr_error $1
+	fi
+}
+# decompress a packet to destination path
+function do_unpack()
+{
+	tarball=$1
+	dstpath=`pwd`
+
+	if [[ $# == 2 ]] ; then
+		dstpath=$2
+	fi
+
+	pr_info "decompress $tarball => $dstpath"
+
+	mkdir -p $dstpath
+	case $tarball in
+		*.tar.gz)
+			tar -xzf $tarball -C $dstpath
+			;;
+
+		*.tar.bz2)
+			tar -xjf $tarball -C $dstpath
+			;;
+
+		*.tar.xz)
+			tar -xJf $tarball -C $dstpath
+			;;
+
+		*.tar.zst)
+			tar -I zstd -xf $tarball -C $dstpath
+			;;
+
+		*.tar)
+			tar -xf $tarball -C $dstpath
+			;;
+
+		*.zip)
+			unzip -qo $tarball -d $dstpath
+			;;
+
+		*)
+			pr_error "decompress Unsupport packet: $tarball"
+			return 1;
+			;;
+	esac
+}
+
+function do_export()
+{
+	BUILD_ARCH=$(uname -m)
+	if [[ $BUILD_ARCH =~ "arm" ]] ; then
+		pr_warn "local($BUILD_ARCH) compile $LIB_NAME"
+		return ;
+	fi
+
+	pr_warn "cross(${CROSS_COMPILE}) compile $LIB_NAME"
+
+	# export cross toolchain
+	export CC=${CROSS_COMPILE}gcc
+	export CXX=${CROSS_COMPILE}g++
+	export AS=${CROSS_COMPILE}as
+	export AR=${CROSS_COMPILE}ar
+	export LD=${CROSS_COMPILE}ld
+	export NM=${CROSS_COMPILE}nm
+	export RANLIB=${CROSS_COMPILE}ranlib
+	export OBJDUMP=${CROSS_COMPILE}objdump
+	export STRIP=${CROSS_COMPILE}strip
+
+	# export cross configure
+	export CONFIG_CROSS=" --build=i686-pc-linux --host=arm-linux "
+
+	# Clear LDFLAGS and CFLAGS
+	export LDFLAGS=
+	export CFLAGS=
+}
+
+function do_fetch()
+{
+	if [ -e ${INST_FILE} ] ; then
+		pr_warn "$LIB_NAME compile and installed alredy"
+		exit ;
+	fi
+
+	if [ -d $LIB_NAME ] ; then
+		pr_warn "$LIB_NAME fetch already"
+		return ;
+	fi
+
+	if [ ! -f ${LIB_NAME}.${PACK_SUFIX} ] ; then
+		wget ${LIB_URL}/${LIB_NAME}.${PACK_SUFIX}
+		check_result "ERROR: download ${LIB_NAME} failure"
+	fi
+
+	do_unpack ${LIB_NAME}.${PACK_SUFIX}
+}
+
+function do_build()
+{
+	cd $LIB_NAME
+
+	do_export
+
+	CROSS_COMPILE=${CROSSTOOL} ./Configure linux-armv4 \
+		threads -shared -no-zlib --prefix=$PREFIX_PATH --openssldir=$PREFIX_PATH
+	check_result "ERROR: configure ${LIB_NAME} failure"
+
+	make -j ${JOBS} && make install
+	check_result "ERROR: compile ${LIB_NAME} failure"
+}
+
+function do_clean()
+{
+	rm -rf *${LIB_NAME}*
+}
+
+if [[ $# == 1 && $1 == -c ]] ;then
+	pr_warn "start clean ${LIB_NAME}"
+	do_clean
+	exit;
+fi
+
+do_fetch
+
+do_build
+

--
Gitblit v1.9.1