Arg parsing, ths sensor, and config file
This commit is contained in:
parent
cc7992bd93
commit
58e7bc053e
4
.gitignore
vendored
4
.gitignore
vendored
@ -1,2 +1,4 @@
|
||||
*.o
|
||||
rpi4b-temp-humidity
|
||||
rpi4b-temp-humidity
|
||||
compile_commands.json
|
||||
.cache/
|
21
Makefile
21
Makefile
@ -1,18 +1,29 @@
|
||||
CC=clang
|
||||
CFLAGS=-std=c99
|
||||
CFLAGS=-std=c99 -g
|
||||
LD=clang
|
||||
LDFLAGS=-lgpio
|
||||
OBJS=main.o util.o lcd.o
|
||||
LDFLAGS=-lgpio -lfigpar
|
||||
OBJS=main.o util.o lcd.o ths.o
|
||||
OUTFILE=rpi4b-temp-humidity
|
||||
|
||||
.include "config.mk"
|
||||
|
||||
${OUTFILE}: ${OBJS}
|
||||
${LD} ${LDFLAGS} -o $@ ${OBJS}
|
||||
|
||||
main.o util.o lcd.o ths.o: util.h
|
||||
main.o lcd.o: lcd.h
|
||||
main.o util.o lcd.o: util.h
|
||||
main.o ths.o: ths.h
|
||||
|
||||
.c.o:
|
||||
${CC} ${CFLAGS} -c $<
|
||||
${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_LCD_VERSION="\"${DEFAULT_LCD_VERSION}\""\
|
||||
$<
|
||||
|
||||
clean:
|
||||
rm -f ${OUTFILE} ${OBJS}
|
||||
|
7
config.mk
Normal file
7
config.mk
Normal file
@ -0,0 +1,7 @@
|
||||
DEFAULT_CONFIG_PATH=config.conf
|
||||
DEFAULT_GPIO_DEVICE=/dev/gpioc0
|
||||
DEFAULT_TEMP_KEY=dev.gpioths.0.temperature
|
||||
DEFAULT_HUMID_KEY=dev.gpioths.0.humidity
|
||||
DEFAULT_FAIL_KEY=dev.gpioths.0.fails
|
||||
DEFAULT_FAIL_LIMIT=5
|
||||
DEFAULT_LCD_VERSION=jp
|
23
lcd.c
23
lcd.c
@ -45,8 +45,9 @@ static void lcd_pulse_enable(LCD *lcd) {
|
||||
gpio_pin_low(lcd->handle, lcd->en);
|
||||
}
|
||||
|
||||
LCD *lcd_open(gpio_handle_t handle, int rs, int rw, int en, int d0, int d1,
|
||||
int d2, int d3, int d4, int d5, int d6, int d7) {
|
||||
LCD *lcd_open(gpio_handle_t handle, gpio_pin_t rs, gpio_pin_t rw, gpio_pin_t en,
|
||||
gpio_pin_t d0, gpio_pin_t d1, gpio_pin_t d2, gpio_pin_t d3,
|
||||
gpio_pin_t d4, gpio_pin_t d5, gpio_pin_t d6, gpio_pin_t d7) {
|
||||
LCD *lcd = malloc_checked(sizeof(LCD));
|
||||
lcd->handle = handle;
|
||||
lcd->rs = rs;
|
||||
@ -60,20 +61,24 @@ LCD *lcd_open(gpio_handle_t handle, int rs, int rw, int en, int d0, int d1,
|
||||
lcd->d5 = d5;
|
||||
lcd->d6 = d6;
|
||||
lcd->d7 = d7;
|
||||
lcd_set_read(lcd, false, true);
|
||||
lcd_set_read(lcd, true, true);
|
||||
lcd_clear(lcd);
|
||||
lcd_call(lcd, 0, 0, 0, 0, 1, 1, 1, 0, 0); // 5x8 font, 2 lines, 8bit
|
||||
lcd_entry_mode(lcd, LCD_INCREMENT, LCD_CURSOR_MOVE);
|
||||
lcd_display_control(lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, LCD_DISPLAY_ON);
|
||||
LOG_VERBOSE("Initializing LCD: rs=%d, rw=%d, en=%d, d={%d, %d, %d, %d, %d,"
|
||||
"%d, %d, %d}\n", rs, rw, en, d0, d1, d2, d3, d4, d5, d6, d7);
|
||||
return lcd;
|
||||
}
|
||||
|
||||
void lcd_close(LCD *lcd) {
|
||||
LOG_VERBOSE("Closing LCD\n");
|
||||
free(lcd);
|
||||
}
|
||||
|
||||
void lcd_call(LCD *lcd, int rs, int d0, int d1, int d2, int d3, int d4, int d5,
|
||||
int d6, int d7) {
|
||||
void lcd_call(LCD *lcd, gpio_value_t rs, gpio_value_t d0, gpio_value_t d1,
|
||||
gpio_value_t d2, gpio_value_t d3, gpio_value_t d4,
|
||||
gpio_value_t d5, gpio_value_t d6, gpio_value_t d7) {
|
||||
while(lcd_is_busy(lcd)) {
|
||||
usleep(5);
|
||||
}
|
||||
@ -105,7 +110,7 @@ size_t lcd_write_string(LCD *lcd, const char *str) {
|
||||
return count;
|
||||
}
|
||||
|
||||
void lcd_move_to(LCD *lcd, int line, int col) {
|
||||
void lcd_move_to(LCD *lcd, gpio_value_t line, gpio_value_t col) {
|
||||
lcd_call(lcd, 0, col & 1, (col >> 1) & 1, (col >> 2) & 1,
|
||||
(col >> 3) & 1, (col >> 4) & 1, (col >> 5) & 1, line, 1);
|
||||
}
|
||||
@ -114,15 +119,15 @@ void lcd_clear(LCD *lcd) {
|
||||
lcd_call(lcd, 0, 1, 0, 0, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void lcd_display_control(LCD *lcd, int b, int c, int d) {
|
||||
void lcd_display_control(LCD *lcd, gpio_value_t b, gpio_value_t c, gpio_value_t d) {
|
||||
lcd_call(lcd, 0, b, c, d, 1, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void lcd_entry_mode(LCD *lcd, int id, int s) {
|
||||
void lcd_entry_mode(LCD *lcd, gpio_value_t id, gpio_value_t s) {
|
||||
lcd_call(lcd, 0, s, id, 1, 0, 0, 0, 0, 0);
|
||||
}
|
||||
|
||||
void lcd_cursor_shift(LCD *lcd, int sc, int rl) {
|
||||
void lcd_cursor_shift(LCD *lcd, gpio_value_t sc, gpio_value_t rl) {
|
||||
lcd_call(lcd, 0, 0, 0, rl, sc, 1, 0, 0, 0);
|
||||
}
|
||||
|
||||
|
44
lcd.h
44
lcd.h
@ -17,31 +17,32 @@
|
||||
typedef struct {
|
||||
gpio_handle_t handle;
|
||||
// pin numbers
|
||||
int rs; // register select
|
||||
int rw; // read-write
|
||||
int en; // enable
|
||||
int d0;
|
||||
int d1;
|
||||
int d2;
|
||||
int d3;
|
||||
int d4;
|
||||
int d5;
|
||||
int d6;
|
||||
int d7;
|
||||
gpio_pin_t rs; // register select
|
||||
gpio_pin_t rw; // read-write
|
||||
gpio_pin_t en; // enable
|
||||
gpio_pin_t d0;
|
||||
gpio_pin_t d1;
|
||||
gpio_pin_t d2;
|
||||
gpio_pin_t d3;
|
||||
gpio_pin_t d4;
|
||||
gpio_pin_t d5;
|
||||
gpio_pin_t d6;
|
||||
gpio_pin_t d7;
|
||||
|
||||
// internal use
|
||||
// gpio_value_ternal use
|
||||
bool is_read;
|
||||
} LCD;
|
||||
|
||||
/*
|
||||
* Allocate and initialize a new LCD object and return a pointer to it. HANDLE
|
||||
* Allocate and initialize a new LCD object and return a pogpio_value_ter to it. HANDLE
|
||||
* is a gpio_handle_t, each of the other arguments corresponds to the GPIO pin
|
||||
* number that the given controller pin is connected to: "register select",
|
||||
* "read-write", "enable", and data pins 0-7.
|
||||
* Return: the newly created object.
|
||||
*/
|
||||
LCD *lcd_open(gpio_handle_t handle, int rs, int rw, int en, int d0, int d1,
|
||||
int d2, int d3, int d4, int d5, int d6, int d7);
|
||||
LCD *lcd_open(gpio_handle_t handle, gpio_pin_t rs, gpio_pin_t rw, gpio_pin_t en,
|
||||
gpio_pin_t d0, gpio_pin_t d1, gpio_pin_t d2, gpio_pin_t d3,
|
||||
gpio_pin_t d4, gpio_pin_t d5, gpio_pin_t d6, gpio_pin_t d7);
|
||||
/*
|
||||
* Close LCD by freeing any resources associated with it.
|
||||
*/
|
||||
@ -49,8 +50,9 @@ void lcd_close(LCD *lcd);
|
||||
/*
|
||||
* Call a single instruction on LCD. Each argument is the value of that pin.
|
||||
*/
|
||||
void lcd_call(LCD *lcd, int rs, int d0, int d1, int d2, int d3, int d4, int d5,
|
||||
int d6, int d7);
|
||||
void lcd_call(LCD *lcd, gpio_value_t rs, gpio_value_t d0, gpio_value_t d1,
|
||||
gpio_value_t d2, gpio_value_t d3, gpio_value_t d4,
|
||||
gpio_value_t d5, gpio_value_t d6, gpio_value_t d7);
|
||||
/*
|
||||
* Write character C to LCD.
|
||||
*/
|
||||
@ -65,7 +67,7 @@ size_t lcd_write_string(LCD *lcd, const char *str);
|
||||
/*
|
||||
* Move the cursor to COL on LINE, which both start from 0.
|
||||
*/
|
||||
void lcd_move_to(LCD *lcd, int line, int col);
|
||||
void lcd_move_to(LCD *lcd, gpio_value_t line, gpio_value_t col);
|
||||
|
||||
/*
|
||||
* Clear LCD and return the cursor to the top right.
|
||||
@ -81,7 +83,7 @@ void lcd_clear(LCD *lcd);
|
||||
/*
|
||||
* Control B, cursor blink, C, cursor visibility, and D, display power for LCD.
|
||||
*/
|
||||
void lcd_display_control(LCD *lcd, int b, int c, int d);
|
||||
void lcd_display_control(LCD *lcd, gpio_value_t b, gpio_value_t c, gpio_value_t d);
|
||||
|
||||
#define LCD_INCREMENT 1
|
||||
#define LCD_DECREMENT 0
|
||||
@ -92,12 +94,12 @@ void lcd_display_control(LCD *lcd, int b, int c, int d);
|
||||
/*
|
||||
* Control shift and increment for LCD.
|
||||
*/
|
||||
void lcd_entry_mode(LCD *lcd, int id, int s);
|
||||
void lcd_entry_mode(LCD *lcd, gpio_value_t id, gpio_value_t s);
|
||||
|
||||
/*
|
||||
* Control direction and shift/move for cursor for LCD.
|
||||
*/
|
||||
void lcd_cursor_shift(LCD *lcd, int sc, int rl);
|
||||
void lcd_cursor_shift(LCD *lcd, gpio_value_t sc, gpio_value_t rl);
|
||||
|
||||
/*
|
||||
* Return: weather the busy flag is set for LCD.
|
||||
|
333
main.c
333
main.c
@ -1,30 +1,327 @@
|
||||
/*
|
||||
* main.c - Program entry and argument handling
|
||||
* Copyright (C) 2024 Alexander Rosenberg
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version. See the LICENSE file for more information.
|
||||
*/
|
||||
#include "lcd.h"
|
||||
#include "util.h"
|
||||
#include "ths.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <err.h>
|
||||
#include <errno.h>
|
||||
#include <stdio.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <string.h>
|
||||
#include <figpar.h>
|
||||
#include <inttypes.h>
|
||||
#include <ctype.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <time.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define RS 2
|
||||
#define RW 3
|
||||
#define EN 4
|
||||
#define D0 17
|
||||
#define D1 27
|
||||
#define D2 22
|
||||
#define D3 10
|
||||
#define D4 9
|
||||
#define D5 14
|
||||
#define D6 15
|
||||
#define D7 18
|
||||
/*
|
||||
* Print help message to standard output.
|
||||
*/
|
||||
void print_help(const char *exec_name);
|
||||
/*
|
||||
* Parse command line arguments and save the options to OPTS.
|
||||
*/
|
||||
void parse_arguments(int argc, char *const *argv);
|
||||
/*
|
||||
* Parse config file PATH.
|
||||
*/
|
||||
void parse_config_file(const char *path);
|
||||
|
||||
int main(int argc, const char **argv) {
|
||||
gpio_handle_t handle = gpio_open(0);
|
||||
int main(int argc, char *const *argv) {
|
||||
parse_arguments(argc, argv);
|
||||
parse_config_file(GLOBAL_OPTS.config_path);
|
||||
gpio_handle_t handle = gpio_open_device(GLOBAL_OPTS.gpio_path);
|
||||
if (handle == GPIO_INVALID_HANDLE) {
|
||||
errx(1, "count not open GPIO handle!");
|
||||
err(1, "could not open GPIO device \"%s\"", GLOBAL_OPTS.gpio_path);
|
||||
}
|
||||
LOG_VERBOSE("Opened GPIO device: \"%s\"\n", GLOBAL_OPTS.gpio_path);
|
||||
LCD *lcd = lcd_open(handle, GLOBAL_OPTS.rs_pin, GLOBAL_OPTS.rw_pin,
|
||||
GLOBAL_OPTS.en_pin, GLOBAL_OPTS.data_pins[0],
|
||||
GLOBAL_OPTS.data_pins[1], GLOBAL_OPTS.data_pins[2],
|
||||
GLOBAL_OPTS.data_pins[3], GLOBAL_OPTS.data_pins[4],
|
||||
GLOBAL_OPTS.data_pins[5], GLOBAL_OPTS.data_pins[6],
|
||||
GLOBAL_OPTS.data_pins[7]);
|
||||
lcd_display_control(lcd, LCD_CURSOR_BLINK, LCD_CURSOR_ON, LCD_DISPLAY_ON);
|
||||
THS *ths = ths_open(GLOBAL_OPTS.temp_key, GLOBAL_OPTS.humid_key,
|
||||
GLOBAL_OPTS.fail_key);
|
||||
while (true) {
|
||||
if (ths_read_fails(ths) > GLOBAL_OPTS.fail_limit) {
|
||||
errx(1, "THS fail limit reached");
|
||||
}
|
||||
lcd_clear(lcd);
|
||||
lcd_write_string(lcd, "temp humi time");
|
||||
char buff[17];
|
||||
int cur_len = snprintf(buff, sizeof(buff), "%4.1fF %3" PRIu32 "%% ",
|
||||
DK_TO_F(ths_read_temp(ths)), ths_read_humid(ths));
|
||||
time_t it = time(NULL);
|
||||
struct tm lt;
|
||||
localtime_r(&it, <);
|
||||
strftime(buff + cur_len, sizeof(buff) - cur_len,
|
||||
"%H:%M", <);
|
||||
lcd_move_to(lcd, 1, 0);
|
||||
lcd_write_string(lcd, buff);
|
||||
usleep(1000 * 1000 * 5);
|
||||
}
|
||||
LCD *lcd = lcd_open(handle, RS, RW, EN, D0, D1, D2, D3, D4, D5, D6, D7);
|
||||
lcd_write_string(lcd, "This is a test");
|
||||
lcd_move_to(lcd, 1, 0);
|
||||
lcd_write_string(lcd, "FreeBSD!");
|
||||
lcd_close(lcd);
|
||||
gpio_close(handle);
|
||||
cleanup_options(&GLOBAL_OPTS);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void print_help(const char *exec_name) {
|
||||
printf("usage: %s [-h] [-v] [-s] [-f CONFIG_PATH]", exec_name);
|
||||
}
|
||||
|
||||
void parse_arguments(int argc, char *const *argv) {
|
||||
int c;
|
||||
while ((c = getopt(argc, argv, "hf:vs")) != -1) {
|
||||
switch (c) {
|
||||
case 'h':
|
||||
print_help(argv[0]);
|
||||
exit(0);
|
||||
case 'f':
|
||||
FREE_CHECKED(GLOBAL_OPTS.config_path);
|
||||
GLOBAL_OPTS.config_path = optarg;
|
||||
LOG_VERBOSE("Config file path set: \"%s\"\n", optarg);
|
||||
break;
|
||||
case 's':
|
||||
GLOBAL_OPTS.strict_config = true;
|
||||
LOG_VERBOSE("Strict config mode enabled\n");
|
||||
break;
|
||||
case 'v':
|
||||
GLOBAL_OPTS.verbose = true;
|
||||
LOG_VERBOSE("Verbose mode enabled\n");
|
||||
break;
|
||||
case '?':
|
||||
default:
|
||||
print_help(argv[0]);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
if (!GLOBAL_OPTS.config_path) {
|
||||
GLOBAL_OPTS.config_path = strdup_checked(DEFAULT_CONFIG_PATH);
|
||||
LOG_VERBOSE("Config file path set: \"%s\"\n", GLOBAL_OPTS.config_path);
|
||||
}
|
||||
}
|
||||
|
||||
static int unknown_key_callback(struct figpar_config *opt, uint32_t line,
|
||||
char *directive, char *value) {
|
||||
if (GLOBAL_OPTS.strict_config) {
|
||||
errx(1, "line %" PRIu32 ": unknown configuration option: \"%s\"",
|
||||
line, directive);
|
||||
} else {
|
||||
warnx("line %" PRIu32 ": unknown configuration option: \"%s\"",
|
||||
line, directive);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_uint_callback(struct figpar_config *opt, uint32_t line,
|
||||
char *directive, char *value) {
|
||||
char last_char;
|
||||
if (sscanf(value, "%" SCNu32 " %c", &opt->value.u_num, &last_char) < 1 ||
|
||||
(last_char && !isdigit(last_char))) {
|
||||
warnx("line %" PRIu32 ": not a valid number \"%s\"", line, value);
|
||||
return 1;
|
||||
}
|
||||
LOG_VERBOSE("Loaded config uint option: %s = %" PRIu32 "\n", directive,
|
||||
opt->value.u_num);
|
||||
opt->type = FIGPAR_TYPE_UINT;
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define CONFIG_UINT_ARR_TYPE FIGPAR_TYPE_DATA1
|
||||
struct UIntArr {
|
||||
uint32_t *arr;
|
||||
size_t size;
|
||||
};
|
||||
|
||||
static int parse_uint_arr_callback(struct figpar_config *opt, uint32_t line,
|
||||
char *directive, char *value) {
|
||||
struct UIntArr *arr = malloc_checked(sizeof(struct UIntArr));
|
||||
arr->size = 0;
|
||||
arr->arr = NULL;
|
||||
uint32_t num;
|
||||
char last_char = 1;
|
||||
int jump_len;
|
||||
while (last_char && sscanf(value, "%" SCNu32 " %c%n",
|
||||
&num, &last_char, &jump_len) >= 1) {
|
||||
if (last_char && !isdigit(last_char)) {
|
||||
warnx("line %" PRIu32 ": not a valid number array \"%s\"",
|
||||
line, value);
|
||||
FREE_CHECKED(arr->arr);
|
||||
free(arr);
|
||||
return 1;
|
||||
}
|
||||
arr->arr = realloc_checked(arr->arr, sizeof(uint32_t) * ++arr->size);
|
||||
arr->arr[arr->size - 1] = num;
|
||||
value += jump_len - 1; // -1 to add back the first digit
|
||||
}
|
||||
opt->type = CONFIG_UINT_ARR_TYPE;
|
||||
opt->value.data = arr;
|
||||
if (GLOBAL_OPTS.verbose) {
|
||||
fprintf(stderr, "Loaded config uint array option: %s = ", directive);
|
||||
for (size_t i = 0; i < arr->size; ++i) {
|
||||
fprintf(stderr, "%" PRIu32, arr->arr[i]);
|
||||
if (i < arr->size - 1) {
|
||||
fputc(' ', stderr);
|
||||
}
|
||||
}
|
||||
fputc('\n', stderr);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int parse_str_callback(struct figpar_config *opt, uint32_t line,
|
||||
char *directive, char *value) {
|
||||
FREE_CHECKED(opt->value.str);
|
||||
if (!value[0]) {
|
||||
opt->type = FIGPAR_TYPE_STR;
|
||||
opt->value.str = NULL;
|
||||
} else {
|
||||
opt->type = FIGPAR_TYPE_STR;
|
||||
opt->value.str = strdup_checked(value);
|
||||
LOG_VERBOSE("Loaded config string option: %s = %s\n", directive, value);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#define REQUIRE_KEY(ind, name) if (entries[ind].type == FIGPAR_TYPE_NONE) {\
|
||||
errx(1, "%s must be specified", # name);\
|
||||
}
|
||||
static void set_options_from_entries(struct figpar_config *entries,
|
||||
size_t nentries) {
|
||||
entries[0].type = FIGPAR_TYPE_NONE;
|
||||
GLOBAL_OPTS.gpio_path = entries[0].value.str;
|
||||
LOG_VERBOSE("Using gpio_path: \"%s\"\n", GLOBAL_OPTS.gpio_path);
|
||||
|
||||
REQUIRE_KEY(1, rs_pin);
|
||||
GLOBAL_OPTS.rs_pin = entries[1].value.u_num;
|
||||
|
||||
REQUIRE_KEY(2, rw_pin);
|
||||
GLOBAL_OPTS.rw_pin = entries[2].value.u_num;
|
||||
|
||||
REQUIRE_KEY(3, en_pin);
|
||||
GLOBAL_OPTS.en_pin = entries[3].value.u_num;
|
||||
|
||||
REQUIRE_KEY(4, data_pins);
|
||||
struct UIntArr *arr = entries[4].value.data;
|
||||
if (arr->size != 8) {
|
||||
errx(1, "data_pins must be an array of 8 uints");
|
||||
}
|
||||
memcpy(GLOBAL_OPTS.data_pins, arr->arr, sizeof(uint32_t) * 8);
|
||||
|
||||
entries[5].type = FIGPAR_TYPE_NONE;
|
||||
GLOBAL_OPTS.temp_key = entries[5].value.str;
|
||||
LOG_VERBOSE("Using temp_key: \"%s\"\n", GLOBAL_OPTS.temp_key);
|
||||
|
||||
entries[6].type = FIGPAR_TYPE_NONE;
|
||||
GLOBAL_OPTS.humid_key = entries[6].value.str;
|
||||
LOG_VERBOSE("Using humid_key: \"%s\"\n", GLOBAL_OPTS.humid_key);
|
||||
|
||||
entries[7].type = FIGPAR_TYPE_NONE;
|
||||
GLOBAL_OPTS.fail_key = entries[7].value.str;
|
||||
LOG_VERBOSE("Using fail_key: \"%s\"\n", GLOBAL_OPTS.fail_key);
|
||||
|
||||
GLOBAL_OPTS.fail_limit = entries[8].value.u_num;
|
||||
|
||||
entries[9].type = FIGPAR_TYPE_NONE;
|
||||
GLOBAL_OPTS.lcd_version = entries[9].value.str;
|
||||
LOG_VERBOSE("Using lcd_version: \"%s\"\n", GLOBAL_OPTS.lcd_version);
|
||||
}
|
||||
|
||||
void parse_config_file(const char *path) {
|
||||
LOG_VERBOSE("Loading config file: \"%s\"\n", path);
|
||||
struct figpar_config entries[] = {
|
||||
{
|
||||
.directive = "gpio_file",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_checked(DEFAULT_GPIO_DEVICE)},
|
||||
},
|
||||
{
|
||||
.directive = "rs_pin",
|
||||
.type = FIGPAR_TYPE_NONE,
|
||||
.action = parse_uint_callback,
|
||||
},
|
||||
{
|
||||
.directive = "rw_pin",
|
||||
.type = FIGPAR_TYPE_NONE,
|
||||
.action = parse_uint_callback,
|
||||
},
|
||||
{
|
||||
.directive = "en_pin",
|
||||
.type = FIGPAR_TYPE_NONE,
|
||||
.action = parse_uint_callback,
|
||||
},
|
||||
{
|
||||
.directive = "data_pins",
|
||||
.type = FIGPAR_TYPE_NONE,
|
||||
.action = parse_uint_arr_callback,
|
||||
},
|
||||
{
|
||||
.directive = "temp_key",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_checked(DEFAULT_TEMP_KEY)},
|
||||
},
|
||||
{
|
||||
.directive = "humid_key",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_checked(DEFAULT_HUMID_KEY)},
|
||||
},
|
||||
{
|
||||
.directive = "fail_key",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_checked(DEFAULT_FAIL_KEY)},
|
||||
},
|
||||
{
|
||||
.directive = "fail_limit",
|
||||
.type = FIGPAR_TYPE_UINT,
|
||||
.action = parse_uint_callback,
|
||||
.value = {.u_num = DEFAULT_FAIL_LIMIT},
|
||||
},
|
||||
{
|
||||
.directive = "lcd_version",
|
||||
.type = FIGPAR_TYPE_STR,
|
||||
.action = parse_str_callback,
|
||||
.value = {.str = strdup_checked(DEFAULT_LCD_VERSION)},
|
||||
},
|
||||
{ .directive = NULL },
|
||||
};
|
||||
size_t entry_count = sizeof(entries) / sizeof(struct figpar_config);
|
||||
errno = 0;
|
||||
int status = parse_config(entries, path, unknown_key_callback,
|
||||
FIGPAR_BREAK_ON_EQUALS);
|
||||
if (status < 0) {
|
||||
err(1, "could not parse config file: \"%s\"", path);
|
||||
} else if (status > 1) {
|
||||
errx(1, "could not parse config file: \"%s\"", path);
|
||||
}
|
||||
set_options_from_entries(entries, entry_count);
|
||||
for (size_t i = 0; i < entry_count; ++i) {
|
||||
switch (entries[i].type) {
|
||||
case FIGPAR_TYPE_STR:
|
||||
FREE_CHECKED(entries[i].value.str);
|
||||
break;
|
||||
case CONFIG_UINT_ARR_TYPE:
|
||||
FREE_CHECKED(((struct UIntArr *) entries[i].value.data)->arr);
|
||||
FREE_CHECKED(entries[i].value.data);
|
||||
break;
|
||||
default: ; // ignore
|
||||
}
|
||||
}
|
||||
LOG_VERBOSE("Finished loading config file\n");
|
||||
}
|
||||
|
107
ths.c
Normal file
107
ths.c
Normal file
@ -0,0 +1,107 @@
|
||||
/*
|
||||
* ths.c - Utilities for reading sysctl based temp. and humidity sensors
|
||||
* Copyright (C) 2024 Alexander Rosenberg
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version. See the LICENSE file for more information.
|
||||
*/
|
||||
#include "ths.h"
|
||||
#include "util.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <sys/sysctl.h>
|
||||
#include <inttypes.h>
|
||||
|
||||
THS *ths_open(const char *temp_key, const char *humid_key, const char *fail_key) {
|
||||
THS *ths = malloc_checked(sizeof(THS));
|
||||
if (temp_key) {
|
||||
ths->temp_mib_len = THS_MIB_BUF_SIZE;
|
||||
if (sysctlnametomib(temp_key, ths->temp_mib, &ths->temp_mib_len) < 0) {
|
||||
warn("could not look up temp. key name: \"%s\"", temp_key);
|
||||
free(ths);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
ths->temp_mib_len = 0;
|
||||
}
|
||||
if (humid_key) {
|
||||
ths->humid_mib_len = THS_MIB_BUF_SIZE;
|
||||
if (sysctlnametomib(humid_key, ths->humid_mib, &ths->humid_mib_len) < 0) {
|
||||
warn("could not look up humid. key name: \"%s\"", humid_key);
|
||||
free(ths);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
ths->humid_mib_len = 0;
|
||||
}
|
||||
if (fail_key) {
|
||||
ths->fail_mib_len = THS_MIB_BUF_SIZE;
|
||||
if (sysctlnametomib(fail_key, ths->fail_mib, &ths->fail_mib_len) < 0) {
|
||||
warn("could not look up fail key name: \"%s\"", fail_key);
|
||||
free(ths);
|
||||
return NULL;
|
||||
}
|
||||
} else {
|
||||
ths->fail_mib_len = 0;
|
||||
}
|
||||
LOG_VERBOSE("Opened THS with temp. key: \"%s\"\n"
|
||||
" humid. key: \"%s\"\n"
|
||||
" fail key: \"%s\"\n",
|
||||
temp_key, humid_key, fail_key);
|
||||
return ths;
|
||||
}
|
||||
|
||||
void ths_close(THS *ths) {
|
||||
free(ths);
|
||||
}
|
||||
|
||||
int32_t ths_read_fails(const THS *ths) {
|
||||
if (!ths->fail_mib_len) {
|
||||
warnx("THS sensor does not support reading failure count");
|
||||
return -1;
|
||||
}
|
||||
uint32_t ret_buf;
|
||||
size_t ret_buf_size = sizeof(uint32_t);
|
||||
if (sysctl(ths->fail_mib, ths->fail_mib_len, &ret_buf,
|
||||
&ret_buf_size, NULL, 0) < 0) {
|
||||
warn("could not read THS fail count");
|
||||
return -1;
|
||||
}
|
||||
if (ret_buf > 0) {
|
||||
LOG_VERBOSE("THS sensor reported: %" PRIu32 " fails\n", ret_buf)
|
||||
}
|
||||
return ret_buf;
|
||||
}
|
||||
|
||||
int32_t ths_read_temp(const THS *ths) {
|
||||
if (!ths->temp_mib_len) {
|
||||
warnx("THS sensor does not support reading temperature");
|
||||
return -1;
|
||||
}
|
||||
uint32_t ret_buf;
|
||||
size_t ret_buf_size = sizeof(uint32_t);
|
||||
if (sysctl(ths->temp_mib, ths->temp_mib_len, &ret_buf,
|
||||
&ret_buf_size, NULL, 0) < 0) {
|
||||
warn("could not read THS temperature");
|
||||
return -1;
|
||||
}
|
||||
return ret_buf;
|
||||
}
|
||||
|
||||
int32_t ths_read_humid(const THS *ths) {
|
||||
if (!ths->humid_mib_len) {
|
||||
warnx("THS sensor does not support reading humidity");
|
||||
return -1;
|
||||
}
|
||||
uint32_t ret_buf;
|
||||
size_t ret_buf_size = sizeof(uint32_t);
|
||||
if (sysctl(ths->humid_mib, ths->humid_mib_len, &ret_buf,
|
||||
&ret_buf_size, NULL, 0) < 0) {
|
||||
warn("could not read THS humidity");
|
||||
return -1;
|
||||
}
|
||||
return ret_buf;
|
||||
|
||||
}
|
65
ths.h
Normal file
65
ths.h
Normal file
@ -0,0 +1,65 @@
|
||||
/*
|
||||
* ths.h - Utilities for reading sysctl based temp. and humidity sensors
|
||||
* Copyright (C) 2024 Alexander Rosenberg
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify it under
|
||||
* the terms of the GNU General Public License as published by the Free Software
|
||||
* Foundation, either version 3 of the License, or (at your option) any later
|
||||
* version. See the LICENSE file for more information.
|
||||
*/
|
||||
#ifndef INCLUDED_THS_H
|
||||
#define INCLUDED_THS_H
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define THS_MIB_BUF_SIZE 8
|
||||
|
||||
typedef struct {
|
||||
size_t temp_mib_len;
|
||||
int temp_mib[THS_MIB_BUF_SIZE];
|
||||
size_t humid_mib_len;
|
||||
int humid_mib[THS_MIB_BUF_SIZE];
|
||||
size_t fail_mib_len;
|
||||
int fail_mib[THS_MIB_BUF_SIZE];
|
||||
} THS;
|
||||
|
||||
/*
|
||||
* Convert deciKelvin to degrees Celsius
|
||||
*/
|
||||
#define DK_TO_C(k) ((k) / 10.0f - 273.15f)
|
||||
/*
|
||||
* Convert deciKelvin to degrees Fahrenheit
|
||||
*/
|
||||
#define DK_TO_F(k) (((DK_TO_C((k))) * 1.8f) + 32.0f)
|
||||
|
||||
/*
|
||||
* Create a new THS from the given sysctl keys. Any of the keys can be null.
|
||||
* Return: the new THS, or NULL if an error occurred.
|
||||
*/
|
||||
THS *ths_open(const char *temp_key, const char *humid_key, const char *fail_key);
|
||||
|
||||
/*
|
||||
* Close THS.
|
||||
*/
|
||||
void ths_close(THS *ths);
|
||||
|
||||
/*
|
||||
* Read the fail cound of THS.
|
||||
* Return: the value read, or -1 on error
|
||||
*/
|
||||
int32_t ths_read_fails(const THS *ths);
|
||||
|
||||
/*
|
||||
* Read the temperature of THS in deciKelvin.
|
||||
* Return: the value read, or -1 on error
|
||||
*/
|
||||
int32_t ths_read_temp(const THS *ths);
|
||||
|
||||
/*
|
||||
* Read the relative humidity of THS as an integer percentage.
|
||||
* Return: the value read, or -1 on error
|
||||
*/
|
||||
int32_t ths_read_humid(const THS *ths);
|
||||
|
||||
#endif
|
19
util.c
19
util.c
@ -10,11 +10,14 @@
|
||||
#include "util.h"
|
||||
|
||||
#include <err.h>
|
||||
#include <string.h>
|
||||
|
||||
struct GlobalFlags GLOBAL_FLAGS = {
|
||||
.config_path = NULL,
|
||||
.gpio_path = NULL,
|
||||
};
|
||||
Options GLOBAL_OPTS;
|
||||
|
||||
void cleanup_options(Options *opts) {
|
||||
FREE_CHECKED(opts->config_path);
|
||||
FREE_CHECKED(opts->gpio_path);
|
||||
}
|
||||
|
||||
void *malloc_checked(size_t n) {
|
||||
return realloc_checked(NULL, n);
|
||||
@ -27,3 +30,11 @@ void *realloc_checked(void *old_ptr, size_t n) {
|
||||
}
|
||||
return ptr;
|
||||
}
|
||||
|
||||
void *strdup_checked(const char *str) {
|
||||
char *new_str = strdup(str);
|
||||
if (!new_str) {
|
||||
errx(1, "out of memory!");
|
||||
}
|
||||
return new_str;
|
||||
}
|
||||
|
49
util.h
49
util.h
@ -11,12 +11,35 @@
|
||||
#define INCLUDED_UTIL_H
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <time.h>
|
||||
#include <stdbool.h>
|
||||
#include <sys/types.h>
|
||||
#include <libgpio.h>
|
||||
|
||||
struct GlobalFlags {
|
||||
const char *config_path; // path to config file
|
||||
const char *gpio_path; // path of GPIO device file to use
|
||||
};
|
||||
extern struct GlobalFlags GLOBAL_FLAGS;
|
||||
typedef struct {
|
||||
char *config_path; // path to config file
|
||||
bool strict_config; // exit if unknown config options is found
|
||||
bool verbose; // be more verbose
|
||||
|
||||
char *gpio_path; // path of GPIO device file to use
|
||||
gpio_pin_t en_pin;
|
||||
gpio_pin_t rs_pin;
|
||||
gpio_pin_t rw_pin;
|
||||
gpio_pin_t data_pins[8];
|
||||
char *lcd_version; // Is LCD jp or eu
|
||||
char *temp_key; // sysctl key for temperature
|
||||
char *humid_key; // sysctl key for humidity
|
||||
char *fail_key; // sysctl key for number of fails
|
||||
uint32_t fail_limit; // limit for number of failures before exit
|
||||
} Options;
|
||||
extern Options GLOBAL_OPTS;
|
||||
|
||||
/*
|
||||
* Cleanup an Options struct.
|
||||
*/
|
||||
void cleanup_options(Options *opts);
|
||||
|
||||
/*
|
||||
* Like malloc(3), except that if allocation fails, an error will be written to
|
||||
@ -30,4 +53,20 @@ void *malloc_checked(size_t n);
|
||||
*/
|
||||
void *realloc_checked(void *old_ptr, size_t n);
|
||||
|
||||
/*
|
||||
* Like strdup(3), except that if allocation fails, an error will be written to
|
||||
* standard error and abort(3) called
|
||||
*/
|
||||
void *strdup_checked(const char *str);
|
||||
|
||||
/*
|
||||
* Like free(3), but do nothing if P is NULL.
|
||||
*/
|
||||
#define FREE_CHECKED(p) if (p) {free(p);}
|
||||
|
||||
/*
|
||||
* Call fprintf(3) to stderr only if verbose mode was enabled.
|
||||
*/
|
||||
#define LOG_VERBOSE(...) if (GLOBAL_OPTS.verbose) {fprintf(stderr, __VA_ARGS__);}
|
||||
|
||||
#endif
|
||||
|
Loading…
Reference in New Issue
Block a user