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], ®_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