APUE course source code
guowenxue
6 hours ago 489b6a3c10ccaff0fd0de55b5030e2992d122a7b
update mqttd program to compatble with android app
3 files deleted
6 files modified
13 files added
7 files renamed
2326 ■■■■■ changed files
.gitignore 2 ●●● patch | view | raw | blame | history
project/4.mqttd/booster/config.c 200 ●●●●● patch | view | raw | blame | history
project/4.mqttd/booster/config.h 71 ●●●●● patch | view | raw | blame | history
project/4.mqttd/booster/logger.c 2 ●●● patch | view | raw | blame | history
project/4.mqttd/booster/makefile 17 ●●●●● patch | view | raw | blame | history
project/4.mqttd/booster/proc.c 12 ●●●●● patch | view | raw | blame | history
project/4.mqttd/booster/proc.h 28 ●●●●● patch | view | raw | blame | history
project/4.mqttd/booster/utils.h 61 ●●●●● patch | view | raw | blame | history
project/4.mqttd/config.c 211 ●●●●● patch | view | raw | blame | history
project/4.mqttd/config.h 83 ●●●●● patch | view | raw | blame | history
project/4.mqttd/etc/iotd.conf 18 ●●●●● patch | view | raw | blame | history
project/4.mqttd/etc/iotd.service 16 ●●●●● patch | view | raw | blame | history
project/4.mqttd/iotd.c 371 ●●●●● patch | view | raw | blame | history
project/4.mqttd/makefile 8 ●●●● patch | view | raw | blame | history
project/4.mqttd/modules/ds18b20.c patch | view | raw | blame | history
project/4.mqttd/modules/ds18b20.h patch | view | raw | blame | history
project/4.mqttd/modules/leds.c 4 ●●● patch | view | raw | blame | history
project/4.mqttd/modules/leds.h patch | view | raw | blame | history
project/4.mqttd/modules/makefile 41 ●●●●● patch | view | raw | blame | history
project/4.mqttd/modules/modules.h 24 ●●●●● patch | view | raw | blame | history
project/4.mqttd/modules/pwm.c 213 ●●●●● patch | view | raw | blame | history
project/4.mqttd/modules/pwm.h 44 ●●●●● patch | view | raw | blame | history
project/4.mqttd/modules/relay.c 157 ●●●●● patch | view | raw | blame | history
project/4.mqttd/modules/relay.h 56 ●●●●● patch | view | raw | blame | history
project/4.mqttd/modules/sht20.c 7 ●●●●● patch | view | raw | blame | history
project/4.mqttd/modules/sht20.h patch | view | raw | blame | history
project/4.mqttd/modules/tsl2561.c 212 ●●●●● patch | view | raw | blame | history
project/4.mqttd/modules/tsl2561.h 34 ●●●●● patch | view | raw | blame | history
project/4.mqttd/mqttd.c 434 ●●●●● patch | view | raw | blame | history
.gitignore
@@ -8,7 +8,7 @@
*.o
lib*.so*
lib*.a
mqttd
iotd
# ctags files
cscope.*
project/4.mqttd/booster/config.c
File was deleted
project/4.mqttd/booster/config.h
File was deleted
project/4.mqttd/booster/logger.c
@@ -101,7 +101,7 @@
    {
        if ( !(fp = fopen(fname, "a+")) )
        {
            fprintf(stderr, "%s() failed: %s\n", __func__, strerror(errno));
            fprintf(stderr, "%s() %s failed: %s\n", __func__, fname, strerror(errno));
            return -2;
        }
        L.fp = fp;
project/4.mqttd/booster/makefile
@@ -12,22 +12,27 @@
#*******************************************************************************
PWD=$(shell pwd )
CFLAGS += -Wno-format-overflow
LIBNAME=$(shell basename ${PWD} )
TOPDIR=$(shell dirname ${PWD} )
BUILD_ARCH=$(shell uname -m)
ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
    CROSS_COMPILE?=arm-linux-gnueabihf-
endif
LIBNAME=$(shell basename ${PWD} )
TOPDIR=$(shell dirname ${PWD} )
OPENLIBS_INCPATH=${TOPDIR}/openlibs/install/include
OPENLIBS_LIBPATH=${TOPDIR}/openlibs/install/lib
all: clean
CFLAGS+=-I${OPENLIBS_INCPATH} -I${TOPDIR}/booster
all: prelibs clean
    @rm -f *.o
    @${CROSS_COMPILE}gcc ${CFLAGS} -I${TOPDIR} -c *.c
    ${CROSS_COMPILE}gcc ${CFLAGS} -c *.c
    ${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
prelibs:
    if [ -n "${CROSS_COMPILE}" ] ; then cd ${TOPDIR}/openlibs/libgpiod && ./build.sh ; fi;
clean:
    @rm -f *.o
    @rm -f *.a
project/4.mqttd/booster/proc.c
@@ -3,7 +3,7 @@
 *                  All rights reserved.
 *
 *       Filename:  proc.c
 *    Description:  This file is the process API
 *    Description:  This file is the process/thread API
 *
 *        Version:  1.0.0(7/06/2020)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
@@ -59,7 +59,6 @@
            break;
    }
}
/* install default signal process functions  */
void install_default_signal(void)
@@ -184,8 +183,6 @@
    return 0;
}
/* ****************************************************************************
 * FunctionName: record_daemon_pid
@@ -345,8 +342,6 @@
    return 0;
}
/* ****************************************************************************
 * FunctionName: set_daemon_running
 * Description : Set the programe running as daemon if it's not running and record
@@ -398,8 +393,6 @@
        goto CleanUp;
CleanUp:
    if( thread_id )
    {
        if( rv )
@@ -412,7 +405,6 @@
    pthread_attr_destroy(&thread_attr);
    return rv;
}
/* excute a linux command by system() */
void exec_system_cmd(const char *format, ...)
@@ -428,5 +420,3 @@
    system(cmd);
}
project/4.mqttd/booster/proc.h
@@ -11,11 +11,10 @@
 *
 ********************************************************************************/
#ifndef __PROC_H_
#define __PROC_H_
#ifndef __UTIL_PROC_H_
#define __UTIL_PROC_H_
#include <signal.h>
#include <time.h>
#define PID_ASCII_SIZE  11
@@ -62,28 +61,5 @@
/* get daemon process ID from $pid_file   */
extern pid_t get_daemon_pid(const char *pid_file);
/* +------------------------+
 * |  inline functions API  |
 * +------------------------+*/
static inline void msleep(unsigned long ms)
{
    struct timespec cSleep;
    unsigned long ulTmp;
    cSleep.tv_sec = ms / 1000;
    if (cSleep.tv_sec == 0)
    {
        ulTmp = ms * 10000;
        cSleep.tv_nsec = ulTmp * 100;
    }
    else
    {
        cSleep.tv_nsec = 0;
    }
    nanosleep(&cSleep, 0);
    return ;
}
#endif
project/4.mqttd/booster/utils.h
New file
@@ -0,0 +1,61 @@
/********************************************************************************
 *      Copyright:  (C) 2020 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  util.h
 *    Description:  This file is common utility functions
 *
 *        Version:  1.0.0(7/06/2012~)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "7/06/2012 09:21:33 PM"
 *
 ********************************************************************************/
#ifndef __UTIL_H_
#define __UTIL_H_
#include <time.h>
#include <stddef.h>
#define container_of(ptr, type, member) ((type *)((char *)(ptr) - offsetof(type, member)))
/* +------------------------+
 * |   time functions API   |
 * +------------------------+*/
static inline void msleep(unsigned long ms)
{
    struct timespec cSleep;
    unsigned long ulTmp;
    cSleep.tv_sec = ms / 1000;
    if (cSleep.tv_sec == 0)
    {
        ulTmp = ms * 10000;
        cSleep.tv_nsec = ulTmp * 100;
    }
    else
    {
        cSleep.tv_nsec = 0;
    }
    nanosleep(&cSleep, 0);
    return ;
}
static inline int check_timeout(time_t *last_time, int interval)
{
    int                  timeout = 0;
    time_t               now;
    time(&now);
    if( difftime(now, *last_time)>interval )
    {
        timeout = 1;
        *last_time = now;
    }
    return timeout;
}
#endif
project/4.mqttd/config.c
New file
@@ -0,0 +1,211 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  config.c
 *    Description:  This file is iotd configure file parser function
 *
 *        Version:  1.0.0(2019年06月25日)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2019年06月25日 22时23分55秒"
 *
 ********************************************************************************/
#include "config.h"
#include "logger.h"
#include "iniparser.h"
int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug)
{
    dictionary          *ini;
    const char          *str;
    int                  val;
    int                  rv = 0;
    logger_t            *logger;
    hwinfo_t            *hwinfo;
    mqtt_ctx_t          *mqtt;
    if( !conf_file || !ctx )
    {
        fprintf(stderr, "%s() Invalid input arguments\n", __func__);
        return -1;
    }
    memset(ctx, 0, sizeof(*ctx));
    logger = &ctx->logger;
    hwinfo = &ctx->hwinfo;
    mqtt = &ctx->mqtt;
    ini = iniparser_load(conf_file);
    if( !ini )
    {
        fprintf(stderr, "ERROR: Configure file '%s' load failed\n", conf_file);
        return -2;
    }
    /*+------------------------------------------------------+
     *|    parser logger settings and start logger system    |
     *+------------------------------------------------------+*/
    if( !debug )
    {
        str = iniparser_getstring(ini, "logger:file", "/tmp/iotd.log");
        strncpy(logger->logfile, str, sizeof(logger->logfile));
        logger->logsize = iniparser_getint(ini, "logger:size", 1024);
        logger->loglevel = iniparser_getint(ini, "logger:level", LOG_LEVEL_INFO);
    }
    else
    {
        strncpy(logger->logfile, "console", sizeof(logger->logfile));
        logger->loglevel = LOG_LEVEL_DEBUG;
        logger->logsize = 0;
    }
    if( log_open(logger->logfile, logger->loglevel, logger->logsize, LOG_LOCK_DISABLE) < 0 )
    {
        fprintf(stderr, "Logger system initialise failure\n");
        return -2;
    }
    log_info("Logger system initialise ok\n");
    /*+------------------------------------------------------+
     *|               parser production ID                   |
     *+------------------------------------------------------+*/
    if( !(str=iniparser_getstring(ini, "common:devid", NULL)) )
    {
        log_error("ERROR: Parser device ID failure\n");
        rv = -3;
        goto cleanup;
    }
    /* cJSON parser ID will get ""  */
    strncpy(mqtt->devid, str, sizeof(mqtt->devid));
    log_info("Parser device ID [%s]\n", mqtt->devid);
    /*+------------------------------------------------------+
     *|       parser hardware module configuration           |
     *+------------------------------------------------------+*/
    /* relay */
    hwinfo->relay=iniparser_getint(ini, "hardware:relay", 0);
    if( !hwinfo->relay )
        log_warn("Parser relay module disabled\n");
    else
        log_info("Parser relay module enabled\n");
    /* RGB 3-colors LED */
    hwinfo->led=iniparser_getint(ini, "hardware:rgbled", 0);
    if( !hwinfo->led )
        log_warn("Parser RGB 3-colors Led module disabled\n");
    else
        log_info("Parser RGB 3-colors Led module enabled\n");
    /* beeper */
    hwinfo->beeper=iniparser_getint(ini, "hardware:beep", 0);
    if( !hwinfo->beeper )
        log_warn("Parser beeper module disabled\n");
    else
        log_info("Parser beeper module enabled\n");
    /* DS18B20 temperature module */
    hwinfo->ds18b20=iniparser_getint(ini, "hardware:ds18b20", 0);
    if( !hwinfo->ds18b20 )
        log_warn("Parser DS18B20 temperature module disabled\n");
    else
        log_info("Parser DS18B20 temperature module enabled\n");
    /* SHT20 temperature and hummidity module */
    hwinfo->sht2x=iniparser_getint(ini, "hardware:sht2x", 0);
    if( !hwinfo->sht2x )
        log_warn("Parser SHT2X temperature and hummidity module disabled\n");
    else
        log_info("Parser SHT2X temperature and hummidity module enabled\n");
    /* TSL2561 light intensity sensor module */
    hwinfo->tsl2561=iniparser_getint(ini, "hardware:tsl2561", 0);
    if( !hwinfo->tsl2561 )
        log_warn("Parser TSL2561 light intensity sensor module disabled\n");
    else
        log_info("Parser TSL2561 light intensity sensor module enabled\n");
    /*+------------------------------------------------------+
     *|              parser broker settings                  |
     *+------------------------------------------------------+*/
    if( !(str=iniparser_getstring(ini, "broker:hostname", NULL)) )
    {
        log_error("ERROR: Parser MQTT broker server hostname failure\n");
        rv = -4;
        goto cleanup;
    }
    strncpy(mqtt->host, str, sizeof(mqtt->host) );
    if( (val=iniparser_getint(ini, "broker:port", -1)) < 0 )
    {
        log_error("ERROR: Parser MQTT broker server port failure\n");
        rv = -5;
        goto cleanup;
    }
    mqtt->port = val;
    log_info("Parser MQTT broker server [%s:%d]\n", mqtt->host, mqtt->port);
    if( (str=iniparser_getstring(ini, "broker:token", NULL)) )
    {
        strncpy(mqtt->token, str, sizeof(mqtt->uid) );
        log_info("Parser broker token [%s]\n", mqtt->token);
    }
    if( (str=iniparser_getstring(ini, "broker:username", NULL)) )
        strncpy(mqtt->uid, str, sizeof(mqtt->uid) );
    if( (str=iniparser_getstring(ini, "broker:password", NULL)) )
        strncpy(mqtt->pwd, str, sizeof(mqtt->pwd) );
    if( mqtt->uid )
        log_info("Parser broker account [%s:%s]\n", mqtt->uid, mqtt->pwd);
    mqtt->keepalive = iniparser_getint(ini, "broker:keepalive", DEF_KEEPALIVE);
    log_info("Parser broker keepalive timeout [%d] seconds\n", mqtt->keepalive);
    /*+------------------------------------------------------+
     *|             parser publisher settings                |
     *+------------------------------------------------------+*/
    if( !(str=iniparser_getstring(ini, "publisher:pubTopic", NULL)) )
    {
        log_error("ERROR: Parser MQTT broker publisher topic failure\n");
        rv = -6;
        goto cleanup;
    }
    snprintf(mqtt->pubTopic, sizeof(mqtt->pubTopic), "%s/%s", str, mqtt->devid);
    mqtt->pubQos = iniparser_getint(ini, "publisher:pubQos", DEF_QOS);
    mqtt->interval = iniparser_getint(ini, "publisher:interval", DEF_PUBINTERVAL);
    log_info("Parser publisher topic \"%s\" with Qos[%d] interval[%d]\n", mqtt->pubTopic, mqtt->pubQos, mqtt->interval);
    /*+------------------------------------------------------+
     *|             parser subscriber settings               |
     *+------------------------------------------------------+*/
    if( !(str=iniparser_getstring(ini, "subsciber:subTopic", NULL)) )
    {
        log_error("ERROR: Parser MQTT broker publisher topic failure\n");
        rv = -7;
        goto cleanup;
    }
    snprintf(mqtt->subTopic, sizeof(mqtt->subTopic), "%s/%s", str, mqtt->devid);
    mqtt->subQos = iniparser_getint(ini, "subsciber:subQos", DEF_QOS);
    log_info("Parser subscriber topic \"%s\" with Qos[%d]\n", mqtt->subTopic, mqtt->subQos);
cleanup:
    if( ini )
        iniparser_freedict(ini);
    if( rv )
        log_close();
    return rv;
}
project/4.mqttd/config.h
New file
@@ -0,0 +1,83 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  config.h
 *    Description:  This file is iotd configure file parser function
 *
 *        Version:  1.0.0(2019年06月25日)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "2019年06月25日 22时23分55秒"
 *
 ********************************************************************************/
#ifndef  __CONF_H_
#define  __CONF_H_
#include "utils.h"
enum
{
    Qos0, /* 发送者只发送一次消息,不进行重试,Broker不会返回确认消息。在Qos0情况下,Broker可能没有接受到消息 */
    Qos1, /* 发送者最少发送一次消息,确保消息到达Broker,Broker需要返回确认消息PUBACK。在Qos1情况下,Broker可能接受到重复消息 */
    Qos2, /* Qos2使用两阶段确认来保证消息的不丢失和不重复。在Qos2情况下,Broker肯定会收到消息,且只收到一次  */
};
#define DEF_KEEPALIVE       30
#define DEF_PUBINTERVAL     120
#define DEF_QOS             Qos2
typedef struct hwinfo_s
{
    int             relay;   /* relay aviable or not.   0:Disable  1: Enable */
    int             led;     /* RGB led aviable or not. 0:Disable  1: Enable */
    int             beeper;  /* beeper aviable or not.  0:Disable  1: Enable */
    int             ds18b20; /* DS1B820 aviable or not. 0:Disable  1: Enable */
    int             sht2x;   /* SHT20  aviable or not.  0:Disable  1: Enable */
    int             tsl2561; /* TSL2561 aviable or not. 0:Disable  1: Enable */
} hwinfo_t;
/* logger settings */
typedef struct logger_s
{
    char            logfile[128]; /* logger record file */
    int             loglevel;     /* logger level  */
    int             logsize;      /* logger file maxsize, oversize will rollback */
} logger_t;
typedef struct mqtt_ctx_s
{
    char            devid[32];    /*  device ID */
    void           *userdata;      /* user data pointer */
    /* Broker settings  */
    char            host[128];  /* MQTT broker server name  */
    int             port;       /* MQTT broker listen port  */
    char            uid[64];    /* username */
    char            pwd[64];    /* password */
    char            token[64];  /* token */
    int             keepalive;  /* MQTT broker send PING message to subsciber/publisher keepalive timeout<seconds> */
    /* Publisher settings */
    char            pubTopic[256]; /* Publisher topic  */
    int             pubQos;        /* Publisher Qos */
    int             interval ;     /* Publish sensor data interval time, unit seconds */
    /* Subscriber settings */
    char            subTopic[256]; /* Subscriber topic */
    int             subQos;        /* Subscriber Qos  */
} mqtt_ctx_t;
typedef struct iotd_ctx_s
{
    logger_t        logger;
    hwinfo_t        hwinfo;
    mqtt_ctx_t      mqtt;
} iotd_ctx_t;
/* get iotd_ctx address by mqtt_ctx address */
#define to_iotd(ctx)    container_of(ctx, iotd_ctx_t, mqtt);
extern int parser_conf(const char *conf_file, iotd_ctx_t *ctx, int debug);
#endif
project/4.mqttd/etc/iotd.conf
File was renamed from project/4.mqttd/etc/mqttd.conf
@@ -1,14 +1,17 @@
[common]
devid="RPi3B#01"
devid="rpi001"
# 树莓派连接的外设信息,0:禁用或未连接  其他: 使能或相关硬件连接的Pin管脚(wPI模式)
# 树莓派连接的外设信息,0:禁用或未连接
[hardware]
# 是否使能 RGB 三色灯模块,0:禁用  1:使能
rgbled=1
# 是否使能继电器模块,0:禁用 1:使能
relay=0
# 是否使能 DS18b20 温度传感器模块,0:禁用  1:使能
ds18b20=1
ds18b20=0
# 是否使能 SHT2X 温湿度传感器模块,0:禁用  1:使能
sht2x=1
@@ -16,7 +19,7 @@
[logger]
# 日志记录文件
file=/tmp/mqttd.log
file=/var/log/iotd.log
# 日志级别: 0:ERROR 1:WARN 2:INFO 3:DEBUG 4:TRACE 
level=2
@@ -44,7 +47,7 @@
[publisher]
# mosquitto_sub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Uplink
# mosquitto_sub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Uplink/rpi001
pubTopic="$Sys/Studio/Uplink"
pubQos=0
@@ -53,7 +56,10 @@
[subsciber]
# mosquitto_pub -h main.iot-yun.club -p 10883 -u lingyun -P lingyun -t \$Sys/Studio/Downlink -m '{"id":"RPi3B#01", "leds":[{"red":"on","green":"off","blue":"on"}]}'
# mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "RedLed":"on" }'
# mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "GreenLed":"on" }'
# mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "BlueLed":"on" }'
# mosquitto_pub -h weike-iot.com -p 8013 -u lingyun -P lingyun -t \$Sys/Studio/Downlink/rpi001 -m '{ "Relay":"on" }'
subTopic="$Sys/Studio/Downlink"
subQos=0
project/4.mqttd/etc/iotd.service
New file
@@ -0,0 +1,16 @@
[Unit]
Description=RaspberryPi iotd program Service
After=network.target
[Service]
Type=simple
ExecStartPre=/bin/rm -f /tmp/.iotd.pid /var/log/iotd.log
ExecStart=/usr/bin/iotd -c /etc/iotd.conf
ExecStop=/bin/rm -f /tmp/.iotd.pid
Restart=always
RestartSec=2
[Install]
WantedBy=multi-user.target
project/4.mqttd/iotd.c
New file
@@ -0,0 +1,371 @@
/*********************************************************************************
 *      Copyright:  (C) 2019 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  main.c
 *    Description:  This file is MQTT publisher/subscriber example program.
 *
 *        Version:  1.0.0(29/01/19)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "29/01/19 15:34:41"
 *
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <time.h>
#include <getopt.h>
#include <libgen.h>
#include <string.h>
#include <errno.h>
#include <mosquitto.h>
#include <cjson/cJSON.h>
#include "logger.h"
#include "proc.h"
#include "config.h"
#include "ds18b20.h"
#include "sht20.h"
#include "leds.h"
#include "relay.h"
#define PROG_VERSION               "v1.0.0"
#define DAEMON_PIDFILE             "/tmp/.iotd.pid"
void *mqtt_worker(void *args);
static void program_usage(char *progname)
{
    printf("Usage: %s [OPTION]...\n", progname);
    printf(" %s is LingYun studio iotd program running on RaspberryPi\n", progname);
    printf("\nMandatory arguments to long options are mandatory for short options too:\n");
    printf(" -b[daemon  ]  Running in daemon mode\n");
    printf(" -d[debug   ]  Running in debug mode\n");
    printf(" -c[conf    ]  Specify configure file\n");
    printf(" -h[help    ]  Display this help information\n");
    printf(" -v[version ]  Display the program version\n");
    printf("\n%s version %s\n", progname, PROG_VERSION);
    return;
}
int main (int argc, char **argv)
{
    int                daemon = 0;
    pthread_t          tid;
    iotd_ctx_t         ctx;
    char               *conf_file="/etc/iotd.conf";
    int                debug = 0;
    int                opt;
    char              *progname=NULL;
    struct option long_options[] = {
        {"conf", required_argument, NULL, 'c'},
        {"daemon", no_argument, NULL, 'b'},
        {"debug", no_argument, NULL, 'd'},
        {"version", no_argument, NULL, 'v'},
        {"help", no_argument, NULL, 'h'},
        {NULL, 0, NULL, 0}
    };
    progname = (char *)basename(argv[0]);
    /* parser the command line parameters */
    while ((opt = getopt_long(argc, argv, "c:bdvh", long_options, NULL)) != -1)
    {
        switch (opt)
        {
            case 'c': /* Set configure file */
                conf_file = optarg;
                break;
            case 'b': /* Set daemon running */
                daemon = 1;
                break;
            case 'd': /* Set debug running */
                debug = 1;
                break;
            case 'v':  /* Get software version */
                printf("%s version %s\n", progname, PROG_VERSION);
                return 0;
            case 'h':  /* Get help information */
                program_usage(progname);
                return 0;
            default:
                break;
        }
    }
    /* parser configure file */
    if( parser_conf(conf_file, &ctx, debug)<0 )
    {
        fprintf(stderr, "Parser iotd configure file failure\n");
        return -2;
    }
    /* install signal proc handler */
    install_default_signal();
    /* check program already running or not, if already running then exit, or set running as daemon */
    if( check_set_program_running(daemon, DAEMON_PIDFILE) < 0 )
        goto cleanup;
    /* initial mosquitto library */
    mosquitto_lib_init();
    /* start MQTT thread */
    if( thread_start(&tid, mqtt_worker, &ctx.mqtt ) < 0 )
    {
        log_error("Start MQTT worker thread failure\n");
        goto cleanup;
    }
    log_info("Start MQTT worker thread ok\n");
    /* control thread loop */
    while( !g_signal.stop )
    {
        sleep(1);
    }
cleanup:
    mosquitto_lib_cleanup();
    log_close();
    return 0;
}
/*
 * +--------------------------------+
 * |      MQTT Thread worker        |
 * +--------------------------------+
 */
/* process publisher(uplink) data */
void pub_connect_callback(struct mosquitto *mosq, void *userdata, int result)
{
    mqtt_ctx_t             *mqtt = (mqtt_ctx_t *)userdata;
    iotd_ctx_t             *iotd = to_iotd(mqtt);
    int                     rv = 0;
    char                    msg[128];
    float                   temp = 0.0; /* temperature */
    float                   rh = 0.0;   /* relative humidity */
    int                     retain = 0;
    static time_t           last_time = 0;
    /* publish time is not arrive */
    if( !check_timeout(&last_time, mqtt->interval) )
        return ;
    log_debug("Publish topic '%s'\n", mqtt->pubTopic);
    if( iotd->hwinfo.ds18b20 )
    {
        log_debug("DS18B20 temperature sensor enabled, start broadcast it\n");
        if( ds18b20_get_temperature(&temp)<0 )
            return ;
        memset(msg, 0, sizeof(msg));
        snprintf(msg, sizeof(msg), "{\"temperature\":%.2f}", temp);
        rv = mosquitto_publish(mosq, NULL, mqtt->pubTopic, strlen(msg), msg, mqtt->pubQos, retain);
        if( rv )
        {
            log_error("Publisher broadcast message '%s' failure: %d\n", msg, rv);
        }
        else
        {
            log_info("Publisher broadcast message '%s' ok\n", msg);
        }
    }
    if( iotd->hwinfo.sht2x )
    {
        log_debug("SHT2X temperature and humidity sensor enabled, start broadcast it\n");
        if( sht2x_get_temp_humidity(&temp, &rh)<0 )
            return ;
        memset(msg, 0, sizeof(msg));
        snprintf(msg, sizeof(msg), "{\"temperature\":%.2f, \"humidity\":%.2f}", temp, rh);
        rv = mosquitto_publish(mosq, NULL, mqtt->pubTopic, strlen(msg), msg, mqtt->pubQos, retain);
        if( rv )
        {
            log_error("Publisher broadcast message '%s' failure: %d\n", msg, rv);
        }
        else
        {
            log_info("Publisher broadcast message '%s' ok\n", msg);
        }
    }
    return ;
}
/* process subscriber(downlink) data */
void proc_json_items(cJSON *root, mqtt_ctx_t *mqtt)
{
    iotd_ctx_t             *iotd = to_iotd(mqtt);
    cJSON                  *item;
    char                    leds[LED_CNT][16] = {"RedLed", "GreenLed", "BlueLed"};
    char                    relays[RELAY_CNT][16] = {"Relay"};
    int                     which;
    if( !root )
    {
        log_error("Invalid input arguments $root\n");
        return ;
    }
    /* parser JSON command for relays */
    if( iotd->hwinfo.relay )
    {
        for(which=0; which<RELAY_CNT; which++)
        {
            item = cJSON_GetObjectItem(root, relays[which]);
            if( !item )
                continue;
            if( strcasecmp(item->valuestring, "on") )
                turn_relay(which, ON);
            else if( strcasecmp(item->valuestring, "off") )
                turn_relay(which, OFF);
        }
    }
    /* parser JSON command for RGB leds */
    if( iotd->hwinfo.led )
    {
        for(which=0; which<LED_CNT; which++)
        {
            item = cJSON_GetObjectItem(root, leds[which]);
            if( !item )
                continue;
            if( strcasecmp(item->valuestring, "on") )
                turn_led(which, ON);
            else if( strcasecmp(item->valuestring, "off") )
                turn_led(which, OFF);
        }
    }
    return ;
}
void sub_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
{
    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
    cJSON                  *root = NULL;
    if ( !message->payloadlen )
    {
        log_error("%s (null)\n", message->topic);
        return ;
    }
    log_info("Subscriber receive message: '%s'\n", message->payload);
    root = cJSON_Parse(message->payload);
    if( !root )
    {
        log_error("cJSON_Parse parser failure: %s\n", cJSON_GetErrorPtr());
        return ;
    }
    /* process receive message data */
    proc_json_items(root, ctx);
    /* must delete it, or it will result memory leak */
    cJSON_Delete(root);
    return ;
}
void mqtt_connect_callback(struct mosquitto *mosq, void *userdata, int result)
{
    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
    if( result )
    {
        log_error("mosquitto connect to broker server failed, rv=%d\n", result);
        return ;
    }
    log_info("mosquitto connect to broker server[%s:%d] successfully\n", ctx->host, ctx->port);
    mosquitto_subscribe(mosq, NULL, ctx->subTopic, ctx->subQos);
}
void mqtt_disconnect_callback(struct mosquitto *mosq, void *userdata, int result)
{
    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)userdata;
    log_warn("mosquitto disconnect to broker server[%s:%d], reason=%d\n", ctx->host, ctx->port, result);
}
void *mqtt_worker(void *args)
{
    mqtt_ctx_t             *ctx = (mqtt_ctx_t *)args;
    struct mosquitto       *mosq;
    bool                    session = true;
    int                     rv = 0;
    mosq = mosquitto_new(ctx->devid, session, ctx);
    if( !mosq )
    {
        log_error("mosquitto_new failure\n");
        return NULL;
    }
    /* connnect to broker by token or uid/pwd */
    if( strlen(ctx->token ) > 0)
    {
        log_info("Using token authentication: '%s'\n", ctx->token);
        mosquitto_username_pw_set(mosq, ctx->token, NULL);
    }
    else if( strlen(ctx->uid)> 0  && strlen(ctx->pwd)> 0 )
    {
        log_info("Using username/password authentication\n");
        mosquitto_username_pw_set(mosq, ctx->uid, ctx->pwd);
    }
    /* set callback functions */
    mosquitto_connect_callback_set(mosq, mqtt_connect_callback);
    mosquitto_disconnect_callback_set(mosq, mqtt_disconnect_callback);
    mosquitto_message_callback_set(mosq, sub_message_callback);
    while( !g_signal.stop )
    {
        /* connect to MQTT broker  */
        if( mosquitto_connect(mosq, ctx->host, ctx->port, ctx->keepalive) )
        {
            log_error("mosquitto connect to broker[%s:%d] failure: %s\n", ctx->host, ctx->port, strerror(errno));
            sleep(1);
            continue;
        }
        while(!g_signal.stop)
        {
            /* periodically publish and report data */
            pub_connect_callback(mosq, ctx, MOSQ_ERR_SUCCESS);
            /* MQTT process in Non-blocking mode, timeout for 1s */
            if( MOSQ_ERR_SUCCESS != (rv = mosquitto_loop(mosq, 1000, 1)) )
            {
                log_warn("MQTT loop error: %s, reconnecting...\n", mosquitto_strerror(rv));
                break;
            }
        }
        mosquitto_disconnect(mosq);
        sleep(1);
    }
    mosquitto_destroy(mosq);
    return NULL;
}
project/4.mqttd/makefile
@@ -12,7 +12,7 @@
#*******************************************************************************
PRJ_PATH=$(shell pwd)
APP_NAME = mqttd
APP_NAME = iotd
BUILD_ARCH=$(shell uname -m)
ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
@@ -26,7 +26,7 @@
CFLAGS = -Wall -Wshadow -Wundef -Wmaybe-uninitialized -D_GNU_SOURCE
# C source file in sub-directory
SRCS=booster
SRCS=booster modules
SRCS_PATH=$(patsubst %,${PRJ_PATH}/%,$(SRCS))
CFLAGS+=$(patsubst %,-I%,$(SRCS_PATH))
LDFLAGS+=$(patsubst %,-L%,$(SRCS_PATH))
@@ -44,13 +44,13 @@
LDFLAGS+=-lpthread
all: entry subdir
    ${CROSS_COMPILE}gcc ${CFLAGS} mqttd.c -o ${APP_NAME} ${LDFLAGS}
    ${CROSS_COMPILE}gcc ${CFLAGS} ${SRCFILES} -o ${APP_NAME} ${LDFLAGS}
entry:
    @echo "Building ${APP_NAME} on ${BUILD_ARCH}"
subdir:
    @for dir in ${libs} ;  do CFLAGS="${CFLAGS}" make -C $${dir}; done
    @for dir in ${libs} ;  do make -C $${dir} ; done
install:
    cp ${APP_NAME} /tftp
project/4.mqttd/modules/ds18b20.c
project/4.mqttd/modules/ds18b20.h
project/4.mqttd/modules/leds.c
File was renamed from project/4.mqttd/booster/leds.c
@@ -102,7 +102,7 @@
    int            i;
    led_info_t    *led;
    log_warn("terminate RGB Led gpios\n");
    log_debug("terminate RGB Led gpios\n");
    if( !ctx )
    {
@@ -148,6 +148,8 @@
    led = &ctx.leds[which];
    log_info("turn Led %s %s\n", led->name, cmd?"on":"off");
    if( OFF == cmd )
    {
        gpiod_line_set_value(led->line, !led->active);
project/4.mqttd/modules/leds.h
project/4.mqttd/modules/makefile
New file
@@ -0,0 +1,41 @@
#********************************************************************************
#      Copyright:  (C) 2023 LingYun IoT System Studio
#                  All rights reserved.
#
#       Filename:  Makefile
#    Description:  This file used compile all the source code to static library
#
#        Version:  1.0.0(11/08/23)
#         Author:  Guo Wenxue <guowenxue@gmail.com>
#      ChangeLog:  1, Release initial version on "11/08/23 16:18:43"
#
#*******************************************************************************
PWD=$(shell pwd)
LIBNAME=$(shell basename ${PWD} )
TOPDIR=$(shell dirname ${PWD} )
BUILD_ARCH=$(shell uname -m)
ifneq ($(findstring $(BUILD_ARCH), "x86_64" "i386"),)
    CROSS_COMPILE?=arm-linux-gnueabihf-
endif
OPENLIBS_INCPATH=${TOPDIR}/openlibs/install/include
OPENLIBS_LIBPATH=${TOPDIR}/openlibs/install/lib
CFLAGS+=-I${OPENLIBS_INCPATH} -I${TOPDIR}/booster
all: prelibs clean
    @rm -f *.o
    ${CROSS_COMPILE}gcc ${CFLAGS} -c *.c
    ${CROSS_COMPILE}ar -rcs  lib${LIBNAME}.a *.o
prelibs:
    if [ -n "${CROSS_COMPILE}" ] ; then cd ${TOPDIR}/openlibs/libgpiod && ./build.sh ; fi;
clean:
    @rm -f *.o
    @rm -f *.a
distclean:
    @make clean
project/4.mqttd/modules/modules.h
New file
@@ -0,0 +1,24 @@
/********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  modules.h
 *    Description:  This file
 *
 *        Version:  1.0.0(08/17/2023)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "08/17/2023 09:27:25 PM"
 *
 ********************************************************************************/
#ifndef  _MODULES_H_
#define  _MODULES_H_
#include "ds18b20.h"
#include "leds.h"
#include "pwm.h"
#include "relay.h"
#include "sht20.h"
#include "tsl2561.h"
#endif   /* ----- #ifndef _MODULES_H_  ----- */
project/4.mqttd/modules/pwm.c
New file
@@ -0,0 +1,213 @@
/*********************************************************************************
 *      Copyright:  (C) 2021 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  pwm.c
 *    Description:  This file is used to control PWM buzzer/Led
 *
 * Pin connection:
 *               PWM Module              Raspberry Pi Board
 *                  VCC       <----->      5V
 *                 buzzer     <----->      #Pin32(BCM GPIO12)
 *                  Led       <----->      #Pin33(BCM GPIO13)
 *                  GND       <----->      GND
 *
 * /boot/config.txt:
 *
 *          dtoverlay=pwm,pin=12,func=4 (Buzzer)
 *          dtoverlay=pwm,pin=13,func=4 (Led)
 *
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include <libgen.h>
#include "logger.h"
#include "utils.h"
#include "pwm.h"
/* check PWM $channel export or not */
int check_pwm(int channel)
{
    char          path[256];
    /* check /sys/class/pwm/pwmchip0/pwmN exist or not */
    snprintf(path, sizeof(path), "%s/pwm%d", PWMCHIP_PATH, channel);
    return access(path, F_OK) ? 0 : 1;
}
/* export($export=1)/unexport($export=0) PWM $channel */
int export_pwm(int channel, int export)
{
    int           fd;
    char          buf[32];
    char          path[256];
    if( export && check_pwm(channel) )
        return 0; /* export already when export  */
    else if( !export && !check_pwm(channel) )
        return 0; /* unexport already when unexport  */
    /* export PWM channel by echo N > /sys/class/pwm/pwmchip0/export */
    snprintf(path, sizeof(path), "%s/%s", PWMCHIP_PATH, export?"export":"unexport");
    if( (fd=open(path, O_WRONLY)) < 0 )
    {
        log_error("open '%s' failed: %s\n", path, strerror(errno));
        return -3;
    }
    snprintf(buf, sizeof(buf), "%d", channel);
    write(fd, buf, strlen(buf));
    msleep(100);
    if( export && check_pwm(channel) )
        return 0; /* export already when export  */
    else if( !export && !check_pwm(channel) )
        return 0; /* unexport already when unexport  */
    return -4;
}
/* configure PWM $channel */
int config_pwm(int channel, int freq, int duty)
{
    int           fd;
    char          buf[32];
    char          path[256];
    int           period;
    int           duty_cycle;
    if( !check_pwm(channel) )
        return -2;
    /* open PWM period file and write period in ns */
    snprintf(path, sizeof(path), "%s/pwm%d/period", PWMCHIP_PATH, channel);
    if( (fd=open(path, O_WRONLY)) < 0 )
    {
        log_error("open '%s' failed: %s\n", path, strerror(errno));
        return -3;
    }
    period = 1000000000/freq;
    snprintf(buf, sizeof(buf), "%d", period);
    write(fd, buf, strlen(buf));
    /* open PWM duty_cycle file and write duty */
    snprintf(path, sizeof(path), "%s/pwm%d/duty_cycle", PWMCHIP_PATH, channel);
    if( (fd=open(path, O_WRONLY)) < 0 )
    {
        log_error("open '%s' failed: %s\n", path, strerror(errno));
        return -3;
    }
    duty_cycle = (period*duty) / 100;
    snprintf(buf, sizeof(buf), "%d", duty_cycle);
    write(fd, buf, strlen(buf));
    return 0;
}
int init_pwm(int channel, int freq, int duty)
{
    int           rv;
    char          buf[32];
    char          path[256];
    if( (rv=export_pwm(channel, 1)) )
    {
        log_error("export PWM channel[%d] failed, rv=%d\n", channel, rv);
        return rv;
    }
    if( (rv=config_pwm(channel, freq, duty)) )
    {
        log_error("config PWM channel[%d] failed, rv=%d\n", channel, rv);
        return rv;
    }
    log_debug("config pwm%d with freq[%d] duty[%d] okay\n", channel, freq, duty);
    return 0;
}
int turn_pwm(int channel, int status)
{
    int           fd;
    char          buf[32];
    char          path[256];
    if( !check_pwm(channel) )
        return -1;
    /* open PWM enable file and enable(1)/disable(0) it */
    snprintf(path, sizeof(path), "%s/pwm%d/enable", PWMCHIP_PATH, channel);
    if( (fd=open(path, O_WRONLY)) < 0 )
    {
        log_error("open '%s' failed: %s\n", path, strerror(errno));
        return -3;
    }
    snprintf(buf, sizeof(buf), "%d", status?1:0);
    write(fd, buf, strlen(buf));
    log_debug("pwm[%d] %s\n", channel, status?"enable":"disable");
    return 0;
}
int term_pwm(int channel)
{
    if( !check_pwm(channel) )
        return 0;
    turn_pwm(channel, DISABLE);
    export_pwm(channel, 0);
    return 0;
}
int turn_beep(int times)
{
    int           rv;
    /* stop beeper beep */
    if(times == 0)
    {
        log_debug("Stop beeper\n");
        term_pwm(CHN_BEEPER);
        return 0;
    }
    rv = init_pwm(CHN_BEEPER, FRQ_BEEPER, 50);
    if(rv < 0)
    {
        log_error("Initial beeper pwm[%d] failed, rv=%d\n", CHN_BEEPER);
        return -2;
    }
    /* turn beeper beep ceaselessly */
    if( times < 0)
    {
        turn_pwm(CHN_BEEPER, ENABLE);
        return 0;
    }
    while( times-- )
    {
        turn_pwm(CHN_BEEPER, ENABLE);
        msleep(800);
        turn_pwm(CHN_BEEPER, DISABLE);
        msleep(800);
    }
    term_pwm(CHN_BEEPER);
    return 0;
}
project/4.mqttd/modules/pwm.h
New file
@@ -0,0 +1,44 @@
/*********************************************************************************
 *      Copyright:  (C) 2021 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  pwm.h
 *    Description:  This file is used to control PWM buzzer/Led
 *
 * Pin connection:
 *               PWM Module              Raspberry Pi Board
 *                  VCC       <----->      5V
 *                 buzzer     <----->      #Pin32(BCM GPIO12)
 *                  Led       <----->      #Pin33(BCM GPIO13)
 *                  GND       <----->      GND
 *
 * /boot/config.txt:
 *
 *          dtoverlay=pwm,pin=12,func=4 (Buzzer)
 *          dtoverlay=pwm,pin=13,func=4 (Led)
 *
 ********************************************************************************/
#ifndef  _PWM_H_
#define  _PWM_H_
#define PWMCHIP_PATH      "/sys/class/pwm/pwmchip0"
#define ENABLE            1
#define DISABLE           0
#define CHN_BEEPER        0
#define FRQ_BEEPER        2700
#define CHN_RGBLED        1
#define FRQ_RGBLED        100
extern int init_pwm(int channel, int freq, int duty);
extern int turn_pwm(int channel, int status);
extern int term_pwm(int channel);
extern int turn_beep(int times);
#endif   /* ----- #ifndef _PWM_H_  ----- */
project/4.mqttd/modules/relay.c
New file
@@ -0,0 +1,157 @@
/*********************************************************************************
 *      Copyright:  (C) 2021 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  relay.c
 *    Description:  This file is used to control Relay
 *
 *
 * Pin connection:
 *                 Relay Module           Raspberry Pi Board
 *                  VCC       <----->      5V
 *                   I        <----->      #Pin16(BCM GPIO23)
 *                  GND       <----->      GND
 *
 * System install:
 *                  sudo apt install -y libgpiod-dev gpiod
 *
 *
 ********************************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <errno.h>
#include <signal.h>
#include <getopt.h>
#include <libgen.h>
#include <gpiod.h>
#include "logger.h"
#include "relay.h"
#define DELAY     500
static relay_info_t    relay_info[RELAY_CNT] =
{
    {"relay1",  23, 1, NULL },
};
int init_relay(relay_ctx_t *ctx)
{
    int            i, rv;
    relay_info_t  *relay;
    if( !ctx )
    {
        log_error("Invalid input arguments\n");
        return -1;
    }
    ctx->relay = relay_info;
    ctx->count = RELAY_CNT;
    ctx->chip = gpiod_chip_open_by_name("gpiochip0");
    if( !ctx->chip )
    {
        log_error("open gpiochip failure, maybe you need running as root\n");
        return -2;
    }
    for(i=0; i<ctx->count; i++)
    {
        relay = &ctx->relay[i];
        relay->line = gpiod_chip_get_line(ctx->chip, relay->gpio);
        if( !relay->line )
        {
            log_error("open gpioline for %s[%d] failed\n", relay->name, relay->gpio);
            rv = -3;
            goto failed;
        }
        rv  = gpiod_line_request_output(relay->line, relay->name, !relay->active);
        if( rv )
        {
            log_error("request gpio output for %5s[%d] failed\n", relay->name, relay->gpio);
            rv = -4;
            goto failed;
        }
        log_debug("request %s[%d] for gpio output okay\n", relay->name, relay->gpio);
    }
    return 0;
failed:
    term_relay(ctx);
    return rv;
}
int term_relay(relay_ctx_t *ctx)
{
    int            i;
    relay_info_t  *relay;
    if( !ctx )
    {
        log_error("Invalid input arguments\n");
        return -1;
    }
    if( !ctx->chip )
        return 0;
    for(i=0; i<ctx->count; i++)
    {
        relay = &ctx->relay[i];
        if( relay->line )
            gpiod_line_release(relay->line);
    }
    gpiod_chip_close(ctx->chip);
    return 0;
}
int turn_relay(int which, int cmd)
{
    int             rv = 0;
    relay_info_t   *relay;
    relay_ctx_t     ctx;
    if( which<0 || which>=RELAY_CNT )
    {
        log_error("Invalid input arguments\n");
        return -1;
    }
    if( (rv=init_relay(&ctx)) < 0 )
    {
        log_error("Initial relay failure, rv=%d\n", rv);
        return -2;
    }
    relay = &ctx.relay[which];
    log_info("turn Led %s %s\n", relay->name, cmd?"on":"off");
    if( OFF == cmd )
    {
        gpiod_line_set_value(relay->line, !relay->active);
    }
    else
    {
        gpiod_line_set_value(relay->line, relay->active);
    }
    term_relay(&ctx);
    return 0;
}
project/4.mqttd/modules/relay.h
New file
@@ -0,0 +1,56 @@
/*********************************************************************************
 *      Copyright:  (C) 2021 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  relay.c
 *    Description:  This file is used to control Relay
 *
 *
 * Pin connection:
 *                 Relay Module           Raspberry Pi Board
 *                  VCC       <----->      5V
 *                   I        <----->      #Pin16(BCM GPIO23)
 *                  GND       <----->      GND
 *
 * System install:
 *                  sudo apt install -y libgpiod-dev gpiod
 *
 *
 ********************************************************************************/
#ifndef  _RELAY_H_
#define  _RELAY_H_
#define ON        1
#define OFF       0
/* relay code */
enum
{
    RELAY1 = 0,
    RELAY_CNT,
};
/* Relay hardware information */
typedef struct relay_info_s
{
    const char         *name;  /* Relay name  */
    int                 gpio;  /* Relay BCM pin number */
    int                 active;/* Relay active GPIO level: 0->low 1->high */
    struct gpiod_line  *line;  /* libgpiod line */
} relay_info_t;
/* Relay API context */
typedef struct relay_ctx_s
{
    struct gpiod_chip   *chip;
    relay_info_t        *relay;
    int                  count;
} relay_ctx_t;
extern int init_relay(relay_ctx_t *ctx);
extern int term_relay(relay_ctx_t *ctx);
extern int turn_relay(int which, int cmd);
#endif   /* ----- #ifndef _RELAY_H_  ----- */
project/4.mqttd/modules/sht20.c
File was renamed from project/4.mqttd/booster/sht20.c
@@ -40,9 +40,8 @@
#include <linux/i2c-dev.h>
#include "logger.h"
#include "utils.h"
#include "sht20.h"
#define msleep(x) usleep(1000*(x));
int i2c_write(int fd, uint8_t slave_addr, uint8_t *data, int len);
int i2c_read(int fd, uint8_t slave_addr, uint8_t *buf, int size);
@@ -91,7 +90,7 @@
    memset(buf, 0, sizeof(buf));
    i2c_read(fd, SHT20_I2CADDR, buf, 3);
    log_dump(LOG_LEVEL_DEBUG, "Temperature sample data: ", (char *)buf, 3);
    log_dump(LOG_LEVEL_TRACE, "Temperature sample data: ", buf, 3);
    if( !sht20_checksum(buf, 2, buf[2]) )
    {
@@ -114,7 +113,7 @@
    memset(buf, 0, sizeof(buf));
    i2c_read(fd, SHT20_I2CADDR, buf, 3);
    log_dump(LOG_LEVEL_DEBUG, "Relative humidity sample data: ", (char *)buf, 3);
    log_dump(LOG_LEVEL_TRACE, "Relative humidity sample data: ", buf, 3);
    if( !sht20_checksum(buf, 2, buf[2]) )
    {
project/4.mqttd/modules/sht20.h
project/4.mqttd/modules/tsl2561.c
New file
@@ -0,0 +1,212 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  tsl2561.c
 *    Description:  This file is the Lux sensor TSL2561 code
 *
 *        Version:  1.0.0(10/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
 *
 * Pin connection:
 *              TSL2561 Module           Raspberry Pi Board
 *                   VCC      <----->      #Pin1(3.3V)
 *                   SDA0     <----->      #Pin27(SDA, BCM GPIO0)
 *                   SCL0     <----->      #Pin28(SCL, BCM GPIO1)
 *                   GND      <----->      GND
 *
 * /boot/config.txt:
 *                  dtoverlay=i2c0,pins_0_1
 *
 ********************************************************************************/
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <math.h>
#include <time.h>
#include <errno.h>
#include <libgen.h>
#include <getopt.h>
#include <fcntl.h>
#include <sys/ioctl.h>
#include <linux/i2c.h>
#include <linux/i2c-dev.h>
#include <sys/types.h>
#include <sys/stat.h>
#include "utils.h"
#include "logger.h"
#include "tsl2561.h"
#define CONTROL_REG                     0x80
#define REG_COUNT                       4
#define POWER_UP                        0x03
#define POWER_DOWN                      0x00
#define OFF                             0
#define ON                              1
/* Register Address  */
enum
{
    /* Channel_0 = DATA0HIGH<<8 + DATA0LOW */
    DATA0LOW = 0x8C,
    DATA0HIGH,
    /* Channel_1 = DATA1HIGH<<8 + DATA1LOW */
    DATA1LOW,
    DATA1HIGH,
};
static const int  regs_addr[REG_COUNT]={DATA0LOW, DATA0HIGH, DATA1LOW, DATA1HIGH};
void tsl2561_power(int fd, int cmd)
{
    struct i2c_msg               msg;
    struct i2c_rdwr_ioctl_data   data;
    unsigned char                buf[2];
    msg.addr= TSL2561_I2CADDR;
    msg.flags=0;  /* write */
    msg.len= 1;
    msg.buf= buf;
    data.nmsgs= 1;
    data.msgs= &msg;
    msg.buf[0]=CONTROL_REG;
    if( ioctl(fd, I2C_RDWR, &data) < 0 )
    {
        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
        return ;
    }
    if( cmd )
        msg.buf[0]=POWER_UP;
    else
        msg.buf[0]=POWER_DOWN;
    if( ioctl(fd, I2C_RDWR, &data) < 0 )
    {
        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
        return ;
    }
    return ;
}
int tsl2561_readreg(int fd, unsigned char regaddr, unsigned char *regval)
{
    struct i2c_msg               msg;
    struct i2c_rdwr_ioctl_data   data;
    unsigned char                buf[2];
    msg.addr= TSL2561_I2CADDR;
    msg.flags=0;  /* write */
    msg.len= 1;
    msg.buf= buf;
    msg.buf[0] = regaddr;
    data.nmsgs= 1;
    data.msgs= &msg;
    if( ioctl(fd, I2C_RDWR, &data) < 0 )
    {
        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
        return -1;
    }
    memset(buf, 0, sizeof(buf));
    msg.addr= TSL2561_I2CADDR;
    msg.flags=I2C_M_RD;  /* read */
    msg.len= 1;
    msg.buf= buf;
    data.nmsgs= 1;
    data.msgs= &msg;
    if( ioctl(fd, I2C_RDWR, &data) < 0 )
    {
        log_error("%s() ioctl failure: %s\n", __func__, strerror(errno));
        return -1;
    }
    *regval = msg.buf[0];
    return 0;
}
int tsl2561_get_lux(float *lux)
{
    int                 i, fd;
    int                 rv = 0;
    char               *dev = TSL2561_I2CDEV;
    float               div = 0.0;
    unsigned char       reg_data[REG_COUNT];
    int                 chn0_data = 0;
    int                 chn1_data = 0;
    if( !lux )
    {
        log_error("Invalid input arguments\n");
        return -1;
    }
    if( (fd=open(dev, O_RDWR)) < 0)
    {
        log_error("i2c device '%s' open failed: %s\n", dev, strerror(errno));
        return -2;
    }
    tsl2561_power(fd, ON);
    msleep(410);  /* t(CONV) MAX 400ms */
    /* Read register Channel0 and channel1 data from register */
    for(i=0; i<REG_COUNT; i++)
    {
        rv = tsl2561_readreg(fd, regs_addr[i], &reg_data[i]);
        if( rv < 0)
            goto failed;
    }
    chn0_data = reg_data[1]*256 + reg_data[0]; /* Channel0 = DATA0HIGH<<8 + DATA0LOW  */
    chn1_data = reg_data[3]*256 + reg_data[2]; /* channel1 = DATA1HIGH<<8 +  DATA1LOW */
    if( chn0_data<=0 || chn1_data<0 )
    {
        rv = -2;
        goto cleanup;
    }
    div = (float)chn1_data / (float)chn0_data;
    if( div>0 && div<=0.5 )
        *lux = 0.304*chn0_data-0.062*chn0_data*pow(div,1.4);
    else if( div>0.5 && div<=0.61 )
        *lux = 0.0224*chn0_data-0.031*chn1_data;
    else if( div>0.61 && div<=0.8 )
        *lux = 0.0128*chn0_data-0.0153*chn1_data;
    else if( div>0.8 && div<=1.3 )
        *lux = 0.00146*chn0_data-0.00112*chn1_data;
    else if( div>1.3 )
        *lux = 0.0;
cleanup:
    tsl2561_power(fd, OFF);
failed:
    close(fd);
    return rv;
}
project/4.mqttd/modules/tsl2561.h
New file
@@ -0,0 +1,34 @@
/*********************************************************************************
 *      Copyright:  (C) 2023 LingYun IoT System Studio
 *                  All rights reserved.
 *
 *       Filename:  tsl2561.h
 *    Description:  This file is the Lux sensor TSL2561 code
 *
 *        Version:  1.0.0(10/08/23)
 *         Author:  Guo Wenxue <guowenxue@gmail.com>
 *      ChangeLog:  1, Release initial version on "10/08/23 17:52:00"
 *
 * Pin connection:
 *              TSL2561 Module           Raspberry Pi Board
 *                   VCC      <----->      #Pin1(3.3V)
 *                   SDA0     <----->      #Pin27(SDA, BCM GPIO0)
 *                   SCL0     <----->      #Pin28(SCL, BCM GPIO1)
 *                   GND      <----->      GND
 *
 * /boot/config.txt:
 *                  dtoverlay=i2c0,pins_0_1
 *
 ********************************************************************************/
#ifndef  _TSL2561_H_
#define  _TSL2561_H_
#define TSL2561_I2CDEV                 "/dev/i2c-0"
#define TSL2561_I2CADDR                 0x39
extern int tsl2561_get_lux(float *lux);
#endif   /* ----- #ifndef _TSL2561_H_  ----- */
project/4.mqttd/mqttd.c
File was deleted