From 900243fb1c5ed46a4bc6c21f91e20bede1914135 Mon Sep 17 00:00:00 2001 From: Alexander Rosenberg Date: Wed, 28 Feb 2024 12:09:18 -0800 Subject: [PATCH] Make button.c and button.h --- Makefile | 8 ++++--- config.mk | 4 ++-- src/button.c | 59 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/button.h | 51 +++++++++++++++++++++++++++++++++++++++++++ src/main.c | 61 ++++++++++++++++++++++++++++++++++++---------------- src/util.h | 1 + 6 files changed, 161 insertions(+), 23 deletions(-) create mode 100644 src/button.c create mode 100644 src/button.h diff --git a/Makefile b/Makefile index f65f7ed..d40105e 100644 --- a/Makefile +++ b/Makefile @@ -1,8 +1,8 @@ CC=clang -CFLAGS=-std=c99 +CFLAGS=-std=c99 -Wall LD=clang LDFLAGS=-lgpio -lfigpar -SRCS=src/main.c src/util.c src/lcd.c src/ths.c +SRCS=src/main.c src/util.c src/lcd.c src/ths.c src/button.c PROG=rpi4b-temp-humidity .include "config.mk" @@ -11,9 +11,10 @@ OBJS=${SRCS:C/^src/bin/:C/.c$/.o/} bin/${PROG}: ${OBJS} ${LD} ${LDFLAGS} -o ${@} ${OBJS} -main.o util.o lcd.o ths.o: util.h +main.o util.o lcd.o ths.o button.o: util.h main.o lcd.o: lcd.h main.o ths.o: ths.h +main.o button.o: button.h ${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/} @mkdir -p bin/ @@ -25,6 +26,7 @@ ${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/} -DDEFAULT_FAIL_KEY="\"${DEFAULT_FAIL_KEY}\""\ -DDEFAULT_FAIL_LIMIT="${DEFAULT_FAIL_LIMIT}"\ -DDEFAULT_LCD_VERSION="\"${DEFAULT_LCD_VERSION}\""\ +-DDEFAULT_REFRESH_TIME="${DEFAULT_REFRESH_TIME}"\ -o ${@} ${.ALLSRC} clean: diff --git a/config.mk b/config.mk index 7b22248..032b9e9 100644 --- a/config.mk +++ b/config.mk @@ -2,7 +2,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_KEY= +DEFAULT_FAIL_KEY=dev.gpioths.0.fails DEFAULT_FAIL_LIMIT=5 DEFAULT_LCD_VERSION=jp +DEFAULT_REFRESH_TIME=5000 diff --git a/src/button.c b/src/button.c new file mode 100644 index 0000000..8f76d6d --- /dev/null +++ b/src/button.c @@ -0,0 +1,59 @@ +/* + * button.c - Call functions if GPIO pins are high + * 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 "button.h" +#include "util.h" + +#include + +static const struct timespec DEBOUNCE_TIME = { + .tv_sec = 0, + .tv_nsec = 10 * 1000 * 1000, // 10ms +}; + +Button *button_new(gpio_handle_t handle, gpio_pin_t pin, ButtonAction action, + void *user_data) { + Button *button = malloc_checked(sizeof(Button)); + button->handle = handle; + button->pin = pin; + gpio_config_t cfg = { + .g_pin = pin, + .g_flags = GPIO_PIN_INPUT | GPIO_PIN_PULLUP, + }; + gpio_pin_set_flags(handle, &cfg); + button->action = action; + button->user_data = user_data; + button->is_down = false; + timespecclear(&button->last_detect); + return button; +} + +void button_delete(Button *button) { + free(button); +} + +int button_dispatch(Button *button) { + struct timespec cur_time, diff_time; + clock_gettime(CLOCK_UPTIME, &cur_time); + timespecsub(&cur_time, &button->last_detect, &diff_time); + if (timespeccmp(&diff_time, &DEBOUNCE_TIME, >=)) { + gpio_value_t status = gpio_pin_get(button->handle, button->pin); + if (!button->is_down && status == GPIO_PIN_LOW) { + button->is_down = true; + button->last_detect= cur_time; + if (button->action) { + return button->action(button->user_data); + } + } else if (button->is_down && status == GPIO_PIN_HIGH) { + button->last_detect = cur_time; + button->is_down = false; + } + } + return 0; +} diff --git a/src/button.h b/src/button.h new file mode 100644 index 0000000..b8415a8 --- /dev/null +++ b/src/button.h @@ -0,0 +1,51 @@ +/* + * button.h - Call functions if GPIO pins are high + * 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_BUTTON_H +#define INCLUDED_BUTTON_H + +#include +#include +#include + +typedef int(*ButtonAction)(void *); + +typedef struct { + gpio_handle_t handle; + gpio_pin_t pin; + ButtonAction action; + void *user_data; + + struct timespec last_detect; + bool is_down; +} Button; + +/* + * Create a Button object with the given HANDLE, PIN and ACTION. USER_DATA will + * be passed to ACTION every time it is run. It is up to the caller to free + * USER_DATA when the Button is freed. + * Return: the created Button. + */ +Button *button_new(gpio_handle_t uandle, gpio_pin_t pin, ButtonAction action, + void *user_data); + +/* + * Delete BUTTON. + * NOTE: This does NOT free USER_DATA. + */ +void button_delete(Button *button); + +/* + * Check if the pin for this button is HIGH, if it is, call ACTION and prevent + * it from being called again until button becomes LOW. + * Return: 0 if nothing is done, otherwise, the return value of ACTION. + */ +int button_dispatch(Button *button); + +#endif diff --git a/src/main.c b/src/main.c index 929dfd2..216b886 100644 --- a/src/main.c +++ b/src/main.c @@ -10,18 +10,18 @@ #include "lcd.h" #include "util.h" #include "ths.h" +#include "button.h" #include #include #include #include -#include #include +#include #include #include -#include -#include #include +#include #include /* @@ -40,6 +40,7 @@ void parse_config_file(const char *path); 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) { err(1, "could not open GPIO device \"%s\"", GLOBAL_OPTS.gpio_path); @@ -57,24 +58,40 @@ int main(int argc, char *const *argv) { if (!GLOBAL_OPTS.fail_key) { warnx("it's probably a bad idea to not set fail_key"); } + struct timespec last_update, cur_time, diff_time; + timespecclear(&last_update); + struct timespec refresh_time = { + .tv_sec = GLOBAL_OPTS.refresh_time / 1000, + .tv_nsec = (GLOBAL_OPTS.refresh_time % 1000) * 1000 * 1000, + }; + Button *btn = button_new(handle, 23, (ButtonAction) printf, "Button Pressed!\n"); while (true) { - if (GLOBAL_OPTS.fail_key && ths_read_fails(ths) > GLOBAL_OPTS.fail_limit) { - errx(1, "THS fail limit reached"); + clock_gettime(CLOCK_UPTIME, &cur_time); + timespecsub(&cur_time, &last_update, &diff_time); + if (timespeccmp(&diff_time, &refresh_time, >=)) { + if (GLOBAL_OPTS.fail_key && 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)); + struct tm lt; + localtime_r(&cur_time.tv_sec, <); + strftime(buff + cur_len, sizeof(buff) - cur_len, + "%H:%M", <); + lcd_move_to(lcd, 1, 0); + lcd_write_string(lcd, buff); + LOG_VERBOSE("Temp. and humid. display updated\n"); + last_update = cur_time; } - 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); + button_dispatch(btn); + // 10ms is probably faster than anyone will press a button + usleep(10 * 1000); } + button_delete(btn); + ths_close(ths); lcd_close(lcd); gpio_close(handle); cleanup_options(&GLOBAL_OPTS); @@ -250,6 +267,8 @@ static void set_options_from_entries(struct figpar_config *entries, 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); + + GLOBAL_OPTS.refresh_time = entries[10].value.u_num; } void parse_config_file(const char *path) { @@ -311,6 +330,12 @@ void parse_config_file(const char *path) { .action = parse_str_callback, .value = {.str = strdup_checked(DEFAULT_LCD_VERSION)}, }, + { + .directive = "refresh_time", + .type = FIGPAR_TYPE_UINT, + .action = parse_uint_callback, + .value = {.u_num = DEFAULT_REFRESH_TIME}, + }, { .directive = NULL }, }; size_t entry_count = sizeof(entries) / sizeof(struct figpar_config); diff --git a/src/util.h b/src/util.h index 42827e1..514d016 100644 --- a/src/util.h +++ b/src/util.h @@ -33,6 +33,7 @@ typedef struct { 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 + uint32_t refresh_time; // time between temp and humid refresh } Options; extern Options GLOBAL_OPTS;