rpi_led_matrix/
args.rs

1//! Provides functions to add arguments to control various parameters of your
2//! RGB LED matrix.
3use crate::options::{LedMatrixOptions, LedRuntimeOptions};
4use clap::{arg, App};
5
6/// Given a clap App, adds arguments specific to the matrix initialization and returns
7/// a new [`App`](clap::App).
8#[must_use]
9#[allow(clippy::cognitive_complexity)]
10pub fn add_matrix_args(app: App<'static>) -> App<'static> {
11    app
12    .arg(
13        arg!(
14            --"gpio-mapping" <name> "'Name of GPIO mapping used'")
15            .default_value("Regular").required(false))
16    .arg(
17        arg!(
18            --rows <rows> "Panel rows. Typically 8, 16, 32 or 64")
19            .default_value("32").required(false))
20    .arg(
21        arg!(
22            --cols <cols> "Panel columns. Typically 32 or 64")
23            .default_value("32").required(false))
24    .arg(
25        arg!(
26            --chain <chained> "Number of daisy-chained panels")
27            .default_value("1").required(false))
28    .arg(
29        arg!(
30            --parallel <parallel> "Parallel chains. range=1..3")
31            .default_value("1").required(false))
32    .arg(
33        arg!(
34            --multiplexing <VAL> "[0,16] Mux type: 0=direct, 1=Stripe, 2=Checkered, 3=Spiral, 4=ZStripe, 5=ZnMirrorZStripe, 6=coreman, 7=Kaler2Scan, 8=ZStripeUneven, 9=P10-128x4-Z, 10=QiangLiQ8, 11=InversedZStripe, 12=P10Outdoor1R1G1-1, 13=P10Outdoor1R1G1-2, 14=P10Outdoor1R1G1-3, 15=P10CoremanMapper, 16=P8Outdoor1R1G1")
35            .default_value("0").required(false))
36    .arg(
37        arg!(
38            --"pixel-mapper" <VAL> "Semicolon-separated list of pixel-mappers to arrange pixels. Optional params after a colon e.g. \"U-mapper;Rotate:90\"\"Available: \"Mirror\", \"Rotate\", \"U-mapper\", \"V-mapper\"")
39            .default_value("").required(false))
40    .arg(
41        arg!(
42            --"pwm-bits" <VAL> "[1,11] PWM bits")
43            .default_value("11").required(false))
44    .arg(
45        arg!(
46            --brightness <percent> "Brightness in percent")
47            .default_value("100").required(false))
48    .arg(
49        arg!(
50            --"scan-mode" <VAL> "0 = progressive; 1 = interlaced")
51            .default_value("0").required(false))
52    .arg(
53        arg!(
54            --"row-addr-type" <VAL> "0 = default; 1 = AB-addressed panels; 2 = direct row select; 3 = ABC-addressed panels; 4 = ABC Shift + DE direct")
55            .default_value("0").required(false))
56    .arg(
57        arg!(
58            --"limit-refresh" <Hz> "Limit refresh rate to this frequency in Hz. Useful to keep a constant refresh rate on loaded system. 0=no limit")
59            .default_value("0").required(false))
60    .arg(
61        arg!(
62            --"rgb-sequence" <SEQ> "Switch if your matrix has led colors swapped")
63            .default_value("RGB").required(false))
64    .arg(
65        arg!(
66            --"pwm-lsb-nanoseconds" <ns> "PWM Nanoseconds for LSB")
67            .default_value("130").required(false))
68    .arg(
69        arg!(
70            --"pwm-dither-bits" <VAL> "[0,2] Time dithering of lower bits")
71            .default_value("0").required(false))
72    .arg(
73        arg!(
74            --"panel-type" <name> "Needed to initialize special panels. Supported: 'FM6126A', 'FM6127'")
75            .default_value("").required(false))
76    .arg(
77        arg!(
78            --"slowdown-gpio" <VAL> "[0,4] Slowdown GPIO. Needed for faster Pis/slower panels")
79            .default_value("1").required(false))
80
81    // Flags
82    .arg(
83        arg!(
84            --"show-refresh" "Show refresh rate"))
85    .arg(
86        arg!(
87            --inverse "Switch if your matrix has inverse colors on"))
88    .arg(
89        arg!(
90            --"no-hardware-pulse" "Don't use hardware pin-pulse generation"))
91    .arg(
92        arg!(
93            --daemon "Make the process run in the background as daemon"))
94    .arg(
95        arg!(
96            --"no-drop-privs" "Don't drop privileges from 'root' after initializing the hardware"))
97}
98
99/// Given the parsed matches, returns `(LedMatrixOptions, LedRuntimeOptions)`
100///
101/// # Panics
102/// If the values we try to parse out are invalid from any of the arguments.
103#[must_use]
104#[rustfmt::skip]
105pub fn matrix_options_from_args(
106    parsed_args: &clap::ArgMatches,
107) -> (LedMatrixOptions, LedRuntimeOptions) {
108    let mut options = LedMatrixOptions::new();
109    let mut rt_options = LedRuntimeOptions::new();
110
111    let gpio_mapping = parsed_args.value_of("gpio-mapping").expect("Invalid value given for gpio_mapping");
112    let rows: u32 = parsed_args.value_of_t("rows").expect("Invalid value given for rows");
113    let cols: u32 = parsed_args.value_of_t("cols").expect("Invalid value given for cols");
114    let chain: u32 = parsed_args.value_of_t("chain").expect("Invalid value given for chain");
115    let parallel: u32 = parsed_args.value_of_t("parallel").expect("Invalid value given for parallel");
116    let multiplexing: u32 = parsed_args.value_of_t("multiplexing").expect("Invalid value given for multiplexing");
117    let pixel_mapper = parsed_args.value_of("pixel-mapper").expect("Invalid value given for pixel_mapper");
118    let pwm_bits: u8 = parsed_args.value_of_t("pwm-bits").expect("Invalid value given for pwm_bits");
119    let brightness: u8 = parsed_args.value_of_t("brightness").expect("Invalid value given for brightness");
120    let scan_mode: u32 = parsed_args.value_of_t("scan-mode").expect("Invalid value given for scan_mode");
121    let row_addr_type: u32 = parsed_args.value_of_t("row-addr-type").expect("Invalid value given for row_addr_type");
122    let limit_refresh: u32 = parsed_args.value_of_t("limit-refresh").expect("Invalid value given for limit_refresh");
123    let rgb_sequence = parsed_args.value_of("rgb-sequence").expect("Invalid value given for rgb_sequence");
124    let pwm_lsb_nanoseconds: u32 = parsed_args.value_of_t("pwm-lsb-nanoseconds").expect("Invalid value given for pwm_lsb_nanoseconds");
125    let pwm_dither_bits: u32 = parsed_args.value_of_t("pwm-dither-bits").expect("Invalid value given for pwm_dither_bits");
126    let panel_type = parsed_args.value_of("panel-type").expect("Invalid value given for panel_type");
127    let slowdown_gpio: u32 = parsed_args.value_of_t("slowdown-gpio").expect("Invalid value given for slowdown_gpio");
128
129    // flags
130    let show_refresh: bool = parsed_args.is_present("show-refresh");
131    let inverse: bool = parsed_args.is_present("inverse");
132    let no_hardware_pulse: bool = parsed_args.is_present("no-hardware-pulse");
133    let daemon: bool = parsed_args.is_present("daemon");
134    let no_drop_privs: bool = parsed_args.is_present("no-drop-privs");
135
136    options.set_hardware_mapping(gpio_mapping);
137    options.set_rows(rows);
138    options.set_cols(cols);
139    options.set_chain_length(chain);
140    options.set_parallel(parallel);
141    options.set_multiplexing(multiplexing);
142    options.set_pixel_mapper_config(pixel_mapper);
143    options.set_pwm_bits(pwm_bits).unwrap();
144    options.set_brightness(brightness).unwrap();
145    options.set_scan_mode(scan_mode);
146    options.set_row_addr_type(row_addr_type);
147    options.set_limit_refresh(limit_refresh);
148    options.set_led_rgb_sequence(rgb_sequence);
149    options.set_pwm_lsb_nanoseconds(pwm_lsb_nanoseconds);
150    options.set_pwm_dither_bits(pwm_dither_bits);
151    options.set_panel_type(panel_type);
152
153    options.set_hardware_pulsing(!no_hardware_pulse);
154    options.set_refresh_rate(show_refresh);
155    options.set_inverse_colors(inverse);
156
157    // Part of RuntimeOptions - not accessable in C-based API
158    rt_options.set_gpio_slowdown(slowdown_gpio);
159    rt_options.set_daemon(daemon);
160    rt_options.set_drop_privileges(!no_drop_privs);
161
162    (options, rt_options)
163}
164
165#[cfg(test)]
166mod tests {
167    use super::*;
168    use clap::App;
169
170    #[test]
171    #[serial_test::serial]
172    fn matrix_args_add() {
173        let app = add_matrix_args(App::new("test"));
174        let matches = app.get_matches_from(vec!["app"]);
175        let _slowdown: u32 = matches.value_of_t("slowdown-gpio").unwrap();
176    }
177
178    #[test]
179    #[serial_test::serial]
180    fn matrix_args_clap_basic() {
181        let app = add_matrix_args(App::new("test"));
182        let matches = app.get_matches_from(vec!["app", "--limit-refresh", "42"]);
183        let slowdown: u32 = matches.value_of_t("limit-refresh").unwrap();
184        assert_eq!(slowdown, 42);
185    }
186
187    #[test]
188    #[serial_test::serial]
189    fn matrix_args_to_options() {
190        let app = add_matrix_args(App::new("test"));
191        let matches = app.get_matches_from(vec!["app", "--pwm-dither-bits", "42"]);
192        let (options, _rt_options) = matrix_options_from_args(&matches);
193        assert_eq!(options.0.pwm_dither_bits, 42);
194    }
195
196    #[test]
197    #[serial_test::serial]
198    fn matrix_args_to_rt_options() {
199        let app = add_matrix_args(App::new("test"));
200        let matches = app.get_matches_from(vec!["app", "--slowdown-gpio", "4"]);
201        let (_options, rt_options) = matrix_options_from_args(&matches);
202        assert_eq!(rt_options.0.gpio_slowdown, 4);
203    }
204
205    #[test]
206    #[serial_test::serial]
207    fn matrix_args_to_rt_options_flag() {
208        let app = add_matrix_args(App::new("test"));
209        let matches = app.get_matches_from(vec!["app", "--daemon"]);
210        let (_options, rt_options) = matrix_options_from_args(&matches);
211        assert_eq!(rt_options.0.daemon, 1);
212    }
213}