From 489b6a3c10ccaff0fd0de55b5030e2992d122a7b Mon Sep 17 00:00:00 2001
From: guowenxue <guowenxue@gmail.com>
Date: Tue, 18 Nov 2025 15:41:51 +0800
Subject: [PATCH] update mqttd program to compatble with android app

---
 project/4.mqttd/booster/proc.h    |   28 
 .gitignore                        |    2 
 project/4.mqttd/etc/iotd.service  |   16 
 project/4.mqttd/iotd.c            |  371 ++++++++++
 project/4.mqttd/modules/modules.h |   24 
 project/4.mqttd/modules/relay.c   |  157 ++++
 project/4.mqttd/booster/proc.c    |   12 
 project/4.mqttd/makefile          |    8 
 project/4.mqttd/booster/makefile  |   19 
 project/4.mqttd/modules/tsl2561.h |   34 
 project/4.mqttd/modules/ds18b20.c |    0 
 project/4.mqttd/modules/leds.c    |   64 
 project/4.mqttd/modules/tsl2561.c |  212 +++++
 project/4.mqttd/config.h          |   83 ++
 project/4.mqttd/modules/ds18b20.h |    0 
 project/4.mqttd/modules/pwm.h     |   44 +
 project/4.mqttd/modules/makefile  |   41 +
 project/4.mqttd/modules/leds.h    |    0 
 project/4.mqttd/modules/sht20.h   |    0 
 project/4.mqttd/config.c          |  211 +++++
 project/4.mqttd/modules/pwm.c     |  213 +++++
 /dev/null                         |  434 ------------
 project/4.mqttd/modules/sht20.c   |    7 
 project/4.mqttd/etc/iotd.conf     |   18 
 project/4.mqttd/booster/logger.c  |    2 
 project/4.mqttd/booster/utils.h   |   61 +
 project/4.mqttd/modules/relay.h   |   56 +
 27 files changed, 1,592 insertions(+), 525 deletions(-)

diff --git a/.gitignore b/.gitignore
index ec35276..b6cae91 100644
--- a/.gitignore
+++ b/.gitignore
@@ -8,7 +8,7 @@
 *.o
 lib*.so*
 lib*.a
-mqttd
+iotd
 
 # ctags files
 cscope.*
diff --git a/project/4.mqttd/booster/config.c b/project/4.mqttd/booster/config.c
deleted file mode 100644
index 43c8d29..0000000
--- a/project/4.mqttd/booster/config.c
+++ /dev/null
@@ -1,200 +0,0 @@
-/*********************************************************************************
- *      Copyright:  (C) 2019 LingYun IoT System Studio
- *                  All rights reserved.
- *
- *       Filename:  config.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 "config.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/4.mqttd/booster/config.h b/project/4.mqttd/booster/config.h
deleted file mode 100644
index d06f1ed..0000000
--- a/project/4.mqttd/booster/config.h
+++ /dev/null
@@ -1,71 +0,0 @@
-/*********************************************************************************
- *      Copyright:  (C) 2019 LingYun IoT System Studio
- *                  All rights reserved.
- *
- *       Filename:  config.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/4.mqttd/booster/logger.c b/project/4.mqttd/booster/logger.c
index b973d06..c049684 100644
--- a/project/4.mqttd/booster/logger.c
+++ b/project/4.mqttd/booster/logger.c
@@ -101,7 +101,7 @@
     {
         if ( !(fp = fopen(fname, "a+")) )
         {
-            fprintf(stderr, "%s() failed: %s\n", __func__, strerror(errno));
+            fprintf(stderr, "%s() %s failed: %s\n", __func__, fname, strerror(errno));
             return -2;
         }
         L.fp = fp;
diff --git a/project/4.mqttd/booster/makefile b/project/4.mqttd/booster/makefile
index 1da2227..dbd9de2 100644
--- a/project/4.mqttd/booster/makefile
+++ b/project/4.mqttd/booster/makefile
@@ -11,23 +11,28 @@
 #
 #*******************************************************************************
 
-PWD=$(shell pwd )
-
-CFLAGS += -Wno-format-overflow
+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
 
-LIBNAME=$(shell basename ${PWD} )
-TOPDIR=$(shell dirname ${PWD} )
+OPENLIBS_INCPATH=${TOPDIR}/openlibs/install/include
+OPENLIBS_LIBPATH=${TOPDIR}/openlibs/install/lib
 
-all: clean
+CFLAGS+=-I${OPENLIBS_INCPATH} -I${TOPDIR}/booster
+
+all: prelibs clean
 	@rm -f *.o
-	@${CROSS_COMPILE}gcc ${CFLAGS} -I${TOPDIR} -c *.c
+	${CROSS_COMPILE}gcc ${CFLAGS} -c *.c
 	${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
 
+prelibs:
+	if [ -n "${CROSS_COMPILE}" ] ; then cd ${TOPDIR}/openlibs/libgpiod && ./build.sh ; fi;
+
 clean:
 	@rm -f *.o
 	@rm -f *.a
diff --git a/project/4.mqttd/booster/proc.c b/project/4.mqttd/booster/proc.c
index b55b202..6cb2109 100644
--- a/project/4.mqttd/booster/proc.c
+++ b/project/4.mqttd/booster/proc.c
@@ -3,7 +3,7 @@
  *                  All rights reserved.
  *
  *       Filename:  proc.c
- *    Description:  This file is the process API
+ *    Description:  This file is the process/thread API
  *
  *        Version:  1.0.0(7/06/2020)
  *         Author:  Guo Wenxue <guowenxue@gmail.com>
@@ -59,7 +59,6 @@
             break;
     }
 }
-
 
 /* install default signal process functions  */
 void install_default_signal(void)
@@ -184,8 +183,6 @@
 
     return 0;
 }
-
-
 
 /* ****************************************************************************
  * FunctionName: record_daemon_pid
@@ -345,8 +342,6 @@
     return 0;
 }
 
-
-
 /* ****************************************************************************
  * FunctionName: set_daemon_running
  * Description : Set the programe running as daemon if it's not running and record
@@ -398,8 +393,6 @@
         goto CleanUp;
 
 CleanUp:
-
-
     if( thread_id )
     {
         if( rv )
@@ -412,7 +405,6 @@
     pthread_attr_destroy(&thread_attr);
     return rv;
 }
-
 
 /* excute a linux command by system() */
 void exec_system_cmd(const char *format, ...)
@@ -428,5 +420,3 @@
 
     system(cmd);
 }
-
-
diff --git a/project/4.mqttd/booster/proc.h b/project/4.mqttd/booster/proc.h
index 1a8a14e..7ee5113 100644
--- a/project/4.mqttd/booster/proc.h
+++ b/project/4.mqttd/booster/proc.h
@@ -11,11 +11,10 @@
  *
  ********************************************************************************/
 
-#ifndef __PROC_H_
-#define __PROC_H_
+#ifndef __UTIL_PROC_H_
+#define __UTIL_PROC_H_
 
 #include <signal.h>
-#include <time.h>
 
 #define PID_ASCII_SIZE  11
 
@@ -62,28 +61,5 @@
 
 /* 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/4.mqttd/booster/utils.h b/project/4.mqttd/booster/utils.h
new file mode 100644
index 0000000..65bbc07
--- /dev/null
+++ b/project/4.mqttd/booster/utils.h
@@ -0,0 +1,61 @@
+/********************************************************************************
+ *      Copyright:  (C) 2020 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  util.h
+ *    Description:  This file is common utility functions
+ *
+ *        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_H_
+#define __UTIL_H_
+
+#include <time.h>
+#include <stddef.h>
+
+#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
+
+/* +------------------------+
+ * |   time 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 ;
+}
+
+static inline int check_timeout(time_t *last_time, int interval)
+{
+    int                  timeout = 0;
+    time_t               now;
+
+    time(&now);
+
+    if( difftime(now, *last_time)>interval )
+    {
+        timeout = 1;
+        *last_time = now;
+    }
+
+    return timeout;
+}
+
+#endif
diff --git a/project/4.mqttd/config.c b/project/4.mqttd/config.c
new file mode 100644
index 0000000..9561acd
--- /dev/null
+++ b/project/4.mqttd/config.c
@@ -0,0 +1,211 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  config.c
+ *    Description:  This file is iotd 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 "config.h"
+#include "logger.h"
+#include "iniparser.h"
+
+int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug)
+{
+    dictionary          *ini;
+    const char          *str;
+    int                  val;
+    int                  rv = 0;
+    logger_t            *logger;
+    hwinfo_t            *hwinfo;
+    mqtt_ctx_t          *mqtt;
+
+    if( !conf_file || !ctx )
+    {
+        fprintf(stderr, "%s() Invalid input arguments\n", __func__);
+        return -1;
+    }
+
+    memset(ctx, 0, sizeof(*ctx));
+    logger = &ctx->logger;
+    hwinfo = &ctx->hwinfo;
+    mqtt = &ctx->mqtt;
+
+    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/iotd.log");
+        strncpy(logger->logfile, str, sizeof(logger->logfile));
+        logger->logsize = iniparser_getint(ini, "logger:size", 1024);
+        logger->loglevel = iniparser_getint(ini, "logger:level", LOG_LEVEL_INFO);
+    }
+    else
+    {
+        strncpy(logger->logfile, "console", sizeof(logger->logfile));
+        logger->loglevel = LOG_LEVEL_DEBUG;
+        logger->logsize = 0;
+    }
+
+    if( log_open(logger->logfile, logger->loglevel, logger->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 ""  */
+    strncpy(mqtt->devid, str, sizeof(mqtt->devid));
+    log_info("Parser device ID [%s]\n", mqtt->devid);
+
+
+    /*+------------------------------------------------------+
+     *|       parser hardware module configuration           |
+     *+------------------------------------------------------+*/
+
+    /* relay */
+    hwinfo->relay=iniparser_getint(ini, "hardware:relay", 0);
+    if( !hwinfo->relay )
+        log_warn("Parser relay module disabled\n");
+    else
+        log_info("Parser relay module enabled\n");
+
+    /* RGB 3-colors LED */
+    hwinfo->led=iniparser_getint(ini, "hardware:rgbled", 0);
+    if( !hwinfo->led )
+        log_warn("Parser RGB 3-colors Led module disabled\n");
+    else
+        log_info("Parser RGB 3-colors Led module enabled\n");
+
+    /* beeper */
+    hwinfo->beeper=iniparser_getint(ini, "hardware:beep", 0);
+    if( !hwinfo->beeper )
+        log_warn("Parser beeper module disabled\n");
+    else
+        log_info("Parser beeper module enabled\n");
+
+    /* DS18B20 temperature module */
+    hwinfo->ds18b20=iniparser_getint(ini, "hardware:ds18b20", 0);
+    if( !hwinfo->ds18b20 )
+        log_warn("Parser DS18B20 temperature module disabled\n");
+    else
+        log_info("Parser DS18B20 temperature module enabled\n");
+
+    /* SHT20 temperature and hummidity module */
+    hwinfo->sht2x=iniparser_getint(ini, "hardware:sht2x", 0);
+    if( !hwinfo->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 */
+    hwinfo->tsl2561=iniparser_getint(ini, "hardware:tsl2561", 0);
+    if( !hwinfo->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(mqtt->host, str, sizeof(mqtt->host) );
+
+    if( (val=iniparser_getint(ini, "broker:port", -1)) < 0 )
+    {
+        log_error("ERROR: Parser MQTT broker server port failure\n");
+        rv = -5;
+        goto cleanup;
+    }
+    mqtt->port = val;
+    log_info("Parser MQTT broker server [%s:%d]\n", mqtt->host, mqtt->port);
+
+    if( (str=iniparser_getstring(ini, "broker:token", NULL)) )
+    {
+        strncpy(mqtt->token, str, sizeof(mqtt->uid) );
+        log_info("Parser broker token [%s]\n", mqtt->token);
+    }
+
+    if( (str=iniparser_getstring(ini, "broker:username", NULL)) )
+        strncpy(mqtt->uid, str, sizeof(mqtt->uid) );
+
+    if( (str=iniparser_getstring(ini, "broker:password", NULL)) )
+        strncpy(mqtt->pwd, str, sizeof(mqtt->pwd) );
+
+    if( mqtt->uid )
+        log_info("Parser broker account [%s:%s]\n", mqtt->uid, mqtt->pwd);
+
+    mqtt->keepalive = iniparser_getint(ini, "broker:keepalive", DEF_KEEPALIVE);
+    log_info("Parser broker keepalive timeout [%d] seconds\n", mqtt->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;
+    }
+    snprintf(mqtt->pubTopic, sizeof(mqtt->pubTopic), "%s/%s", str, mqtt->devid);
+
+    mqtt->pubQos = iniparser_getint(ini, "publisher:pubQos", DEF_QOS);
+    mqtt->interval = iniparser_getint(ini, "publisher:interval", DEF_PUBINTERVAL);
+    log_info("Parser publisher topic \"%s\" with Qos[%d] interval[%d]\n", mqtt->pubTopic, mqtt->pubQos, mqtt->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;
+    }
+    snprintf(mqtt->subTopic, sizeof(mqtt->subTopic), "%s/%s", str, mqtt->devid);
+
+    mqtt->subQos = iniparser_getint(ini, "subsciber:subQos", DEF_QOS);
+    log_info("Parser subscriber topic \"%s\" with Qos[%d]\n", mqtt->subTopic, mqtt->subQos);
+
+cleanup:
+    if( ini )
+        iniparser_freedict(ini);
+
+    if( rv )
+        log_close();
+
+    return rv;
+}
+
diff --git a/project/4.mqttd/config.h b/project/4.mqttd/config.h
new file mode 100644
index 0000000..bea993a
--- /dev/null
+++ b/project/4.mqttd/config.h
@@ -0,0 +1,83 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  config.h
+ *    Description:  This file is iotd 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_
+
+#include "utils.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 hwinfo_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 */
+} hwinfo_t;
+
+/* logger settings */
+typedef struct logger_s
+{
+    char            logfile[128]; /* logger record file */
+    int             loglevel;     /* logger level  */
+    int             logsize;      /* logger file maxsize, oversize will rollback */
+} logger_t;
+
+typedef struct mqtt_ctx_s
+{
+    char			devid[32];    /*  device ID */
+    void           *userdata;      /* user data pointer */
+
+    /* Broker settings  */
+    char            host[128];  /* MQTT broker server name  */
+    int             port;       /* MQTT broker listen port  */
+    char            uid[64];    /* username */
+    char            pwd[64];    /* password */
+    char            token[64];  /* token */
+    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;
+
+typedef struct iotd_ctx_s
+{
+    logger_t        logger;
+    hwinfo_t        hwinfo;
+    mqtt_ctx_t      mqtt;
+} iotd_ctx_t;
+
+/* get iotd_ctx address by mqtt_ctx address */
+#define to_iotd(ctx)	container_of(ctx, iotd_ctx_t, mqtt);
+
+extern int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug);
+
+#endif
+
diff --git a/project/4.mqttd/etc/mqttd.conf b/project/4.mqttd/etc/iotd.conf
similarity index 63%
rename from project/4.mqttd/etc/mqttd.conf
rename to project/4.mqttd/etc/iotd.conf
index 8417746..a32c09f 100644
--- a/project/4.mqttd/etc/mqttd.conf
+++ b/project/4.mqttd/etc/iotd.conf
@@ -1,14 +1,17 @@
 [common]
-devid="RPi3B#01"
+devid="rpi001"
 
-# 树莓派连接的外设信息,0:禁用或未连接  其他: 使能或相关硬件连接的Pin管脚(wPI模式)
+# 树莓派连接的外设信息,0:禁用或未连接
 [hardware]
 
 # 是否使能 RGB 三色灯模块,0:禁用  1:使能
 rgbled=1
 
+# 是否使能继电器模块,0:禁用 1:使能
+relay=0
+
 # 是否使能 DS18b20 温度传感器模块,0:禁用  1:使能
-ds18b20=1
+ds18b20=0
 
 # 是否使能 SHT2X 温湿度传感器模块,0:禁用  1:使能
 sht2x=1
@@ -16,7 +19,7 @@
 [logger]
 
 # 日志记录文件
-file=/tmp/mqttd.log
+file=/var/log/iotd.log
 
 # 日志级别: 0:ERROR 1:WARN 2:INFO 3:DEBUG 4:TRACE 
 level=2
@@ -44,7 +47,7 @@
 
 [publisher]
 
-# mosquitto_sub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Uplink
+# mosquitto_sub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Uplink/rpi001
 pubTopic="$Sys/Studio/Uplink"
 pubQos=0
 
@@ -53,7 +56,10 @@
 
 [subsciber]
 
-# 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"}]}'
+# mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "RedLed":"on" }'
+# mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "GreenLed":"on" }'
+# mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "BlueLed":"on" }'
+# mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "Relay":"on" }'
 subTopic="$Sys/Studio/Downlink"
 subQos=0
 
diff --git a/project/4.mqttd/etc/iotd.service b/project/4.mqttd/etc/iotd.service
new file mode 100644
index 0000000..92ad25f
--- /dev/null
+++ b/project/4.mqttd/etc/iotd.service
@@ -0,0 +1,16 @@
+[Unit]
+Description=RaspberryPi iotd program Service
+After=network.target
+
+[Service]
+Type=simple
+ExecStartPre=/bin/rm -f /tmp/.iotd.pid /var/log/iotd.log
+ExecStart=/usr/bin/iotd -c /etc/iotd.conf
+ExecStop=/bin/rm -f /tmp/.iotd.pid
+
+Restart=always
+RestartSec=2
+
+[Install]
+WantedBy=multi-user.target
+
diff --git a/project/4.mqttd/iotd.c b/project/4.mqttd/iotd.c
new file mode 100644
index 0000000..d56f9a5
--- /dev/null
+++ b/project/4.mqttd/iotd.c
@@ -0,0 +1,371 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  main.c
+ *    Description:  This file is MQTT publisher/subscriber example program.
+ *
+ *        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 "proc.h"
+#include "config.h"
+#include "ds18b20.h"
+#include "sht20.h"
+#include "leds.h"
+#include "relay.h"
+
+#define PROG_VERSION               "v1.0.0"
+#define DAEMON_PIDFILE             "/tmp/.iotd.pid"
+
+void *mqtt_worker(void *args);
+
+static void program_usage(char *progname)
+{
+    printf("Usage: %s [OPTION]...\n", progname);
+    printf(" %s is LingYun studio iotd program running on RaspberryPi\n", progname);
+
+    printf("\nMandatory arguments to long options are mandatory for short options too:\n");
+    printf(" -b[daemon  ]  Running in daemon mode\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 = 0;
+    pthread_t          tid;
+    iotd_ctx_t         ctx;
+    char               *conf_file="/etc/iotd.conf";
+    int                debug = 0;
+    int                opt;
+    char              *progname=NULL;
+
+    struct option long_options[] = {
+        {"conf", required_argument, NULL, 'c'},
+        {"daemon", no_argument, NULL, 'b'},
+        {"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:bdvh", long_options, NULL)) != -1)
+    {
+        switch (opt)
+        {
+            case 'c': /* Set configure file */
+                conf_file = optarg;
+                break;
+
+            case 'b': /* Set daemon running */
+                daemon = 1;
+                break;
+
+            case 'd': /* Set debug running */
+                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;
+        }
+    }
+
+    /* parser configure file */
+    if( parser_conf(conf_file, &ctx, debug)<0 )
+    {
+        fprintf(stderr, "Parser iotd configure file failure\n");
+        return -2;
+    }
+
+    /* install signal proc handler */
+    install_default_signal();
+
+    /* check program already running or not, if already running then exit, or set running as daemon */
+    if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 )
+        goto cleanup;
+
+    /* initial mosquitto library */
+    mosquitto_lib_init();
+
+    /* start MQTT thread */
+    if( thread_start(&tid, mqtt_worker, &ctx.mqtt ) < 0 )
+    {
+        log_error("Start MQTT worker thread failure\n");
+        goto cleanup;
+    }
+    log_info("Start MQTT worker thread ok\n");
+
+    /* control thread loop */
+    while( !g_signal.stop )
+    {
+        sleep(1);
+    }
+
+cleanup:
+    mosquitto_lib_cleanup();
+    log_close();
+
+    return 0;
+}
+
+
+/*
+ * +--------------------------------+
+ * |      MQTT Thread worker        |
+ * +--------------------------------+
+ */
+
+/* process publisher(uplink) data */
+void pub_connect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    mqtt_ctx_t             *mqtt = (mqtt_ctx_t *)userdata;
+    iotd_ctx_t             *iotd = to_iotd(mqtt);
+    int                     rv = 0;
+    char                    msg[128];
+    float                   temp = 0.0; /* temperature */
+    float                   rh = 0.0;   /* relative humidity */
+    int                     retain = 0;
+    static time_t           last_time = 0;
+
+    /* publish time is not arrive */
+    if( !check_timeout(&last_time, mqtt->interval) )
+        return ;
+
+    log_debug("Publish topic '%s'\n", mqtt->pubTopic);
+
+    if( iotd->hwinfo.ds18b20 )
+    {
+        log_debug("DS18B20 temperature sensor enabled, start broadcast it\n");
+        if( ds18b20_get_temperature(&temp)<0 )
+            return ;
+
+        memset(msg, 0, sizeof(msg));
+        snprintf(msg, sizeof(msg), "{\"temperature\":%.2f}", temp);
+        rv = mosquitto_publish(mosq, NULL, mqtt->pubTopic, strlen(msg), msg, mqtt->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( iotd->hwinfo.sht2x )
+    {
+        log_debug("SHT2X temperature and humidity sensor enabled, start broadcast it\n");
+        if( sht2x_get_temp_humidity(&temp, &rh)<0 )
+            return ;
+
+        memset(msg, 0, sizeof(msg));
+        snprintf(msg, sizeof(msg), "{\"temperature\":%.2f, \"humidity\":%.2f}", temp, rh);
+        rv = mosquitto_publish(mosq, NULL, mqtt->pubTopic, strlen(msg), msg, mqtt->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);
+        }
+    }
+
+    return ;
+}
+
+/* process subscriber(downlink) data */
+void proc_json_items(cJSON *root, mqtt_ctx_t *mqtt)
+{
+    iotd_ctx_t             *iotd = to_iotd(mqtt);
+    cJSON                  *item;
+    char                    leds[LED_CNT][16] = {"RedLed", "GreenLed", "BlueLed"};
+    char                    relays[RELAY_CNT][16] = {"Relay"};
+    int                     which;
+
+    if( !root )
+    {
+        log_error("Invalid input arguments $root\n");
+        return ;
+    }
+
+    /* parser JSON command for relays */
+    if( iotd->hwinfo.relay )
+    {
+        for(which=0; which<RELAY_CNT; which++)
+        {
+            item = cJSON_GetObjectItem(root, relays[which]);
+            if( !item )
+                continue;
+
+            if( strcasecmp(item->valuestring, "on") )
+                turn_relay(which, ON);
+            else if( strcasecmp(item->valuestring, "off") )
+                turn_relay(which, OFF);
+        }
+    }
+
+    /* parser JSON command for RGB leds */
+    if( iotd->hwinfo.led )
+    {
+        for(which=0; which<LED_CNT; which++)
+        {
+            item = cJSON_GetObjectItem(root, leds[which]);
+            if( !item )
+                continue;
+
+            if( strcasecmp(item->valuestring, "on") )
+                turn_led(which, ON);
+            else if( strcasecmp(item->valuestring, "off") )
+                turn_led(which, OFF);
+        }
+    }
+
+    return ;
+}
+
+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;
+
+    if ( !message->payloadlen )
+    {
+        log_error("%s (null)\n", message->topic);
+        return ;
+    }
+
+    log_info("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 ;
+    }
+
+    /* process receive message data */
+    proc_json_items(root, ctx);
+
+    /* must delete it, or it will result memory leak */
+    cJSON_Delete(root);
+
+    return ;
+}
+
+void mqtt_connect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+
+    if( result )
+    {
+        log_error("mosquitto connect to broker server failed, rv=%d\n", result);
+        return ;
+    }
+
+    log_info("mosquitto connect to broker server[%s:%d] successfully\n", ctx->host, ctx->port);
+    mosquitto_subscribe(mosq, NULL, ctx->subTopic, ctx->subQos);
+}
+
+void mqtt_disconnect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
+
+    log_warn("mosquitto disconnect to broker server[%s:%d], reason=%d\n", ctx->host, ctx->port, result);
+}
+
+void *mqtt_worker(void *args)
+{
+    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)args;
+    struct mosquitto       *mosq;
+    bool                    session = true;
+    int                     rv = 0;
+
+    mosq = mosquitto_new(ctx->devid, session, ctx);
+    if( !mosq )
+    {
+        log_error("mosquitto_new failure\n");
+        return NULL;
+    }
+
+    /* connnect to broker by token or uid/pwd */
+    if( strlen(ctx->token ) > 0)
+    {
+        log_info("Using token authentication: '%s'\n", ctx->token);
+        mosquitto_username_pw_set(mosq, ctx->token, NULL);
+    }
+    else if( strlen(ctx->uid)> 0  && strlen(ctx->pwd)> 0 )
+    {
+        log_info("Using username/password authentication\n");
+        mosquitto_username_pw_set(mosq, ctx->uid, ctx->pwd);
+    }
+
+    /* set callback functions */
+    mosquitto_connect_callback_set(mosq, mqtt_connect_callback);
+    mosquitto_disconnect_callback_set(mosq, mqtt_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("mosquitto connect to broker[%s:%d] failure: %s\n", ctx->host, ctx->port, strerror(errno));
+            sleep(1);
+            continue;
+        }
+
+        while(!g_signal.stop)
+        {
+            /* periodically publish and report data */
+            pub_connect_callback(mosq, ctx, MOSQ_ERR_SUCCESS);
+
+            /* MQTT process in Non-blocking mode, timeout for 1s */
+            if( MOSQ_ERR_SUCCESS != (rv = mosquitto_loop(mosq, 1000, 1)) )
+            {
+                log_warn("MQTT loop error: %s, reconnecting...\n", mosquitto_strerror(rv));
+                break;
+            }
+        }
+
+        mosquitto_disconnect(mosq);
+        sleep(1);
+    }
+
+    mosquitto_destroy(mosq);
+    return NULL;
+}
+
diff --git a/project/4.mqttd/makefile b/project/4.mqttd/makefile
index 0eaa3c7..c55ec31 100644
--- a/project/4.mqttd/makefile
+++ b/project/4.mqttd/makefile
@@ -12,7 +12,7 @@
 #*******************************************************************************
 
 PRJ_PATH=$(shell pwd)
-APP_NAME = mqttd
+APP_NAME = iotd
 
 BUILD_ARCH=$(shell uname -m)
 ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
@@ -26,7 +26,7 @@
 CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized -D_GNU_SOURCE
 
 # C source file in sub-directory
-SRCS=booster
+SRCS=booster modules
 SRCS_PATH=$(patsubst %,${PRJ_PATH}/%,$(SRCS))
 CFLAGS+=$(patsubst %,-I%,$(SRCS_PATH))
 LDFLAGS+=$(patsubst %,-L%,$(SRCS_PATH))
@@ -44,13 +44,13 @@
 LDFLAGS+=-lpthread
 
 all: entry subdir
-	${CROSS_COMPILE}gcc ${CFLAGS} mqttd.c -o ${APP_NAME} ${LDFLAGS}
+	${CROSS_COMPILE}gcc ${CFLAGS} ${SRCFILES} -o ${APP_NAME} ${LDFLAGS}
 
 entry:
 	@echo "Building ${APP_NAME} on ${BUILD_ARCH}"
 
 subdir:
-	@for dir in ${libs} ;  do CFLAGS="${CFLAGS}" make -C $${dir}; done
+	@for dir in ${libs} ;  do make -C $${dir} ; done
 
 install:
 	cp ${APP_NAME} /tftp
diff --git a/project/4.mqttd/booster/ds18b20.c b/project/4.mqttd/modules/ds18b20.c
similarity index 100%
rename from project/4.mqttd/booster/ds18b20.c
rename to project/4.mqttd/modules/ds18b20.c
diff --git a/project/4.mqttd/booster/ds18b20.h b/project/4.mqttd/modules/ds18b20.h
similarity index 100%
rename from project/4.mqttd/booster/ds18b20.h
rename to project/4.mqttd/modules/ds18b20.h
diff --git a/project/4.mqttd/booster/leds.c b/project/4.mqttd/modules/leds.c
similarity index 71%
rename from project/4.mqttd/booster/leds.c
rename to project/4.mqttd/modules/leds.c
index 2e46182..febdfcf 100644
--- a/project/4.mqttd/booster/leds.c
+++ b/project/4.mqttd/modules/leds.c
@@ -64,31 +64,31 @@
         return -2;
     }
 
-	for(i=0; i<ctx->count; i++)
-	{
-		if( which == i )
-		{
-			led = &ctx->leds[i];
+    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;
-			}
+            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;
-			}
+            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);
-		}
-	}
+            log_debug("request %5s led[%d] for gpio output okay\n", led->name, led->gpio);
+        }
+    }
 
     return 0;
 
@@ -102,7 +102,7 @@
     int            i;
     led_info_t    *led;
 
-    log_warn("terminate RGB Led gpios\n");
+    log_debug("terminate RGB Led gpios\n");
 
     if( !ctx )
     {
@@ -114,15 +114,15 @@
         return 0;
 
     for(i=0; i<ctx->count; i++)
-	{
-		if( which == i )
-		{
-			led = &ctx->leds[i];
+    {
+        if( which == i )
+        {
+            led = &ctx->leds[i];
 
-			if( led->line )
-				gpiod_line_release(led->line);
-		}
-	}
+            if( led->line )
+                gpiod_line_release(led->line);
+        }
+    }
 
     gpiod_chip_close(ctx->chip);
     return 0;
@@ -148,6 +148,8 @@
 
     led = &ctx.leds[which];
 
+    log_info("turn Led %s %s\n", led->name, cmd?"on":"off");
+
     if( OFF == cmd )
     {
         gpiod_line_set_value(led->line, !led->active);
diff --git a/project/4.mqttd/booster/leds.h b/project/4.mqttd/modules/leds.h
similarity index 100%
rename from project/4.mqttd/booster/leds.h
rename to project/4.mqttd/modules/leds.h
diff --git a/project/4.mqttd/modules/makefile b/project/4.mqttd/modules/makefile
new file mode 100644
index 0000000..dbd9de2
--- /dev/null
+++ b/project/4.mqttd/modules/makefile
@@ -0,0 +1,41 @@
+#********************************************************************************
+#      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: prelibs clean
+	@rm -f *.o
+	${CROSS_COMPILE}gcc ${CFLAGS} -c *.c
+	${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
+
+prelibs:
+	if [ -n "${CROSS_COMPILE}" ] ; then cd ${TOPDIR}/openlibs/libgpiod && ./build.sh ; fi;
+
+clean:
+	@rm -f *.o
+	@rm -f *.a
+
+distclean:
+	@make clean
diff --git a/project/4.mqttd/modules/modules.h b/project/4.mqttd/modules/modules.h
new file mode 100644
index 0000000..2e2c32c
--- /dev/null
+++ b/project/4.mqttd/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/4.mqttd/modules/pwm.c b/project/4.mqttd/modules/pwm.c
new file mode 100644
index 0000000..ffaa7be
--- /dev/null
+++ b/project/4.mqttd/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 "utils.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/4.mqttd/modules/pwm.h b/project/4.mqttd/modules/pwm.h
new file mode 100644
index 0000000..1e07f6a
--- /dev/null
+++ b/project/4.mqttd/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/4.mqttd/modules/relay.c b/project/4.mqttd/modules/relay.c
new file mode 100644
index 0000000..97e494c
--- /dev/null
+++ b/project/4.mqttd/modules/relay.c
@@ -0,0 +1,157 @@
+/*********************************************************************************
+ *      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];
+    log_info("turn Led %s %s\n", relay->name, cmd?"on":"off");
+
+    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/4.mqttd/modules/relay.h b/project/4.mqttd/modules/relay.h
new file mode 100644
index 0000000..c1d8c1d
--- /dev/null
+++ b/project/4.mqttd/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/4.mqttd/booster/sht20.c b/project/4.mqttd/modules/sht20.c
similarity index 96%
rename from project/4.mqttd/booster/sht20.c
rename to project/4.mqttd/modules/sht20.c
index d3e0b8f..a96d0c6 100644
--- a/project/4.mqttd/booster/sht20.c
+++ b/project/4.mqttd/modules/sht20.c
@@ -40,9 +40,8 @@
 #include <linux/i2c-dev.h>
 
 #include "logger.h"
+#include "utils.h"
 #include "sht20.h"
-
-#define msleep(x) usleep(1000*(x));
 
 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);
@@ -91,7 +90,7 @@
 
     memset(buf, 0, sizeof(buf));
     i2c_read(fd, SHT20_I2CADDR, buf, 3);
-    log_dump(LOG_LEVEL_DEBUG, "Temperature sample data: ", (char *)buf, 3);
+    log_dump(LOG_LEVEL_TRACE, "Temperature sample data: ", buf, 3);
 
     if( !sht20_checksum(buf, 2, buf[2]) )
     {
@@ -114,7 +113,7 @@
 
     memset(buf, 0, sizeof(buf));
     i2c_read(fd, SHT20_I2CADDR, buf, 3);
-    log_dump(LOG_LEVEL_DEBUG, "Relative humidity sample data: ", (char *)buf, 3);
+    log_dump(LOG_LEVEL_TRACE, "Relative humidity sample data: ", buf, 3);
 
     if( !sht20_checksum(buf, 2, buf[2]) )
     {
diff --git a/project/4.mqttd/booster/sht20.h b/project/4.mqttd/modules/sht20.h
similarity index 100%
rename from project/4.mqttd/booster/sht20.h
rename to project/4.mqttd/modules/sht20.h
diff --git a/project/4.mqttd/modules/tsl2561.c b/project/4.mqttd/modules/tsl2561.c
new file mode 100644
index 0000000..7bb19d7
--- /dev/null
+++ b/project/4.mqttd/modules/tsl2561.c
@@ -0,0 +1,212 @@
+/*********************************************************************************
+ *      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:
+ *              TSL2561 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 "utils.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];
+    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++)
+    {
+        rv = tsl2561_readreg(fd, regs_addr[i], &reg_data[i]);
+        if( rv < 0)
+            goto failed;
+    }
+
+    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 cleanup;
+    }
+
+    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;
+
+cleanup:
+    tsl2561_power(fd, OFF);
+
+failed:
+    close(fd);
+    return rv;
+}
diff --git a/project/4.mqttd/modules/tsl2561.h b/project/4.mqttd/modules/tsl2561.h
new file mode 100644
index 0000000..540a0bd
--- /dev/null
+++ b/project/4.mqttd/modules/tsl2561.h
@@ -0,0 +1,34 @@
+/*********************************************************************************
+ *      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:
+ *              TSL2561 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/4.mqttd/mqttd.c b/project/4.mqttd/mqttd.c
deleted file mode 100644
index a22a6b9..0000000
--- a/project/4.mqttd/mqttd.c
+++ /dev/null
@@ -1,434 +0,0 @@
-/*********************************************************************************
- *      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 "proc.h"
-#include "config.h"
-#include "ds18b20.h"
-#include "sht20.h"
-#include "leds.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="/etc/mqttd.conf";
-	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;
-
-	/* parser configure file */
-	if( mqttd_parser_conf(conf_file, &ctx, debug)<0 )
-	{
-		fprintf(stderr, "Parser mqtted configure file failure\n");
-		return -2;
-	}
-
-	/* install signal proc handler */
-	install_default_signal();
-
-	/* check program already running or not, if already running then exit, or set running as daemon */
-	if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 )
-		goto cleanup;
-
-	/* initial mosquitto library */
-	mosquitto_lib_init();
-
-	/* create MQTT subsciber thread  */
-	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");
-
-	/* create MQTT publisher thread  */
-	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;
-	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);
-					}
-				}
-			}
-		}
-	}
-}
-
-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;
-}
-

--
Gitblit v1.9.1