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
|
CC=clang
|
||||||
CFLAGS=-std=c99
|
CFLAGS=-std=c99 -Wall
|
||||||
LD=clang
|
LD=clang
|
||||||
LDFLAGS=-lgpio -lfigpar
|
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
|
PROG=rpi4b-temp-humidity
|
||||||
|
|
||||||
.include "config.mk"
|
.include "config.mk"
|
||||||
@ -11,9 +11,10 @@ OBJS=${SRCS:C/^src/bin/:C/.c$/.o/}
|
|||||||
bin/${PROG}: ${OBJS}
|
bin/${PROG}: ${OBJS}
|
||||||
${LD} ${LDFLAGS} -o ${@} ${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 lcd.o: lcd.h
|
||||||
main.o ths.o: ths.h
|
main.o ths.o: ths.h
|
||||||
|
main.o button.o: button.h
|
||||||
|
|
||||||
${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
||||||
@mkdir -p bin/
|
@mkdir -p bin/
|
||||||
@ -25,6 +26,7 @@ ${OBJS}: ${.TARGET:C/^bin/src/:C/.o$/.c/}
|
|||||||
-DDEFAULT_FAIL_KEY="\"${DEFAULT_FAIL_KEY}\""\
|
-DDEFAULT_FAIL_KEY="\"${DEFAULT_FAIL_KEY}\""\
|
||||||
-DDEFAULT_FAIL_LIMIT="${DEFAULT_FAIL_LIMIT}"\
|
-DDEFAULT_FAIL_LIMIT="${DEFAULT_FAIL_LIMIT}"\
|
||||||
-DDEFAULT_LCD_VERSION="\"${DEFAULT_LCD_VERSION}\""\
|
-DDEFAULT_LCD_VERSION="\"${DEFAULT_LCD_VERSION}\""\
|
||||||
|
-DDEFAULT_REFRESH_TIME="${DEFAULT_REFRESH_TIME}"\
|
||||||
-o ${@} ${.ALLSRC}
|
-o ${@} ${.ALLSRC}
|
||||||
|
|
||||||
clean:
|
clean:
|
||||||
|
@ -2,7 +2,7 @@ DEFAULT_CONFIG_PATH=config.conf
|
|||||||
DEFAULT_GPIO_DEVICE=/dev/gpioc0
|
DEFAULT_GPIO_DEVICE=/dev/gpioc0
|
||||||
DEFAULT_TEMP_KEY=dev.gpioths.0.temperature
|
DEFAULT_TEMP_KEY=dev.gpioths.0.temperature
|
||||||
DEFAULT_HUMID_KEY=dev.gpioths.0.humidity
|
DEFAULT_HUMID_KEY=dev.gpioths.0.humidity
|
||||||
#DEFAULT_FAIL_KEY=dev.gpioths.0.fails
|
DEFAULT_FAIL_KEY=dev.gpioths.0.fails
|
||||||
DEFAULT_FAIL_KEY=
|
|
||||||
DEFAULT_FAIL_LIMIT=5
|
DEFAULT_FAIL_LIMIT=5
|
||||||
DEFAULT_LCD_VERSION=jp
|
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
|
61
src/main.c
61
src/main.c
@ -10,18 +10,18 @@
|
|||||||
#include "lcd.h"
|
#include "lcd.h"
|
||||||
#include "util.h"
|
#include "util.h"
|
||||||
#include "ths.h"
|
#include "ths.h"
|
||||||
|
#include "button.h"
|
||||||
|
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
#include <err.h>
|
#include <err.h>
|
||||||
#include <errno.h>
|
#include <errno.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <sys/sysctl.h>
|
|
||||||
#include <string.h>
|
#include <string.h>
|
||||||
|
#include <ctype.h>
|
||||||
#include <figpar.h>
|
#include <figpar.h>
|
||||||
#include <inttypes.h>
|
#include <inttypes.h>
|
||||||
#include <ctype.h>
|
|
||||||
#include <sys/sysctl.h>
|
|
||||||
#include <time.h>
|
#include <time.h>
|
||||||
|
#include <sys/time.h>
|
||||||
#include <unistd.h>
|
#include <unistd.h>
|
||||||
|
|
||||||
/*
|
/*
|
||||||
@ -40,6 +40,7 @@ void parse_config_file(const char *path);
|
|||||||
int main(int argc, char *const *argv) {
|
int main(int argc, char *const *argv) {
|
||||||
parse_arguments(argc, argv);
|
parse_arguments(argc, argv);
|
||||||
parse_config_file(GLOBAL_OPTS.config_path);
|
parse_config_file(GLOBAL_OPTS.config_path);
|
||||||
|
|
||||||
gpio_handle_t handle = gpio_open_device(GLOBAL_OPTS.gpio_path);
|
gpio_handle_t handle = gpio_open_device(GLOBAL_OPTS.gpio_path);
|
||||||
if (handle == GPIO_INVALID_HANDLE) {
|
if (handle == GPIO_INVALID_HANDLE) {
|
||||||
err(1, "could not open GPIO device \"%s\"", GLOBAL_OPTS.gpio_path);
|
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) {
|
if (!GLOBAL_OPTS.fail_key) {
|
||||||
warnx("it's probably a bad idea to not set 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) {
|
while (true) {
|
||||||
if (GLOBAL_OPTS.fail_key && ths_read_fails(ths) > GLOBAL_OPTS.fail_limit) {
|
clock_gettime(CLOCK_UPTIME, &cur_time);
|
||||||
errx(1, "THS fail limit reached");
|
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);
|
button_dispatch(btn);
|
||||||
lcd_write_string(lcd, "temp humi time");
|
// 10ms is probably faster than anyone will press a button
|
||||||
char buff[17];
|
usleep(10 * 1000);
|
||||||
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_delete(btn);
|
||||||
|
ths_close(ths);
|
||||||
lcd_close(lcd);
|
lcd_close(lcd);
|
||||||
gpio_close(handle);
|
gpio_close(handle);
|
||||||
cleanup_options(&GLOBAL_OPTS);
|
cleanup_options(&GLOBAL_OPTS);
|
||||||
@ -250,6 +267,8 @@ static void set_options_from_entries(struct figpar_config *entries,
|
|||||||
entries[9].type = FIGPAR_TYPE_NONE;
|
entries[9].type = FIGPAR_TYPE_NONE;
|
||||||
GLOBAL_OPTS.lcd_version = entries[9].value.str;
|
GLOBAL_OPTS.lcd_version = entries[9].value.str;
|
||||||
LOG_VERBOSE("Using lcd_version: \"%s\"\n", GLOBAL_OPTS.lcd_version);
|
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) {
|
void parse_config_file(const char *path) {
|
||||||
@ -311,6 +330,12 @@ void parse_config_file(const char *path) {
|
|||||||
.action = parse_str_callback,
|
.action = parse_str_callback,
|
||||||
.value = {.str = strdup_checked(DEFAULT_LCD_VERSION)},
|
.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 },
|
{ .directive = NULL },
|
||||||
};
|
};
|
||||||
size_t entry_count = sizeof(entries) / sizeof(struct figpar_config);
|
size_t entry_count = sizeof(entries) / sizeof(struct figpar_config);
|
||||||
|
@ -33,6 +33,7 @@ typedef struct {
|
|||||||
char *humid_key; // sysctl key for humidity
|
char *humid_key; // sysctl key for humidity
|
||||||
char *fail_key; // sysctl key for number of fails
|
char *fail_key; // sysctl key for number of fails
|
||||||
uint32_t fail_limit; // limit for number of failures before exit
|
uint32_t fail_limit; // limit for number of failures before exit
|
||||||
|
uint32_t refresh_time; // time between temp and humid refresh
|
||||||
} Options;
|
} Options;
|
||||||
extern Options GLOBAL_OPTS;
|
extern Options GLOBAL_OPTS;
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user