| project/lightd/config.c | ●●●●● patch | view | raw | blame | history | |
| project/lightd/config.h | ●●●●● patch | view | raw | blame | history | |
| project/lightd/etc/lightd.conf | ●●●●● patch | view | raw | blame | history | |
| project/lightd/etc/lightd.service | ●●●●● patch | view | raw | blame | history | |
| project/lightd/hal/ds18b20.c | ●●●●● patch | view | raw | blame | history | |
| project/lightd/hal/ds18b20.h | ●●●●● patch | view | raw | blame | history | |
| project/lightd/hal/gpio.c | ●●●●● patch | view | raw | blame | history | |
| project/lightd/lightd.c | ●●●●● patch | view | raw | blame | history |
project/lightd/config.c
@@ -41,6 +41,7 @@ logger = &ctx->logger; hwinfo = &ctx->hwinfo; mqtt = &ctx->mqtt; mqtt->userdata = (void *)hwinfo; ini = iniparser_load(conf_file); if( !ini ) @@ -94,7 +95,25 @@ *| parser hardware module configuration | *+------------------------------------------------------+*/ /* parser GPIO output pins */ /* DS18B20 temperature sensor status */ hwinfo->ds18b20=iniparser_getint(ini, "hardware:ds18b20", 0); if( !hwinfo->ds18b20 ) log_warn("Parser DS18B20 temperature module disabled\n"); else log_info("Parser DS18B20 temperature module enabled\n"); /* TSL2561 lux sensor status */ hwinfo->tsl2561=iniparser_getint(ini, "hardware:tsl2561", 0); if( !hwinfo->tsl2561) log_warn("Parser TSL2561 lux sensor module disabled\n"); else log_info("Parser TSL2561 lux sensor module enabled\n"); /* TSL2561 lux sensor threshold value */ hwinfo->lux_threshold = iniparser_getdouble(ini, "hardware:lux_threshold", 0.1); log_info("parser LUX enable and threshold value set be [%.03f]\n", hwinfo->lux_threshold); /* parser GPIO output pins for relay */ if( !(str=iniparser_getstring(ini, "hardware:gpio_outpin", NULL)) ) { log_warn("parser no GPIO output pins\n"); @@ -108,7 +127,7 @@ hwinfo->light_intval = iniparser_getint(ini, "hardware:light_intval", 20); log_info("parser relay controled light interval time [%d]\n", hwinfo->light_intval); /* parser GPIO input pins */ /* parser GPIO input pins for infrared */ if( !(str=iniparser_getstring(ini, "hardware:gpio_inpin", NULL)) ) { log_warn("parser no GPIO input pins\n"); @@ -118,10 +137,6 @@ parser_gpio_info(_TYPE_INPUT, &hwinfo->gpio, (char *)str); log_info("parser [%d] GPIO input pins\n", hwinfo->gpio.incnt); } hwinfo->lux_threshold = iniparser_getdouble(ini, "hardware:lux_threshold", 0.1); log_info("parser LUX enable and threshold value set be [%.03f]\n", hwinfo->lux_threshold); /*+------------------------------------------------------+ *| parser broker settings | project/lightd/config.h
@@ -29,6 +29,7 @@ typedef struct mqtt_ctx_s { char devid[32]; /* device ID */ void *userdata; /* user data pointer */ /* Broker settings */ char host[128]; /* MQTT broker server name */ @@ -61,6 +62,8 @@ gpio_t gpio; /* gpio information */ int light_intval; /* light on interval */ float lux_threshold;/* lux threshold value */ int ds18b20; /* ds18b20 enable or not */ int tsl2561; /* tsl2561 enable or not */ } hwinfo_t; typedef struct iotd_ctx_s project/lightd/etc/lightd.conf
@@ -4,16 +4,23 @@ # 树莓派连接的外设信息,使能或相关硬件连接的Pin管脚 [hardware] # 红外感应灯GPIO输入控制, 格式: {名称:BCM编码:控制电平} # Indoor(#16)<->(black/gray) Hallway(#18)<->(yellow/red) gpio_inpin={infrared_indoor:23:1},{infrared_hallway:24:1} # 是否使能 DS18B20 温湿度传感器模块,0:禁用 1:使能 ds18b20=1 # 是否使能 TSL2561 光强传感器模块,0:禁用 1:使能 tsl2561=1 # TSL2561 光强传感器光强阈值 lux_threshold=0.10 # 红外传感器(有人时输出高电平), 格式: {名称:BCM编码:控制电平} # Indoor(#16)<->(black/gray) Hallway(#12)<->(yellow/red) # 测试延时: while true; do gpioget gpiochip0 18; sleep 1; done gpio_inpin={infrared_indoor:23:1},{infrared_hallway:18:1} # 继电器GPIO输出控制, 格式: {名称:BCM编码:控制电平} # Relay(1) <-> #35 Relay(2) <-> #37 gpio_outpin={light_indoor:19:1},{light_hallway:26:1} # TSL2561 光强传感器光强阈值 lux_threshold=0.10 # 红外探测到人后,继电器控制灯亮的时长 light_intval=15 project/lightd/etc/lightd.service
New file @@ -0,0 +1,15 @@ [Unit] Description=RaspberryPi Auto light program Service After=network.target [Service] Type=simple ExecStartPre=/bin/rm -f /tmp/.lightd.pid /var/log/lightd.log ExecStart=/usr/bin/lightd -c /etc/lightd.conf Restart=always RestartSec=2 [Install] WantedBy=multi-user.target project/lightd/hal/ds18b20.c
New file @@ -0,0 +1,118 @@ /********************************************************************************* * Copyright: (C) 2023 LingYun IoT System Studio * All rights reserved. * * Filename: ds18b20.c * Description: This file is temperature sensor DS18B20 code * * Version: 1.0.0(2023/8/10) * Author: Guo Wenxue <guowenxue@gmail.com> * ChangeLog: 1, Release initial version on "2023/8/10 12:13:26" * * Pin connection: * * DS18B20 Module Raspberry Pi Board * VCC <-----> #Pin1(3.3V) * DQ <-----> #Pin7(BCM GPIO4) * GND <-----> GND * * /boot/config.txt: * * dtoverlay=w1-gpio-pullup,gpiopin=4 * ********************************************************************************/ #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 error: %s\n", strerror(errno)); return -2; } while((direntp = readdir(dirp)) != NULL) { if(strstr(direntp->d_name,"28-")) { /* find and get the chipset SN filename */ strcpy(chip,direntp->d_name); found = 1; break; } } closedir(dirp); if( !found ) { log_error("Can not find ds18b20 in %s\n", w1_path); return -3; } /* get DS18B20 sample file full path: /sys/bus/w1/devices/28-xxxx/w1_slave */ strncat(w1_path, chip, sizeof(w1_path)-strlen(w1_path)); strncat(w1_path, "/w1_slave", sizeof(w1_path)-strlen(w1_path)); /* open file /sys/bus/w1/devices/28-xxxx/w1_slave to get temperature */ if( (fd=open(w1_path, O_RDONLY)) < 0 ) { log_error("open %s error: %s\n", w1_path, strerror(errno)); return -4; } if(read(fd, buf, sizeof(buf)) < 0) { log_error("read %s error: %s\n", w1_path, strerror(errno)); return -5; } ptr = strstr(buf, "t="); if( !ptr ) { log_error("ERROR: Can not get temperature\n"); return -6; } ptr+=2; /* convert string value to float value */ *temp = atof(ptr)/1000; close(fd); return 0; } project/lightd/hal/ds18b20.h
New file @@ -0,0 +1,31 @@ /********************************************************************************* * Copyright: (C) 2023 LingYun IoT System Studio * All rights reserved. * * Filename: ds18b20.h * Description: This file is temperature sensor DS18B20 code * * Version: 1.0.0(2023/8/10) * Author: Guo Wenxue <guowenxue@gmail.com> * ChangeLog: 1, Release initial version on "2023/8/10 12:13:26" * * Pin connection: * * DS18B20 Module Raspberry Pi Board * VCC <-----> #Pin1(3.3V) * DQ <-----> #Pin7(BCM GPIO4) * GND <-----> GND * * /boot/config.txt: * * dtoverlay=w1-gpio-pullup,gpiopin=4 * ********************************************************************************/ #ifndef _DS18B20_H_ #define _DS18B20_H_ extern int ds18b20_get_temperature(float *temp); #endif /* ----- #ifndef _DS18B20_H_ ----- */ project/lightd/hal/gpio.c
@@ -21,13 +21,6 @@ #include "gpio.h" #define RPI_GPIONAME "gpiochip0" #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; project/lightd/lightd.c
@@ -26,13 +26,13 @@ #include "util_proc.h" #include "config.h" #include "tsl2561.h" #include "ds18b20.h" #define PROG_VERSION "v1.0.0" #define DAEMON_PIDFILE "/tmp/.lightd.pid" int check_sample_time(time_t *last_time, int interval); void *thingsboard_subsciber(void *args); void *thingsboard_publisher(void *args); int check_timeout(time_t *last_time, int interval); void *thingsboard_worker(void *args); static void program_usage(char *progname) { @@ -129,10 +129,10 @@ /* * +--------------------------------+ * | MQTT Subscriber Thread | * | ThingsBoard MQTT Thread | * +--------------------------------+ */ if( thread_start(&tid, thingsboard_subsciber, &ctx.mqtt ) < 0 ) if( thread_start(&tid, thingsboard_worker, &ctx.mqtt ) < 0 ) { log_error("Start MQTT subsciber worker thread failure\n"); goto cleanup; @@ -148,12 +148,22 @@ while( ! g_signal.stop ) { /* TSL2561 update lux in every 5 minutes */ if(check_sample_time(&last_time, 300)) if(check_timeout(&last_time, 300)) { /* The TSL2561 sensor is not present, do not turn on the light */ if( !hwinfo->tsl2561 ) { log_error("TSL2561 is not present, do not turn on the light\n"); lux = hwinfo->lux_threshold + 100.0; goto nextloop; } /* The TSL2561 sensor failed, do not turn on the light */ if( tsl2561_get_lux(&lux) < 0 ) { log_error("TSL2561 sample Lux failed\n"); log_error("TSL2561 sample failed, do not turn on the light\n"); lux = hwinfo->lux_threshold + 100.0; goto nextloop; } if( lux > hwinfo->lux_threshold ) @@ -162,12 +172,13 @@ log_info("Lux[%.3f] <= Treshold[%.3f], need auto light\n", lux, hwinfo->lux_threshold); } /* It's bright enough, no lighting is needed */ if( lux > hwinfo->lux_threshold ) { sleep(1); continue; goto nextloop; } /* It's not bright enough, lighting is necessary */ rv = infrared_detect(); if( rv != 0 ) { @@ -182,7 +193,8 @@ turn_light("hallway", OFF); } msleep(100); nextloop: sleep(1); } cleanup: @@ -192,20 +204,69 @@ return 0; } int check_sample_time(time_t *last_time, int interval) /* * +--------------------------------+ * | ThingsBoard MQTT Thread | * +--------------------------------+ */ int check_timeout(time_t *last_time, int interval) { int need = 0; /* no need sample now */ int timeout = 0; time_t now; time(&now); if( difftime(now, *last_time)>interval ) { need = 1; /* need sample now */ timeout = 1; *last_time = now; } return need; return timeout; } /* compatible with MQTT publish callback function */ void pub_connect_callback(struct mosquitto *mosq, void *userdata, int result) { mqtt_ctx_t *ctx = (mqtt_ctx_t *)userdata; hwinfo_t *hwinfo = (hwinfo_t *)ctx->userdata; int rv = 0; char msg[128]; float temp = 0.0; /* temperature */ int retain = 0; static time_t last_time = 0; /* publish time is not arrive */ if( !check_timeout(&last_time, ctx->interval) ) { return ; } log_debug("Publish topic '%s'\n", ctx->pubTopic); if( hwinfo->ds18b20 ) { memset(msg, 0, sizeof(msg)); log_debug("DS18B20 temperature sensor enabled, start broadcast it\n"); if( ds18b20_get_temperature(&temp)<0 ) return ; snprintf(msg, sizeof(msg), "{\"temperature\":%.2f}", temp); rv = mosquitto_publish(mosq, NULL, ctx->pubTopic, strlen(msg), msg, 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); } } return ; } void sub_connect_callback(struct mosquitto *mosq, void *userdata, int result) @@ -214,11 +275,11 @@ if( result ) { log_error("Subscriber connect to broker server failed, rv=%d\n", result); log_error("mosquitto connect to broker server failed, rv=%d\n", result); return ; } log_info("Subscriber connect to broker server[%s:%d] successfully\n", ctx->host, ctx->port); log_info("mosquitto connect to broker server[%s:%d] successfully\n", ctx->host, ctx->port); mosquitto_subscribe(mosq, NULL, ctx->subTopic, ctx->subQos); } @@ -226,7 +287,7 @@ { mqtt_ctx_t *ctx = (mqtt_ctx_t *)userdata; log_warn("Subscriber disconnect to broker server[%s:%d], reason=%d\n", ctx->host, ctx->port, result); log_warn("mosquitto disconnect to broker server[%s:%d], reason=%d\n", ctx->host, ctx->port, result); } void proc_json_items(cJSON *root, mqtt_ctx_t *ctx) @@ -282,8 +343,6 @@ } status = cJSON_IsTrue(item_status) ? ON : OFF; log_info("parser JSON message to control '%s' status '%d'\n", device, status); /* 映射 Light 名称并调用控制函数 */ if ( strstr(device, "Indoor") || strstr(device, "indoor")) { @@ -326,11 +385,12 @@ return ; } void *thingsboard_subsciber(void *args) void *thingsboard_worker(void *args) { mqtt_ctx_t *ctx = (mqtt_ctx_t *)args; struct mosquitto *mosq; bool session = true; int rv = 0; mosq = mosquitto_new(ctx->devid, session, ctx); if( !mosq ) @@ -342,7 +402,7 @@ /* connnect to broker by token or uid/pwd */ if( strlen(ctx->token ) > 0) { log_info("Using token authentication\n"); log_info("Using token authentication: '%s'\n", ctx->token); mosquitto_username_pw_set(mosq, ctx->token, NULL); } else if( strlen(ctx->uid)> 0 && strlen(ctx->pwd)> 0 ) @@ -361,16 +421,27 @@ /* connect to MQTT broker */ if( mosquitto_connect(mosq, ctx->host, ctx->port, ctx->keepalive) ) { log_error("Subscriber connect to broker[%s:%d] failure: %s\n", ctx->host, ctx->port, strerror(errno)); msleep(1000); log_error("mosquitto connect to broker[%s:%d] failure: %s\n", ctx->host, ctx->port, strerror(errno)); sleep(1); continue; } /* -1: use default timeout 1000ms 1: unused */ mosquitto_loop_forever(mosq, -1, 1); while(!g_signal.stop) { pub_connect_callback(mosq, ctx, MOSQ_ERR_SUCCESS); /* MQTT process in nonblock mode, timeout for 1s */ if( MOSQ_ERR_SUCCESS != (rv = mosquitto_loop(mosq, 1000, 1)) ) { log_warn("MQTT loop error: %s, reconnecting...\n", mosquitto_strerror(rv)); break; } } mosquitto_disconnect(mosq); sleep(1); } mosquitto_destroy(mosq); return NULL; }