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