Skip to content

Commit

Permalink
Merge pull request #51 from Nitrokey/led
Browse files Browse the repository at this point in the history
Change LED patterns
  • Loading branch information
robin-nitrokey authored Jul 2, 2022
2 parents e0b4461 + 7c18e42 commit 0395723
Show file tree
Hide file tree
Showing 4 changed files with 146 additions and 72 deletions.
8 changes: 8 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
# Unreleased

## Features

- Change the LED patterns so that the LED is off by default, blinks white during a user confirmation request and blinks blue when winking([#34][])

[#34]: https://github.com/Nitrokey/nitrokey-3-firmware/issues/34

# v1.0.3 (2022-04-11)

This release fixes a FIDO authentication issue with Google.
Expand Down
1 change: 1 addition & 0 deletions runners/lpc55/board/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -36,5 +36,6 @@ pub use specifics::{
pub mod clock_controller;
pub mod nfc;
pub mod trussed;
pub mod ui;

// pub use rgb_led::RgbLed;
96 changes: 24 additions & 72 deletions runners/lpc55/board/src/trussed.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,10 @@ use crate::hal::{
peripherals::rtc::Rtc,
typestates::init_state,
};
use crate::ui::Status;
use crate::traits::buttons::{Press, Edge};
use crate::traits::rgb_led::{Intensities, RgbLed};
use trussed::platform::{consent, ui};
use crate::traits::rgb_led::RgbLed;
use trussed::platform::{self, consent};

// Assuming there will only be one way to
// get user presence, this should be fine.
Expand All @@ -32,8 +33,8 @@ RGB: RgbLed,
{
rtc: Rtc<init_state::Enabled>,
buttons: Option<BUTTONS>,
status: Status,
rgb: Option<RGB>,
wink: Option<core::ops::Range<Duration>>,
provisioner: bool,
}

Expand All @@ -48,25 +49,23 @@ RGB: RgbLed,
rgb: Option<RGB>,
provisioner: bool,
) -> Self {
let wink = None;
let status = Default::default();
#[cfg(not(feature = "no-buttons"))]
let ui = Self { rtc, buttons: _buttons, rgb, wink, provisioner };
let ui = Self { rtc, buttons: _buttons, status, rgb, provisioner };
#[cfg(feature = "no-buttons")]
let ui = Self { rtc, buttons: None, rgb, wink, provisioner };
let ui = Self { rtc, buttons: None, status, rgb, provisioner };

ui
}
}

// color codes Conor picked
const BLACK: Intensities = Intensities { red: 0, green: 0, blue: 0 };
const RED: Intensities = Intensities { red: u8::MAX, green: 0, blue: 0 };
const GREEN: Intensities = Intensities { red: 0, green: u8::MAX, blue: 0x02 };
#[allow(dead_code)]
const BLUE: Intensities = Intensities { red: 0, green: 0, blue: u8::MAX };
const TEAL: Intensities = Intensities { red: 0, green: u8::MAX, blue: 0x5a };
const ORANGE: Intensities = Intensities { red: u8::MAX, green: 0x7e, blue: 0 };
const WHITE: Intensities = Intensities { red: u8::MAX, green: u8::MAX, blue: u8::MAX };
fn refresh_ui(&mut self, uptime: Duration) {
if let Some(rgb) = &mut self.rgb {
self.status.refresh(uptime);
let mode = self.status.led_mode(self.provisioner);
rgb.set(mode.color(uptime));
}
}
}

impl<BUTTONS, RGB> trussed::platform::UserInterface for UserInterface<BUTTONS,RGB>
where
Expand Down Expand Up @@ -101,71 +100,24 @@ RGB: RgbLed,
}
}

fn set_status(&mut self, status: ui::Status) {
if let Some(rgb) = &mut self.rgb {

match status {
ui::Status::Idle => {
if self.provisioner {
// white
rgb.set(WHITE.into());
} else {
// green
rgb.set(GREEN.into());
}
},
ui::Status::Processing => {
// teal
rgb.set(TEAL.into());
}
ui::Status::WaitingForUserPresence => {
// orange
rgb.set(ORANGE.into());
},
ui::Status::Error => {
// Red
rgb.set(RED.into());
},
}

}

// Abort winking if the device is no longer idle
if status != ui::Status::Idle {
self.wink = None;
}
fn set_status(&mut self, status: platform::ui::Status) {
let uptime = self.uptime();
self.status.update(status, uptime);
self.refresh_ui(uptime);
}

fn refresh(&mut self) {
if self.rgb.is_none() {
return;
}

if let Some(wink) = self.wink.clone() {
let time = self.uptime();
if wink.contains(&time) {
// 250 ms white, 250 ms off
let color = if (time - wink.start).as_millis() % 500 < 250 {
WHITE
} else {
BLACK
};
self.rgb.as_mut().unwrap().set(color.into());
return;
} else {
self.set_status(ui::Status::Idle);
self.wink = None;
}
}
let uptime = self.uptime();
self.refresh_ui(uptime);
}

fn uptime(&mut self) -> Duration {
self.rtc.uptime()
}

fn wink(&mut self, duration: Duration) {
let time = self.uptime();
self.wink = Some(time..time + duration);
self.rgb.as_mut().unwrap().set(WHITE.into());
let uptime = self.uptime();
self.status = Status::Winking(uptime..uptime + duration);
self.refresh_ui(uptime);
}
}
113 changes: 113 additions & 0 deletions runners/lpc55/board/src/ui.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
use core::{ops::Range, time::Duration};

use trussed::platform::ui;

use crate::traits::rgb_led::Intensities;

const BLACK: Intensities = Intensities { red: 0, green: 0, blue: 0 };
const RED: Intensities = Intensities { red: u8::MAX, green: 0, blue: 0 };
const BLUE: Intensities = Intensities { red: 0, green: 0, blue: u8::MAX };
const TEAL: Intensities = Intensities { red: 0, green: u8::MAX, blue: 0x5a };
const WHITE: Intensities = Intensities { red: u8::MAX, green: u8::MAX, blue: u8::MAX };

pub enum Status {
Idle,
Processing,
WaitingForUserPresence(Duration),
Winking(Range<Duration>),
Error,
}

impl Status {
pub fn update(&mut self, status: ui::Status, uptime: Duration) {
if matches!(self, Self::Winking(_)) && status == ui::Status::Idle {
return;
}
*self = (status, uptime).into();
}

pub fn refresh(&mut self, uptime: Duration) {
if let Self::Winking(ref range) = self {
if !range.contains(&uptime) {
*self = Self::Idle;
}
}
}

pub fn led_mode(&self, is_provisioner: bool) -> LedMode {
match self {
Self::Idle => if is_provisioner {
LedMode::constant(WHITE)
} else {
LedMode::constant(BLACK)
},
Self::Processing => LedMode::constant(TEAL),
Self::WaitingForUserPresence(start) => LedMode::simple_blinking(WHITE, *start),
Self::Error => LedMode::constant(RED),
Self::Winking(range) => LedMode::simple_blinking(BLUE, range.start),
}
}
}

impl Default for Status {
fn default() -> Self {
Self::Idle
}
}

impl From<(ui::Status, Duration)> for Status {
fn from((status, uptime): (ui::Status, Duration)) -> Self {
match status {
ui::Status::Idle => Self::Idle,
ui::Status::Processing => Self::Processing,
ui::Status::WaitingForUserPresence => Self::WaitingForUserPresence(uptime),
ui::Status::Error => Self::Error,
}
}
}

pub enum LedMode {
Constant {
color: Intensities,
},
Blinking {
on_color: Intensities,
off_color: Intensities,
period: Duration,
start: Duration,
},
}

impl LedMode {
pub fn constant(color: Intensities) -> Self {
Self::Constant { color }
}

pub fn blinking(
on_color: Intensities,
off_color: Intensities,
period: Duration,
start: Duration,
) -> Self {
Self::Blinking { on_color, off_color, period, start }
}

pub fn simple_blinking(color: Intensities, start: Duration) -> Self {
Self::blinking(color, BLACK, Duration::from_millis(500), start)
}

pub fn color(&self, uptime: Duration) -> Intensities {
match self {
Self::Constant { color } => *color,
Self::Blinking { on_color, off_color, period, start } => {
let delta = (uptime - *start).as_millis() % period.as_millis();
let is_on = delta < period.as_millis() / 2;
if is_on {
*on_color
} else {
*off_color
}
},
}
}
}

0 comments on commit 0395723

Please sign in to comment.