RocketLogger  2.0.0
rocketlogger.c
Go to the documentation of this file.
1 
32 #include <argp.h>
33 #include <ctype.h>
34 #include <error.h>
35 #include <stdint.h>
36 #include <stdio.h>
37 #include <stdlib.h>
38 #include <string.h>
39 
40 #include <linux/limits.h>
41 #include <unistd.h>
42 
43 #include "log.h"
44 #include "rl.h"
45 #include "rl_lib.h"
46 #include "version.h"
47 
51 #define ARGP_ARGUMENTS_COUNT 1
52 
53 #define OPT_FILE_SIZE 1
54 
55 #define OPT_SAMPLES_COUNT 2
56 
57 #define OPT_SET_DEFAULT 3
58 
59 #define OPT_RESET_DEFAULT 4
60 
61 #define OPT_CALIBRATION 5
62 
63 #define OPT_JSON 6
64 
65 #define OPT_CLI 7
66 
70 const char *argp_program_version = "RocketLogger Command Line Interface";
71 
75 const char *argp_program_bug_address = "<https://github.com/ETHZ-TEC/RocketLogger/issues>";
76 
80 static char doc[] =
81  "RocketLogger CLI -- manage your RocketLogger measurements.\n"
82  "Control or configure measurements using the actions specified by ACTION. "
83  "Supported values for ACTION are:\n"
84  "\n"
85  " Measurement control:\n"
86  " start\tStart a new measurement with provided configuration\n"
87  " stop\tStop measurement running in the background\n"
88  "\n"
89  " Measurement configuration and status management:\n"
90  " config\tDisplay configuration, not starting a new or affecting a "
91  "running measurement\n"
92  " status\tDisplay the current sampling status\n";
93 
97 static char args_doc[] = "ACTION";
98 
102 static struct argp_option options[] = {
103  {0, 0, 0, OPTION_DOC, "Basic measurement configuration options:", 1},
104  {"samples", OPT_SAMPLES_COUNT, "COUNT", 0,
105  "Number of samples to record before stopping measurement (k, M, G, T "
106  "scaling suffixes can be used).",
107  0},
108  {"channel", 'c', "SELECTION", 0, "Channel selection to sample. A comma "
109  "separated list of the channel names (V1, "
110  "V2, V3, V4, I1L, I1H, I2L, and/or I2H) "
111  "or 'all' to enable all channels.",
112  0},
113  {"rate", 'r', "RATE", 0, "Sampling rate in Hz. Supported values are: 1, "
114  "10, 100, 1k, 2k, 4k, 8k, 16k, 32k, 64k.",
115  0},
116  {"update", 'u', "RATE", 0,
117  "Measurement data update rate in Hz. Supported values: 1, 2, 5, 10.", 0},
118  {"output", 'o', "FILE", 0,
119  "Store data to specified file. Use zero to disable file storage.", 0},
120  {"interactive", 'i', 0, 0,
121  "Display measurement data in the command line interface.", 0},
122  {"background", 'b', 0, 0,
123  "Start measurement in the background and exit after start.", 0},
124 
125  {0, 0, 0, OPTION_DOC,
126  "Measurement configuration options for storing measurement files:", 3},
127  {"format", 'f', "FORMAT", 0, "Select file format: 'csv', 'rld'.", 0},
128  {"size", OPT_FILE_SIZE, "SIZE", 0,
129  "Select max file size (k, M, G, T scaling suffixes can be used).", 0},
130  {"comment", 'C', "COMMENT", 0, "Comment stored in file header. Comment is "
131  "ignored if file saving is disabled.",
132  0},
133 
134  {0, 0, 0, OPTION_DOC, "Setting and resetting the stored default:", 4},
135  {"default", OPT_SET_DEFAULT, 0, 0, "Set current configuration as default. "
136  "Supported for all measurement "
137  "configurations.",
138  0},
139  {"reset", OPT_RESET_DEFAULT, 0, 0,
140  "Reset default configuration to factory defaults and ignores any other "
141  "provided configuration without notice. Only allowed in combination with "
142  "the 'config' action.",
143  0},
144 
145  {0, 0, 0, OPTION_DOC, "Optional arguments for extended sampling features:",
146  5},
147  {"digital", 'd', "BOOL", OPTION_ARG_OPTIONAL,
148  "Enable logging of digital inputs. Enabled by default.", 0},
149  {"ambient", 'a', "BOOL", OPTION_ARG_OPTIONAL,
150  "Enable logging of ambient sensors, if available. Disabled by default.",
151  0},
152  {"aggregate", 'g', "MODE", 0, "Data aggregation mode for low sample rates. "
153  "Existing modes: 'average', 'downsample'.",
154  0},
155  {"high-range", 'h', "SELECTION", 0,
156  "Force high range measurements on selected channels. A comma separated "
157  "list of the channel names (I1H and/or I2H) or 'all' to force high range "
158  "measurements all channels. Inactive per default.",
159  0},
160  {"calibration", OPT_CALIBRATION, 0, 0, "Ignore existing calibration "
161  "values. Use this option for device "
162  "calibration measurements only.",
163  0},
164  {"web", 'w', "BOOL", OPTION_ARG_OPTIONAL,
165  "Enable web server plotting. Enabled per default.", 0},
166 
167  {0, 0, 0, OPTION_DOC, "Optional arguments for status and config actions:",
168  6},
169  {"json", OPT_JSON, 0, 0,
170  "Print configuration or status as JSON formatted string.", 0},
171  {"cli", OPT_CLI, 0, 0, "Print configuration as full CLI command.", 0},
172 
173  {0, 0, 0, OPTION_DOC, "Generic program switches:", 7},
174  {"verbose", 'v', 0, 0, "Produce verbose output", 0},
175  {"quiet", 'q', 0, 0, "Do not produce any output", 0},
176  {"silent", 's', 0, OPTION_ALIAS, 0, 0},
177 
178  {0, 0, 0, 0, 0, 0},
179 };
180 
184 struct arguments {
189  bool cli;
190  bool json;
191  bool silent;
192  bool verbose;
193 };
194 
195 /* local function declarations */
196 static error_t parse_opt(int key, char *arg, struct argp_state *state);
197 static void parse_bool(char const *arg, struct argp_state *state,
198  bool *const value);
199 static void parse_bool_named_list(char const *arg, struct argp_state *state,
200  char const *const *const names,
201  bool *const values, int size);
202 static void parse_uint32(char const *arg, struct argp_state *state,
203  uint32_t *const value);
204 static void parse_uint64(char const *arg, struct argp_state *state,
205  uint64_t *const value);
206 static void print_config(rl_config_t const *const config);
207 static void print_version(FILE *stream, struct argp_state *state);
208 
212 void (*argp_program_version_hook)(FILE *, struct argp_state *) = print_version;
213 
217 static struct argp argp = {
218  .options = options, .parser = parse_opt, .args_doc = args_doc, .doc = doc,
219 };
220 
224 static char const *const log_filename = RL_MEASUREMENT_LOG_FILE;
225 
233 int main(int argc, char *argv[]) {
234  rl_config_t config;
235 
236  // load default configuration
237  int res = rl_config_read_default(&config);
238  if (res < 0) {
239  error(EXIT_FAILURE, errno, "failed reading default configuration file");
240  }
241 
242  // argument structure with default values
243  struct arguments arguments = {
244  .args = {NULL},
245  .config = &config,
246  .config_reset = false,
247  .config_set_default = false,
248  .cli = false,
249  .json = false,
250  .silent = false,
251  .verbose = false,
252  };
253 
254  // parse CLI arguments and options using argp
255  int argp_status = argp_parse(&argp, argc, argv, 0, 0, &arguments);
256  if (argp_status != 0) {
257  error(0, argp_status, "argument parsing failed");
258  }
259 
260  // init log module with appropriate verbosity level
261  if (arguments.verbose) {
262  rl_log_init(log_filename, RL_LOG_VERBOSE);
263  } else if (arguments.silent) {
264  rl_log_init(log_filename, RL_LOG_IGNORE);
265  } else {
266  rl_log_init(log_filename, RL_LOG_WARNING);
267  }
268 
269  // set effective user ID of the process
270  int ret = setuid(0);
271  if (ret < 0) {
272  rl_log(RL_LOG_ERROR, "Failed setting effective user ID. %d message: %s",
273  errno, strerror(errno));
274  exit(EXIT_FAILURE);
275  }
276 
277  rl_log(RL_LOG_VERBOSE, "running with real user ID: %d", getuid());
278  rl_log(RL_LOG_VERBOSE, "running with effective user ID: %d", geteuid());
279  rl_log(RL_LOG_VERBOSE, "running with real group ID: %d", getgid());
280  rl_log(RL_LOG_VERBOSE, "running with effective group ID: %d", getegid());
281 
282 
283  char const *const action = arguments.args[0];
284 
285  // validate arguments
286  bool valid_action =
287  (strcmp(action, "start") == 0 || strcmp(action, "stop") == 0 ||
288  strcmp(action, "config") == 0 || strcmp(action, "status") == 0);
289  if (!valid_action) {
290  rl_log(RL_LOG_ERROR, "unknown action '%s'", action);
291  exit(EXIT_FAILURE);
292  }
293 
294  if (arguments.cli && arguments.json) {
295  rl_log(RL_LOG_ERROR, "cannot format in output as JSON and and CLI "
296  "string at the same time.");
297  exit(EXIT_FAILURE);
298  }
299 
300  // validate sampling configuration
301  int valid_config = rl_config_validate(&config);
302  if (valid_config < 0) {
303  rl_log(RL_LOG_ERROR, "invalid configuration, check message above");
304  exit(EXIT_FAILURE);
305  }
306 
307  // reset config if requested
308  if (arguments.config_reset) {
309  if (strcmp(action, "config") != 0) {
311  "the --reset option is only allowed for config action.");
312  exit(EXIT_FAILURE);
313  }
315  rl_log(RL_LOG_INFO, "Configuration was reset to factory default.");
316  }
317  // store config as default
320  if (!(arguments.silent || arguments.json | arguments.cli)) {
321  printf("The following configuration was saved as new default:\n");
323  }
324  }
325 
326  // configure and run system in the requested MODE
327  if (strcmp(action, "start") == 0) {
328  // check if already sampling
329  if (rl_is_sampling()) {
330  rl_log(RL_LOG_ERROR, "RocketLogger is still running.\n"
331  "Stop with `rocketlogger stop` first.\n");
332  exit(EXIT_FAILURE);
333  }
334 
335  if (arguments.verbose) {
336  print_config(&config);
337  }
338  rl_log(RL_LOG_INFO, "Starting measurement...\n");
339  rl_run(&config);
340  }
341  if (strcmp(action, "stop") == 0) {
342  // exit with error if not sampling
343  if (!rl_is_sampling()) {
344  rl_log(RL_LOG_ERROR, "RocketLogger is not running.\n");
345  exit(EXIT_FAILURE);
346  }
347 
348  if (!arguments.silent) {
349  printf("Wait for measurement to stop...\n");
350  }
351  rl_stop();
352  }
353  if (strcmp(action, "config") == 0) {
354  if (arguments.json) {
356  } else if (arguments.cli) {
358  } else {
360  }
361  }
362  if (strcmp(action, "status") == 0) {
363  rl_status_t status;
364  int res = rl_get_status(&status);
365  if (res < 0) {
366  rl_log(RL_LOG_ERROR, "Failed getting RocketLogger status (%d).\n",
367  res);
368  exit(EXIT_FAILURE);
369  }
370  if (arguments.json) {
371  rl_status_print_json(&status);
372  } else {
373  rl_status_print(&status);
374  }
375  }
376  exit(EXIT_SUCCESS);
377 }
378 
387 static error_t parse_opt(int key, char *arg, struct argp_state *state) {
388  // get pointer to where run configuration is stored
389  struct arguments *arguments = state->input;
391 
392  // parse actual argument
393  switch (key) {
394  /* options with shortcuts */
395  case 'q':
396  case 's':
397  /* quiet/silent switch: no value */
398  arguments->silent = true;
399  arguments->verbose = false;
400  break;
401  case 'v':
402  /* verbose switch: no value */
403  arguments->verbose = true;
404  arguments->silent = false;
405  break;
406  case 'b':
407  /* run in background: no value */
408  config->background_enable = true;
409  break;
410  case 'i':
411  /* display measurements interactively: no value */
412  config->interactive_enable = true;
413  break;
414  case 'c':
415  /* channel selection: mandatory SELECTION value */
416  parse_bool_named_list(arg, state, RL_CHANNEL_NAMES,
418  break;
419  case 'r':
420  /* sampling rate: mandatory RATE value */
421  parse_uint32(arg, state, &config->sample_rate);
422  break;
423  case 'u':
424  /* measurement update rate: mandatory RATE value */
425  parse_uint32(arg, state, &config->update_rate);
426  break;
427  case 'o':
428  /* measurement output file: mandatory FILE value */
429  if (arg != NULL) {
430  if (strlen(arg) == 1 && arg[0] == '0') {
431  config->file_enable = false;
432  } else {
433  config->file_enable = true;
434  strncpy(config->file_name, arg, PATH_MAX - 1);
435  }
436  } else {
437  argp_usage(state);
438  }
439  break;
440  case 'f':
441  /* measurement file format: mandatory FORMAT value */
442  if (strcmp(arg, "csv") == 0 || strcmp(arg, "CSV") == 0) {
444  } else if (strcmp(arg, "rld") == 0 || strcmp(arg, "RLD") == 0) {
446  } else {
447  argp_usage(state);
448  }
449  break;
450  case 'C':
451  /* measurement file comment: mandatory COMMENT value */
452  if (arg != NULL) {
453  config->file_comment = arg;
454  } else {
455  argp_usage(state);
456  }
457  break;
458  case 'd':
459  /* digital channel: optional BOOL value */
460  if (arg != NULL) {
461  parse_bool(arg, state, &config->digital_enable);
462  } else {
463  config->digital_enable = true;
464  }
465  break;
466  case 'a':
467  /* ambient sensors: optional BOOL value */
468  if (arg != NULL) {
469  parse_bool(arg, state, &config->ambient_enable);
470  } else {
471  config->ambient_enable = true;
472  }
473  break;
474  case 'g':
475  /* data aggregation mode: mandatory MODE value */
476  if (strcmp(arg, "downsample") == 0) {
478  } else if (strcmp(arg, "average") == 0) {
480  } else {
481  argp_usage(state);
482  }
483  break;
484  case 'h':
485  /* force high-range current measurement: mandatory SELECTION value */
486  parse_bool_named_list(arg, state, RL_CHANNEL_FORCE_NAMES,
489  break;
490  case 'w':
491  /* web interface enable: optional BOOL value */
492  if (arg != NULL) {
493  parse_bool(arg, state, &config->web_enable);
494  } else {
495  config->web_enable = true;
496  }
497  break;
498 
499  /* options without shortcuts */
500  case OPT_SAMPLES_COUNT:
501  /* sample count: mandatory COUNT value */
502  parse_uint64(arg, state, &config->sample_limit);
503  break;
504  case OPT_FILE_SIZE:
505  /* maximum file size: mandatory SIZE value */
506  parse_uint64(arg, state, &config->file_size);
507  break;
508  case OPT_CLI:
509  /* CLI format the config output: no value */
510  arguments->cli = true;
511  break;
512  case OPT_JSON:
513  /* JSON format the status and config output: no value */
514  arguments->json = true;
515  break;
516  case OPT_SET_DEFAULT:
517  /* set configuration as default: no value */
519  break;
520  case OPT_RESET_DEFAULT:
521  /* reset configuration to system default: no value */
522  arguments->config_reset = true;
523  break;
524  case OPT_CALIBRATION:
525  /* perform calibration measurement: no value */
526  config->calibration_ignore = true;
527  break;
528 
529  /* unnamed argument options */
530  case ARGP_KEY_ARG:
531  // check for too many arguments
532  if (state->arg_num >= ARGP_ARGUMENTS_COUNT) {
533  argp_usage(state);
534  }
535  arguments->args[state->arg_num] = arg;
536  break;
537  case ARGP_KEY_END:
538  // check for not enough arguments
539  if (state->arg_num < ARGP_ARGUMENTS_COUNT) {
540  argp_usage(state);
541  }
542  break;
543  default:
544  return ARGP_ERR_UNKNOWN;
545  }
546 
547  return 0;
548 }
549 
559 static void parse_bool(char const *arg, struct argp_state *state,
560  bool *const value) {
561  if (strlen(arg) == 1) {
562  if (arg[0] == '0') {
563  *value = false;
564  return;
565  } else if (arg[0] == '1') {
566  *value = true;
567  return;
568  }
569  }
570  if (strcmp(arg, "true") == 0 || strcmp(arg, "True") == 0 ||
571  strcmp(arg, "TRUE") == 0) {
572  *value = true;
573  return;
574  }
575  if (strcmp(arg, "false") == 0 || strcmp(arg, "False") == 0 ||
576  strcmp(arg, "FALSE") == 0) {
577  *value = false;
578  return;
579  }
580  argp_usage(state);
581 }
582 
596 static void parse_bool_named_list(char const *arg, struct argp_state *state,
597  char const *const *const names,
598  bool *const values, int size) {
599  // check for all enable argument
600  if (strcmp(arg, "all") == 0) {
601  memset(values, true, size * sizeof(bool));
602  return;
603  }
604  // reset values
605  memset(values, false, size * sizeof(bool));
606 
607  // split input by comma
608  char const *split_pos = arg;
609  while (*arg != '\0') {
610  // find next argument name
611  split_pos = strchr(arg, ',');
612  char arg_name[16] = {0};
613  if (split_pos == NULL) {
614  strncpy(arg_name, arg, sizeof(arg_name) - 1);
615  arg = arg + strlen(arg); // set to end to exit loop when done
616  } else {
617  strncpy(arg_name, arg, split_pos - arg);
618  arg = split_pos + 1; // next argument starts right after comma
619  }
620 
621  // convert parsed name
622  char *ptr = arg_name;
623  do {
624  *ptr = toupper(*ptr);
625  } while (*ptr++);
626 
627  // process channel name
628  int i = 0;
629  while (i < size) {
630  if (strcmp(arg_name, names[i]) == 0) {
631  values[i] = true;
632  break;
633  }
634  i++;
635  }
636  // check valid channel was set
637  if (i == size) {
638  argp_usage(state);
639  return;
640  }
641  }
642 }
643 
653 static void parse_uint32(char const *arg, struct argp_state *state,
654  uint32_t *const value) {
655  uint64_t temp;
656  parse_uint64(arg, state, &temp);
657  if ((temp >> 32) == 0) {
658  *value = (uint32_t)temp;
659  } else {
660  argp_usage(state);
661  }
662 }
663 
673 static void parse_uint64(char const *arg, struct argp_state *state,
674  uint64_t *const value) {
675  char *suffix = NULL;
676  *value = strtoull(arg, &suffix, 10);
677 
678  // check for scaling suffix and apply it iteratively
679  if (suffix != NULL) {
680  switch (*suffix) {
681  case 'T':
682  *value = *value * 1000;
683  /* FALL THROUGH */
684  case 'G':
685  *value = *value * 1000;
686  /* FALL THROUGH */
687  case 'M':
688  *value = *value * 1000;
689  /* FALL THROUGH */
690  case 'k':
691  *value = *value * 1000;
692  /* FALL THROUGH */
693  case '\0':
694  break;
695  default:
696  argp_usage(state);
697  return;
698  }
699  } else {
700  argp_usage(state);
701  return;
702  }
703 }
704 
711 static void print_version(FILE *stream, struct argp_state *state) {
712  (void)state; // suppress unused parameter warning
713  fprintf(stream, "%s %s\n", argp_program_version, PROJECT_VERSION);
714  fprintf(stream, " git@%s (%s)\n", GIT_DESCRIPTION, GIT_DATE);
715  fprintf(stream, " compiled at %s\n", COMPILE_DATE);
716 }
717 
723 static void print_config(rl_config_t const *const config) {
724  printf("\nRocketLogger Configuration:\n");
726  printf("\n");
727 }
int rl_log(rl_log_level_t log_level, char const *const format,...)
Definition: log.c:82
int rl_log_init(char const *const log_file, rl_log_level_t verbosity)
Definition: log.c:49
@ RL_LOG_ERROR
Error.
Definition: log.h:49
@ RL_LOG_WARNING
Warning.
Definition: log.h:50
@ RL_LOG_VERBOSE
Verbose.
Definition: log.h:52
@ RL_LOG_INFO
Information.
Definition: log.h:51
@ RL_LOG_IGNORE
Ignore log (only for verbosity configuration)
Definition: log.h:48
char const *const RL_CHANNEL_NAMES[RL_CHANNEL_COUNT]
RocketLogger channel names.
Definition: rl.c:95
void rl_status_print_json(rl_status_t const *const status)
Definition: rl.c:731
char const *const RL_CHANNEL_FORCE_NAMES[RL_CHANNEL_SWITCHED_COUNT]
RocketLogger force range channel names.
Definition: rl.c:99
void rl_config_print_json(rl_config_t const *const config)
Definition: rl.c:275
void rl_config_reset(rl_config_t *const config)
Definition: rl.c:360
void rl_config_print(rl_config_t const *const config)
Definition: rl.c:142
int rl_config_read_default(rl_config_t *const config)
Definition: rl.c:364
int rl_config_write_default(rl_config_t const *const config)
Definition: rl.c:414
int rl_config_validate(rl_config_t const *const config)
Definition: rl.c:431
void rl_status_print(rl_status_t const *const status)
Definition: rl.c:709
void rl_config_print_cmd(rl_config_t const *const config)
Definition: rl.c:211
@ RL_FILE_FORMAT_RLD
CSV format.
Definition: rl.h:143
@ RL_FILE_FORMAT_CSV
Definition: rl.h:142
#define RL_CHANNEL_SWITCHED_COUNT
Number of RocketLogger switched channels (allowing to force range)
Definition: rl.h:58
#define RL_MEASUREMENT_LOG_FILE
RocketLogger measurement log file.
Definition: rl.h:49
#define RL_CHANNEL_COUNT
Number of RocketLogger analog channels.
Definition: rl.h:56
@ RL_AGGREGATION_MODE_AVERAGE
Aggregate using down sampling.
Definition: rl.h:130
@ RL_AGGREGATION_MODE_DOWNSAMPLE
Definition: rl.h:129
int rl_stop(void)
Definition: rl_lib.c:185
bool rl_is_sampling(void)
Definition: rl_lib.c:59
int rl_run(rl_config_t *const config)
Definition: rl_lib.c:100
int rl_get_status(rl_status_t *const status)
Definition: rl_lib.c:77
int main(int argc, char *argv[])
Definition: rocketlogger.c:233
#define OPT_SAMPLES_COUNT
Definition: rocketlogger.c:55
#define OPT_JSON
Definition: rocketlogger.c:63
const char * argp_program_version
Definition: rocketlogger.c:70
#define OPT_SET_DEFAULT
Definition: rocketlogger.c:57
#define ARGP_ARGUMENTS_COUNT
Definition: rocketlogger.c:51
#define OPT_CLI
Definition: rocketlogger.c:65
#define OPT_RESET_DEFAULT
Definition: rocketlogger.c:59
const char * argp_program_bug_address
Definition: rocketlogger.c:75
#define OPT_CALIBRATION
Definition: rocketlogger.c:61
void(* argp_program_version_hook)(FILE *, struct argp_state *)
Definition: rocketlogger.c:212
#define OPT_FILE_SIZE
Definition: rocketlogger.c:53
rl_config_t * config
program arguments
Definition: rocketlogger.c:186
bool config_set_default
whether to reset the stored default config
Definition: rocketlogger.c:188
bool verbose
flag for silent output
Definition: rocketlogger.c:192
bool cli
whether to save provided config as default
Definition: rocketlogger.c:189
bool json
flag for CLI command formatted config output
Definition: rocketlogger.c:190
bool silent
flag for JSON formatted output
Definition: rocketlogger.c:191
char * args[ARGP_ARGUMENTS_COUNT]
Definition: rocketlogger.c:185
bool config_reset
pointer to sampling configuration
Definition: rocketlogger.c:187
Definition: rl.h:154
bool channel_force_range[RL_CHANNEL_SWITCHED_COUNT]
Current channels to force to high range.
Definition: rl.h:170
char file_name[PATH_MAX]
Data file name.
Definition: rl.h:184
bool interactive_enable
Display measurement data interactively in CLI while sampling.
Definition: rl.h:160
char const * file_comment
File comment.
Definition: rl.h:190
uint64_t file_size
Maximum data file size.
Definition: rl.h:188
rl_aggregation_mode_t aggregation_mode
Sample aggregation mode (for sampling rates below lowest native one)
Definition: rl.h:172
bool background_enable
Put the measurement process in background after successful start.
Definition: rl.h:158
bool ambient_enable
Enable logging of ambient sensor.
Definition: rl.h:180
uint64_t sample_limit
Sample limit (0 for continuous)
Definition: rl.h:162
bool web_enable
Enable web interface connection.
Definition: rl.h:176
rl_file_format_t file_format
File format.
Definition: rl.h:186
uint32_t sample_rate
Sampling rate.
Definition: rl.h:164
bool channel_enable[RL_CHANNEL_COUNT]
Channels to sample.
Definition: rl.h:168
uint32_t update_rate
Data update rate.
Definition: rl.h:166
bool file_enable
Enable storing measurements to file.
Definition: rl.h:182
bool digital_enable
Enable digital inputs.
Definition: rl.h:174
bool calibration_ignore
Perform calibration measurement (ignore existing calibration)
Definition: rl.h:178
Definition: rl.h:201
char const *const COMPILE_DATE
Compilation date of the program.
char const *const PROJECT_VERSION
The RocketLogger software version string.
char const *const GIT_DESCRIPTION
Git code revision description of the code base.
char const *const GIT_DATE
Date of the of last git commit.