Allow user to select temperature unit
This commit is contained in:
		
							
								
								
									
										311
									
								
								src/config.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										311
									
								
								src/config.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,311 @@ | ||||
| /* | ||||
|  * config.h - Config file parsing | ||||
|  * 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 "config.h" | ||||
| #include "util.h" | ||||
|  | ||||
| #include <figpar.h> | ||||
| #include <err.h> | ||||
| #include <inttypes.h> | ||||
| #include <ctype.h> | ||||
| #include <string.h> | ||||
| #include <errno.h> | ||||
|  | ||||
| static int unknown_key_callback(struct figpar_config *opt, uint32_t line, | ||||
|                                  char *directive, char *value) { | ||||
|     if (GLOBAL_OPTS.strict_config) { | ||||
|         errx(1, "line %" PRIu32 ": unknown configuration option: \"%s\"", | ||||
|              line, directive); | ||||
|     } else { | ||||
|         warnx("line %" PRIu32 ": unknown configuration option: \"%s\"", | ||||
|               line, directive); | ||||
|     } | ||||
|     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); | ||||
|         return 1; | ||||
|     } | ||||
|     LOG_VERBOSE("Loaded config uint option: %s = %" PRIu32 "\n", directive, | ||||
|                 opt->value.u_num); | ||||
|     opt->type = FIGPAR_TYPE_INT; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int parse_temp_unit_callback(struct figpar_config *opt, uint32_t line, | ||||
|                                     char *directive, char *value) { | ||||
|     if (strcasecmp(value, "c") == 0) { | ||||
|         opt->value.num = TEMP_UNIT_C; | ||||
|     } else if (strcasecmp(value, "f") == 0) { | ||||
|         opt->value.num = TEMP_UNIT_F; | ||||
|     } else { | ||||
|         warnx("not a valid temperature unit: \"%s\"", value); | ||||
|         return 1; | ||||
|     } | ||||
|     LOG_VERBOSE("Loaded temperature unit config option: %s = %c\n", directive, | ||||
|                 toupper(value[0])); | ||||
|     opt->type = FIGPAR_TYPE_UINT; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #define CONFIG_UINT_ARR_TYPE FIGPAR_TYPE_DATA1 | ||||
| struct UIntArr { | ||||
|     uint32_t *arr; | ||||
|     size_t size; | ||||
| }; | ||||
|  | ||||
| static int parse_uint_arr_callback(struct figpar_config *opt, uint32_t line, | ||||
|                                    char *directive, char *value) { | ||||
|     struct UIntArr *arr = malloc_checked(sizeof(struct UIntArr)); | ||||
|     arr->size = 0; | ||||
|     arr->arr = NULL; | ||||
|     uint32_t num; | ||||
|     char last_char = 1; | ||||
|     int jump_len; | ||||
|     while (last_char && sscanf(value, "%" SCNu32 " %c%n", | ||||
|                                &num, &last_char, &jump_len) >= 1) { | ||||
|         if (last_char && !isdigit(last_char)) { | ||||
|             warnx("line %" PRIu32 ": not a valid number array \"%s\"", | ||||
|                   line, value); | ||||
|             FREE_CHECKED(arr->arr); | ||||
|             free(arr); | ||||
|             return 1; | ||||
|         } | ||||
|         arr->arr = realloc_checked(arr->arr, sizeof(uint32_t) * ++arr->size); | ||||
|         arr->arr[arr->size - 1] = num; | ||||
|         value += jump_len - 1; // -1 to add back the first digit | ||||
|     } | ||||
|     opt->type = CONFIG_UINT_ARR_TYPE; | ||||
|     opt->value.data = arr; | ||||
|     if (GLOBAL_OPTS.verbose) { | ||||
|         fprintf(stderr, "Loaded config uint array option: %s = ", directive); | ||||
|         for (size_t i = 0; i < arr->size; ++i) { | ||||
|             fprintf(stderr, "%" PRIu32, arr->arr[i]); | ||||
|             if (i < arr->size - 1)  { | ||||
|                 fputc(' ', stderr); | ||||
|             } | ||||
|         } | ||||
|         fputc('\n', stderr); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int parse_str_callback(struct figpar_config *opt, uint32_t line, | ||||
|                               char *directive, char *value) { | ||||
|     FREE_CHECKED(opt->value.str); | ||||
|     if (!value[0]) { | ||||
|         opt->type = FIGPAR_TYPE_STR; | ||||
|         opt->value.str = NULL; | ||||
|     } else { | ||||
|         opt->type = FIGPAR_TYPE_STR; | ||||
|         opt->value.str = strdup_checked(value); | ||||
|         LOG_VERBOSE("Loaded config string option: %s = %s\n", directive, value); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static char *steal_opt_if_set(char *str) { | ||||
|     if (str && !*str) { | ||||
|         free(str); | ||||
|         return NULL; | ||||
|     } | ||||
|     return str; | ||||
| } | ||||
|  | ||||
|  | ||||
| #define REQUIRE_KEY(ind, name) if (entries[ind].type == FIGPAR_TYPE_NONE) {\ | ||||
| errx(1, "%s must be specified", # name);\ | ||||
| } | ||||
| static void set_options_from_entries(struct figpar_config *entries, | ||||
|                                      size_t nentries) { | ||||
|     entries[0].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.gpio_path = entries[0].value.str; | ||||
|     LOG_VERBOSE("Using gpio_path: \"%s\"\n", GLOBAL_OPTS.gpio_path); | ||||
|  | ||||
|     REQUIRE_KEY(1, rs_pin); | ||||
|     GLOBAL_OPTS.rs_pin = entries[1].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(2, rw_pin); | ||||
|     GLOBAL_OPTS.rw_pin = entries[2].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(3, en_pin); | ||||
|     GLOBAL_OPTS.en_pin = entries[3].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(4, data_pins); | ||||
|     struct UIntArr *arr = entries[4].value.data; | ||||
|     if (arr->size != 8) { | ||||
|         errx(1, "data_pins must be an array of 8 uints"); | ||||
|     } | ||||
|     memcpy(GLOBAL_OPTS.data_pins, arr->arr, sizeof(uint32_t) * 8); | ||||
|  | ||||
|     entries[5].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.temp_key = steal_opt_if_set(entries[5].value.str); | ||||
|     LOG_VERBOSE("Using temp_key: \"%s\"\n", GLOBAL_OPTS.temp_key); | ||||
|  | ||||
|     entries[6].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.humid_key = steal_opt_if_set(entries[6].value.str); | ||||
|     LOG_VERBOSE("Using humid_key: \"%s\"\n", GLOBAL_OPTS.humid_key); | ||||
|  | ||||
|     entries[7].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.fail_key = steal_opt_if_set(entries[7].value.str); | ||||
|     LOG_VERBOSE("Using fail_key: \"%s\"\n", GLOBAL_OPTS.fail_key); | ||||
|  | ||||
|     GLOBAL_OPTS.fail_limit = entries[8].value.u_num; | ||||
|  | ||||
|     entries[9].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.database_location = entries[9].value.str; | ||||
|     LOG_VERBOSE("Using database_location: \"%s\"\n", | ||||
|                 GLOBAL_OPTS.database_location); | ||||
|  | ||||
|     GLOBAL_OPTS.refresh_time = entries[10].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(11, sel_pin); | ||||
|     GLOBAL_OPTS.sel_pin = entries[11].value.u_num; | ||||
|      | ||||
|     REQUIRE_KEY(12, up_pin); | ||||
|     GLOBAL_OPTS.up_pin = entries[12].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(13, down_pin); | ||||
|     GLOBAL_OPTS.down_pin = entries[13].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(14, back_pin); | ||||
|     GLOBAL_OPTS.back_pin = entries[14].value.u_num; | ||||
|  | ||||
|     GLOBAL_OPTS.temp_unit = entries[15].value.num; | ||||
| } | ||||
|  | ||||
| static char *strdup_default_opt(const char *def) { | ||||
|     if (!*def) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return strdup_checked(def); | ||||
| } | ||||
|  | ||||
| void parse_config_file(const char *path) { | ||||
|     LOG_VERBOSE("Loading config file: \"%s\"\n", path); | ||||
|     struct figpar_config entries[] = { | ||||
|         { | ||||
|             .directive = "gpio_file", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_GPIO_DEVICE)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "rs_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "rw_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "en_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "data_pins", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_arr_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "temp_key", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_TEMP_KEY)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "humid_key", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_HUMID_KEY)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "fail_key", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_FAIL_KEY)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "fail_limit", | ||||
|             .type = FIGPAR_TYPE_UINT, | ||||
|             .action = parse_uint_callback, | ||||
|             .value = {.u_num = DEFAULT_FAIL_LIMIT}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "database_location", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_DATABASE_LOCATION)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "refresh_time", | ||||
|             .type = FIGPAR_TYPE_UINT, | ||||
|             .action = parse_uint_callback, | ||||
|             .value = {.u_num = DEFAULT_REFRESH_TIME}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "sel_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "up_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "down_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "back_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "temp_unit", | ||||
|             .type = FIGPAR_TYPE_INT, | ||||
|             .action = parse_temp_unit_callback, | ||||
|             .value = {.num = DEFAULT_TEMP_UNIT} | ||||
|         }, | ||||
|         { .directive = NULL }, | ||||
|     }; | ||||
|     size_t entry_count = sizeof(entries) / sizeof(struct figpar_config); | ||||
|     errno = 0; | ||||
|     int status = parse_config(entries, path, unknown_key_callback, | ||||
|                               FIGPAR_BREAK_ON_EQUALS); | ||||
|     if (status < 0) { | ||||
|         err(1, "could not parse config file: \"%s\"", path); | ||||
|     } else if (status > 1) { | ||||
|         errx(1, "could not parse config file: \"%s\"", path); | ||||
|     } | ||||
|     set_options_from_entries(entries, entry_count); | ||||
|     for (size_t i = 0; i < entry_count; ++i) { | ||||
|         switch (entries[i].type) { | ||||
|         case FIGPAR_TYPE_STR: | ||||
|             FREE_CHECKED(entries[i].value.str); | ||||
|             break; | ||||
|         case CONFIG_UINT_ARR_TYPE: | ||||
|             FREE_CHECKED(((struct UIntArr *) entries[i].value.data)->arr); | ||||
|             FREE_CHECKED(entries[i].value.data); | ||||
|             break; | ||||
|         default: ; // ignore | ||||
|         } | ||||
|     } | ||||
|     LOG_VERBOSE("Finished loading config file\n"); | ||||
| } | ||||
							
								
								
									
										15
									
								
								src/config.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										15
									
								
								src/config.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,15 @@ | ||||
| /* | ||||
|  * config.h - Config file parsing | ||||
|  * 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_CONFIG_H | ||||
| #define INCLUDED_CONFIG_H | ||||
|  | ||||
| void parse_config_file(const char *path); | ||||
|  | ||||
| #endif | ||||
							
								
								
									
										274
									
								
								src/main.c
									
									
									
									
									
								
							
							
						
						
									
										274
									
								
								src/main.c
									
									
									
									
									
								
							| @ -12,6 +12,7 @@ | ||||
| #include "ths.h" | ||||
| #include "button.h" | ||||
| #include "menu.h" | ||||
| #include "config.h" | ||||
| #include "ui/screen.h" | ||||
| #include "ui/statsby.h" | ||||
| #include "ui/datapoints.h" | ||||
| @ -41,10 +42,6 @@ void print_help(const char *exec_name); | ||||
|  * Parse command line arguments and save the options to OPTS. | ||||
|  */ | ||||
| void parse_arguments(int argc, char *const *argv); | ||||
| /* | ||||
|  * Parse config file PATH. | ||||
|  */ | ||||
| void parse_config_file(const char *path); | ||||
| /* | ||||
|  * Setup signals used to ensure clean termination. | ||||
|  */ | ||||
| @ -167,275 +164,6 @@ void parse_arguments(int argc, char *const *argv) { | ||||
|     } | ||||
| } | ||||
|  | ||||
| static int unknown_key_callback(struct figpar_config *opt, uint32_t line, | ||||
|                                  char *directive, char *value) { | ||||
|     if (GLOBAL_OPTS.strict_config) { | ||||
|         errx(1, "line %" PRIu32 ": unknown configuration option: \"%s\"", | ||||
|              line, directive); | ||||
|     } else { | ||||
|         warnx("line %" PRIu32 ": unknown configuration option: \"%s\"", | ||||
|               line, directive); | ||||
|     } | ||||
|     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); | ||||
|         return 1; | ||||
|     } | ||||
|     LOG_VERBOSE("Loaded config uint option: %s = %" PRIu32 "\n", directive, | ||||
|                 opt->value.u_num); | ||||
|     opt->type = FIGPAR_TYPE_UINT; | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| #define CONFIG_UINT_ARR_TYPE FIGPAR_TYPE_DATA1 | ||||
| struct UIntArr { | ||||
|     uint32_t *arr; | ||||
|     size_t size; | ||||
| }; | ||||
|  | ||||
| static int parse_uint_arr_callback(struct figpar_config *opt, uint32_t line, | ||||
|                                    char *directive, char *value) { | ||||
|     struct UIntArr *arr = malloc_checked(sizeof(struct UIntArr)); | ||||
|     arr->size = 0; | ||||
|     arr->arr = NULL; | ||||
|     uint32_t num; | ||||
|     char last_char = 1; | ||||
|     int jump_len; | ||||
|     while (last_char && sscanf(value, "%" SCNu32 " %c%n", | ||||
|                                &num, &last_char, &jump_len) >= 1) { | ||||
|         if (last_char && !isdigit(last_char)) { | ||||
|             warnx("line %" PRIu32 ": not a valid number array \"%s\"", | ||||
|                   line, value); | ||||
|             FREE_CHECKED(arr->arr); | ||||
|             free(arr); | ||||
|             return 1; | ||||
|         } | ||||
|         arr->arr = realloc_checked(arr->arr, sizeof(uint32_t) * ++arr->size); | ||||
|         arr->arr[arr->size - 1] = num; | ||||
|         value += jump_len - 1; // -1 to add back the first digit | ||||
|     } | ||||
|     opt->type = CONFIG_UINT_ARR_TYPE; | ||||
|     opt->value.data = arr; | ||||
|     if (GLOBAL_OPTS.verbose) { | ||||
|         fprintf(stderr, "Loaded config uint array option: %s = ", directive); | ||||
|         for (size_t i = 0; i < arr->size; ++i) { | ||||
|             fprintf(stderr, "%" PRIu32, arr->arr[i]); | ||||
|             if (i < arr->size - 1)  { | ||||
|                 fputc(' ', stderr); | ||||
|             } | ||||
|         } | ||||
|         fputc('\n', stderr); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static int parse_str_callback(struct figpar_config *opt, uint32_t line, | ||||
|                               char *directive, char *value) { | ||||
|     FREE_CHECKED(opt->value.str); | ||||
|     if (!value[0]) { | ||||
|         opt->type = FIGPAR_TYPE_STR; | ||||
|         opt->value.str = NULL; | ||||
|     } else { | ||||
|         opt->type = FIGPAR_TYPE_STR; | ||||
|         opt->value.str = strdup_checked(value); | ||||
|         LOG_VERBOSE("Loaded config string option: %s = %s\n", directive, value); | ||||
|     } | ||||
|     return 0; | ||||
| } | ||||
|  | ||||
| static char *steal_opt_if_set(char *str) { | ||||
|     if (str && !*str) { | ||||
|         free(str); | ||||
|         return NULL; | ||||
|     } | ||||
|     return str; | ||||
| } | ||||
|  | ||||
|  | ||||
| #define REQUIRE_KEY(ind, name) if (entries[ind].type == FIGPAR_TYPE_NONE) {\ | ||||
| errx(1, "%s must be specified", # name);\ | ||||
| } | ||||
| static void set_options_from_entries(struct figpar_config *entries, | ||||
|                                      size_t nentries) { | ||||
|     entries[0].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.gpio_path = entries[0].value.str; | ||||
|     LOG_VERBOSE("Using gpio_path: \"%s\"\n", GLOBAL_OPTS.gpio_path); | ||||
|  | ||||
|     REQUIRE_KEY(1, rs_pin); | ||||
|     GLOBAL_OPTS.rs_pin = entries[1].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(2, rw_pin); | ||||
|     GLOBAL_OPTS.rw_pin = entries[2].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(3, en_pin); | ||||
|     GLOBAL_OPTS.en_pin = entries[3].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(4, data_pins); | ||||
|     struct UIntArr *arr = entries[4].value.data; | ||||
|     if (arr->size != 8) { | ||||
|         errx(1, "data_pins must be an array of 8 uints"); | ||||
|     } | ||||
|     memcpy(GLOBAL_OPTS.data_pins, arr->arr, sizeof(uint32_t) * 8); | ||||
|  | ||||
|     entries[5].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.temp_key = steal_opt_if_set(entries[5].value.str); | ||||
|     LOG_VERBOSE("Using temp_key: \"%s\"\n", GLOBAL_OPTS.temp_key); | ||||
|  | ||||
|     entries[6].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.humid_key = steal_opt_if_set(entries[6].value.str); | ||||
|     LOG_VERBOSE("Using humid_key: \"%s\"\n", GLOBAL_OPTS.humid_key); | ||||
|  | ||||
|     entries[7].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.fail_key = steal_opt_if_set(entries[7].value.str); | ||||
|     LOG_VERBOSE("Using fail_key: \"%s\"\n", GLOBAL_OPTS.fail_key); | ||||
|  | ||||
|     GLOBAL_OPTS.fail_limit = entries[8].value.u_num; | ||||
|  | ||||
|     entries[9].type = FIGPAR_TYPE_NONE; | ||||
|     GLOBAL_OPTS.database_location = entries[9].value.str; | ||||
|     LOG_VERBOSE("Using database_location: \"%s\"\n", | ||||
|                 GLOBAL_OPTS.database_location); | ||||
|  | ||||
|     GLOBAL_OPTS.refresh_time = entries[10].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(11, sel_pin); | ||||
|     GLOBAL_OPTS.sel_pin = entries[11].value.u_num; | ||||
|      | ||||
|     REQUIRE_KEY(12, up_pin); | ||||
|     GLOBAL_OPTS.up_pin = entries[12].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(13, down_pin); | ||||
|     GLOBAL_OPTS.down_pin = entries[13].value.u_num; | ||||
|  | ||||
|     REQUIRE_KEY(14, back_pin); | ||||
|     GLOBAL_OPTS.back_pin = entries[14].value.u_num; | ||||
| } | ||||
|  | ||||
| static char *strdup_default_opt(const char *def) { | ||||
|     if (!*def) { | ||||
|         return NULL; | ||||
|     } | ||||
|     return strdup_checked(def); | ||||
| } | ||||
|  | ||||
| void parse_config_file(const char *path) { | ||||
|     LOG_VERBOSE("Loading config file: \"%s\"\n", path); | ||||
|     struct figpar_config entries[] = { | ||||
|         { | ||||
|             .directive = "gpio_file", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_GPIO_DEVICE)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "rs_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "rw_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "en_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "data_pins", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_arr_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "temp_key", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_TEMP_KEY)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "humid_key", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_HUMID_KEY)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "fail_key", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_FAIL_KEY)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "fail_limit", | ||||
|             .type = FIGPAR_TYPE_UINT, | ||||
|             .action = parse_uint_callback, | ||||
|             .value = {.u_num = DEFAULT_FAIL_LIMIT}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "database_location", | ||||
|             .type = FIGPAR_TYPE_STR, | ||||
|             .action = parse_str_callback, | ||||
|             .value = {.str = strdup_default_opt(DEFAULT_DATABASE_LOCATION)}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "refresh_time", | ||||
|             .type = FIGPAR_TYPE_UINT, | ||||
|             .action = parse_uint_callback, | ||||
|             .value = {.u_num = DEFAULT_REFRESH_TIME}, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "sel_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "up_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "down_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { | ||||
|             .directive = "back_pin", | ||||
|             .type = FIGPAR_TYPE_NONE, | ||||
|             .action = parse_uint_callback, | ||||
|         }, | ||||
|         { .directive = NULL }, | ||||
|     }; | ||||
|     size_t entry_count = sizeof(entries) / sizeof(struct figpar_config); | ||||
|     errno = 0; | ||||
|     int status = parse_config(entries, path, unknown_key_callback, | ||||
|                               FIGPAR_BREAK_ON_EQUALS); | ||||
|     if (status < 0) { | ||||
|         err(1, "could not parse config file: \"%s\"", path); | ||||
|     } else if (status > 1) { | ||||
|         errx(1, "could not parse config file: \"%s\"", path); | ||||
|     } | ||||
|     set_options_from_entries(entries, entry_count); | ||||
|     for (size_t i = 0; i < entry_count; ++i) { | ||||
|         switch (entries[i].type) { | ||||
|         case FIGPAR_TYPE_STR: | ||||
|             FREE_CHECKED(entries[i].value.str); | ||||
|             break; | ||||
|         case CONFIG_UINT_ARR_TYPE: | ||||
|             FREE_CHECKED(((struct UIntArr *) entries[i].value.data)->arr); | ||||
|             FREE_CHECKED(entries[i].value.data); | ||||
|             break; | ||||
|         default: ; // ignore | ||||
|         } | ||||
|     } | ||||
|     LOG_VERBOSE("Finished loading config file\n"); | ||||
| } | ||||
|  | ||||
| static void exit_signal_callback(int sig) { | ||||
|     LOG_VERBOSE("Caught signal %d. Exiting...\n", sig); | ||||
|     RUNNING = false; | ||||
|  | ||||
| @ -24,15 +24,6 @@ typedef struct { | ||||
|     int fail_mib[THS_MIB_BUF_SIZE]; | ||||
| } THS; | ||||
|  | ||||
| /* | ||||
|  * Convert deciKelvin to degrees Celsius | ||||
|  */ | ||||
| #define DK_TO_C(k) ((k) / 10.0f - 273.15f) | ||||
| /* | ||||
|  * Convert deciKelvin to degrees Fahrenheit | ||||
|  */ | ||||
| #define DK_TO_F(k) (((DK_TO_C((k))) * 1.8f) + 32.0f) | ||||
|  | ||||
| /* | ||||
|  * Create a new THS from the given sysctl keys. Any of the keys can be null. | ||||
|  * Return: the new THS, or NULL if an error occurred. | ||||
|  | ||||
| @ -8,7 +8,6 @@ | ||||
|  * version. See the LICENSE file for more information. | ||||
|  */ | ||||
| #include "datapoints.h" | ||||
| #include "../ths.h" | ||||
|  | ||||
| #include <inttypes.h> | ||||
| #include <time.h> | ||||
| @ -128,8 +127,8 @@ static void data_points_show(DataPointsScreen *screen, SensorState *state) { | ||||
|         lcd_move_to(state->lcd, 0, 0); | ||||
|         lcd_write_string(state->lcd, str_time); | ||||
|         char data_line[17]; | ||||
|         snprintf(data_line, 17, "%02ds %.1fF %d%%", broken_time.tm_sec, | ||||
|                  DK_TO_F(temp), humid); | ||||
|         snprintf(data_line, 17, "%02ds %.1f%c %d%%", broken_time.tm_sec, | ||||
|                  convert_temperature(temp), GLOBAL_OPTS.temp_unit, humid); | ||||
|         lcd_move_to(state->lcd, 1, 0); | ||||
|         lcd_write_string(state->lcd, data_line); | ||||
|     } | ||||
|  | ||||
| @ -8,7 +8,6 @@ | ||||
|  * version. See the LICENSE file for more information. | ||||
|  */ | ||||
| #include "screen.h" | ||||
| #include "../ths.h" | ||||
|  | ||||
| #include <err.h> | ||||
| #include <inttypes.h> | ||||
| @ -149,8 +148,9 @@ static bool stats_screen_dispatch(StatsScreen *screen, SensorState *state) { | ||||
|         lcd_clear(state->lcd); | ||||
|         lcd_write_string(state->lcd, "temp  humi time"); | ||||
|         char buff[17]; | ||||
|         int cur_len = snprintf(buff, sizeof(buff), "%4.1fF %3" PRIu32 "%% ", | ||||
|                                DK_TO_F(state->temp), state->humid); | ||||
|         int cur_len = snprintf(buff, sizeof(buff), "%.1f%c %3" PRIu32 "%% ", | ||||
|                                convert_temperature(state->temp), | ||||
|                                GLOBAL_OPTS.temp_unit, state->humid); | ||||
|         struct tm lt; | ||||
|         localtime_r(&cur_time, <); | ||||
|         strftime(buff + cur_len, sizeof(buff) - cur_len, | ||||
|  | ||||
| @ -8,7 +8,6 @@ | ||||
|  * version. See the LICENSE file for more information. | ||||
|  */ | ||||
| #include "statrange.h" | ||||
| #include "../ths.h" | ||||
|  | ||||
| #include <inttypes.h> | ||||
| #include <time.h> | ||||
| @ -117,7 +116,9 @@ static void stat_range_show_data(StatRangeScreen *screen, SensorState *state) { | ||||
|         lcd_move_to(state->lcd, 1, 0); | ||||
|         if (data.npoints) { | ||||
|             char data_line[17]; | ||||
|             snprintf(data_line, 17, "%.1fF %d%%", DK_TO_F(data.temp), data.humid); | ||||
|             snprintf(data_line, 17, "%.1f%c %d%%", | ||||
|                      convert_temperature(data.temp), | ||||
|                      GLOBAL_OPTS.temp_unit, data.humid); | ||||
|             lcd_write_string(state->lcd, data_line); | ||||
|         } else { | ||||
|             lcd_write_string(state->lcd, "No data!"); | ||||
|  | ||||
| @ -8,7 +8,6 @@ | ||||
|  * version. See the LICENSE file for more information. | ||||
|  */ | ||||
| #include "statsby.h" | ||||
| #include "../ths.h" | ||||
|  | ||||
| #include <err.h> | ||||
|  | ||||
| @ -122,7 +121,9 @@ static void stats_by_show_stats(StatsByScreen *screen, SensorState *state) { | ||||
|         lcd_move_to(state->lcd, 1, 0); | ||||
|         if (data.npoints) { | ||||
|             char data_string[17]; | ||||
|             snprintf(data_string, 17, "T:%.1fF H:%d%%", DK_TO_F(data.temp), data.humid); | ||||
|             snprintf(data_string, 17, "T:%.1f%c H:%d%%", | ||||
|                      convert_temperature(data.temp), GLOBAL_OPTS.temp_unit, | ||||
|                      data.humid); | ||||
|             lcd_write_string(state->lcd, data_string); | ||||
|         } else { | ||||
|             lcd_write_string(state->lcd, "No data!"); | ||||
|  | ||||
							
								
								
									
										21
									
								
								src/util.c
									
									
									
									
									
								
							
							
						
						
									
										21
									
								
								src/util.c
									
									
									
									
									
								
							| @ -331,3 +331,24 @@ bool get_average_for_range(sqlite3 *db, uint64_t start, uint64_t end, | ||||
|     sqlite3_reset(AVG_FOR_RANGE_QUERY); | ||||
|     return success; | ||||
| } | ||||
|  | ||||
| /* | ||||
|  * Convert deciKelvin to degrees Celsius | ||||
|  */ | ||||
| #define DK_TO_C(k) ((k) / 10.0f - 273.15f) | ||||
| /* | ||||
|  * Convert deciKelvin to degrees Fahrenheit | ||||
|  */ | ||||
| #define DK_TO_F(k) (((DK_TO_C((k))) * 1.8f) + 32.0f) | ||||
|  | ||||
| float convert_temperature(int dk) { | ||||
|     switch (GLOBAL_OPTS.temp_unit) { | ||||
|     case TEMP_UNIT_C: | ||||
|         return DK_TO_C(dk); | ||||
|     case TEMP_UNIT_F: | ||||
|         return DK_TO_F(dk); | ||||
|     default: | ||||
|         warnx("Unknown temperature unit: 0x%x", GLOBAL_OPTS.temp_unit); | ||||
|         return 0.0f; | ||||
|     } | ||||
| } | ||||
|  | ||||
							
								
								
									
										12
									
								
								src/util.h
									
									
									
									
									
								
							
							
						
						
									
										12
									
								
								src/util.h
									
									
									
									
									
								
							| @ -19,6 +19,11 @@ | ||||
| #include <libgpio.h> | ||||
| #include <sqlite3.h> | ||||
|  | ||||
| typedef enum { | ||||
|     TEMP_UNIT_F = 'F', | ||||
|     TEMP_UNIT_C = 'C' | ||||
| } TemperatureUnit; | ||||
|  | ||||
| typedef struct { | ||||
|     char *config_path; // path to config file | ||||
|     bool strict_config; // exit if unknown config options is found | ||||
| @ -41,6 +46,8 @@ typedef struct { | ||||
|     gpio_pin_t down_pin; | ||||
|     gpio_pin_t back_pin; | ||||
|     gpio_pin_t sel_pin; | ||||
|  | ||||
|     TemperatureUnit temp_unit; | ||||
| } Options; | ||||
| extern Options GLOBAL_OPTS; | ||||
|  | ||||
| @ -183,4 +190,9 @@ typedef struct { | ||||
| bool get_average_for_range(sqlite3 *db, uint64_t start, uint64_t end, | ||||
|                            UtilAverageRange *data); | ||||
|  | ||||
| /* | ||||
|  * Convert the temperature in deciKelvin DK to either F or C. | ||||
|  */ | ||||
| float convert_temperature(int dk); | ||||
|  | ||||
| #endif | ||||
|  | ||||
		Reference in New Issue
	
	Block a user