RaspberrPi project source code
Guo Wenxue
2024-12-29 e30a4c8103e221201e5bfc1e3f9b19e7a86f68d4
Add iotd program

Signed-off-by: Guo Wenxue <guowenxue@gmail.com>
19 files added
2467 ■■■■■ changed files
project/iotd/etc/iotd.conf 79 ●●●●● patch | view | raw | blame | history
project/iotd/hal/ds18b20.c 106 ●●●●● patch | view | raw | blame | history
project/iotd/hal/ds18b20.h 19 ●●●●● patch | view | raw | blame | history
project/iotd/hal/gpio.c 294 ●●●●● patch | view | raw | blame | history
project/iotd/hal/gpio.h 58 ●●●●● patch | view | raw | blame | history
project/iotd/hal/hal.c 89 ●●●●● patch | view | raw | blame | history
project/iotd/hal/hal.h 42 ●●●●● patch | view | raw | blame | history
project/iotd/hal/makefile 15 ●●●●● patch | view | raw | blame | history
project/iotd/hal/sht20.c 466 ●●●●● patch | view | raw | blame | history
project/iotd/hal/sht20.h 33 ●●●●● patch | view | raw | blame | history
project/iotd/hal/tsl2561.c 198 ●●●●● patch | view | raw | blame | history
project/iotd/hal/tsl2561.h 42 ●●●●● patch | view | raw | blame | history
project/iotd/main.c 223 ●●●●● patch | view | raw | blame | history
project/iotd/makefile 64 ●●●●● patch | view | raw | blame | history
project/iotd/src/conf.c 304 ●●●●● patch | view | raw | blame | history
project/iotd/src/conf.h 46 ●●●●● patch | view | raw | blame | history
project/iotd/src/makefile 15 ●●●●● patch | view | raw | blame | history
project/iotd/src/mqtt.c 329 ●●●●● patch | view | raw | blame | history
project/iotd/src/mqtt.h 45 ●●●●● patch | view | raw | blame | history
project/iotd/etc/iotd.conf
New file
@@ -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
project/iotd/hal/ds18b20.c
New file
@@ -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;
}
project/iotd/hal/ds18b20.h
New file
@@ -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
project/iotd/hal/gpio.c
New file
@@ -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;
}
project/iotd/hal/gpio.h
New file
@@ -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_  ----- */
project/iotd/hal/hal.c
New file
@@ -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 ;
}
project/iotd/hal/hal.h
New file
@@ -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_  ----- */
project/iotd/hal/makefile
New file
@@ -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
project/iotd/hal/sht20.c
New file
@@ -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
project/iotd/hal/sht20.h
New file
@@ -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
project/iotd/hal/tsl2561.c
New file
@@ -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;
}
project/iotd/hal/tsl2561.h
New file
@@ -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_  ----- */
project/iotd/main.c
New file
@@ -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);
    }
}
project/iotd/makefile
New file
@@ -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
project/iotd/src/conf.c
New file
@@ -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;
}
project/iotd/src/conf.h
New file
@@ -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_  ----- */
project/iotd/src/makefile
New file
@@ -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
project/iotd/src/mqtt.c
New file
@@ -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;
}
project/iotd/src/mqtt.h
New file
@@ -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_  ----- */