From e30a4c8103e221201e5bfc1e3f9b19e7a86f68d4 Mon Sep 17 00:00:00 2001
From: Guo Wenxue <guowenxue@gmail.com>
Date: Sun, 29 Dec 2024 16:23:37 +0800
Subject: [PATCH] Add iotd program

---
 project/iotd/hal/gpio.h    |   58 +
 project/iotd/hal/sht20.c   |  466 +++++++++++
 project/iotd/hal/hal.c     |   89 ++
 project/iotd/makefile      |   64 +
 project/iotd/hal/hal.h     |   42 +
 project/iotd/src/mqtt.c    |  329 +++++++
 project/iotd/etc/iotd.conf |   79 +
 project/iotd/hal/sht20.h   |   33 
 project/iotd/hal/gpio.c    |  294 +++++++
 project/iotd/src/mqtt.h    |   45 +
 project/iotd/src/makefile  |   15 
 project/iotd/hal/makefile  |   15 
 project/iotd/hal/ds18b20.h |   19 
 project/iotd/src/conf.c    |  304 +++++++
 project/iotd/hal/ds18b20.c |  106 ++
 project/iotd/hal/tsl2561.c |  198 ++++
 project/iotd/main.c        |  223 +++++
 project/iotd/src/conf.h    |   46 +
 project/iotd/hal/tsl2561.h |   42 +
 19 files changed, 2,467 insertions(+), 0 deletions(-)

diff --git a/project/iotd/etc/iotd.conf b/project/iotd/etc/iotd.conf
new file mode 100644
index 0000000..1c392a7
--- /dev/null
+++ b/project/iotd/etc/iotd.conf
@@ -0,0 +1,79 @@
+
+[common]
+id="RPI3B#01"
+
+[logger]
+
+# 日志记录文件
+file=/tmp/iotd.log
+
+# 日志级别: 0:error 1:warnning 2:info 3:debug 4:trace
+level=2
+
+# 日志回滚大小
+size=1024
+
+
+#+-------------------------------------------+
+#|         Hardware configuration            |
+#+-------------------------------------------+
+
+# 树莓派连接的外设信息,0:禁用或未连接  其他: 使能或相关硬件连接的Pin管脚(BCM)
+[hardware]
+
+# LED或继电器等GPIO输出控制, 格式: {名称:BCM编码:控制电平}
+gpio_outpin={light_indoor:19:1},{light_hallway:26:1}
+
+# 红外探测到人后,继电器控制灯亮的时长
+light_intval=15
+
+# 按键或红外感应灯GPIO输入控制, 格式: {名称:BCM编码:控制:电平}
+gpio_inpin={infrared_indoor:23:1},{infrared_hallway:24:1}
+
+# 是否使能 TSL2561 光强传感器模块,0:禁用  X:光强阈值
+lux=1
+lux_threshold=0.10
+
+# 是否使能 DS18b20 温度传感器模块,0:禁用  1:是能
+# ds18b20=1
+
+# 是否使能 SHT2X 温湿度传感器模块,0:禁用  1:使能
+# sht2x=0
+
+#+-------------------------------------------+
+#|           MQTT configuration              |
+#+-------------------------------------------+
+
+[broker]
+
+# broker 服务器地址和端口号
+hostname="weike-iot.com"
+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肯定会收到消息,且只收到一次
+
+
+[subsciber]
+
+subTopic="$Sys/Studio/Downlink/iotd"
+subQos=0
+
+
+[publisher]
+
+pubTopic="$Sys/Studio/Uplink/"
+pubQos=0
+
+# Publisher上报传感器数据的周期,单位是秒
+interval=60
+
+
diff --git a/project/iotd/hal/ds18b20.c b/project/iotd/hal/ds18b20.c
new file mode 100644
index 0000000..2037af5
--- /dev/null
+++ b/project/iotd/hal/ds18b20.c
@@ -0,0 +1,106 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2018 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  ds18b20.c
+ *    Description:  This file is temperature sensor DS18B20 code
+ *
+ *        Version:  1.0.0(2018/10/14)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2018/10/14 12:13:26"
+ *
+ ********************************************************************************/
+
+#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;
+    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 '%s' error: %s\n", w1_path, 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/iotd/hal/ds18b20.h b/project/iotd/hal/ds18b20.h
new file mode 100644
index 0000000..58540a8
--- /dev/null
+++ b/project/iotd/hal/ds18b20.h
@@ -0,0 +1,19 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2018 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  ds18b20.c
+ *    Description:  This file is temperature sensor DS18B20 code
+ *
+ *        Version:  1.0.0(2018/10/14)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2018/10/14 12:13:26"
+ *
+ ********************************************************************************/
+
+#ifndef __DS18B20_H
+#define __DS18B20_H
+
+extern int ds18b20_get_temperature(float *temp);
+
+#endif
diff --git a/project/iotd/hal/gpio.c b/project/iotd/hal/gpio.c
new file mode 100644
index 0000000..b614958
--- /dev/null
+++ b/project/iotd/hal/gpio.c
@@ -0,0 +1,294 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  gpio.c
+ *    Description:  This file is GPIO input/output functions
+ *
+ *        Version:  1.0.0(2019年06月24日)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2019年06月24日 23时46分47秒"
+ *
+ ********************************************************************************/
+
+#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"
+#include "util_proc.h"
+#include "gpio.h"
+
+#define RPI_GPIONAME        "gpiochip0"
+
+static struct gpiod_chip    *s_chip;
+static gpio_t               *s_gpio = NULL;
+
+int gpio_init(gpio_t *gpio)
+{
+    int                 i;
+    int                 rv;
+
+    s_gpio = gpio;
+
+    if( !gpio )
+    {
+        log_error("Invalid input arguments $gpio\n");
+        return -1;
+    }
+
+
+    if( !gpio->incnt && !gpio->outcnt )
+    {
+        log_warn("WARNNING: No GPIO pins configured\n");
+        return 0;
+    }
+
+    /*  gpiod open chip */
+    s_chip = gpiod_chip_open_by_name(RPI_GPIONAME);
+    if( !s_chip )
+    {
+        log_error("gpiod open chip failure, maybe you need running as root\n");
+        return -2;
+    }
+    log_info("gpiod initialise open chip ok\n");
+
+
+    /*  gpiod request all output pins */
+    for(i=0; i<gpio->outcnt; i++)
+    {
+        gpio->output[i].line = gpiod_chip_get_line(s_chip, gpio->output[i].pin);
+        if( !gpio->output[i].line )
+        {
+            log_error("gpiod get line for '%s' pin[#%d] failure\n", gpio->output[i].name, gpio->output[i].pin );
+            return -2;
+        }
+
+        if( gpiod_line_request_output(gpio->output[i].line, gpio->output[i].name, !gpio->output[i].active_level) < 0 )
+        {
+            log_error("gpiod request '%s' pin[#%d] output failure: %s\n", gpio->output[i].name, gpio->output[i].pin, strerror(errno));
+        }
+        else
+        {
+            log_info("gpiod request '%s' pin[#%d] output ok\n", gpio->output[i].name, gpio->output[i].pin);
+        }
+    }
+
+
+    /*  gpiod request all input pins */
+    for(i=0; i<gpio->incnt; i++)
+    {
+        gpio->input[i].line = gpiod_chip_get_line(s_chip, gpio->input[i].pin);
+        if( !gpio->input[i].line )
+        {
+            log_error("gpiod get line for '%s' pin[#%d] failure\n", gpio->input[i].name, gpio->input[i].pin );
+            return -2;
+        }
+
+        if( gpio->output[i].active_level )
+            rv = gpiod_line_request_rising_edge_events(gpio->input[i].line, gpio->input[i].name) ;
+        else
+            rv = gpiod_line_request_falling_edge_events(gpio->input[i].line, gpio->input[i].name) ;
+
+        if( rv < 0 )
+        {
+            log_error("gpiod request '%s' pin[#%d] event edge [%s] failure: %s\n",
+                    gpio->input[i].name, gpio->input[i].pin, gpio->output[i].active_level?"rising":"falling", strerror(errno));
+        }
+        else
+        {
+            log_info("gpiod request '%s' pin[#%d] event edge [%s] ok\n",
+                    gpio->input[i].name, gpio->input[i].pin, gpio->output[i].active_level?"rising":"falling");
+        }
+    }
+
+    return 0;
+}
+
+
+void gpio_term(void)
+{
+    int              i;
+
+    log_info("start teriminated GPIO\n");
+
+    if( !s_gpio->incnt && !s_gpio->outcnt )
+    {
+        return ;
+    }
+
+
+    for(i=0; i<s_gpio->outcnt; i++)
+    {
+        gpiod_line_release(s_gpio->output[i].line);
+    }
+
+
+    for(i=0; i<s_gpio->incnt; i++)
+    {
+        gpiod_line_release(s_gpio->input[i].line);
+    }
+
+    gpiod_chip_close(s_chip);
+}
+
+void gpio_out(char *name, char *cmd)
+{
+    int              i;
+    int              found = 0;
+
+    for( i=0; i<s_gpio->outcnt; i++ )
+    {
+        if( !strncasecmp(s_gpio->output[i].name, name, strlen(name)))
+        {
+            found = 1;
+            break;
+        }
+    }
+
+    if( !found )
+    {
+        log_error("GPIO output for '%s' pin not found\n", name);
+        return ;
+    }
+
+    if( strstr(cmd, "on") )
+    {
+        gpiod_line_set_value(s_gpio->output[i].line, s_gpio->output[i].active_level);
+    }
+    else if( strstr(cmd, "off") )
+    {
+        gpiod_line_set_value(s_gpio->output[i].line, !s_gpio->output[i].active_level);
+    }
+
+    return ;
+}
+
+
+void *light_on_worker(void *arg)
+{
+    int                      i;
+    int                      found = 0;
+    char                    *name = (char *)arg;
+    gpio_info_t             *gpiout;
+
+    if( !name )
+    {
+        log_error("Invalid input arugment\n");
+        return NULL;
+    }
+
+    if( s_gpio->light_intval <= 0 )
+    {
+        log_error("Invalid light_intval value[%d] in configure file\n", s_gpio->light_intval);
+        return NULL;
+    }
+
+    for(i=0; i<s_gpio->outcnt; i++)
+    {
+        if( strstr(s_gpio->output[i].name, name) )
+        {
+            gpiout = &(s_gpio->output[i]);
+            found = 1;
+        }
+    }
+
+    if( !found )
+    {
+        log_error("Light '%s' not found\n", name);
+        return NULL;
+    }
+
+    log_info("Trun on %s for [%d] seconds\n", gpiout->name, s_gpio->light_intval);
+
+    gpio_out(gpiout->name, "on");
+    sleep(s_gpio->light_intval);
+    gpio_out(gpiout->name, "off");
+
+    return NULL;
+}
+
+/* Return value: 0(LOW): Nobody detected, !0: indoor or hallway detected */
+int infrared_detect(void)
+{
+    int                      i;
+    int                      rv = 0;
+    int                      res = 0;
+
+    struct gpiod_line_event  ev;
+    struct gpiod_line_bulk   bulk, event_bulk;
+    struct gpiod_line       *line;
+
+    int                      num_lines = 0;
+
+
+    gpiod_line_bulk_init(&bulk);
+
+    for(i=0; i<s_gpio->incnt; i++)
+    {
+        if( strstr(s_gpio->input[i].name, "infrared"))
+        {
+            gpiod_line_bulk_add(&bulk, s_gpio->input[i].line);
+            num_lines ++;
+        }
+    }
+
+    if( !num_lines )
+    {
+        log_error("No infrared detect gpiod lines found\n");
+        return 0;
+    }
+
+    log_debug("infrared start detect event now...\n");
+    rv = gpiod_line_event_wait_bulk(&bulk, NULL, &event_bulk);
+    if( rv <= 0 )
+    {
+        log_error("infrared gpiod line wait[%s] event failure!\n", s_gpio->input[i].name);
+        return 0;
+    }
+
+    for (i=0; i<num_lines; i++)
+    {
+        line = gpiod_line_bulk_get_line(&event_bulk, 0);
+
+        /* must call this function to  clear the event  */
+        rv = gpiod_line_event_read(line, &ev);
+        if( rv < 0)
+        {
+            log_error("gpiod_line_event_read get failure\n");
+            break;
+        }
+
+        for(i=0; i<s_gpio->incnt; i++)
+        {
+            if(line == s_gpio->input[i].line)
+            {
+                if( s_gpio->input[i].active_level != gpiod_line_get_value(line) )
+                {
+                    log_debug("infrared '%s' detect get wrong power level\n", s_gpio->input[i].name);
+                    continue;
+                }
+
+                if( strstr(s_gpio->input[i].name, "indoor") )
+                {
+                    log_debug("indoor infrared gpiod wait get event.\n", s_gpio->input[i].name);
+                    res |= FLAG_INFRARED_INDOOR;
+                }
+                else if( strstr(s_gpio->input[i].name, "hallway") )
+                {
+                    log_debug("hallway infrared gpiod wait get event.\n", s_gpio->input[i].name);
+                    res |= FLAG_INFRARED_HALLWAY;
+                }
+            }
+        }
+    }
+
+    return res;
+}
+
+
diff --git a/project/iotd/hal/gpio.h b/project/iotd/hal/gpio.h
new file mode 100644
index 0000000..29e970a
--- /dev/null
+++ b/project/iotd/hal/gpio.h
@@ -0,0 +1,58 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  gpio.h
+ *    Description:  This file is GPIO input/output functions
+ *
+ *        Version:  1.0.0(2019年06月24日)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2019年06月24日 23时46分47秒"
+ *
+ ********************************************************************************/
+
+#ifndef  _GPIO_H_
+#define  _GPIO_H_
+
+#include <gpiod.h>
+
+#define GPIO_MAXOUT         8
+#define GPIO_MAXIN          4
+
+typedef struct gpio_info_s
+{
+    char                 name[32];      /*  GPIO connected module name */
+    int                  pin;           /*  GPIO BCM pin number */
+    int                  active_level;  /*  active power level */
+    struct gpiod_line   *line;          /*  gpiod line */
+} gpio_info_t;
+
+
+typedef struct gpio_s
+{
+    gpio_info_t          output[GPIO_MAXOUT]; /* GPIO output pins */
+    int                  outcnt;              /* GPIO output numbers */
+    int                  light_intval;        /* light on interval time */
+
+    gpio_info_t          input[GPIO_MAXIN];   /* GPIO input pins */
+    int                  incnt;               /* GPIO input numbers */
+    int                  infrared_enable;     /* infrared enable or not */
+} gpio_t;
+
+extern int gpio_init(gpio_t *gpio);
+extern void gpio_term(void);
+
+/* turn which light on/off */
+extern void gpio_out(char *name, char *cmd);
+
+
+/*thread work body to turn light $name on for some seconds */
+void *light_on_worker(void *arg);
+
+/* Return value: 0(LOW): Nobody detected, !0: Which infrared detect incoming */
+#define FLAG_INFRARED_INDOOR          (1<<0)
+#define FLAG_INFRARED_HALLWAY         (1<<1)
+extern int infrared_detect(void);
+
+#endif   /* ----- #ifndef _GPIO_H_  ----- */
+
diff --git a/project/iotd/hal/hal.c b/project/iotd/hal/hal.c
new file mode 100644
index 0000000..4c40b0e
--- /dev/null
+++ b/project/iotd/hal/hal.c
@@ -0,0 +1,89 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  hal.c
+ *    Description:  This file is RPi HAL(Hardware Abstract Layer) initial functions
+ *
+ *        Version:  1.0.0(2019年06月24日)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2019年06月24日 23时46分47秒"
+ *
+ ********************************************************************************/
+
+#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"
+#include "hal.h"
+
+int hal_init(hal_ctx_t *ctx)
+{
+    if(!ctx)
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( ctx->sht2x_enable )
+    {
+        if( sht2x_init()< 0 )
+        {
+            log_error("R&H sensor SHT2X initialise failure\n");
+            return -2;
+        }
+        else
+        {
+            log_info("R&H sensor SHT2X initialise okay\n");
+        }
+    }
+
+    if( ctx->lux_enable )
+    {
+        if( tsl2561_init() < 0 )
+        {
+            log_error("LUX sensor TSL2561 initialise failure\n");
+            return -2;
+        }
+        else
+        {
+            log_info("LUX sensor TSL2561 initialise okay\n");
+        }
+    }
+
+    gpio_init(&ctx->gpio);
+
+    return 0;
+}
+
+
+void hal_term(hal_ctx_t *ctx)
+{
+    if(!ctx)
+    {
+        log_error("Invalid input arguments\n");
+        return ;
+    }
+
+
+    if( ctx->sht2x_enable )
+    {
+        sht2x_term();
+    }
+
+    if( ctx->lux_enable )
+    {
+        tsl2561_term();
+    }
+
+    gpio_term();
+
+    return ;
+}
+
diff --git a/project/iotd/hal/hal.h b/project/iotd/hal/hal.h
new file mode 100644
index 0000000..be4af23
--- /dev/null
+++ b/project/iotd/hal/hal.h
@@ -0,0 +1,42 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  hal.h
+ *    Description:  This file is RPi HAL(Hardware Abstract Layer) initial functions
+ *
+ *        Version:  1.0.0(2019年06月24日)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2019年06月24日 23时46分47秒"
+ *
+ ********************************************************************************/
+
+#ifndef  _HAL_H_
+#define  _HAL_H_
+
+#include "ds18b20.h"
+#include "sht20.h"
+#include "tsl2561.h"
+#include "gpio.h"
+
+
+typedef struct hal_ctx_s
+{
+    int                  ds18b20_enable; /* 0:Disable   !0: Enable */
+    int                  sht2x_enable;   /* 0:Disable   !0: Enable */
+
+    int                  lux_enable;     /* 0:Disable   !0: Enable */
+    float                lux_threshold;  /* Lux Threshold value */
+
+    gpio_t               gpio;           /* gpio intput/output pins  */
+} hal_ctx_t;
+
+
+/* init hardware */
+extern int hal_init(hal_ctx_t *ctx);
+
+/* terminal hardware */
+extern void hal_term(hal_ctx_t *ctx);
+
+#endif   /* ----- #ifndef _HAL_H_  ----- */
+
diff --git a/project/iotd/hal/makefile b/project/iotd/hal/makefile
new file mode 100644
index 0000000..608137a
--- /dev/null
+++ b/project/iotd/hal/makefile
@@ -0,0 +1,15 @@
+
+PWD=$(shell pwd )
+
+LIBNAME=$(shell basename ${PWD} )
+TOPDIR=$(shell dirname ${PWD} )
+
+all: clean
+	@rm -f *.o
+	@${CROSSTOOL}gcc ${CFLAGS} -I${TOPDIR} -c *.c 
+	${CROSSTOOL}ar -rcs  lib${LIBNAME}.a *.o
+
+clean:
+	@rm -f *.o
+	@rm -f *.a
+
diff --git a/project/iotd/hal/sht20.c b/project/iotd/hal/sht20.c
new file mode 100644
index 0000000..25a2dc6
--- /dev/null
+++ b/project/iotd/hal/sht20.c
@@ -0,0 +1,466 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2018 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(2018/10/14)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2018/10/14 12:13:26"
+ *
+ ********************************************************************************/
+
+#include <stdio.h>
+#include <fcntl.h>
+#include <unistd.h>
+#include <sys/ioctl.h>
+#include <linux/types.h>
+#include <sys/stat.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <sys/types.h>
+#include <string.h>
+#include <stdint.h>
+#include <time.h>
+#include <errno.h>
+#include <string.h>
+
+#include "sht20.h"
+#include "logger.h"
+#include "util_proc.h"
+
+static int s_sht2x_fd = -1;
+
+void sht2x_term(void)
+{
+    close(s_sht2x_fd);
+    s_sht2x_fd = -1;
+    log_warn("Terminate SHT2X\n");
+}
+
+static inline void dump_buf(const char *prompt, uint8_t *buf, int size)
+{
+    int          i;
+
+    if( !buf )
+    {
+        return ;
+    }
+
+    if( prompt )
+    {
+        printf("%s ", prompt);
+    }
+
+    for(i=0; i<size; i++)
+    {
+        printf("%02x ", buf[i]);
+    }
+    printf("\n");
+
+    return ;
+}
+
+#ifdef I2C_API_RDWR  /* Use I2C userspace driver read/write API */
+
+int sht2x_softreset(int fd)
+{
+    uint8_t           buf[4];
+
+    if( fd<0 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    /* software reset SHT2x */
+    memset(buf, 0, sizeof(buf));
+
+    buf[0] = SOFTRESET;
+    write(fd, buf, 1);
+
+    msleep(50);
+
+    return 0;
+}
+
+int sht2x_init(void)
+{
+
+    if( (s_sht2x_fd=open("/dev/i2c-1", O_RDWR)) < 0)
+    {
+        log_error("i2c device open failed: %s\n", strerror(errno));
+        return -1;
+    }
+
+    /* set I2C mode and SHT2x slave address */
+    ioctl(s_sht2x_fd, I2C_TENBIT, 0);    /* Not 10-bit but 7-bit mode */
+    ioctl(s_sht2x_fd, I2C_SLAVE, 0x40); /* set SHT2x slava address 0x40*/
+
+    if( sht2x_softreset(s_sht2x_fd) < 0 )
+    {
+        log_error("SHT2x softreset failure\n");
+        sht2x_term();
+        return -2;
+    }
+
+    log_debug("SHT2X initialise ok, s_sht2x_fd=%d\n", s_sht2x_fd);
+    return s_sht2x_fd;
+}
+
+int sht2x_get_temp_humidity(float *temp, float *rh)
+{
+    uint8_t           buf[4];
+
+    if( !temp || !rh )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( s_sht2x_fd < 0 )
+    {
+        if( sht2x_init() < 0)
+        {
+            log_error("SHT2x initialise failure\n");
+            return -2;
+        }
+    }
+
+    /* send trigger temperature measure command and read the data */
+    memset(buf, 0, sizeof(buf));
+    buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;
+    write(s_sht2x_fd, buf, 1);
+
+    msleep(85); /* datasheet: typ=66, max=85 */
+
+    memset(buf, 0, sizeof(buf));
+    read(s_sht2x_fd, buf, 3);
+    //dump_buf("Temperature sample data: ", buf, 3);
+    *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
+
+    /* send trigger humidity measure command and read the data */
+    memset(buf, 0, sizeof(buf));
+    buf[0] = TRIGGER_HUMIDITY_NO_HOLD;
+    write(s_sht2x_fd, buf, 1);
+
+    msleep(29); /* datasheet: typ=22, max=29 */
+    memset(buf, 0, sizeof(buf));
+
+    read(s_sht2x_fd, buf, 3);
+    //dump_buf("Relative humidity sample data: ", buf, 3);
+    *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
+
+    return 0;
+}
+
+int sht2x_get_serialnumber(uint8_t *serialnumber, int size)
+{
+    uint8_t           buf[4];
+
+    if( !serialnumber || size!=8 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( s_sht2x_fd < 0 )
+    {
+        if( sht2x_init() < 0)
+        {
+            log_error("SHT2x initialise failure\n");
+            return -2;
+        }
+    }
+
+    /* Read SerialNumber from Location 1 */
+    memset(buf, 0, sizeof(buf));
+    buf[0] = 0xfa;  /* command for readout on-chip memory */
+    buf[1] = 0x0f;  /* on-chip memory address */
+    write(s_sht2x_fd, buf, 2);
+
+    memset(buf, 0, sizeof(buf));
+    read(s_sht2x_fd, buf, 4);
+
+    serialnumber[5]=buf[0]; /* Read SNB_3 */
+    serialnumber[4]=buf[1]; /* Read SNB_2 */
+    serialnumber[3]=buf[2]; /* Read SNB_1 */
+    serialnumber[2]=buf[3]; /* Read SNB_0 */
+
+    /* Read SerialNumber from Location 2 */
+    memset(buf, 0, sizeof(buf) );
+    buf[0]=0xfc;  /* command for readout on-chip memory */
+    buf[1]=0xc9;  /* on-chip memory address */
+    write(s_sht2x_fd, buf, 2);
+
+    memset(buf, 0, sizeof(buf) );
+    read(s_sht2x_fd, buf, 4);
+
+    serialnumber[1]=buf[0]; /* Read SNC_1 */
+    serialnumber[0]=buf[1]; /* Read SNC_0 */
+    serialnumber[7]=buf[2]; /* Read SNA_1 */
+    serialnumber[6]=buf[3]; /* Read SNA_0 */
+
+    //dump_buf("SHT2x Serial number: ", serialnumber, 8);
+
+    return 0;
+}
+
+#elif (defined I2C_API_IOCTL)  /* Use I2C userspace driver read/write API */
+
+int sht2x_softreset(int fd)
+{
+    struct i2c_msg               msg;
+    struct i2c_rdwr_ioctl_data   sht2x_data;
+    uint8_t                      buf[2];
+
+    if( fd<0 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    msg.addr= 0x40;
+    msg.flags=0; //write
+    msg.len= 1;
+    msg.buf= buf;
+    msg.buf[0]=SOFTRESET;
+
+    sht2x_data.nmsgs= 1;
+    sht2x_data.msgs= &msg;
+
+    if( ioctl(fd, I2C_RDWR, &sht2x_data) < 0 )
+    {
+        log_error("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
+        sht2x_term();
+        return -2;
+    }
+
+    msleep(50);
+
+    return 0;
+}
+
+
+int sht2x_init(void)
+{
+    if( (s_sht2x_fd=open("/dev/i2c-1", O_RDWR)) < 0)
+    {
+        log_error("i2c device open failed: %s\n", strerror(errno));
+        return -1;
+    }
+
+    if( sht2x_softreset(s_sht2x_fd) < 0 )
+    {
+        log_error("SHT2x softreset failure\n");
+        sht2x_term();
+        return -2;
+    }
+
+    log_debug("SHT2X initialise ok, s_sht2x_fd=%d\n", s_sht2x_fd);
+    return 0;
+}
+
+int sht2x_get_serialnumber(uint8_t *serialnumber, int size)
+{
+    struct i2c_msg               msgs[2];
+    struct i2c_rdwr_ioctl_data   sht2x_data;
+    uint8_t                      sbuf[2];
+    uint8_t                      rbuf[4];
+
+    if( !serialnumber || size!=8 )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( s_sht2x_fd < 0 )
+    {
+        if( sht2x_init() < 0)
+        {
+            log_error("SHT2x initialise failure\n");
+            return -2;
+        }
+    }
+
+    /*+------------------------------------------+
+     *|     Read SerialNumber from Location 1    |
+     *+------------------------------------------+*/
+
+    msgs[0].addr= 0x40;
+    msgs[0].flags=0; //write
+    msgs[0].len= 2;
+    msgs[0].buf= sbuf;
+    msgs[0].buf[0]=0xfa;  /* command for readout on-chip memory */
+    msgs[0].buf[1]=0x0f;  /* on-chip memory address */
+
+    msgs[1].addr=0x40;
+    msgs[1].flags=I2C_M_RD; //write
+    msgs[1].len= 4;
+    msgs[1].buf= rbuf;
+
+    sht2x_data.nmsgs= 2;
+    sht2x_data.msgs= msgs;
+
+    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
+    {
+        log_error("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
+        sht2x_term();
+        return -2;
+    }
+
+    serialnumber[5]=rbuf[0]; /* Read SNB_3 */
+    serialnumber[4]=rbuf[1]; /* Read SNB_2 */
+    serialnumber[3]=rbuf[2]; /* Read SNB_1 */
+    serialnumber[2]=rbuf[3]; /* Read SNB_0 */
+
+
+    /*+------------------------------------------+
+     *|     Read SerialNumber from Location 2    |
+     *+------------------------------------------+*/
+
+    msgs[0].addr= 0x40;
+    msgs[0].flags=0; //write
+    msgs[0].len= 2;
+    msgs[0].buf= sbuf;
+    msgs[0].buf[0]=0xfc;  /* command for readout on-chip memory */
+    msgs[0].buf[1]=0xc9;  /* on-chip memory address */
+
+    msgs[1].addr=0x40;
+    msgs[1].flags=I2C_M_RD; //write
+    msgs[1].len= 4;
+    msgs[1].buf= rbuf;
+
+    sht2x_data.nmsgs= 2;
+    sht2x_data.msgs= msgs;
+
+    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
+    {
+        log_error("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
+        sht2x_term();
+        return -2;
+    }
+
+    serialnumber[1]=rbuf[0]; /* Read SNC_1 */
+    serialnumber[0]=rbuf[1]; /* Read SNC_0 */
+    serialnumber[7]=rbuf[2]; /* Read SNA_1 */
+    serialnumber[6]=rbuf[3]; /* Read SNA_0 */
+
+    //dump_buf("SHT2x Serial number: ", serialnumber, 8);
+
+    return 0;
+}
+
+
+int sht2x_get_temp_humidity(float *temp, float *rh)
+{
+    struct i2c_msg               msg;
+    struct i2c_rdwr_ioctl_data   sht2x_data;
+    uint8_t                      buf[4];
+
+    if( !temp || !rh )
+    {
+        log_error("Invalid input arguments\n");
+        return -1;
+    }
+
+    if( s_sht2x_fd < 0 )
+    {
+        if( sht2x_init() < 0)
+        {
+            log_error("SHT2x initialise failure\n");
+            return -2;
+        }
+    }
+
+    /*+------------------------------------------+
+     *|       measure and get temperature        |
+     *+------------------------------------------+*/
+
+    msg.addr= 0x40;
+    msg.flags=0; //write
+    msg.len= 1;
+    msg.buf= buf;
+    msg.buf[0]=TRIGGER_TEMPERATURE_NO_HOLD;  /* trigger temperature without hold I2C bus */
+
+    sht2x_data.nmsgs= 1;
+    sht2x_data.msgs= &msg;
+
+    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
+    {
+        log_error("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
+        sht2x_term();
+        return -2;
+    }
+
+    msleep(85);
+
+    memset(buf, 0, sizeof(buf));
+    msg.addr=0x40;
+    msg.flags=I2C_M_RD; //write
+    msg.len= 3;
+    msg.buf= buf;
+
+    sht2x_data.nmsgs= 1;
+    sht2x_data.msgs= &msg;
+
+    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
+    {
+        log_error("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
+        sht2x_term();
+        return -2;
+    }
+
+    //dump_buf("Temperature sample data: ", buf, 3);
+    *temp = 175.72 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 46.85;
+
+
+    /*+------------------------------------------+
+     *|    measure and get relative humidity     |
+     *+------------------------------------------+*/
+
+    msg.addr= 0x40;
+    msg.flags=0; //write
+    msg.len= 1;
+    msg.buf= buf;
+    msg.buf[0]=TRIGGER_HUMIDITY_NO_HOLD;  /* trigger humidity without hold I2C bus */
+
+    sht2x_data.nmsgs= 1;
+    sht2x_data.msgs= &msg;
+
+    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
+    {
+        log_error("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
+        sht2x_term();
+        return -2;
+    }
+
+    msleep(29);
+
+    memset(buf, 0, sizeof(buf));
+    msg.addr=0x40;
+    msg.flags=I2C_M_RD; //write
+    msg.len= 3;
+    msg.buf= buf;
+
+    sht2x_data.nmsgs= 1;
+    sht2x_data.msgs= &msg;
+
+    if( ioctl(s_sht2x_fd, I2C_RDWR, &sht2x_data) < 0 )
+    {
+        log_error("sht2x I2C_RDWR ioctl failure: %s\n", strerror(errno));
+        sht2x_term();
+        return -2;
+    }
+
+    //dump_buf("Relative humidity sample data: ", buf, 3);
+    *rh = 125 * (((((int) buf[0]) << 8) + buf[1]) / 65536.0) - 6;
+
+    return 0;
+}
+
+#endif
+
diff --git a/project/iotd/hal/sht20.h b/project/iotd/hal/sht20.h
new file mode 100644
index 0000000..e327105
--- /dev/null
+++ b/project/iotd/hal/sht20.h
@@ -0,0 +1,33 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2018 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(2018/10/14)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "2018/10/14 12:13:26"
+ *
+ ********************************************************************************/
+#ifndef __SHT20_H
+#define __SHT20_H
+
+#include <stdio.h>
+#include <stdint.h>
+#include <time.h>
+
+#define SOFTRESET                        0xFE
+#define TRIGGER_TEMPERATURE_NO_HOLD      0xF3
+#define TRIGGER_HUMIDITY_NO_HOLD         0xF5
+
+//#define I2C_API_IOCTL  /* Use I2C userspace driver ioctl API */
+#define I2C_API_RDWR /* Use I2C userspace driver read/write API */
+
+int sht2x_init(void);
+int sht2x_get_serialnumber(uint8_t *serialnumber, int size);
+int sht2x_get_temp_humidity(float *temp, float *rh);
+void sht2x_term(void);
+
+#endif
+
diff --git a/project/iotd/hal/tsl2561.c b/project/iotd/hal/tsl2561.c
new file mode 100644
index 0000000..2a8dcb6
--- /dev/null
+++ b/project/iotd/hal/tsl2561.c
@@ -0,0 +1,198 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  tsl2561.c
+ *    Description:  This file is the Lux sensor TSL2561 API functions on RaspberryPi,
+ *                  which connected to I2C-1
+ *
+ *        Version:  1.0.0(04/07/19)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "04/07/19 17:39:38"
+ *
+ ********************************************************************************/
+
+#include <string.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <math.h>
+#include <errno.h>
+#include <time.h>
+
+#include <sys/ioctl.h>
+#include <linux/i2c.h>
+#include <linux/i2c-dev.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "logger.h"
+#include "util_proc.h"
+#include "tsl2561.h"
+
+int s_tsl_fd = -1;
+
+static const int  regs_addr[REG_COUNT]={DATA0LOW, DATA0HIGH, DATA1LOW, DATA1HIGH};
+
+int tsl2561_init(void)
+{
+    if(s_tsl_fd > 0)
+        return 0;
+
+    if( (s_tsl_fd=open("/dev/i2c-1", O_RDWR)) < 0)
+    {
+        log_error("TSL2561 I2C device setup failure: %s\n", strerror(errno));
+        return -1;
+    }
+
+    log_debug("TSL2561 initialise ok, s_tsl_fd=%d\n", s_tsl_fd);
+    return s_tsl_fd;
+}
+
+void tsl2561_term(void)
+{
+    close(s_tsl_fd);
+    s_tsl_fd = -1;
+    log_warn("Terminate TSL2561.\n");
+}
+
+
+#define ON          1
+#define OFF         0
+
+int tsl2561_power(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(s_tsl_fd, I2C_RDWR, &data) < 0 )
+    {
+        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return -1;
+    }
+
+
+    if( cmd )
+        msg.buf[0]=POWER_UP;
+    else
+        msg.buf[0]=POWER_DOWN;
+
+    if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 )
+    {
+        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return -1;
+    }
+
+    return 0;
+}
+
+int tsl2561_readreg(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(s_tsl_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_I2C_ADDR;
+    msg.flags=I2C_M_RD;  /* read */
+    msg.len= 1;
+    msg.buf= buf;
+
+    data.nmsgs= 1;
+    data.msgs= &msg;
+
+    if( ioctl(s_tsl_fd, I2C_RDWR, &data) < 0 )
+    {
+        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
+        return -1;
+    }
+
+    *regval = msg.buf[0];
+    return 0;
+}
+
+
+
+float tsl2561_get_lux(void)
+{
+    int                 i;
+    unsigned char       reg_data[REG_COUNT];
+
+    int                 chn0_data = 0;
+    int                 chn1_data = 0;
+
+    float               div = 0.0;
+    float               lux = 0.0;
+
+
+    tsl2561_power(ON);
+
+    msleep(410);  /* t(CONV) MAX 400ms */
+
+    /* Read register Channel0 and channel1 data from register */
+    for(i=0; i<REG_COUNT; i++)
+    {
+        tsl2561_readreg(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;
+
+    log_debug("TSLl2561 get lux: %.3f\n", lux);
+
+OUT:
+    tsl2561_power(OFF);
+    return lux;
+}
+
+
diff --git a/project/iotd/hal/tsl2561.h b/project/iotd/hal/tsl2561.h
new file mode 100644
index 0000000..3a92a6a
--- /dev/null
+++ b/project/iotd/hal/tsl2561.h
@@ -0,0 +1,42 @@
+/********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  tsl2561.h
+ *    Description:  This head file is the Lux sensor TSL2561 API functions on RaspberryPi
+ *
+ *        Version:  1.0.0(04/07/19)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "04/07/19 17:42:35"
+ *
+ ********************************************************************************/
+
+#ifndef  _TSL2561_H_
+#define  _TSL2561_H_
+
+#define TSL2561_I2C_ADDR                0x39
+
+#define CONTROL_REG                     0x80
+#define REG_COUNT                       4
+
+#define POWER_UP                        0x03
+#define POWER_DOWN                      0x00
+
+/* Register Address  */
+enum
+{
+    /* Channel_0 = DATA0HIGH<<8 + DATA0LOW */
+    DATA0LOW = 0x8C,
+    DATA0HIGH,
+
+    /* Channel_1 = DATA1HIGH<<8 + DATA1LOW */
+    DATA1LOW,
+    DATA1HIGH,
+};
+
+extern int tsl2561_init(void);
+extern void tsl2561_term(void);
+extern float tsl2561_get_lux(void);
+
+#endif   /* ----- #ifndef _TSL2561_H_  ----- */
+
diff --git a/project/iotd/main.c b/project/iotd/main.c
new file mode 100644
index 0000000..c72ecce
--- /dev/null
+++ b/project/iotd/main.c
@@ -0,0 +1,223 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  main.c
+ *    Description:  This is the lux/relay/infrared auto light 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 <time.h>
+#include <unistd.h>
+#include <getopt.h>
+#include <libgen.h>
+#include <string.h>
+#include <math.h>
+
+#include <cjson/cJSON.h>
+#include <mosquitto.h>
+
+#include "util_proc.h"
+#include "logger.h"
+#include "hal.h"
+
+#include "conf.h"
+#include "mqtt.h"
+
+#define PROG_VERSION               "v1.0.0"
+#define DAEMON_PIDFILE             "/tmp/.iotd.pid"
+
+void control_thread_loop(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;
+    iotd_ctx_t         ctx;
+    hal_ctx_t         *hal_ctx = &ctx.hal_ctx;
+    char               *conf_file="/etc/iotd.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;
+
+    if( parser_conf(conf_file, &ctx, debug)<0 )
+    {
+        fprintf(stderr, "Parser mqtted configure file failure\n");
+        return -2;
+    }
+
+
+    if( hal_init(hal_ctx) < 0 )
+    {
+        log_error("Initialise hardware failure\n");
+        return -3;
+    }
+    log_info("Initialise hardware okay.\n");
+
+
+    install_default_signal();
+    if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 )
+        goto OUT;
+
+
+    mosquitto_lib_init();
+
+    /*+--------------------------------------------+
+     *|  Start  MQTT subsciber thread if enable    |
+     *+--------------------------------------------+*/
+    if( ctx.mqtt_ctx.sub_enable )
+    {
+        if( thread_start(&tid, mqtt_sub_worker, &ctx ) < 0 )
+        {
+            log_error("Start MQTT subsciber worker thread failure\n");
+            goto OUT;
+        }
+        else
+        {
+            log_info("Start MQTT subsciber worker thread ok\n");
+        }
+    }
+
+    /*+--------------------------------------------+
+     *|  Start  MQTT publisher thread if enable    |
+     *+--------------------------------------------+*/
+    if( ctx.mqtt_ctx.pub_enable )
+    {
+        if( thread_start(&tid, mqtt_pub_worker, &ctx ) < 0 )
+        {
+            log_error("Start MQTT publisher worker thread failure\n");
+            goto OUT;
+        }
+        else
+        {
+            log_info("Start MQTT publisher worker thread ok\n");
+        }
+    }
+
+    /*+--------------------------------------------+
+     *|      Control thread start dead loop        |
+     *+--------------------------------------------+*/
+    control_thread_loop(&ctx);
+
+OUT:
+    mosquitto_lib_cleanup();
+    hal_term(hal_ctx);
+    log_close();
+
+    return 0;
+} /* ----- End of main() ----- */
+
+
+void control_thread_loop(void *args)
+{
+    iotd_ctx_t            *ctx = (iotd_ctx_t *)args;
+    hal_ctx_t             *hal_ctx;
+    float                  lux = 0.0;
+    int                    rv;
+
+    hal_ctx = &ctx->hal_ctx;
+
+    log_debug("infrared configured [%d], lux configured [%d]\n", hal_ctx->gpio.infrared_enable, hal_ctx->lux_enable);
+
+    while( ! g_signal.stop )
+    {
+        if( hal_ctx->gpio.infrared_enable )
+        {
+            if( hal_ctx->lux_enable )
+            {
+                lux = tsl2561_get_lux();
+
+                log_debug("TSL2561 get Lux[%.3f] Treshold[%.3f].\n", lux, hal_ctx->lux_threshold);
+
+                if( lux > hal_ctx->lux_threshold )
+                {
+                    log_info("Lux[%.3f] > Treshold[%.3f], don't need light on and sleep now.\n", lux, hal_ctx->lux_threshold);
+                    if( hal_ctx->gpio.light_intval > 0)
+                        sleep( hal_ctx->gpio.light_intval );
+                    else
+                        sleep(30);
+
+                    continue;
+                }
+            }
+
+            rv = infrared_detect();
+            if( rv & FLAG_INFRARED_INDOOR )
+            {
+                log_info("Someone incoming detected by indoor infrared\n");
+                thread_start(NULL, light_on_worker, "indoor" );
+            }
+
+            if( rv & FLAG_INFRARED_HALLWAY )
+            {
+                log_info("Someone incoming detected by hallway infrared\n");
+                thread_start(NULL, light_on_worker, "hallway" );
+            }
+        }
+
+        msleep(100);
+    }
+}
+
+
diff --git a/project/iotd/makefile b/project/iotd/makefile
new file mode 100644
index 0000000..3797387
--- /dev/null
+++ b/project/iotd/makefile
@@ -0,0 +1,64 @@
+#********************************************************************************
+#      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 = iotd
+
+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
+
+# sub-directory compiled to a library and need to link
+SRCS=src hal booster
+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
+LDFLAGS+=-lmosquitto -lgpiod -lcjson -lm
+
+# sub-directory need to entry and compile
+SUBDIR=${SRCS}
+
+all: entry subdir
+	${CROSS_COMPILE}gcc ${CFLAGS} main.c -o ${APP_NAME} ${LDFLAGS}
+
+entry:
+	@echo "Building ${APP_NAME} on ${BUILD_ARCH}"
+
+subdir:
+	@for dir in ${SUBDIR} ; do if [ ! -e $${dir} ] ; then ln -s ../$${dir}; fi; done
+	@for dir in ${SUBDIR} ;  do make CFLAGS="${CFLAGS}" -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 ${SUBDIR} ; do if [ -e $${dir} ] ; then make clean -C $${dir}; fi; done
+	@rm -f ${APP_NAME}
+	@rm -f cscope.* tags
+	@rm -f *.log *.db
+
diff --git a/project/iotd/src/conf.c b/project/iotd/src/conf.c
new file mode 100644
index 0000000..3e7f296
--- /dev/null
+++ b/project/iotd/src/conf.c
@@ -0,0 +1,304 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  conf.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 "conf.h"
+#include "logger.h"
+#include "iniparser.h"
+
+enum
+{
+    _TYPE_INPUT,
+    _TYPE_OUTPUT,
+};
+
+/* conf format:  "{light_indoor:6:0},{light_livroomL:13:0},{light_livroomR:19:0},{light_hallway:26:0}"  */
+int parser_gpio_info(int type, gpio_t *gpio, char *conf)
+{
+    char              *ptr;
+    char              *pstart;
+    char              *pend;
+    char               buf[64];
+    int                cnt = 0;
+    int                rv = 0;
+
+    if( !gpio || !conf || strlen(conf)<3 )
+    {
+        log_error("Invalid input arguments.\n");
+        return -1;
+    }
+
+    pstart = strchr(conf, '{');
+    if( !pstart )
+        return 0;
+
+    pend = strchr(pstart, '}');
+
+    while( pstart && pend )
+    {
+        memset(buf, 0, sizeof(buf));
+
+        strncpy(buf, pstart+1, pend-pstart-1);
+
+        /* parser and get the GPIO name, BCM pin number, active power level */
+
+        {
+            /* check GPIO configure name too long or not */
+            ptr = strchr(buf, ':');
+            if( !ptr )
+            {
+                log_error("Found invalid GPIO configure: %s\n", buf);
+                goto NEXT_LOOP;
+            }
+
+            if( ptr-buf > sizeof(gpio->input[cnt].name) )
+            {
+                log_error("Found GPIO name too long\n", buf);
+                goto NEXT_LOOP;
+            }
+
+            /* use sscanf() to parser GPIO configured values */
+            if(_TYPE_INPUT == type )
+            {
+                rv = sscanf(buf, "%[^:]:%d:%d", gpio->input[cnt].name, &gpio->input[cnt].pin, &gpio->input[cnt].active_level);
+                if( 3 == rv)
+                {
+                    log_info("parser GPIO input[%s] BCM[%d] active[%d]\n", gpio->input[cnt].name, gpio->input[cnt].pin, gpio->input[cnt].active_level);
+                    if( strstr(gpio->input[cnt].name, "infrared") )
+                    {
+                        log_info("parser GPIO enable infrared detect\n");
+                        gpio->infrared_enable = 1;
+                    }
+                    cnt++;
+                    gpio->incnt = cnt;
+                }
+                else
+                {
+                    log_error("Found invalid GPIO configure: %s\n", buf);
+                }
+            }
+            else
+            {
+                rv = sscanf(buf, "%[^:]:%d:%d", gpio->output[cnt].name, &gpio->output[cnt].pin, &gpio->output[cnt].active_level);
+                if( 3 == rv)
+                {
+                    log_info("parser GPIO output[%s] BCM[%d] active[%d]\n", gpio->output[cnt].name, gpio->output[cnt].pin, gpio->output[cnt].active_level);
+                    cnt++;
+                    gpio->outcnt = cnt;
+                }
+                else
+                {
+                    log_error("Found invalid GPIO configure: %s\n", buf);
+                }
+            }
+        }
+
+NEXT_LOOP:
+        pstart = strchr(pend, '{');
+        if( pstart )
+            pend = strchr(pstart, '}');
+    }
+
+
+    return gpio->outcnt;
+}
+
+int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug)
+{
+    dictionary          *ini;
+    const char          *str;
+    int                  val;
+    log_ctx_t           *log_ctx;
+    hal_ctx_t           *hal_ctx;
+    mqtt_ctx_t          *mqtt_ctx;
+    gpio_t              *gpio;
+
+
+    if( !conf_file || !ctx )
+    {
+        fprintf(stderr, "ERROR: parser configure file or ctx is NULL\n");
+        return 0;
+    }
+
+    memset(ctx, 0, sizeof(*ctx));
+
+    log_ctx = &ctx->log_ctx;
+    hal_ctx = &ctx->hal_ctx;
+    mqtt_ctx = &ctx->mqtt_ctx;
+
+
+    ini = iniparser_load(conf_file);
+    if( !ini )
+    {
+        fprintf(stderr, "ERROR: cannot parse file: '%s'\n", conf_file);
+        return -1;
+    }
+
+
+    /*+------------------------------------------------------+
+     *|    parser logger settings and start logger system    |
+     *+------------------------------------------------------+*/
+    if( !debug )
+    {
+        str = iniparser_getstring(ini, "logger:file", "/tmp/iotd.log");
+        strncpy(log_ctx->logfile, str, sizeof(log_ctx->logfile));
+        log_ctx->logsize = iniparser_getint(ini, "logger:size", 1024);
+        log_ctx->loglevel = iniparser_getint(ini, "logger:level", LOG_LEVEL_DEBUG);
+    }
+    else
+    {
+        strncpy(log_ctx->logfile, "console", sizeof(log_ctx->logfile));
+        log_ctx->loglevel = LOG_LEVEL_DEBUG;
+    }
+
+    if( log_open(log_ctx->logfile, log_ctx->loglevel, log_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:id", NULL)) )
+    {
+        log_error("ERROR: parser production ID failure\n");
+        return -2;
+    }
+    /* cJSON parser ID will get ""  */
+    snprintf(mqtt_ctx->id, sizeof(mqtt_ctx->id), "\"%s\"", str);
+    log_info("parser production ID [%s]\n", mqtt_ctx->id);
+
+
+    /*+------------------------------------------------------+
+     *|      parser hardware module configuration ID         |
+     *+------------------------------------------------------+*/
+
+    gpio = &hal_ctx->gpio;
+
+    /* parser GPIO output pins  */
+    if( !(str=iniparser_getstring(ini, "hardware:gpio_outpin", NULL)) )
+    {
+        log_warn("parser no GPIO output pins\n");
+    }
+    else
+    {
+        parser_gpio_info(_TYPE_OUTPUT, gpio, (char *)str);
+        log_info("parser [%d] GPIO output pins configured\n", gpio->outcnt);
+    }
+
+    gpio->light_intval = iniparser_getint(ini, "hardware:light_intval", 20);
+    log_info("parser relay controled light interval time [%d]\n", gpio->light_intval);
+
+    /* parser GPIO input pins  */
+    if( !(str=iniparser_getstring(ini, "hardware:gpio_inpin", NULL)) )
+    {
+        log_warn("parser no GPIO input pins\n");
+    }
+    else
+    {
+        parser_gpio_info(_TYPE_INPUT, gpio, (char *)str);
+        log_info("parser [%d] GPIO input pins\n", gpio->incnt);
+    }
+
+    hal_ctx->lux_enable = iniparser_getint(ini, "hardware:lux", 0);
+    if( hal_ctx->lux_enable )
+    {
+        hal_ctx->lux_threshold = iniparser_getdouble(ini, "hardware:lux_threshold", 0.1);
+        log_info("parser LUX enable and threshold value set be [%.03f]\n", hal_ctx->lux_threshold);
+    }
+
+    hal_ctx->sht2x_enable = iniparser_getint(ini, "hardware:sht2x", 0);
+    if( hal_ctx->sht2x_enable )
+    {
+        log_info("parser SHT2x sensor enabled\n");
+    }
+
+    hal_ctx->ds18b20_enable = iniparser_getint(ini, "hardware:ds18b20", 0);
+    if( hal_ctx->ds18b20_enable )
+    {
+        log_info("parser DS18B20 sensor enabled\n");
+    }
+
+
+    /*+------------------------------------------------------+
+     *|              parser broker settings                  |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "broker:hostname", NULL)) )
+    {
+        log_error("ERROR: Parser broker server hostname failure\n");
+        return -2;
+    }
+    strncpy(mqtt_ctx->host, str, sizeof(mqtt_ctx->host) );
+
+    if( (val=iniparser_getint(ini, "broker:port", -1)) < 0 )
+    {
+        log_error("ERROR: Parser broker server port failure\n");
+        return -2;
+    }
+    mqtt_ctx->port = val;
+    log_info("Parser broker server [%s:%d]\n", mqtt_ctx->host, mqtt_ctx->port);
+
+    str=iniparser_getstring(ini, "broker:username", NULL);
+    strncpy(mqtt_ctx->uid, str, sizeof(mqtt_ctx->uid) );
+
+    str=iniparser_getstring(ini, "broker:password", NULL);
+    strncpy(mqtt_ctx->pwd, str, sizeof(mqtt_ctx->pwd) );
+
+    log_info("Parser broker author by [%s:%s]\n", mqtt_ctx->uid, mqtt_ctx->pwd);
+
+    mqtt_ctx->keepalive = iniparser_getint(ini, "broker:keepalive", 30);
+    log_info("Parser broker keepalive timeout [%d] seconds\n", mqtt_ctx->keepalive);
+
+    /*+------------------------------------------------------+
+     *|             parser subscriber settings               |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "subsciber:subTopic", NULL)) )
+    {
+        log_warn("WARNNING: Parser MQTT subscribe topic failure\n");
+    }
+    else
+    {
+        strncpy(mqtt_ctx->subTopic, str, sizeof(mqtt_ctx->subTopic) );
+        mqtt_ctx->subQos = iniparser_getint(ini, "subsciber:subQos", 0);
+        mqtt_ctx->sub_enable = 1;
+
+        log_info("Parser subscriber topic \"%s\" with Qos[%d]\n", mqtt_ctx->subTopic, mqtt_ctx->subQos);
+    }
+
+    /*+------------------------------------------------------+
+     *|             parser publisher settings                |
+     *+------------------------------------------------------+*/
+
+    if( !(str=iniparser_getstring(ini, "publisher:pubTopic", NULL)) )
+    {
+        log_warn("WARNNING: Parser MQTT publisher topic failure\n");
+    }
+    else
+    {
+        strncpy(mqtt_ctx->pubTopic, str, sizeof(mqtt_ctx->pubTopic) );
+        mqtt_ctx->pubQos = iniparser_getint(ini, "publisher:pubQos", 0);
+        mqtt_ctx->interval = iniparser_getint(ini, "publisher:interval", 60);
+        mqtt_ctx->pub_enable = 1;
+
+        log_info("Parser publisher topic \"%s\" with Qos[%d]\n", mqtt_ctx->pubTopic, mqtt_ctx->pubQos);
+    }
+
+
+    return 0;
+}
+
diff --git a/project/iotd/src/conf.h b/project/iotd/src/conf.h
new file mode 100644
index 0000000..0c37f03
--- /dev/null
+++ b/project/iotd/src/conf.h
@@ -0,0 +1,46 @@
+/*********************************************************************************
+ *      Copyright:  (C) 2019 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  conf.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 "hal.h"
+#include "mqtt.h"
+
+enum
+{
+    Qos0, /* 发送者只发送一次消息,不进行重试,Broker不会返回确认消息。在Qos0情况下,Broker可能没有接受到消息 */
+    Qos1, /* 发送者最少发送一次消息,确保消息到达Broker,Broker需要返回确认消息PUBACK。在Qos1情况下,Broker可能接受到重复消息 */
+    Qos2, /* Qos2使用两阶段确认来保证消息的不丢失和不重复。在Qos2情况下,Broker肯定会收到消息,且只收到一次  */
+};
+
+
+typedef struct log_ctx_s
+{
+    /* logger settings */
+    char          logfile[128]; /* logger record file */
+    int           loglevel;     /* logger level  */
+    int           logsize;      /* logger file maxsize, oversize will rollback */
+} log_ctx_t;
+
+typedef struct iotd_ctx_s
+{
+    log_ctx_t     log_ctx;
+    hal_ctx_t     hal_ctx;
+    mqtt_ctx_t    mqtt_ctx;
+} iotd_ctx_t;
+
+
+extern int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug);
+
+#endif   /* ----- #ifndef _CONF_H_  ----- */
+
diff --git a/project/iotd/src/makefile b/project/iotd/src/makefile
new file mode 100644
index 0000000..3b18c24
--- /dev/null
+++ b/project/iotd/src/makefile
@@ -0,0 +1,15 @@
+
+PWD=$(shell pwd )
+
+LIBNAME=$(shell basename ${PWD} )
+TOPDIR=$(shell dirname ${PWD} )
+
+all: clean
+	@rm -f *.o
+	${CROSSTOOL}gcc ${CFLAGS} -I${TOPDIR} -c *.c 
+	${CROSSTOOL}ar -rcs  lib${LIBNAME}.a *.o
+
+clean:
+	@rm -f *.o
+	@rm -f *.a
+
diff --git a/project/iotd/src/mqtt.c b/project/iotd/src/mqtt.c
new file mode 100644
index 0000000..05707b6
--- /dev/null
+++ b/project/iotd/src/mqtt.c
@@ -0,0 +1,329 @@
+/********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  mqtt.h
+ *    Description:  This head file is MQTT subscriber and publisher thread code
+ *
+ *        Version:  1.0.0(20/04/21)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "20/04/21 15:46:42"
+ *
+ ********************************************************************************/
+
+#include <string.h>
+#include <math.h>
+#include <cjson/cJSON.h>
+#include <mosquitto.h>
+#include <errno.h>
+
+#include "util_proc.h"
+#include "logger.h"
+#include "hal.h"
+#include "conf.h"
+
+
+/*+-------------------------------------------+
+ *|                                           |
+ *|    MQTT publisher thread worker code      |
+ *|                                           |
+ *+-------------------------------------------+*/
+
+
+void pub_connect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    iotd_ctx_t             *ctx = (iotd_ctx_t *)userdata;
+    mqtt_ctx_t             *mqtt_ctx;
+    hal_ctx_t              *hal_ctx;
+    int                     rv = 0;
+    char                    msg[128];
+    float                   temp = 0.0; /* temperature */
+    float                   rh = 0.0;   /* relative humidity */
+    int                     retain = 0;
+
+    mqtt_ctx = &ctx->mqtt_ctx;
+    hal_ctx = &ctx->hal_ctx;
+
+
+    if( result )
+    {
+        log_error("Publisher connect to broker server[%s:%d] failed, rv=%d\n", mqtt_ctx->host, mqtt_ctx->port, result);
+        return ;
+    }
+    log_info("Publisher connect to broker server[%s:%d] successfully\n", mqtt_ctx->host, mqtt_ctx->port);
+
+
+    if( hal_ctx->ds18b20_enable )
+    {
+        memset(msg, 0, sizeof(msg));
+
+        if( ds18b20_get_temperature(&temp) < 0 )
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"error\" }", mqtt_ctx->id);
+        else
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"%.2f\" }", mqtt_ctx->id, temp);
+
+        rv = mosquitto_publish(mosq, NULL, mqtt_ctx->pubTopic, strlen(msg), msg, mqtt_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( hal_ctx->sht2x_enable )
+    {
+        memset(msg, 0, sizeof(msg));
+
+        if( sht2x_get_temp_humidity(&temp, &rh) < 0 )
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"error\", \"RH\":\"error\" }", mqtt_ctx->id);
+        else
+            snprintf(msg, sizeof(msg), "{ \"id\":%s, \"temp\":\"%.2f\", \"RH\":\"%.2f\" }", mqtt_ctx->id, temp, rh);
+
+        rv = mosquitto_publish(mosq, NULL, mqtt_ctx->pubTopic, strlen(msg), msg, mqtt_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", mqtt_ctx->host, mqtt_ctx->port);
+    mosquitto_disconnect(mosq);
+
+    return ;
+}
+
+
+void *mqtt_pub_worker(void *args)
+{
+    struct mosquitto       *mosq;
+    bool                    session = true;
+
+    iotd_ctx_t             *ctx = (iotd_ctx_t *)args;
+    mqtt_ctx_t             *mqtt_ctx;
+
+    if( !ctx )
+    {
+        log_error("Invalid input arguments\n");
+        return NULL;
+    }
+
+    mqtt_ctx = &ctx->mqtt_ctx;
+
+
+    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(mqtt_ctx->uid)> 0  && strlen(mqtt_ctx->pwd)> 0 )
+        mosquitto_username_pw_set(mosq, mqtt_ctx->uid, mqtt_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, mqtt_ctx->host, mqtt_ctx->port, mqtt_ctx->keepalive) )
+        {
+            log_error("Publisher connect to broker[%s:%d] failure: %s\n", mqtt_ctx->host, mqtt_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( mqtt_ctx->interval );
+    }
+
+    mosquitto_destroy(mosq);
+    return NULL;
+}
+
+/*+-------------------------------------------+
+ *|                                           |
+ *|    MQTT Subscriber thread worker code     |
+ *|                                           |
+ *+-------------------------------------------+*/
+
+void sub_connect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    iotd_ctx_t             *ctx = (iotd_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->mqtt_ctx.host, ctx->mqtt_ctx.port);
+
+    log_info("Subscriber subTopic '%s' with Qos[%d]\n", ctx->mqtt_ctx.subTopic, ctx->mqtt_ctx.subQos);
+    mosquitto_subscribe(mosq, NULL, ctx->mqtt_ctx.subTopic, ctx->mqtt_ctx.subQos);
+}
+
+void sub_disconnect_callback(struct mosquitto *mosq, void *userdata, int result)
+{
+    iotd_ctx_t             *ctx = (iotd_ctx_t *)userdata;
+
+    log_warn("Subscriber disconnect to broker server[%s:%d], reason=%d\n",
+            ctx->mqtt_ctx.host, ctx->mqtt_ctx.port, result);
+}
+
+void proc_json_items(cJSON *root)
+{
+    int                    i;
+    char                  *value;
+    cJSON                 *item;
+
+    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);
+        }
+        else if( cJSON_Array != item->type )
+        {
+            value = cJSON_Print(item);
+
+            log_debug("JSON Parser key: %s value: %s\n", item->string, value);
+            if( strstr(item->string, "light") || strstr(item->string, "led") || strstr(item->string, "relay"))
+            {
+                if( strstr(value, "on") || strstr(value, "off") )
+                {
+                    log_info("parser get turn '%s' %s from JSON string\n", item->string, value);
+                    gpio_out(item->string, value);
+                }
+            }
+
+            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)
+{
+    iotd_ctx_t             *ctx = (iotd_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 ;
+    }
+
+    /* check ID matched or not */
+    item = cJSON_GetObjectItem(root, "id");
+    if( !item )
+    {
+        log_error("cJSON_Parse get ID failure: %s\n", cJSON_GetErrorPtr());
+        goto OUT;
+    }
+
+    value = cJSON_PrintUnformatted(item);
+    if( strcasecmp(value, ctx->mqtt_ctx.id) )
+    {
+        log_warn("cJSON_Parse get ID not matchs [%s<->%s], drop this message!\n", value, ctx->mqtt_ctx.id);
+        free(value);
+        goto OUT;
+    }
+
+    free(value);
+
+    /* proc JSON mesage */
+    proc_json_items(root);
+
+OUT:
+    cJSON_Delete(root); /* must delete it, or it will result memory leak */
+    return ;
+}
+
+
+void *mqtt_sub_worker(void *args)
+{
+    struct mosquitto       *mosq;
+    bool                    session = true;
+
+    iotd_ctx_t             *ctx = (iotd_ctx_t *)args;
+    mqtt_ctx_t             *mqtt_ctx;
+
+    if( !ctx )
+    {
+        log_error("Invalid input arguments\n");
+        return NULL;
+    }
+
+    mqtt_ctx = &ctx->mqtt_ctx;
+
+
+    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(mqtt_ctx->uid)> 0  && strlen(mqtt_ctx->pwd)> 0 )
+        mosquitto_username_pw_set(mosq, mqtt_ctx->uid, mqtt_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, mqtt_ctx->host, mqtt_ctx->port, mqtt_ctx->keepalive) )
+        {
+            log_error("Subscriber connect to broker[%s:%d] failure: %s\n", mqtt_ctx->host, mqtt_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/iotd/src/mqtt.h b/project/iotd/src/mqtt.h
new file mode 100644
index 0000000..6e2605a
--- /dev/null
+++ b/project/iotd/src/mqtt.h
@@ -0,0 +1,45 @@
+/********************************************************************************
+ *      Copyright:  (C) 2021 LingYun IoT System Studio
+ *                  All rights reserved.
+ *
+ *       Filename:  mqtt.h
+ *    Description:  This head file is MQTT subscriber and publisher thread code
+ *
+ *        Version:  1.0.0(20/04/21)
+ *         Author:  Guo Wenxue <guowenxue@gmail.com>
+ *      ChangeLog:  1, Release initial version on "20/04/21 15:46:42"
+ *
+ ********************************************************************************/
+#ifndef  _MQTT_H_
+#define  _MQTT_H_
+
+
+typedef struct mqtt_ctx_s
+{
+    char          id[32];       /*  production ID */
+
+    /* 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> */
+
+    /* Subscriber settings */
+    int           sub_enable;    /* Subscriber enable or not  */
+    char          subTopic[256]; /* Subscriber topic */
+    int           subQos;        /* Subscriber Qos  */
+
+    /* Publisher settings */
+    int           pub_enable;    /* Publisher enable or not  */
+    char          pubTopic[256]; /* Publisher topic */
+    int           pubQos;        /* Publisher Qos   */
+    int           interval;      /* Publish interval */
+} mqtt_ctx_t;
+
+
+extern void *mqtt_pub_worker(void *args);
+extern void *mqtt_sub_worker(void *args);
+
+#endif   /* ----- #ifndef _MQTT_H_  ----- */
+

--
Gitblit v1.9.1