Make button.c and button.h

This commit is contained in:
Alexander Rosenberg 2024-02-28 12:09:18 -08:00
parent 03d554f34f
commit 900243fb1c
Signed by: school-rpi4
GPG Key ID: 5CCFC80B0B47B04B
6 changed files with 161 additions and 23 deletions

View File

@ -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:

View File

@ -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

59
src/button.c Normal file
View File

@ -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 <sys/time.h>
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;
}

51
src/button.h Normal file
View File

@ -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 <sys/types.h>
#include <libgpio.h>
#include <stdbool.h>
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

View File

@ -10,18 +10,18 @@
#include "lcd.h"
#include "util.h"
#include "ths.h"
#include "button.h"
#include <unistd.h>
#include <err.h>
#include <errno.h>
#include <stdio.h>
#include <sys/sysctl.h>
#include <string.h>
#include <ctype.h>
#include <figpar.h>
#include <inttypes.h>
#include <ctype.h>
#include <sys/sysctl.h>
#include <time.h>
#include <sys/time.h>
#include <unistd.h>
/*
@ -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, &lt);
strftime(buff + cur_len, sizeof(buff) - cur_len,
"%H:%M", &lt);
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, &lt);
strftime(buff + cur_len, sizeof(buff) - cur_len,
"%H:%M", &lt);
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);

View File

@ -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;