Add config files and service file
This commit is contained in:
parent
338d9360a6
commit
7ad8ea25a6
2
.gitignore
vendored
2
.gitignore
vendored
@ -1,5 +1,5 @@
|
||||
bin/
|
||||
*.core
|
||||
rpi4b-temp-humidity
|
||||
compile_commands.json
|
||||
.cache/
|
||||
config.conf
|
||||
|
44
Makefile
44
Makefile
@ -11,10 +11,14 @@ SRCS=src/main.c src/util.c src/lcd.c src/ths.c src/button.c src/ui/screen.c\
|
||||
src/ui/exportscreen.c
|
||||
PROG=rpi4b-temp-humidity
|
||||
|
||||
all: config.conf bin/${PROG}
|
||||
|
||||
OBJS=${SRCS:C/^src/bin/:C/.c$/.o/}
|
||||
bin/${PROG}: ${OBJS}
|
||||
${LD} ${LDFLAGS} -o ${@} ${OBJS}
|
||||
|
||||
bin/main.o bin/config.o: config.mk
|
||||
|
||||
bin/main.o bin/util.o bin/lcd.o bin/ths.o bin/button.o: src/util.h
|
||||
bin/ui/screen.o bin/ui/statsby.o bin/ui/datesel.o: src/util.h
|
||||
bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/util.h
|
||||
@ -30,7 +34,6 @@ bin/ui/statrange.o: src/ui/screen.h
|
||||
bin/main.o bin/ui/datesel.o bin/ui/statsby.o: src/ui/datesel.h
|
||||
bin/ui/datapoints.o bin/ui/timesel.o bin/ui/statrange.o: src/ui/datesel.h
|
||||
|
||||
|
||||
bin/main.o bin/ui/timesel.o bin/ui/datapoints.o: src/ui/timesel.h
|
||||
bin/ui/statrange.o: src/ui/timesel.h
|
||||
|
||||
@ -49,23 +52,34 @@ bin/main.o bin/drive.o bin/ui/exportscreen.o: src/drive.h
|
||||
.error Invalid temperature unit "${DEFAULT_TEMP_UNIT}"
|
||||
.endif
|
||||
|
||||
DEFINES:=\
|
||||
-DDEFAULT_CONFIG_PATH="${DEFAULT_CONFIG_PATH}"\
|
||||
-DDEFAULT_GPIO_DEVICE="${DEFAULT_GPIO_DEVICE}"\
|
||||
-DDEFAULT_TEMP_KEY="${DEFAULT_TEMP_KEY}"\
|
||||
-DDEFAULT_HUMID_KEY="${DEFAULT_HUMID_KEY}"\
|
||||
-DDEFAULT_FAIL_KEY="${DEFAULT_FAIL_KEY}"\
|
||||
-DDEFAULT_FAIL_LIMIT="${DEFAULT_FAIL_LIMIT}"\
|
||||
-DDEFAULT_DATABASE_LOCATION="${DEFAULT_DATABASE_LOCATION}"\
|
||||
-DDEFAULT_REFRESH_TIME="${DEFAULT_REFRESH_TIME}"\
|
||||
-DDEFAULT_TEMP_UNIT="${DEFAULT_TEMP_UNIT}"\
|
||||
-DDEFAULT_EXPORT_FILE_NAME="${DEFAULT_EXPORT_FILE_NAME}"\
|
||||
|
||||
${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
||||
@mkdir -p ${.TARGET:H}
|
||||
${CC} ${CFLAGS} -c\
|
||||
-DDEFAULT_CONFIG_PATH="\"${DEFAULT_CONFIG_PATH}\""\
|
||||
-DDEFAULT_GPIO_DEVICE="\"${DEFAULT_GPIO_DEVICE}\""\
|
||||
-DDEFAULT_TEMP_KEY="\"${DEFAULT_TEMP_KEY}\""\
|
||||
-DDEFAULT_HUMID_KEY="\"${DEFAULT_HUMID_KEY}\""\
|
||||
-DDEFAULT_FAIL_KEY="\"${DEFAULT_FAIL_KEY}\""\
|
||||
-DDEFAULT_FAIL_LIMIT="${DEFAULT_FAIL_LIMIT}"\
|
||||
-DDEFAULT_DATABASE_LOCATION="\"${DEFAULT_DATABASE_LOCATION}\""\
|
||||
-DDEFAULT_REFRESH_TIME="${DEFAULT_REFRESH_TIME}"\
|
||||
-DDEFAULT_TEMP_UNIT="'${DEFAULT_TEMP_UNIT}'"\
|
||||
-DDEFAULT_EXPORT_FILE_NAME="\"${DEFAULT_EXPORT_FILE_NAME}\""\
|
||||
-o ${@} ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
||||
${CC} ${CFLAGS} -c ${DEFINES} -o ${@} ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
||||
|
||||
config.conf: config.conf.m4
|
||||
m4 ${DEFINES} config.conf.m4 >config.conf
|
||||
|
||||
install: all
|
||||
mkdir -p "${PREFIX}/bin"
|
||||
install -o root -g wheel "bin/${PROG}" "${PREFIX}/bin"
|
||||
mkdir -p "${PREFIX}/etc/rpi4b-temp-humidity"
|
||||
install -b -o root -g wheel -m 0644 config.conf "${PREFIX}/etc/rpi4b-temp-humidity"
|
||||
install -o root -g wheel -m 0555 rpi4b-temp-humidity "${PREFIX}/etc/rc.d"
|
||||
|
||||
clean:
|
||||
rm -rf bin/
|
||||
rm -rf config.conf bin/
|
||||
|
||||
.SUFFIXES: .c .o
|
||||
.PHONY: clean
|
||||
.PHONY: all install clean
|
||||
|
49
config.conf.m4
Normal file
49
config.conf.m4
Normal file
@ -0,0 +1,49 @@
|
||||
dnl The following line enables macro expansion in # comments
|
||||
changecom(`//')
|
||||
# These options do not have default value and MUST be set before running
|
||||
|
||||
# LCD pins (the ones below match up with the diagram in README.md)
|
||||
# Register select
|
||||
rs_pin = 4
|
||||
# Read-write
|
||||
rw_pin = 17
|
||||
# Enable
|
||||
en_pin = 27
|
||||
# Optional backlight pin, set to -1 to disable
|
||||
bl_pin = 26
|
||||
# d0-d7
|
||||
data_pins = 22 10 9 11 5 6 13 19
|
||||
|
||||
# Button pins
|
||||
sel_pin = 14
|
||||
back_pin = 15
|
||||
up_pin = 18
|
||||
down_pin = 23
|
||||
|
||||
# Options below this comment have compile-time defaults (which are show below)
|
||||
|
||||
# GPIO device file
|
||||
#gpio_file = DEFAULT_GPIO_DEVICE
|
||||
|
||||
# sysctl(8) keys for the temperature sensor
|
||||
#temp_key = DEFAULT_TEMP_KEY
|
||||
#humid_key = DEFAULT_HUMID_KEY
|
||||
# Set to empty to disable
|
||||
#fail_key = DEFAULT_FAIL_KEY
|
||||
|
||||
# Number of temperature sensor fails before exit
|
||||
#fail_limit = DEFAULT_FAIL_LIMIT
|
||||
|
||||
# Database location
|
||||
#database_location = DEFAULT_DATABASE_LOCATION
|
||||
|
||||
# Time between data points, note that this is probably limited by the kernel's
|
||||
# gpioths driver (5 seconds)
|
||||
#refresh_time = DEFAULT_REFRESH_TIME
|
||||
|
||||
# Temperature unit F for Fahrenheit, C for Celsius
|
||||
#temp_unit = DEFAULT_TEMP_UNIT
|
||||
|
||||
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
||||
# .csv will be appended for CSV exports
|
||||
#export_file_name = DEFAULT_EXPORT_FILE_NAME
|
25
config.mk
25
config.mk
@ -2,14 +2,35 @@
|
||||
SQLITE3_LDFLAGS=-L/usr/local/lib -lsqlite3
|
||||
SQLITE3_CFLAGS=-I/usr/local/include
|
||||
|
||||
# Install prefix
|
||||
PREFIX=/usr/local
|
||||
|
||||
# Default option values, empty means NULL (for strings)
|
||||
DEFAULT_CONFIG_PATH=config.conf
|
||||
# Config file path
|
||||
DEFAULT_CONFIG_PATH=/usr/local/etc/rpi4b-temp-humidity/config.conf
|
||||
|
||||
# GPIO device
|
||||
DEFAULT_GPIO_DEVICE=/dev/gpioc0
|
||||
|
||||
# sysctl(8) keys for the temperature sensor
|
||||
DEFAULT_TEMP_KEY=dev.gpioths.0.temperature
|
||||
DEFAULT_HUMID_KEY=dev.gpioths.0.humidity
|
||||
# Set this to empty to disable checking failures
|
||||
DEFAULT_FAIL_KEY=dev.gpioths.0.fails
|
||||
|
||||
# Max failures of the temperature sensor before exit
|
||||
DEFAULT_FAIL_LIMIT=5
|
||||
DEFAULT_DATABASE_LOCATION=/var/db/rpi4-temp-humidity/db.sqlite
|
||||
|
||||
# Database location
|
||||
DEFAULT_DATABASE_LOCATION=/var/db/rpi4b-temp-humidity/db.sqlite
|
||||
|
||||
# Time between data points, note that this is probably limited by the kernel's
|
||||
# gpioths driver (5 seconds)
|
||||
DEFAULT_REFRESH_TIME=5000
|
||||
|
||||
# F for Fahrenheit, C for Celsius
|
||||
DEFAULT_TEMP_UNIT=F
|
||||
|
||||
# The base name for export, .sqlite3 will be appended for SQLite exports and
|
||||
# .csv will be appended for CSV exports
|
||||
DEFAULT_EXPORT_FILE_NAME=env_data
|
||||
|
44
rpi4b-temp-humidity
Normal file
44
rpi4b-temp-humidity
Normal file
@ -0,0 +1,44 @@
|
||||
#!/bin/sh
|
||||
|
||||
. /etc/rc.subr
|
||||
|
||||
name=rpi4b_temp_humidity
|
||||
rcvar=rpi4b_temp_humidity_enable
|
||||
|
||||
command="/usr/sbin/daemon"
|
||||
procname="/usr/local/bin/rpi4b-temp-humidity"
|
||||
|
||||
start_precmd="${name}_prestart"
|
||||
|
||||
load_rc_config ${name}
|
||||
: ${rpi4b_temp_humidity_enable:=NO}
|
||||
: ${rpi4b_temp_humidity_config_file:=""}
|
||||
: ${rpi4b_temp_humidity_strict_config:=NO}
|
||||
: ${rpi4b_temp_humidity_verbose:=YES}
|
||||
: ${rpi4b_temp_humidity_log_file:="/var/log/rpi4b-temp-humidity.log"}
|
||||
|
||||
rpi4b_temp_humidity_prestart()
|
||||
{
|
||||
if checkyesno rpi4b_temp_humidity_strict_config; then
|
||||
rc_flags="-s ${rc_flags}"
|
||||
fi
|
||||
if ! [ -z "${rpi4b_temp_humidity_config_file}" ]; then
|
||||
rc_flags="-f \"${rpi4b_temp_humidity_config_file}\" ${rc_flags}"
|
||||
fi
|
||||
if checkyesno rpi4b_temp_humidity_verbose; then
|
||||
rc_flags="-v ${rc_flags}"
|
||||
fi
|
||||
if ! [ -z "${rpi4b_temp_humidity_log_file}" ]; then
|
||||
# simple log rotation
|
||||
mv "${rpi4b_temp_humidity_log_file}" "${rpi4b_temp_humidity_log_file}.old"
|
||||
rpi4b_temp_humidity_daemon_flags="-o \"${rpi4b_temp_humidity_log_file}\""
|
||||
if ! mkdir -p "$(dirname "${rpi4b_temp_humidity_log_file}")"; then
|
||||
return 1
|
||||
fi
|
||||
else
|
||||
rpi4b_temp_humidity_daemon_flags="-f"
|
||||
fi
|
||||
rc_flags="${rpi4b_temp_humidity_daemon_flags} ${procname} ${rc_flags}"
|
||||
}
|
||||
|
||||
run_rc_command "$1"
|
14
src/config.c
14
src/config.c
@ -221,7 +221,7 @@ void parse_config_file(const char *path) {
|
||||
.directive = "gpio_file",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_GPIO_DEVICE)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_GPIO_DEVICE))},
|
||||
},
|
||||
{
|
||||
.directive = "rs_pin",
|
||||
@ -247,19 +247,19 @@ void parse_config_file(const char *path) {
|
||||
.directive = "temp_key",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_TEMP_KEY)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_TEMP_KEY))},
|
||||
},
|
||||
{
|
||||
.directive = "humid_key",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_HUMID_KEY)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_HUMID_KEY))},
|
||||
},
|
||||
{
|
||||
.directive = "fail_key",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_FAIL_KEY)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_FAIL_KEY))},
|
||||
},
|
||||
{
|
||||
.directive = "fail_limit",
|
||||
@ -271,7 +271,7 @@ void parse_config_file(const char *path) {
|
||||
.directive = "database_location",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_DATABASE_LOCATION)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_DATABASE_LOCATION))},
|
||||
},
|
||||
{
|
||||
.directive = "refresh_time",
|
||||
@ -303,7 +303,7 @@ void parse_config_file(const char *path) {
|
||||
.directive = "temp_unit",
|
||||
.type = FIGPAR_TYPE_INT,
|
||||
.action = parse_temp_unit_callback,
|
||||
.value = {.num = DEFAULT_TEMP_UNIT},
|
||||
.value = {.num = STRINGIFY(DEFAULT_TEMP_UNIT)[0]},
|
||||
},
|
||||
{
|
||||
.directive = "bl_pin",
|
||||
@ -315,7 +315,7 @@ void parse_config_file(const char *path) {
|
||||
.directive = "export_file_name",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_default_opt(DEFAULT_EXPORT_FILE_NAME)},
|
||||
.value = {.str = strdup_default_opt(STRINGIFY(DEFAULT_EXPORT_FILE_NAME))},
|
||||
},
|
||||
{ .directive = NULL },
|
||||
};
|
||||
|
53
src/main.c
53
src/main.c
@ -35,6 +35,7 @@
|
||||
#include <stdatomic.h>
|
||||
#include <signal.h>
|
||||
#include <sqlite3.h>
|
||||
#include <libgen.h>
|
||||
|
||||
/*
|
||||
* Print help message to standard output.
|
||||
@ -49,9 +50,13 @@ void parse_arguments(int argc, char *const *argv);
|
||||
*/
|
||||
void setup_signals(void);
|
||||
/*
|
||||
* Open an sqlite3 database connection.
|
||||
* Open the sqlite3 database connection.
|
||||
*/
|
||||
void open_database(void);
|
||||
/*
|
||||
* Create the env_data table in DATABASE
|
||||
*/
|
||||
void create_db_table(void);
|
||||
/*
|
||||
* Read the temp. and humid., store it in the configured database, and output it
|
||||
* to the given uint32_t pointers.
|
||||
@ -93,6 +98,7 @@ int main(int argc, char *const *argv) {
|
||||
GLOBAL_OPTS.data_pins[7], GLOBAL_OPTS.bl_pin);
|
||||
setup_signals();
|
||||
open_database();
|
||||
create_db_table();
|
||||
initialize_util_queries(DATABASE);
|
||||
pthread_t bg_update;
|
||||
start_update_thread(&bg_update);
|
||||
@ -167,7 +173,7 @@ void parse_arguments(int argc, char *const *argv) {
|
||||
}
|
||||
}
|
||||
if (!GLOBAL_OPTS.config_path) {
|
||||
GLOBAL_OPTS.config_path = strdup_checked(DEFAULT_CONFIG_PATH);
|
||||
GLOBAL_OPTS.config_path = strdup_checked(STRINGIFY(DEFAULT_CONFIG_PATH));
|
||||
LOG_VERBOSE("Config file path set: \"%s\"\n", GLOBAL_OPTS.config_path);
|
||||
}
|
||||
}
|
||||
@ -190,18 +196,43 @@ void setup_signals() {
|
||||
SIGNAL_SETUP_CHECKED(SIGUSR1, SIG_IGN); // used to kill export operations
|
||||
}
|
||||
|
||||
static const char *CREATE_DB_TABLE_QUERY =
|
||||
"CREATE TABLE IF NOT EXISTS env_data("
|
||||
"time INTEGER PRIMARY KEY,"
|
||||
"temp INTEGER,"
|
||||
"humid INTEGER"
|
||||
");";
|
||||
void create_db_table() {
|
||||
char *errmsg;
|
||||
int status = sqlite3_exec(DATABASE, CREATE_DB_TABLE_QUERY, NULL, NULL,
|
||||
&errmsg);
|
||||
if (status != SQLITE_OK) {
|
||||
errx(1, "could not create table. sqlite3 error follows:\n%s",
|
||||
errmsg ? errmsg : "No message generated");
|
||||
}
|
||||
LOG_VERBOSE("Ensured env_data table existance\n");
|
||||
}
|
||||
|
||||
void open_database() {
|
||||
int status = sqlite3_config(SQLITE_CONFIG_SERIALIZED);
|
||||
if (status != SQLITE_OK) {
|
||||
errx(1, "failed to enable multi-thread mode for sqlite: %s",
|
||||
sqlite3_errstr(status));
|
||||
}
|
||||
char *to_free = strdup_checked(GLOBAL_OPTS.database_location);
|
||||
char *db_dir = dirname(to_free);
|
||||
if (!mkdirs(db_dir, 0755)) {
|
||||
errx(1, "failed to create database directory: %s", db_dir);
|
||||
}
|
||||
free(to_free);
|
||||
status = sqlite3_open(GLOBAL_OPTS.database_location, &DATABASE);
|
||||
if (status != SQLITE_OK) {
|
||||
sqlite3_close(DATABASE);
|
||||
errx(1, "failed to open database: %s",
|
||||
sqlite3_errstr(status));
|
||||
}
|
||||
LOG_VERBOSE("Successfully opened database at \"%s\"\n",
|
||||
GLOBAL_OPTS.database_location);
|
||||
}
|
||||
|
||||
void update_stats(THS *ths, sqlite3_stmt *insert_statement) {
|
||||
@ -229,28 +260,10 @@ void update_stats(THS *ths, sqlite3_stmt *insert_statement) {
|
||||
}
|
||||
}
|
||||
|
||||
static const char *CREATE_DB_TABLE_QUERY =
|
||||
"CREATE TABLE IF NOT EXISTS env_data("
|
||||
"time INTEGER PRIMARY KEY,"
|
||||
"temp INTEGER,"
|
||||
"humid INTEGER"
|
||||
");";
|
||||
static void create_db_table() {
|
||||
char *errmsg;
|
||||
int status = sqlite3_exec(DATABASE, CREATE_DB_TABLE_QUERY, NULL, NULL,
|
||||
&errmsg);
|
||||
if (status != SQLITE_OK) {
|
||||
errx(1, "could not create table. sqlite3 error follows:\n%s",
|
||||
errmsg ? errmsg : "No message generated");
|
||||
}
|
||||
LOG_VERBOSE("Ensured env_data table existance\n");
|
||||
}
|
||||
|
||||
static const char *UPDATE_STATS_QUERY =
|
||||
"INSERT INTO env_data (time, temp, humid) "
|
||||
"VALUES(?, ?, ?);";
|
||||
static void *update_thread_action(void *_ignored) {
|
||||
create_db_table();
|
||||
sqlite3_stmt *insert_statement = NULL;
|
||||
int status = sqlite3_prepare_v2(DATABASE, UPDATE_STATS_QUERY, -1,
|
||||
&insert_statement, NULL);
|
||||
|
39
src/util.c
39
src/util.c
@ -18,6 +18,7 @@
|
||||
#include <unistd.h>
|
||||
#include <fcntl.h>
|
||||
#include <pthread.h>
|
||||
#include <errno.h>
|
||||
|
||||
const char *PERIOD_LABELS[] = {
|
||||
"HOUR",
|
||||
@ -164,7 +165,7 @@ static const char *AVG_FOR_PERIOD_QUERY_STR =
|
||||
"min(humid) AS minhumid\n"
|
||||
"FROM env_data\n"
|
||||
"WHERE time >= stime\n"
|
||||
"AND time <= etime);\n";
|
||||
"AND time <= etime);";
|
||||
static sqlite3_stmt *AVG_FOR_PERIOD_QUERY;
|
||||
static const char *DATA_POINT_QUERY_STR =
|
||||
"SELECT max(time) IS NULL, max(time), temp, humid FROM env_data WHERE time < ?1\n"
|
||||
@ -528,3 +529,39 @@ bool export_database_as_csv(sqlite3 *db, const char *dest) {
|
||||
pthread_cleanup_pop(true); // close file
|
||||
return status;
|
||||
}
|
||||
|
||||
static bool ensure_dir_exists(const char *path, mode_t mode) {
|
||||
struct stat statbuf;
|
||||
errno = 0;
|
||||
if (mkdir(path, mode) < 0) {
|
||||
if (errno != EEXIST) {
|
||||
warn("mkdir failed: %s", path);
|
||||
return false;
|
||||
}
|
||||
if (stat(path, &statbuf) < 0) {
|
||||
warn("mkdir and stat failed: %s", path);
|
||||
return false;
|
||||
} else if (!S_ISDIR(statbuf.st_mode)) {
|
||||
warn("not a directory: %s", path);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool mkdirs(const char *path, mode_t mode) {
|
||||
LOG_VERBOSE("Creating directory: \"%s\"\n", path)
|
||||
char *copy = strdup_checked(path);
|
||||
char *work_str = copy, *token;
|
||||
while ((token = strsep(&work_str, "/"))) {
|
||||
if (token != copy) {
|
||||
token[-1] = '/';
|
||||
}
|
||||
if (*copy && *token && !ensure_dir_exists(copy, mode)) {
|
||||
free(copy);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
free(copy);
|
||||
return true;
|
||||
}
|
||||
|
@ -19,6 +19,9 @@
|
||||
#include <libgpio.h>
|
||||
#include <sqlite3.h>
|
||||
|
||||
#define _STRINGIFY_LIT(s) (#s)
|
||||
#define STRINGIFY(v) _STRINGIFY_LIT(v)
|
||||
|
||||
typedef enum {
|
||||
TEMP_UNIT_F = 'F',
|
||||
TEMP_UNIT_C = 'C'
|
||||
@ -239,4 +242,10 @@ bool export_database(sqlite3 *db, const char *dest);
|
||||
*/
|
||||
bool export_database_as_csv(sqlite3 *db, const char *dest);
|
||||
|
||||
/*
|
||||
* Create all the missing directories along path.
|
||||
* Return: true on success, false otherwise
|
||||
*/
|
||||
bool mkdirs(const char *path, mode_t mode);
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user