Make button.c and button.h
This commit is contained in:
parent
03d554f34f
commit
900243fb1c
8
Makefile
8
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:
|
||||
|
@ -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
59
src/button.c
Normal 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
51
src/button.h
Normal 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
|
37
src/main.c
37
src/main.c
@ -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,7 +58,17 @@ 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) {
|
||||
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");
|
||||
}
|
||||
@ -66,15 +77,21 @@ int main(int argc, char *const *argv) {
|
||||
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, <);
|
||||
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);
|
||||
usleep(1000 * 1000 * 5);
|
||||
LOG_VERBOSE("Temp. and humid. display updated\n");
|
||||
last_update = cur_time;
|
||||
}
|
||||
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);
|
||||
|
@ -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;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user