~ruther/simple-clock

d6c87cb811c8551e769a19bc6e921ed6ff55c8dc — František Boháček 1 year, 10 months ago 4a0e2e8
feat(src): replace DisplayView with hide, show on clock parts

The clock is meant to show only clock, so introducing
DisplayView trait was an overkill, a simpler
solution has been implemented instead.

Previous solution did not allow for showing and hiding individual parts,
hiding parts may be useful when setting time, to blink the current part
being set.
8 files changed, 167 insertions(+), 211 deletions(-)

M source/src/clock_app.rs
M source/src/clock_display.rs
M source/src/clock_display_viewer.rs
D source/src/display_view.rs
D source/src/display_view/clock_display_view.rs
D source/src/display_view/date_display_view.rs
M source/src/lib.rs
M source/src/main.rs
M source/src/clock_app.rs => source/src/clock_app.rs +9 -4
@@ 5,7 5,7 @@ use stm32f1xx_hal::rtc::Rtc;

use crate::{
    brightness_manager::BrightnessManager, button::ButtonState,
    clock_display_viewer::ClockDisplayViewer, clock_state::ClockState, display_view::DisplayViews,
    clock_display_viewer::{ClockDisplayViewer, DisplayView}, clock_state::ClockState,
};

pub struct ClockApp {


@@ 14,6 14,7 @@ pub struct ClockApp {
    state: ClockState,
    buttons: [Box<dyn ClockButton + Send>; 4],
    brightness: BrightnessManager,
    current_view: DisplayView
}

struct AppState<'a> {


@@ 21,6 22,7 @@ struct AppState<'a> {
    display: &'a mut ClockDisplayViewer,
    state: &'a mut ClockState,
    brightness: &'a mut BrightnessManager,
    current_view: &'a mut DisplayView
}

trait ClockButton {


@@ 47,6 49,7 @@ impl ClockApp {
            rtc,
            display,
            state,
            current_view: DisplayView::ClockView,
            buttons: [
                Box::new(ButtonSwitchView),
                Box::new(ButtonChangeTime),


@@ 83,6 86,7 @@ impl ClockApp {
                display: &mut self.display,
                state: &mut self.state,
                brightness: &mut self.brightness,
                current_view: &mut self.current_view
            },
        );
    }


@@ 97,9 101,10 @@ impl ClockButton for ButtonSwitchView {
        match state {
            ButtonState::JustPressed => {
                let display = app.display;
                let current_view =
                    display.current_view().unwrap_or(DisplayViews::ClockView) as usize;
                display.set_current_view(((current_view + 1) % DisplayViews::count()).into());
                let current_view = *app.current_view as usize;
                let new_view = ((current_view + 1) % core::mem::variant_count::<DisplayView>()).try_into().unwrap();
                display.set_current_view(new_view);
                *app.current_view = new_view;
            }
            _ => (),
        }

M source/src/clock_display.rs => source/src/clock_display.rs +29 -18
@@ 64,10 64,21 @@ impl ClockDisplay {
        &mut self,
        part: DisplayPart,
        number: u32,
        pad: bool,
        pad: bool
    ) -> Result<(), DisplayError> {
        let offset = Self::get_part_offset(part);
        let size = Self::get_part_size(part);

        self.show_number_at(offset, size, number, pad)
    }

    pub fn show_number_at(
        &mut self,
        offset: usize,
        size: usize,
        number: u32,
        pad: bool,
    ) -> Result<(), DisplayError> {
        let mut data = self.display.data();

        let mut number = number;


@@ 148,22 159,7 @@ impl ClockDisplay {
        self.display.update()
    }

    fn update_colon(&mut self) {
        let data = self.display.data();
        if self.colon {
            self.display
                .set_digit(COLON_DIGIT_1, data[COLON_DIGIT_1] | 0b1);
            self.display
                .set_digit(COLON_DIGIT_2, data[COLON_DIGIT_2] | 0b1);
        } else {
            self.display
                .set_digit(COLON_DIGIT_1, data[COLON_DIGIT_1] & 0xFE);
            self.display
                .set_digit(COLON_DIGIT_2, data[COLON_DIGIT_2] & 0xFE);
        }
    }

    fn get_part_size(part: DisplayPart) -> usize {
    pub fn get_part_size(part: DisplayPart) -> usize {
        match part {
            DisplayPart::Whole => SIDE_DISPLAY_1_SIZE + MAIN_DISPLAY_SIZE + SIDE_DISPLAY_2_SIZE,
            DisplayPart::MainDisplay => MAIN_DISPLAY_SIZE,


@@ 172,7 168,7 @@ impl ClockDisplay {
        }
    }

    fn get_part_offset(part: DisplayPart) -> usize {
    pub fn get_part_offset(part: DisplayPart) -> usize {
        match part {
            DisplayPart::Whole => 0,
            DisplayPart::MainDisplay => MAIN_DISPLAY_OFFSET,


@@ 180,4 176,19 @@ impl ClockDisplay {
            DisplayPart::SideDisplay2 => SIDE_DISPLAY_2_OFFSET,
        }
    }

    fn update_colon(&mut self) {
        let data = self.display.data();
        if self.colon {
            self.display
                .set_digit(COLON_DIGIT_1, data[COLON_DIGIT_1] | 0b1);
            self.display
                .set_digit(COLON_DIGIT_2, data[COLON_DIGIT_2] | 0b1);
        } else {
            self.display
                .set_digit(COLON_DIGIT_1, data[COLON_DIGIT_1] & 0xFE);
            self.display
                .set_digit(COLON_DIGIT_2, data[COLON_DIGIT_2] & 0xFE);
        }
    }
}

M source/src/clock_display_viewer.rs => source/src/clock_display_viewer.rs +122 -26
@@ 1,57 1,153 @@
use core::mem;

use crate::{
    clock_display::ClockDisplay,
    clock_display::{ClockDisplay, DisplayPart},
    clock_state::ClockState,
    display_view::{
        clock_display_view::ClockDisplayView, date_display_view::DateDisplayView, DisplayView,
        DisplayViews,
    },
};
use alloc::{boxed::Box, vec, vec::Vec};
use stm32f1xx_hal::timer;

#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(usize)]
pub enum DisplayView {
    ClockView = 0,
    ClockSecondsView = 1,
    ClockDateView = 2,
    DateView = 3,
}

impl TryFrom<usize> for DisplayView {
    fn try_from(value: usize) -> Result<Self, ()> {
        if value <= DisplayView::DateView as usize {
            unsafe { core::mem::transmute(value) }
        } else {
            Err(())
        }
    }

    type Error = ();
}

#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(usize)]
pub enum ClockPart {
    Hours = 0,
    Minutes = 1,
    Seconds = 2,
    Year = 3,
    Month = 4,
    Day = 5,
}

impl TryFrom<usize> for ClockPart {
    fn try_from(value: usize) -> Result<Self, ()> {
        if value <= ClockPart::Day as usize {
            unsafe { core::mem::transmute(value) }
        } else {
            Err(())
        }
    }

    type Error = ();
}

pub struct ClockDisplayViewer {
    clock_display: ClockDisplay,
    views: Vec<Box<dyn DisplayView + Send>>,
    current_view: Option<DisplayViews>,
    parts: [bool; core::mem::variant_count::<ClockPart>()]
}

impl ClockDisplayViewer {
    pub fn new(clock_display: ClockDisplay) -> Self {
        Self {
            clock_display,
            views: vec![
                Box::new(ClockDisplayView::new()),
                Box::new(ClockDisplayView::with_seconds()),
                Box::new(ClockDisplayView::with_date()),
                Box::new(DateDisplayView::new()),
            ],
            current_view: None,
            parts: [false; core::mem::variant_count::<ClockPart>()]
        }
    }

    pub fn clock_display(&mut self) -> &mut ClockDisplay {
        &mut self.clock_display
    pub fn show(&mut self, part: ClockPart) {
        self.parts[part as usize] = true;
    }

    pub fn current_view(&self) -> Option<DisplayViews> {
        self.current_view
    pub fn hide(&mut self, part: ClockPart) {
        self.parts[part as usize] = false;
    }

    pub fn hide_all(&mut self) {
        for part in self.parts.iter_mut() {
            *part = false;
        }

        self.clock_display.hide(DisplayPart::MainDisplay);
        self.clock_display.hide(DisplayPart::SideDisplay1);
        self.clock_display.hide(DisplayPart::SideDisplay2);
    }

    pub fn set_current_view(&mut self, view: DisplayViews) {
        self.current_view = Some(view);
    pub fn clock_display(&mut self) -> &mut ClockDisplay {
        &mut self.clock_display
    }

    pub fn clear_current_view(&mut self) {
        self.current_view = None;
    pub fn set_current_view(&mut self, view: DisplayView) {
        self.hide_all();
        match view {
            DisplayView::ClockView => {
                self.show(ClockPart::Hours);
                self.show(ClockPart::Minutes);
            },
            DisplayView::ClockSecondsView => {
                self.show(ClockPart::Hours);
                self.show(ClockPart::Minutes);
                self.show(ClockPart::Seconds);
            },
            DisplayView::ClockDateView => {
                self.show(ClockPart::Hours);
                self.show(ClockPart::Minutes);
                self.show(ClockPart::Day);
                self.show(ClockPart::Month);
            },
            DisplayView::DateView => {
                self.show(ClockPart::Day);
                self.show(ClockPart::Month);
                self.show(ClockPart::Year);
            }
        }
    }

    pub fn update(&mut self, state: &ClockState) -> nb::Result<(), timer::Error> {
        self.clock_display.update()?;

        if let Some(view) = self.current_view {
            let view = &mut self.views[view as usize];
            view.update_display(state, &mut self.clock_display).unwrap(); // TODO: get rid of the unwrap
        for (i, show) in self.parts.iter().enumerate().filter(|(_, x)| **x) {
            if !show {
                continue;
            }

            let part: ClockPart = ClockPart::try_from(i).unwrap();
            match part {
                ClockPart::Day => {
                    self.clock_display.show_ordinal(DisplayPart::SideDisplay1, state.calendar().day() as u32, true).unwrap();
                },
                ClockPart::Month => {
                    self.clock_display.show_ordinal(DisplayPart::SideDisplay2, state.calendar().month() as u32, true).unwrap();
                },
                ClockPart::Year => {
                    self.clock_display.show_number(DisplayPart::MainDisplay, state.calendar().year() as u32, true).unwrap();
                },
                ClockPart::Hours => {
                    self.clock_display.show_number_at(ClockDisplay::get_part_offset(DisplayPart::MainDisplay), 2, state.calendar().hours() as u32, true).unwrap();
                },
                ClockPart::Minutes => {
                    self.clock_display.show_number_at(ClockDisplay::get_part_offset(DisplayPart::MainDisplay) + 2, 2, state.calendar().minutes() as u32, true).unwrap();
                },
                ClockPart::Seconds => {
                    self.clock_display.show_number(DisplayPart::SideDisplay2, state.calendar().seconds() as u32, true).unwrap();
                },
            }
        }

        if self.parts[ClockPart::Hours as usize] && self.parts[ClockPart::Minutes as usize] {
            self.clock_display.set_colon(state.calendar().seconds() % 2 == 0);
        } else {
            self.clock_display.set_colon(false);
        }

        Ok(())
    }
}

D source/src/display_view.rs => source/src/display_view.rs +0 -54
@@ 1,54 0,0 @@
use crate::{
    clock_display::{ClockDisplay, DisplayError},
    clock_state::ClockState,
};

pub mod clock_display_view;
pub mod date_display_view;

#[derive(Copy, Clone, PartialEq, Eq)]
#[repr(usize)]
pub enum DisplayViews {
    ClockView = 0,
    ClockSecondsView = 1,
    ClockDateView = 2,
    DateView = 3,
}

impl DisplayViews {
    pub fn count() -> usize {
        4
    }
}

impl From<usize> for DisplayViews {
    fn from(value: usize) -> Self {
        match value {
            0 => Self::ClockView,
            1 => Self::ClockSecondsView,
            2 => Self::ClockDateView,
            3 => Self::DateView,
            _ => panic!(),
        }
    }
}

#[derive(Debug)]
pub enum DisplayViewError {
    Unknown,
    DisplayError(DisplayError),
}

impl From<DisplayError> for DisplayViewError {
    fn from(value: DisplayError) -> Self {
        Self::DisplayError(value)
    }
}

pub trait DisplayView {
    fn update_display(
        &mut self,
        state: &ClockState,
        display: &mut ClockDisplay,
    ) -> Result<(), DisplayViewError>;
}

D source/src/display_view/clock_display_view.rs => source/src/display_view/clock_display_view.rs +0 -69
@@ 1,69 0,0 @@
use crate::{
    clock_display::{ClockDisplay, DisplayPart},
    clock_state::ClockState,
};

use super::{DisplayView, DisplayViewError};

pub struct ClockDisplayView {
    show_seconds: bool,
    show_date: bool,
}

impl Default for ClockDisplayView {
    fn default() -> Self {
        Self::new()
    }
}

impl ClockDisplayView {
    pub fn new() -> Self {
        Self {
            show_date: false,
            show_seconds: false,
        }
    }

    pub fn with_seconds() -> Self {
        Self {
            show_date: false,
            show_seconds: true,
        }
    }

    pub fn with_date() -> Self {
        Self {
            show_date: true,
            show_seconds: false,
        }
    }
}

impl DisplayView for ClockDisplayView {
    fn update_display(
        &mut self,
        state: &ClockState,
        display: &mut ClockDisplay,
    ) -> Result<(), DisplayViewError> {
        let calendar = state.calendar();

        if self.show_seconds {
            display.hide(DisplayPart::SideDisplay1);
            display.show_number(DisplayPart::SideDisplay2, calendar.seconds() as u32, true)?;
        } else if self.show_date {
            display.show_ordinal(DisplayPart::SideDisplay1, calendar.day() as u32, true)?;
            display.show_ordinal(DisplayPart::SideDisplay2, calendar.month() as u32, true)?;
        } else {
            display.hide(DisplayPart::SideDisplay1);
            display.hide(DisplayPart::SideDisplay2);
        }

        display.set_colon(calendar.seconds() % 2 == 0);
        display.show_number(
            DisplayPart::MainDisplay,
            (calendar.hours() as u32) * 100 + (calendar.minutes() as u32),
            true,
        )?;
        Ok(())
    }
}

D source/src/display_view/date_display_view.rs => source/src/display_view/date_display_view.rs +0 -33
@@ 1,33 0,0 @@
use crate::clock_display::DisplayPart;

use super::DisplayView;

pub struct DateDisplayView;

impl DateDisplayView {
    pub fn new() -> Self {
        Self
    }
}

impl Default for DateDisplayView {
    fn default() -> Self {
        Self::new()
    }
}

impl DisplayView for DateDisplayView {
    fn update_display(
        &mut self,
        state: &crate::clock_state::ClockState,
        display: &mut crate::clock_display::ClockDisplay,
    ) -> Result<(), super::DisplayViewError> {
        let calendar = state.calendar();

        display.show_ordinal(DisplayPart::SideDisplay1, calendar.day() as u32, true)?;
        display.show_ordinal(DisplayPart::SideDisplay2, calendar.month() as u32, true)?;
        display.show_number(DisplayPart::MainDisplay, calendar.year() as u32, false)?;
        display.set_colon(false);
        Ok(())
    }
}

M source/src/lib.rs => source/src/lib.rs +2 -1
@@ 1,5 1,6 @@
#![cfg_attr(test, no_main)]
#![no_std]
#![feature(variant_count)]

use defmt_rtt as _; // global logger



@@ 16,8 17,8 @@ pub mod clock_display_viewer;
pub mod clock_state;
pub mod count_down;
pub mod display;
pub mod display_view;
pub mod linear_interpolation;
pub mod mono_timer;
pub mod number_digits;
pub mod seven_segments;


M source/src/main.rs => source/src/main.rs +5 -6
@@ 1,6 1,7 @@
#![no_std]
#![no_main]
#![feature(alloc_error_handler)]
#![feature(variant_count)]

extern crate alloc;



@@ 13,7 14,6 @@ pub mod clock_display_viewer;
pub mod clock_state;
pub mod count_down;
pub mod display;
pub mod display_view;
pub mod linear_interpolation;
pub mod number_digits;
pub mod seven_segments;


@@ 23,7 23,7 @@ use button::{ActiveHigh, Button, ButtonState};
use calendar::Calendar;
use clock_app::{ClockApp, ClockInterrupt};
use clock_display::{ClockDisplay, DisplayPart};
use clock_display_viewer::ClockDisplayViewer;
use clock_display_viewer::{ClockDisplayViewer, DisplayView};
use clock_state::ClockState;
use core::{alloc::Layout, cell::RefCell, convert::Infallible, panic::PanicInfo};
use cortex_m::asm::wfi;


@@ 31,7 31,6 @@ use cortex_m_rt::entry;
use count_down::{CountDown, CountDowner};
use critical_section::Mutex;
use display::Display;
use display_view::DisplayViews;
use embedded_alloc::Heap;
use embedded_hal::digital::v2::OutputPin;
use fugit::MicrosDurationU32;


@@ 291,7 290,7 @@ fn main() -> ! {
        &mut afio.mapr,
        &clocks,
    );
    display.set_current_view(DisplayViews::ClockSecondsView);
    display.set_current_view(DisplayView::ClockView);

    let mut rtc = match Rtc::restore_or_new(dp.RTC, &mut backup_domain) {
        Restored(rtc) => rtc,


@@ 367,7 366,7 @@ fn panic(info: &PanicInfo) -> ! {
        let mut app = APP.borrow_ref_mut(cs);
        let app = app.as_mut().unwrap();
        let display = app.display();
        display.clear_current_view();
        display.hide_all();
        let _ = display
            .clock_display()
            .show_text(DisplayPart::MainDisplay, "Erro");


@@ 387,7 386,7 @@ fn oom(_: Layout) -> ! {
        let mut app = APP.borrow_ref_mut(cs);
        let app = app.as_mut().unwrap();
        let display = app.display();
        display.clear_current_view();
        display.hide_all();
        let _ = display
            .clock_display()
            .show_text(DisplayPart::MainDisplay, "oom");

Do not follow this link