dashboard/mode/
running.rs

1use eg_seven_segment::SevenSegmentStyleBuilder;
2use embedded_graphics::prelude::Transform;
3use embedded_graphics::prelude::WebColors;
4use embedded_graphics::primitives::PrimitiveStyle;
5use embedded_graphics::primitives::{Rectangle, StyledDrawable};
6
7use embedded_graphics::text::renderer::CharacterStyle;
8use embedded_graphics::{
9    Drawable,
10    geometry::AnchorX,
11    pixelcolor::Rgb666,
12    prelude::{Point, RgbColor, Size},
13    text::{Alignment, Text},
14};
15
16use super::init_running::{
17    BATT_HEIGHT, BATT_POS, BATT_WIDTH, EFF_FONT_HEIGHT, EFF_FONT_WIDTH, EFF_POS, SPEED_FONT_HEIGHT,
18    SPEED_FONT_WIDTH,
19};
20use crate::display_mod::{CENTER_POINT, DisplayDevice};
21
22fn greater_than_10(val: u32) -> bool {
23    val >= 10
24}
25
26fn render_speed_widgets(display: &mut DisplayDevice, speed: u32, prev_speed: u32) {
27    const DIGIT_SPACING: u32 = 4;
28    let speed_style = SevenSegmentStyleBuilder::new()
29        .digit_size(Size::new(SPEED_FONT_WIDTH, SPEED_FONT_HEIGHT))
30        .digit_spacing(DIGIT_SPACING)
31        .segment_width(6)
32        .segment_color(Rgb666::RED)
33        .inactive_segment_color(Rgb666::BLACK)
34        .build();
35    let mut clear_style = speed_style.clone();
36    clear_style.set_text_color(Some(Rgb666::BLACK));
37
38    const SPEED_POS: Point = Point::new(
39        CENTER_POINT.x + SPEED_FONT_WIDTH as i32,
40        CENTER_POINT.y + SPEED_FONT_HEIGHT as i32 / 2,
41    );
42    const CLEAR_TEXT_POS: Point = Point::new(
43        CENTER_POINT.x - (DIGIT_SPACING as i32),
44        CENTER_POINT.y + SPEED_FONT_HEIGHT as i32 / 2,
45    );
46
47    let mut str_buffer = itoa::Buffer::new();
48    let speed_str = str_buffer.format(speed);
49
50    // Clear dead digits
51    if greater_than_10(prev_speed) && !greater_than_10(speed) {
52        Text::with_alignment("8", CLEAR_TEXT_POS, clear_style, Alignment::Right)
53            .draw(display)
54            .unwrap();
55    }
56    // Render Speed
57    Text::with_alignment(speed_str, SPEED_POS, speed_style, Alignment::Right)
58        .draw(display)
59        .unwrap();
60}
61
62fn render_tach_widgets(display: &mut DisplayDevice, rpm: u32, _prev_rpm: u32) {
63    // Define Styles
64    let tach_line_width = 3;
65
66    // The number of tach lines per 1000rpm
67    let tach_lines = 5;
68    // Maximum RPM Represented is 5000rpm
69    let max_tach_lines = tach_lines * 5;
70
71    let tach_empty_style = PrimitiveStyle::with_fill(Rgb666::CSS_SILVER);
72
73    let tach_line_style = PrimitiveStyle::with_fill(Rgb666::RED);
74    let tach_line = Rectangle::new(
75        CENTER_POINT.x_axis() - Point::new(max_tach_lines * tach_line_width * 2, -15),
76        Size::new(tach_line_width as u32, 55),
77    );
78
79    let tach_divider_style = PrimitiveStyle::with_fill(Rgb666::CSS_DEEP_PINK);
80    let tach_divider_line = tach_line.resized_width(tach_line_width as u32 + 2, AnchorX::Left);
81
82    // Render Tachometer
83    // Determines the distance between tachometer bars
84    let tach_spacer = 4;
85    // Maximum RPM Represented is 5000rpm
86    let display_rpm = ((rpm as f32 / 5000f32) * max_tach_lines as f32) as i32;
87    for i in 0..=display_rpm {
88        let (bar, bar_style) = if (i % tach_lines) == 0 {
89            (tach_divider_line, tach_divider_style)
90        } else {
91            (tach_line, tach_line_style)
92        };
93        bar.translate(Point::new(i * tach_line_width as i32 * tach_spacer, 0))
94            .draw_styled(&bar_style, display)
95            .unwrap();
96    }
97    for i in (display_rpm + 1)..=max_tach_lines {
98        let tach_line = if (i % tach_lines) == 0 {
99            tach_divider_line
100        } else {
101            tach_line
102        };
103        tach_line
104            .translate(Point::new(i * tach_line_width as i32 * tach_spacer, 0))
105            .draw_styled(&tach_empty_style, display)
106            .unwrap();
107    }
108}
109
110fn render_efficiency_gui(display: &mut DisplayDevice, efficiency: u8, prev_efficiency: u8) {
111    const DIGIT_SPACING: u32 = 2;
112    let eff_style = SevenSegmentStyleBuilder::new()
113        .digit_size(Size::new(EFF_FONT_WIDTH, EFF_FONT_HEIGHT))
114        .digit_spacing(DIGIT_SPACING)
115        .segment_width(3)
116        .segment_color(Rgb666::GREEN)
117        .inactive_segment_color(Rgb666::BLACK)
118        .build();
119    let mut clear_style = eff_style.clone();
120    clear_style.set_text_color(Some(Rgb666::BLACK));
121
122    let mut str_buffer = itoa::Buffer::new();
123    let efficiency_str = str_buffer.format(efficiency);
124
125    const EFF_TEXT_POS: Point = Point::new(
126        EFF_POS.x + EFF_FONT_WIDTH as i32,
127        EFF_POS.y + EFF_FONT_HEIGHT as i32 / 2,
128    );
129    const CLEAR_TEXT_POS: Point = Point::new(
130        EFF_POS.x - (DIGIT_SPACING as i32),
131        EFF_POS.y + EFF_FONT_HEIGHT as i32 / 2,
132    );
133
134    // Clear Dead Digits
135    if prev_efficiency >= 100 && efficiency < 100 {
136        Text::with_alignment("88", CLEAR_TEXT_POS, clear_style, Alignment::Right)
137            .draw(display)
138            .unwrap();
139    } else if prev_efficiency >= 10 && efficiency < 10 {
140        Text::with_alignment("8", CLEAR_TEXT_POS, clear_style, Alignment::Right)
141            .draw(display)
142            .unwrap();
143    }
144    // Render Efficiency
145    Text::with_alignment(efficiency_str, EFF_TEXT_POS, eff_style, Alignment::Right)
146        .draw(display)
147        .unwrap();
148}
149
150fn render_battery_gui(display: &mut DisplayDevice, battery_health: u8, prev_battery_health: u8) {
151    let mut str_buffer = itoa::Buffer::new();
152    let battery_health_str = str_buffer.format(battery_health);
153
154    let clear_style = PrimitiveStyle::with_fill(Rgb666::BLACK);
155    let fill_style = PrimitiveStyle::with_fill(Rgb666::GREEN);
156
157    const BATT_FONT_WIDTH: u32 = 10;
158    const BATT_FONT_HEIGHT: u32 = 20;
159
160    const DIGIT_SPACING: u32 = 2;
161    let batt_text_style = SevenSegmentStyleBuilder::new()
162        .digit_size(Size::new(BATT_FONT_WIDTH, BATT_FONT_HEIGHT))
163        .digit_spacing(DIGIT_SPACING)
164        .segment_width(2)
165        .segment_color(Rgb666::WHITE)
166        .inactive_segment_color(Rgb666::BLACK)
167        .build();
168    let mut clear_text_style = batt_text_style.clone();
169    clear_text_style.set_text_color(Some(Rgb666::BLACK));
170
171    const BATT_TEXT_POS: Point = Point::new(
172        BATT_POS.x - 1 * (BATT_WIDTH / 2 + BATT_FONT_WIDTH) as i32,
173        BATT_POS.y + 40,
174    );
175    const CLEAR_TEXT_POS: Point = Point::new(
176        BATT_POS.x - 1 * (BATT_WIDTH / 2 + BATT_FONT_WIDTH * 2) as i32 - DIGIT_SPACING as i32,
177        BATT_POS.y + 40,
178    );
179
180    let battery_level = ((100 - battery_health) as f32 / 100f32 * BATT_HEIGHT as f32) as u32;
181
182    let batt_outline = Rectangle::new(BATT_POS, Size::new(BATT_WIDTH, battery_level));
183    let batt_fill = Rectangle::with_corners(
184        BATT_POS + Point::new(0, battery_level as i32),
185        BATT_POS + Size::new(BATT_WIDTH - 1, BATT_HEIGHT),
186    );
187
188    // Render Battery Rating
189    batt_outline.draw_styled(&clear_style, display).unwrap();
190    batt_fill.draw_styled(&fill_style, display).unwrap();
191
192    // Clear Dead Digits
193    if prev_battery_health >= 100 && battery_health < 100 {
194        Text::with_alignment("88", CLEAR_TEXT_POS, clear_text_style, Alignment::Right)
195            .draw(display)
196            .unwrap();
197    } else if prev_battery_health >= 10 && battery_health < 10 {
198        Text::with_alignment("8", CLEAR_TEXT_POS, clear_text_style, Alignment::Right)
199            .draw(display)
200            .unwrap();
201    }
202    // Render Battery Percentage
203    Text::with_alignment(
204        battery_health_str,
205        BATT_TEXT_POS,
206        batt_text_style,
207        Alignment::Right,
208    )
209    .draw(display)
210    .unwrap();
211}
212
213pub fn render_running_gui(display: &mut DisplayDevice) {
214    ///////////////////////////////
215    // Render Graphics
216    ///////////////////////////////
217    let prev_rpm = 1500;
218    let prev_speed = 20;
219
220    let rpm = 1500;
221    let speed = 20;
222    render_tach_widgets(display, rpm as u32, prev_rpm as u32);
223    render_speed_widgets(display, speed as u32, prev_speed as u32);
224    render_efficiency_gui(display, 50, 50);
225    render_battery_gui(display, 50, 50);
226}