Ctx, SD
This commit is contained in:
parent
8dda09689d
commit
d8865488e3
12 changed files with 328 additions and 166 deletions
41
Cargo.lock
generated
41
Cargo.lock
generated
|
@ -42,7 +42,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "bcb2392079bf27198570d6af79ecbd9ec7d8f16d3ec6b60933922fdb66287127"
|
checksum = "bcb2392079bf27198570d6af79ecbd9ec7d8f16d3ec6b60933922fdb66287127"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"heapless",
|
"heapless",
|
||||||
"nom 4.2.3",
|
"nom",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -74,6 +74,7 @@ dependencies = [
|
||||||
"bitflags",
|
"bitflags",
|
||||||
"cortex-m",
|
"cortex-m",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
|
"embedded-sdmmc",
|
||||||
"modular-bitfield",
|
"modular-bitfield",
|
||||||
"nb 1.1.0",
|
"nb 1.1.0",
|
||||||
"num-traits",
|
"num-traits",
|
||||||
|
@ -341,12 +342,14 @@ dependencies = [
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "embedded-sdmmc"
|
name = "embedded-sdmmc"
|
||||||
version = "0.5.0"
|
version = "0.3.0"
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
checksum = "2f4d14180a76a8af24a45a0e1a4f9c97491b05a3b962d59d5e4ce0e6ab103736"
|
checksum = "6d3bf0a2b5becb87e9a329d9290f131e4d10fec39b56d129926826a7cbea1e7a"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"byteorder",
|
"byteorder",
|
||||||
"embedded-hal",
|
"embedded-hal",
|
||||||
|
"log",
|
||||||
|
"nb 0.1.3",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
|
@ -530,6 +533,7 @@ version = "0.10.0"
|
||||||
dependencies = [
|
dependencies = [
|
||||||
"atsamd-hal",
|
"atsamd-hal",
|
||||||
"cortex-m-rt",
|
"cortex-m-rt",
|
||||||
|
"embedded-sdmmc",
|
||||||
"usb-device",
|
"usb-device",
|
||||||
]
|
]
|
||||||
|
|
||||||
|
@ -619,16 +623,6 @@ dependencies = [
|
||||||
"version_check 0.1.5",
|
"version_check 0.1.5",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "nom"
|
|
||||||
version = "6.2.2"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "c6a7a9657c84d5814c6196b68bb4429df09c18b1573806259fba397ea4ad0d44"
|
|
||||||
dependencies = [
|
|
||||||
"memchr",
|
|
||||||
"version_check 0.9.4",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "num-integer"
|
name = "num-integer"
|
||||||
version = "0.1.45"
|
version = "0.1.45"
|
||||||
|
@ -832,8 +826,6 @@ dependencies = [
|
||||||
"epd-waveshare",
|
"epd-waveshare",
|
||||||
"maduino_zero_4g",
|
"maduino_zero_4g",
|
||||||
"panic-halt",
|
"panic-halt",
|
||||||
"tinybmp",
|
|
||||||
"tinytga",
|
|
||||||
"tz-rs",
|
"tz-rs",
|
||||||
"tzdb",
|
"tzdb",
|
||||||
]
|
]
|
||||||
|
@ -939,25 +931,6 @@ dependencies = [
|
||||||
"weezl",
|
"weezl",
|
||||||
]
|
]
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tinybmp"
|
|
||||||
version = "0.4.0"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "0e959c507975d768a226a08227d56791f6e60bddcf714ad7ef67ae2d20bae743"
|
|
||||||
dependencies = [
|
|
||||||
"embedded-graphics",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
|
||||||
name = "tinytga"
|
|
||||||
version = "0.4.1"
|
|
||||||
source = "registry+https://github.com/rust-lang/crates.io-index"
|
|
||||||
checksum = "756dcc0078b35e5f8ab052f19a48225821fd046011342d31deef6b8d5c825d2b"
|
|
||||||
dependencies = [
|
|
||||||
"embedded-graphics",
|
|
||||||
"nom 6.2.2",
|
|
||||||
]
|
|
||||||
|
|
||||||
[[package]]
|
[[package]]
|
||||||
name = "typenum"
|
name = "typenum"
|
||||||
version = "1.16.0"
|
version = "1.16.0"
|
||||||
|
|
|
@ -12,12 +12,10 @@ atsamd-hal = { version = "0.15.1", default_features = false, features = ["samd21
|
||||||
cfg-if = "1.0.0"
|
cfg-if = "1.0.0"
|
||||||
embedded-graphics = "0.7.1"
|
embedded-graphics = "0.7.1"
|
||||||
embedded-text = "0.5.0"
|
embedded-text = "0.5.0"
|
||||||
embedded-sdmmc = { version = "0.5.0", default_features = false }
|
embedded-sdmmc = { version = "0.3.0", default_features = false }
|
||||||
epd-waveshare = "0.5.0"
|
epd-waveshare = "0.5.0"
|
||||||
maduino_zero_4g = { git = "https://github.com/ZettaScript/atsamd", branch = "maduino-zero-4g", features = ["usb"] }
|
maduino_zero_4g = { git = "https://github.com/ZettaScript/atsamd", branch = "maduino-zero-4g", features = ["usb"] }
|
||||||
panic-halt = "0.2.0"
|
panic-halt = "0.2.0"
|
||||||
tinybmp = "0.4.0"
|
|
||||||
tinytga = "0.4.1"
|
|
||||||
tz-rs = { version = "0.6.14", default_features = false, features = ["const"] }
|
tz-rs = { version = "0.6.14", default_features = false, features = ["const"] }
|
||||||
tzdb = { version = "0.5.7", optional = true }
|
tzdb = { version = "0.5.7", optional = true }
|
||||||
|
|
||||||
|
@ -31,6 +29,7 @@ simulator = ["embedded-graphics-simulator", "tzdb"]
|
||||||
|
|
||||||
[profile.release]
|
[profile.release]
|
||||||
lto = "fat"
|
lto = "fat"
|
||||||
|
opt-level = 3
|
||||||
|
|
||||||
[patch."https://github.com/ZettaScript/atsamd"]
|
[patch."https://github.com/ZettaScript/atsamd"]
|
||||||
maduino_zero_4g = { path = "../atsamd/boards/maduino_zero_4g" }
|
maduino_zero_4g = { path = "../atsamd/boards/maduino_zero_4g" }
|
||||||
|
|
|
@ -14,7 +14,7 @@ rustup target add thumbv6m-none-eabi
|
||||||
## Run simulator
|
## Run simulator
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
cargo run
|
cargo run --features simulator
|
||||||
```
|
```
|
||||||
|
|
||||||
## Build
|
## Build
|
||||||
|
@ -59,7 +59,7 @@ Note: at most 5 outputs of the 74HC565 may be used as GPO.
|
||||||
https://www.makerfabs.com/maduino-zero-4g-lte-sim7600.html
|
https://www.makerfabs.com/maduino-zero-4g-lte-sim7600.html
|
||||||
https://www.waveshare.com/1.54inch-e-Paper-Module.htm1
|
https://www.waveshare.com/1.54inch-e-Paper-Module.htm1
|
||||||
https://bulkmemorycards.com/shop/microsd-cards/microsd-32gb/sd-32gb-class-10/32gb-microsd-ultra-sandisk-memory-card-2/
|
https://bulkmemorycards.com/shop/microsd-cards/microsd-32gb/sd-32gb-class-10/32gb-microsd-ultra-sandisk-memory-card-2/
|
||||||
Maybe an ESP for WIFI and Bluetooth.
|
Maybe an ESP for WIFI and Bluetooth: https://www.sparkfun.com/products/18034
|
||||||
|
|
||||||
## crates
|
## crates
|
||||||
bitmap-font ?
|
bitmap-font ?
|
||||||
|
|
|
@ -1,9 +1,11 @@
|
||||||
use crate::display::Display;
|
use crate::{display::Display, keypad::KeyEvents, state::ModeState, Context};
|
||||||
|
|
||||||
|
pub mod clock;
|
||||||
pub mod dial;
|
pub mod dial;
|
||||||
|
|
||||||
pub trait App {
|
pub trait App {
|
||||||
type State;
|
type AppModeState;
|
||||||
|
|
||||||
fn update(display: &mut Display);
|
/// Return Some if the mode should be changed
|
||||||
|
fn update(context: &mut Context, mode_state: &mut Self::AppModeState) -> Option<ModeState>;
|
||||||
}
|
}
|
||||||
|
|
112
src/apps/clock.rs
Normal file
112
src/apps/clock.rs
Normal file
|
@ -0,0 +1,112 @@
|
||||||
|
use crate::{
|
||||||
|
apps,
|
||||||
|
apps::App,
|
||||||
|
display::Display,
|
||||||
|
keypad::*,
|
||||||
|
state::{self, ModeState},
|
||||||
|
strf, Context,
|
||||||
|
};
|
||||||
|
|
||||||
|
use arrayvec::ArrayString;
|
||||||
|
use embedded_graphics::{
|
||||||
|
mono_font::{ascii::FONT_10X20, MonoTextStyleBuilder},
|
||||||
|
pixelcolor::BinaryColor,
|
||||||
|
prelude::*,
|
||||||
|
text::{Alignment, Text},
|
||||||
|
};
|
||||||
|
|
||||||
|
pub struct Clock;
|
||||||
|
|
||||||
|
pub struct ClockState {
|
||||||
|
year: i32,
|
||||||
|
month: u8,
|
||||||
|
day: u8,
|
||||||
|
week_day: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ClockState {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
year: 0,
|
||||||
|
month: 0,
|
||||||
|
day: 0,
|
||||||
|
week_day: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App for Clock {
|
||||||
|
type AppModeState = ClockState;
|
||||||
|
|
||||||
|
fn update(ctx: &mut Context, mode_state: &mut ClockState) -> Option<ModeState> {
|
||||||
|
// TODO move at init
|
||||||
|
let clock_text_style = MonoTextStyleBuilder::new()
|
||||||
|
.font(&FONT_10X20)
|
||||||
|
.text_color(BinaryColor::On)
|
||||||
|
.background_color(BinaryColor::Off)
|
||||||
|
.build();
|
||||||
|
|
||||||
|
for key_event in &ctx.key_events {
|
||||||
|
if key_event.event_type != KeyEventType::Pressed {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match key_event.key {
|
||||||
|
_ => {
|
||||||
|
if let Some(ch) = key_event.get_char(KeyInputMode::Digit) {
|
||||||
|
let mut buf = [0; 4];
|
||||||
|
return Some(ModeState::Dial(apps::dial::DialState {
|
||||||
|
line: ArrayString::from(&*ch.encode_utf8(&mut buf)).unwrap(),
|
||||||
|
}));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if ctx.hm_change {
|
||||||
|
Text::with_alignment(
|
||||||
|
unsafe {
|
||||||
|
core::str::from_utf8_unchecked(&strf::fmt_time_hm(
|
||||||
|
ctx.state.hour,
|
||||||
|
ctx.state.minute,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
ctx.display.inner().bounding_box().center() + Point::new(0, 10),
|
||||||
|
clock_text_style,
|
||||||
|
Alignment::Center,
|
||||||
|
)
|
||||||
|
.draw(ctx.display.inner_mut())
|
||||||
|
.unwrap();
|
||||||
|
|
||||||
|
let year_ = ctx.now.year();
|
||||||
|
let month_ = ctx.now.month();
|
||||||
|
let day_ = ctx.now.month_day();
|
||||||
|
let week_day_ = ctx.now.week_day();
|
||||||
|
if (year_, month_, day_, week_day_)
|
||||||
|
!= (
|
||||||
|
mode_state.year,
|
||||||
|
mode_state.month,
|
||||||
|
mode_state.day,
|
||||||
|
mode_state.week_day,
|
||||||
|
) {
|
||||||
|
mode_state.year = year_;
|
||||||
|
mode_state.month = month_;
|
||||||
|
mode_state.day = day_;
|
||||||
|
mode_state.week_day = week_day_;
|
||||||
|
Text::with_alignment(
|
||||||
|
unsafe {
|
||||||
|
core::str::from_utf8_unchecked(&strf::fmt_time_ymdw(
|
||||||
|
year_, month_, day_, week_day_,
|
||||||
|
))
|
||||||
|
},
|
||||||
|
ctx.display.inner().bounding_box().center() + Point::new(0, -20),
|
||||||
|
clock_text_style,
|
||||||
|
Alignment::Center,
|
||||||
|
)
|
||||||
|
.draw(ctx.display.inner_mut())
|
||||||
|
.unwrap();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1,45 @@
|
||||||
|
use crate::{
|
||||||
|
apps::App,
|
||||||
|
display::Display,
|
||||||
|
keypad::{Key, KeyEvent, KeyEventType, KeyEvents, KeyInputMode},
|
||||||
|
state::{ModeState, State},
|
||||||
|
Context,
|
||||||
|
};
|
||||||
|
|
||||||
|
use arrayvec::ArrayString;
|
||||||
|
|
||||||
|
pub struct Dial;
|
||||||
|
|
||||||
|
pub struct DialState {
|
||||||
|
// TODO what should max length be?
|
||||||
|
pub line: ArrayString<255>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl App for Dial {
|
||||||
|
type AppModeState = DialState;
|
||||||
|
|
||||||
|
fn update(ctx: &mut Context, mode_state: &mut DialState) -> Option<ModeState> {
|
||||||
|
for key_event in &ctx.key_events {
|
||||||
|
if key_event.event_type != KeyEventType::Pressed {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
match key_event.key {
|
||||||
|
Key::HangUp => {
|
||||||
|
return Some(ModeState::Clock(Default::default()));
|
||||||
|
}
|
||||||
|
Key::PickUp => {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
_ => {
|
||||||
|
if let Some(ch) = key_event.get_char(KeyInputMode::Digit) {
|
||||||
|
if mode_state.line.try_push(ch).is_err() {
|
||||||
|
// TODO
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
None
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
29
src/fs.rs
Normal file
29
src/fs.rs
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
use atsamd_hal::{delay::Delay, pac::Peripherals, prelude::*, sercom::spi::EightBit};
|
||||||
|
use embedded_sdmmc::{Controller, SdMmcSpi, VolumeIdx};
|
||||||
|
|
||||||
|
pub struct Fs {
|
||||||
|
controller: Controller<SdMmcSpi<maduino_zero_4g::SdSpi, maduino_zero_4g::SdCs>, ClockMock>,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Fs {
|
||||||
|
pub fn new(spi: maduino_zero_4g::SdSpi, cs: maduino_zero_4g::SdCs) -> Self {
|
||||||
|
let controller = Controller::new(SdMmcSpi::new(spi, cs), ClockMock);
|
||||||
|
Self { controller }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
struct ClockMock;
|
||||||
|
|
||||||
|
// TODO
|
||||||
|
impl embedded_sdmmc::TimeSource for ClockMock {
|
||||||
|
fn get_timestamp(&self) -> embedded_sdmmc::Timestamp {
|
||||||
|
embedded_sdmmc::Timestamp {
|
||||||
|
year_since_1970: 0,
|
||||||
|
zero_indexed_month: 0,
|
||||||
|
zero_indexed_day: 0,
|
||||||
|
hours: 0,
|
||||||
|
minutes: 0,
|
||||||
|
seconds: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -1 +1 @@
|
||||||
|
pub trait Widget {}
|
||||||
|
|
|
@ -4,6 +4,8 @@ const NB_KEYS: usize = 21;
|
||||||
/// Key repeat max delay (ms)
|
/// Key repeat max delay (ms)
|
||||||
const REPEAT_DELAY: u64 = 500;
|
const REPEAT_DELAY: u64 = 500;
|
||||||
|
|
||||||
|
pub type KeyEvents = ArrayVec<KeyEvent, 4>;
|
||||||
|
|
||||||
pub struct Keypad {
|
pub struct Keypad {
|
||||||
last_key: Option<(Key, u64, u8)>,
|
last_key: Option<(Key, u64, u8)>,
|
||||||
pressed: [bool; NB_KEYS],
|
pressed: [bool; NB_KEYS],
|
||||||
|
@ -19,7 +21,7 @@ impl Default for Keypad {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Keypad {
|
impl Keypad {
|
||||||
pub fn update(&mut self) -> ArrayVec<KeyEvent, 4> {
|
pub fn update(&mut self) -> KeyEvents {
|
||||||
self.get_keys()
|
self.get_keys()
|
||||||
.into_iter()
|
.into_iter()
|
||||||
.zip(self.pressed.iter_mut())
|
.zip(self.pressed.iter_mut())
|
||||||
|
@ -97,7 +99,7 @@ impl Keypad {
|
||||||
.for_each(|key| keys[key as usize] = true);
|
.for_each(|key| keys[key as usize] = true);
|
||||||
keys
|
keys
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(not(feature = "simulator"))]
|
#[cfg(not(feature = "simulator"))]
|
||||||
fn get_keys(&self) -> [bool; NB_KEYS] {
|
fn get_keys(&self) -> [bool; NB_KEYS] {
|
||||||
[false; NB_KEYS]
|
[false; NB_KEYS]
|
||||||
|
@ -137,26 +139,41 @@ impl Key {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub struct KeyEvent {
|
pub struct KeyEvent {
|
||||||
event_type: KeyEventType,
|
pub event_type: KeyEventType,
|
||||||
key: Key,
|
pub key: Key,
|
||||||
repeats: u8,
|
pub repeats: u8,
|
||||||
}
|
}
|
||||||
|
|
||||||
/*impl KeyEvent {
|
impl KeyEvent {
|
||||||
pub fn get_char(&self, key_input_mode: KeyInputMode) -> Option<char> {
|
pub fn get_char(&self, key_input_mode: KeyInputMode) -> Option<char> {
|
||||||
use Key::*;
|
use Key::*;
|
||||||
match key_input_mode {
|
match key_input_mode {
|
||||||
|
|
||||||
KeyInputMode::Digit => match self.key {
|
KeyInputMode::Digit => match self.key {
|
||||||
D0 =>
|
D0 => Some('0'),
|
||||||
|
D1 => Some('1'),
|
||||||
|
D2 => Some('2'),
|
||||||
|
D3 => Some('3'),
|
||||||
|
D4 => Some('4'),
|
||||||
|
D5 => Some('5'),
|
||||||
|
D6 => Some('6'),
|
||||||
|
D7 => Some('7'),
|
||||||
|
D8 => Some('8'),
|
||||||
|
D9 => Some('9'),
|
||||||
|
Asterisk => Some('*'),
|
||||||
|
Hash => Some('#'),
|
||||||
|
_ => None,
|
||||||
|
},
|
||||||
|
_ => {
|
||||||
|
None /* TODO */
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}*/
|
}
|
||||||
//ncb1 upz2 tdk3
|
//ncb1 upz2 tdk3
|
||||||
//eow4 lqh5 age6
|
//eow4 lqh5 age6
|
||||||
//sfx7 rmj8 ivy9
|
//sfx7 rmj8 ivy9
|
||||||
|
|
||||||
|
#[derive(Eq, PartialEq)]
|
||||||
pub enum KeyEventType {
|
pub enum KeyEventType {
|
||||||
Pressed,
|
Pressed,
|
||||||
Down,
|
Down,
|
||||||
|
|
192
src/main.rs
192
src/main.rs
|
@ -5,25 +5,19 @@ mod apps;
|
||||||
mod config;
|
mod config;
|
||||||
mod display;
|
mod display;
|
||||||
mod energy;
|
mod energy;
|
||||||
|
mod fs;
|
||||||
mod gui;
|
mod gui;
|
||||||
mod keypad;
|
mod keypad;
|
||||||
mod state;
|
mod state;
|
||||||
mod strf;
|
mod strf;
|
||||||
mod time;
|
mod time;
|
||||||
|
|
||||||
|
use apps::App;
|
||||||
|
use display::Display;
|
||||||
use energy::EnergyStatus;
|
use energy::EnergyStatus;
|
||||||
|
use keypad::KeyEvents;
|
||||||
use state::*;
|
use state::*;
|
||||||
|
|
||||||
cfg_if::cfg_if! {
|
|
||||||
if #[cfg(not(feature = "simulator"))] {
|
|
||||||
use maduino_zero_4g as bsp;
|
|
||||||
use bsp::hal;
|
|
||||||
|
|
||||||
use hal::pac::{CorePeripherals, Peripherals};
|
|
||||||
use hal::prelude::*;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
use arrayvec::ArrayString;
|
use arrayvec::ArrayString;
|
||||||
use core::fmt::Write;
|
use core::fmt::Write;
|
||||||
use embedded_graphics::{
|
use embedded_graphics::{
|
||||||
|
@ -33,36 +27,55 @@ use embedded_graphics::{
|
||||||
primitives::{Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, StrokeAlignment},
|
primitives::{Line, PrimitiveStyle, PrimitiveStyleBuilder, Rectangle, StrokeAlignment},
|
||||||
text::{Alignment, Text},
|
text::{Alignment, Text},
|
||||||
};
|
};
|
||||||
|
|
||||||
#[cfg(not(feature = "simulator"))]
|
#[cfg(not(feature = "simulator"))]
|
||||||
use panic_halt as _;
|
use maduino_zero_4g::{
|
||||||
|
self as bsp,
|
||||||
static mut STATE: State = State {
|
hal::{
|
||||||
energy: EnergyStatus {
|
clock::GenericClockController,
|
||||||
battery: 255,
|
delay::Delay,
|
||||||
charging: false,
|
pac::{CorePeripherals, Peripherals},
|
||||||
},
|
prelude::*,
|
||||||
hour: 0,
|
|
||||||
minute: 0,
|
|
||||||
mode: Mode::Clock {
|
|
||||||
year: 0,
|
|
||||||
month: 0,
|
|
||||||
day: 0,
|
|
||||||
week_day: 0,
|
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
#[cfg(not(feature = "simulator"))]
|
||||||
fn state() -> &'static State {
|
use panic_halt as _;
|
||||||
unsafe { &STATE }
|
use tz::DateTime;
|
||||||
}
|
|
||||||
|
|
||||||
fn state_mut() -> &'static mut State {
|
|
||||||
unsafe { &mut STATE }
|
|
||||||
}
|
|
||||||
|
|
||||||
#[cfg_attr(not(feature = "simulator"), bsp::entry)]
|
#[cfg_attr(not(feature = "simulator"), bsp::entry)]
|
||||||
fn main() -> ! {
|
fn main() -> ! {
|
||||||
let mut display = display::Display::new();
|
cfg_if::cfg_if! {
|
||||||
|
if #[cfg(not(feature = "simulator"))] {
|
||||||
|
let mut peripherals = Peripherals::take().unwrap();
|
||||||
|
let mut pins = maduino_zero_4g::Pins::new(peripherals.PORT);
|
||||||
|
let core = CorePeripherals::take().unwrap();
|
||||||
|
let mut clocks = GenericClockController::with_external_32kosc(
|
||||||
|
peripherals.GCLK,
|
||||||
|
&mut peripherals.PM,
|
||||||
|
&mut peripherals.SYSCTRL,
|
||||||
|
&mut peripherals.NVMCTRL,
|
||||||
|
);
|
||||||
|
let mut delay = Delay::new(core.SYST, &mut clocks);
|
||||||
|
|
||||||
|
let mut fs = fs::Fs::new(atsamd_hal::sercom::spi::Config::new(
|
||||||
|
&peripherals.PM,
|
||||||
|
peripherals.SERCOM4,
|
||||||
|
atsamd_hal::sercom::spi::Pads::default().data_in(pins.sd_miso).data_out(pins.sd_mosi).sclk(pins.sd_sck),
|
||||||
|
10_u32.mhz(),
|
||||||
|
).enable(), pins.d4.into_push_pull_output());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut state = State {
|
||||||
|
energy: EnergyStatus {
|
||||||
|
battery: 255,
|
||||||
|
charging: false,
|
||||||
|
},
|
||||||
|
hour: 0,
|
||||||
|
minute: 0,
|
||||||
|
};
|
||||||
|
let mut mode_state = ModeState::Clock(Default::default());
|
||||||
|
|
||||||
|
let mut display = Display::new();
|
||||||
let mut keypad = keypad::Keypad::default();
|
let mut keypad = keypad::Keypad::default();
|
||||||
|
|
||||||
let thin_stroke = PrimitiveStyle::with_stroke(BinaryColor::On, 1);
|
let thin_stroke = PrimitiveStyle::with_stroke(BinaryColor::On, 1);
|
||||||
|
@ -73,11 +86,6 @@ fn main() -> ! {
|
||||||
.text_color(BinaryColor::On)
|
.text_color(BinaryColor::On)
|
||||||
.background_color(BinaryColor::Off)
|
.background_color(BinaryColor::Off)
|
||||||
.build();
|
.build();
|
||||||
let clock_text_style = MonoTextStyleBuilder::new()
|
|
||||||
.font(&FONT_10X20)
|
|
||||||
.text_color(BinaryColor::On)
|
|
||||||
.background_color(BinaryColor::Off)
|
|
||||||
.build();
|
|
||||||
|
|
||||||
Line::new(Point::new(0, 13), Point::new(199, 13))
|
Line::new(Point::new(0, 13), Point::new(199, 13))
|
||||||
.into_styled(thin_stroke)
|
.into_styled(thin_stroke)
|
||||||
|
@ -87,13 +95,19 @@ fn main() -> ! {
|
||||||
#[cfg(feature = "simulator")]
|
#[cfg(feature = "simulator")]
|
||||||
display.update();
|
display.update();
|
||||||
|
|
||||||
let mut update = true;
|
let mut ctx = Context {
|
||||||
let mut hm_change = true;
|
display: &mut display,
|
||||||
|
hm_change: true,
|
||||||
|
key_events: Default::default(),
|
||||||
|
now: DateTime::from_timespec(0, 0, tz::TimeZoneRef::utc()).unwrap(),
|
||||||
|
update: true,
|
||||||
|
state: &mut state,
|
||||||
|
};
|
||||||
loop {
|
loop {
|
||||||
let energy_status = energy::get_energy_status();
|
let energy_status = energy::get_energy_status();
|
||||||
|
|
||||||
if energy_status != state().energy {
|
if energy_status != ctx.state.energy {
|
||||||
update = true;
|
ctx.update = true;
|
||||||
|
|
||||||
Text::with_alignment(
|
Text::with_alignment(
|
||||||
unsafe { core::str::from_utf8_unchecked(&strf::fmt_energy(&energy_status)) },
|
unsafe { core::str::from_utf8_unchecked(&strf::fmt_energy(&energy_status)) },
|
||||||
|
@ -101,21 +115,21 @@ fn main() -> ! {
|
||||||
statusbar_text_style,
|
statusbar_text_style,
|
||||||
Alignment::Left,
|
Alignment::Left,
|
||||||
)
|
)
|
||||||
.draw(display.inner_mut())
|
.draw(ctx.display.inner_mut())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
|
|
||||||
state_mut().energy = energy_status;
|
ctx.state.energy = energy_status;
|
||||||
}
|
}
|
||||||
|
|
||||||
let now = time::now();
|
ctx.now = time::now();
|
||||||
|
|
||||||
let hour = now.hour();
|
let hour = ctx.now.hour();
|
||||||
let minute = now.minute();
|
let minute = ctx.now.minute();
|
||||||
if (hour, minute) != (state().hour, state().minute) {
|
if (hour, minute) != (ctx.state.hour, ctx.state.minute) {
|
||||||
update = true;
|
ctx.update = true;
|
||||||
hm_change = true;
|
ctx.hm_change = true;
|
||||||
state_mut().hour = hour;
|
ctx.state.hour = hour;
|
||||||
state_mut().minute = minute;
|
ctx.state.minute = minute;
|
||||||
|
|
||||||
Text::with_alignment(
|
Text::with_alignment(
|
||||||
unsafe { core::str::from_utf8_unchecked(&strf::fmt_time_hm(hour, minute)) },
|
unsafe { core::str::from_utf8_unchecked(&strf::fmt_time_hm(hour, minute)) },
|
||||||
|
@ -123,67 +137,41 @@ fn main() -> ! {
|
||||||
statusbar_text_style,
|
statusbar_text_style,
|
||||||
Alignment::Right,
|
Alignment::Right,
|
||||||
)
|
)
|
||||||
.draw(display.inner_mut())
|
.draw(ctx.display.inner_mut())
|
||||||
.unwrap();
|
.unwrap();
|
||||||
}
|
}
|
||||||
|
|
||||||
let key_events = keypad.update();
|
ctx.key_events = keypad.update();
|
||||||
|
|
||||||
match &mut state_mut().mode {
|
if let Some(new_mode_state) = match &mut mode_state {
|
||||||
Mode::Clock {
|
ModeState::Clock(clock_state) => apps::clock::Clock::update(&mut ctx, clock_state),
|
||||||
year,
|
ModeState::Dial(dial_state) => apps::dial::Dial::update(&mut ctx, dial_state),
|
||||||
month,
|
} {
|
||||||
day,
|
mode_state = new_mode_state;
|
||||||
week_day,
|
|
||||||
} => {
|
|
||||||
if hm_change {
|
|
||||||
Text::with_alignment(
|
|
||||||
unsafe { core::str::from_utf8_unchecked(&strf::fmt_time_hm(hour, minute)) },
|
|
||||||
display.inner().bounding_box().center() + Point::new(0, 10),
|
|
||||||
clock_text_style,
|
|
||||||
Alignment::Center,
|
|
||||||
)
|
|
||||||
.draw(display.inner_mut())
|
|
||||||
.unwrap();
|
|
||||||
|
|
||||||
let year_ = now.year();
|
|
||||||
let month_ = now.month();
|
|
||||||
let day_ = now.month_day();
|
|
||||||
let week_day_ = now.week_day();
|
|
||||||
if (year_, month_, day_, week_day_) != (*year, *month, *day, *week_day) {
|
|
||||||
*year = year_;
|
|
||||||
*month = month_;
|
|
||||||
*day = day_;
|
|
||||||
*week_day = week_day_;
|
|
||||||
Text::with_alignment(
|
|
||||||
unsafe {
|
|
||||||
core::str::from_utf8_unchecked(&strf::fmt_time_ymdw(
|
|
||||||
year_, month_, day_, week_day_,
|
|
||||||
))
|
|
||||||
},
|
|
||||||
display.inner().bounding_box().center() + Point::new(0, -20),
|
|
||||||
clock_text_style,
|
|
||||||
Alignment::Center,
|
|
||||||
)
|
|
||||||
.draw(display.inner_mut())
|
|
||||||
.unwrap();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(feature = "simulator")]
|
#[cfg(feature = "simulator")]
|
||||||
{
|
{
|
||||||
display.update();
|
ctx.display.update();
|
||||||
std::thread::sleep(core::time::Duration::from_millis(50));
|
std::thread::sleep(core::time::Duration::from_millis(50));
|
||||||
}
|
}
|
||||||
#[cfg(not(feature = "simulator"))]
|
#[cfg(not(feature = "simulator"))]
|
||||||
if update {
|
if ctx.update {
|
||||||
display.update();
|
ctx.display.update();
|
||||||
}
|
}
|
||||||
// TODO sleep on samd
|
#[cfg(not(feature = "simulator"))]
|
||||||
|
delay.delay_ms(50_u8);
|
||||||
|
|
||||||
update = false;
|
ctx.update = false;
|
||||||
hm_change = false;
|
ctx.hm_change = false;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub struct Context<'a> {
|
||||||
|
pub state: &'a mut State,
|
||||||
|
pub display: &'a mut Display,
|
||||||
|
pub hm_change: bool,
|
||||||
|
pub now: DateTime,
|
||||||
|
pub update: bool,
|
||||||
|
pub key_events: KeyEvents,
|
||||||
|
}
|
||||||
|
|
20
src/state.rs
20
src/state.rs
|
@ -1,18 +1,18 @@
|
||||||
use crate::energy::EnergyStatus;
|
use crate::{apps, energy::EnergyStatus};
|
||||||
|
|
||||||
|
/*pub struct State {
|
||||||
|
pub global: GlobalState,
|
||||||
|
pub mode: ModeState,
|
||||||
|
}*/
|
||||||
|
|
||||||
/// Global state
|
|
||||||
pub struct State {
|
pub struct State {
|
||||||
pub energy: EnergyStatus,
|
pub energy: EnergyStatus,
|
||||||
pub hour: u8,
|
pub hour: u8,
|
||||||
pub minute: u8,
|
pub minute: u8,
|
||||||
pub mode: Mode,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub enum Mode {
|
#[warn(clippy::large_enum_variant)]
|
||||||
Clock {
|
pub enum ModeState {
|
||||||
year: i32,
|
Clock(apps::clock::ClockState),
|
||||||
month: u8,
|
Dial(apps::dial::DialState),
|
||||||
day: u8,
|
|
||||||
week_day: u8,
|
|
||||||
},
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,3 @@
|
||||||
use tz::DateTime;
|
|
||||||
|
|
||||||
use crate::energy::EnergyStatus;
|
use crate::energy::EnergyStatus;
|
||||||
|
|
||||||
static WEEK_DAYS: [[u8; 3]; 7] = [
|
static WEEK_DAYS: [[u8; 3]; 7] = [
|
||||||
|
|
Loading…
Reference in a new issue