Make button.c and button.h
This commit is contained in:
		
							
								
								
									
										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 "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, <); | ||||
|             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); | ||||
|  | ||||
| @ -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; | ||||
|  | ||||
|  | ||||
		Reference in New Issue
	
	Block a user