Add iotd program
Signed-off-by: Guo Wenxue <guowenxue@gmail.com>
New file |
| | |
| | | |
| | | [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 |
| | | |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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; |
| | | } |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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 |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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; |
| | | } |
| | | |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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_ ----- */ |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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 ; |
| | | } |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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_ ----- */ |
| | | |
New file |
| | |
| | | |
| | | 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 |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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 |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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 |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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; |
| | | } |
| | | |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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_ ----- */ |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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); |
| | | } |
| | | } |
| | | |
| | | |
New file |
| | |
| | | #******************************************************************************** |
| | | # 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 |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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; |
| | | } |
| | | |
New file |
| | |
| | | /********************************************************************************* |
| | | * 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_ ----- */ |
| | | |
New file |
| | |
| | | |
| | | 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 |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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; |
| | | } |
| | | |
New file |
| | |
| | | /******************************************************************************** |
| | | * 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_ ----- */ |
| | | |