diff --git a/Makefile b/Makefile index 70ab191..f4f0d4d 100644 --- a/Makefile +++ b/Makefile @@ -7,7 +7,7 @@ LD=clang LDFLAGS=-lgpio -lfigpar -lpthread ${SQLITE3_LDFLAGS} SRCS=src/main.c src/util.c src/lcd.c src/ths.c src/button.c src/ui/screen.c\ src/ui/datesel.c src/ui/statsby.c src/ui/datapoints.c src/ui/timesel.c\ - src/ui/statrange.c src/config.c + src/ui/statrange.c src/config.c src/ui/blankscreen.c PROG=rpi4b-temp-humidity OBJS=${SRCS:C/^src/bin/:C/.c$/.o/} @@ -38,6 +38,7 @@ bin/main.o bin/config.o: src/config.h bin/main.o bin/button.o bin/ui/screen.o: src/button.h bin/main.o bin/ui/statsby.o: src/ui/statsby.h bin/main.o bin/ui/datapoints.o: src/ui/datapoints.h +bin/main.o bin/ui/blankscreen.o: src/ui/blankscreen.h bin/main.o bin/ui/statrange.o: src/ui/statrange.h .if "${DEFAULT_TEMP_UNIT}" != "F" && "${DEFAULT_TEMP_UNIT}" != "C" &&\ diff --git a/src/config.c b/src/config.c index 78b88ee..0ef5540 100644 --- a/src/config.c +++ b/src/config.c @@ -1,5 +1,5 @@ /* - * config.h - Config file parsing + * config.c - Config file parsing * Copyright (C) 2024 Alexander Rosenberg * * This program is free software: you can redistribute it and/or modify it under @@ -29,17 +29,32 @@ static int unknown_key_callback(struct figpar_config *opt, uint32_t line, return 0; } +static int parse_int_callback(struct figpar_config *opt, uint32_t line, + char *directive, char *value) { + char last_char; + if (sscanf(value, "%" SCNi32 " %c", &opt->value.num, &last_char) < 1 || + (last_char && !isdigit(last_char))) { + warnx("line %" PRIu32 ": not a valid number \"%s\"", line, value); + return 1; + } + LOG_VERBOSE("Loaded config int option: %s = %" PRIi32 "\n", directive, + opt->value.num); + opt->type = FIGPAR_TYPE_INT; + return 0; +} + static int parse_uint_callback(struct figpar_config *opt, uint32_t line, char *directive, char *value) { char last_char; if (sscanf(value, "%" SCNu32 " %c", &opt->value.u_num, &last_char) < 1 || (last_char && !isdigit(last_char))) { - warnx("line %" PRIu32 ": not a valid number \"%s\"", line, value); + warnx("line %" PRIu32 ": not a valid unsigned number \"%s\"", line, + value); return 1; } LOG_VERBOSE("Loaded config uint option: %s = %" PRIu32 "\n", directive, opt->value.u_num); - opt->type = FIGPAR_TYPE_INT; + opt->type = FIGPAR_TYPE_UINT; return 0; } @@ -183,6 +198,8 @@ static void set_options_from_entries(struct figpar_config *entries, GLOBAL_OPTS.back_pin = entries[14].value.u_num; GLOBAL_OPTS.temp_unit = entries[15].value.num; + + GLOBAL_OPTS.bl_pin = entries[16].value.num; } static char *strdup_default_opt(const char *def) { @@ -283,6 +300,12 @@ void parse_config_file(const char *path) { .action = parse_temp_unit_callback, .value = {.num = DEFAULT_TEMP_UNIT} }, + { + .directive = "bl_pin", + .type = FIGPAR_TYPE_INT, + .action = parse_int_callback, + .value = {.num = -1} + }, { .directive = NULL }, }; size_t entry_count = sizeof(entries) / sizeof(struct figpar_config); diff --git a/src/lcd.c b/src/lcd.c index 22f7d9e..a6f736b 100644 --- a/src/lcd.c +++ b/src/lcd.c @@ -47,9 +47,11 @@ static void lcd_pulse_enable(LCD *lcd) { LCD *lcd_open(gpio_handle_t handle, gpio_pin_t rs, gpio_pin_t rw, gpio_pin_t en, gpio_pin_t d0, gpio_pin_t d1, gpio_pin_t d2, gpio_pin_t d3, - gpio_pin_t d4, gpio_pin_t d5, gpio_pin_t d6, gpio_pin_t d7) { + gpio_pin_t d4, gpio_pin_t d5, gpio_pin_t d6, gpio_pin_t d7, + int32_t bl) { LOG_VERBOSE("Initializing LCD: rs=%d, rw=%d, en=%d, d={%d, %d, %d, %d, %d, " - "%d, %d, %d}\n", rs, rw, en, d0, d1, d2, d3, d4, d5, d6, d7); + "%d, %d, %d}, bl=%d\n", rs, rw, en, d0, d1, d2, d3, d4, d5, d6, + d7, bl); LCD *lcd = malloc_checked(sizeof(LCD)); lcd->handle = handle; lcd->rs = rs; @@ -63,13 +65,18 @@ LCD *lcd_open(gpio_handle_t handle, gpio_pin_t rs, gpio_pin_t rw, gpio_pin_t en, lcd->d5 = d5; lcd->d6 = d6; lcd->d7 = d7; + lcd->bl = bl; gpio_pin_output(lcd->handle, lcd->rw); gpio_pin_output(lcd->handle, lcd->en); gpio_pin_output(lcd->handle, lcd->rs); + if (lcd->bl >= 0) { + gpio_pin_output(lcd->handle, lcd->bl); + } lcd_clear(lcd); lcd_call(lcd, 0, 0, 0, 0, 1, 1, 1, 0, 0); // 5x8 font, 2 lines, 8bit lcd_entry_mode(lcd, LCD_INCREMENT, LCD_CURSOR_MOVE); lcd_display_control(lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, LCD_DISPLAY_ON); + lcd_backlight_set_enabled(lcd, true); LOG_VERBOSE("LCD Initialization done\n"); return lcd; } @@ -145,3 +152,9 @@ bool lcd_is_busy(LCD *lcd) { usleep(1); return busy; } + +void lcd_backlight_set_enabled(LCD *lcd, bool enable) { + if (lcd->bl > 0) { + gpio_pin_set(lcd->handle, lcd->bl, enable); + } +} diff --git a/src/lcd.h b/src/lcd.h index f853fa5..ef2b840 100644 --- a/src/lcd.h +++ b/src/lcd.h @@ -28,6 +28,7 @@ typedef struct { gpio_pin_t d5; gpio_pin_t d6; gpio_pin_t d7; + int32_t bl; // gpio_value_ternal use bool is_read; @@ -37,12 +38,14 @@ typedef struct { * Allocate and initialize a new LCD object and return a pogpio_value_ter to it. HANDLE * is a gpio_handle_t, each of the other arguments corresponds to the GPIO pin * number that the given controller pin is connected to: "register select", - * "read-write", "enable", and data pins 0-7. + * "read-write", "enable", and data pins 0-7. If BL is 0 or greater, it is a pin + * that will enable the backlight when HIGH, and disable the backlight when LOW. * Return: the newly created object. */ LCD *lcd_open(gpio_handle_t handle, gpio_pin_t rs, gpio_pin_t rw, gpio_pin_t en, gpio_pin_t d0, gpio_pin_t d1, gpio_pin_t d2, gpio_pin_t d3, - gpio_pin_t d4, gpio_pin_t d5, gpio_pin_t d6, gpio_pin_t d7); + gpio_pin_t d4, gpio_pin_t d5, gpio_pin_t d6, gpio_pin_t d7, + int32_t bl); /* * Close LCD by freeing any resources associated with it. */ @@ -106,4 +109,9 @@ void lcd_cursor_shift(LCD *lcd, gpio_value_t sc, gpio_value_t rl); */ bool lcd_is_busy(LCD *lcd); +/* + * Set the backlight's power to ENABLE. + */ +void lcd_backlight_set_enabled(LCD *lcd, bool enable); + #endif diff --git a/src/main.c b/src/main.c index 4631a0a..c3b2c03 100644 --- a/src/main.c +++ b/src/main.c @@ -17,6 +17,7 @@ #include "ui/statsby.h" #include "ui/datapoints.h" #include "ui/statrange.h" +#include "ui/blankscreen.h" #include #include @@ -88,7 +89,7 @@ int main(int argc, char *const *argv) { GLOBAL_OPTS.data_pins[1], GLOBAL_OPTS.data_pins[2], GLOBAL_OPTS.data_pins[3], GLOBAL_OPTS.data_pins[4], GLOBAL_OPTS.data_pins[5], GLOBAL_OPTS.data_pins[6], - GLOBAL_OPTS.data_pins[7]); + GLOBAL_OPTS.data_pins[7], GLOBAL_OPTS.bl_pin); setup_signals(); open_database(); initialize_util_queries(DATABASE); @@ -104,6 +105,7 @@ int main(int argc, char *const *argv) { screen_manager_add(screen_manager, (Screen *) stats_by_screen_new()); screen_manager_add(screen_manager, (Screen *) data_points_screen_new()); screen_manager_add(screen_manager, (Screen *) stat_range_screen_new()); + screen_manager_add(screen_manager, blank_screen_new()); while (RUNNING) { lock_stat_globals(); uint32_t temp = LAST_TEMP; diff --git a/src/ui/blankscreen.c b/src/ui/blankscreen.c new file mode 100644 index 0000000..28e60b1 --- /dev/null +++ b/src/ui/blankscreen.c @@ -0,0 +1,34 @@ +/* + * blankscreen.c - Screen that disables the display + * 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 "blankscreen.h" + +static bool blank_screen_dispatch(Screen *screen, + SensorState *state) { + if (state->back_down || state->sel_down || state->up_down || + state->down_down) { + lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, + LCD_DISPLAY_ON); + lcd_backlight_set_enabled(state->lcd, true); + return true; + } else if (state->force_draw) { + lcd_display_control(state->lcd, LCD_CURSOR_NO_BLINK, LCD_CURSOR_OFF, + LCD_DISPLAY_OFF); + lcd_backlight_set_enabled(state->lcd, false); + } + return false; +} + +Screen *blank_screen_new() { + Screen *s = malloc_checked(sizeof(Screen)); + screen_init(s, "Blank Display", + (ScreenDispatchFunc) blank_screen_dispatch, + (ScreenCleanupFunc) free); + return s; +} diff --git a/src/ui/blankscreen.h b/src/ui/blankscreen.h new file mode 100644 index 0000000..a52f2fc --- /dev/null +++ b/src/ui/blankscreen.h @@ -0,0 +1,20 @@ +/* + * blankscreen.h - Screen that disables the display + * 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_BLANKSCREEN_H +#define INCLUDED_BLANKSCREEN_H + +#include "screen.h" + +/* + * Create a new blank screen that will simply disable the display. + */ +Screen *blank_screen_new(void); + +#endif diff --git a/src/util.h b/src/util.h index 64f501d..a663e24 100644 --- a/src/util.h +++ b/src/util.h @@ -34,6 +34,7 @@ typedef struct { gpio_pin_t rs_pin; gpio_pin_t rw_pin; gpio_pin_t data_pins[8]; + gpio_pin_t bl_pin; char *database_location; // location of sqlite3 database char *temp_key; // sysctl key for temperature char *humid_key; // sysctl key for humidity