add wpm calculation and cute puppygirl animations for the oled

This commit is contained in:
2025-12-30 00:53:54 +01:00
parent 5012c6d494
commit 157f8f15f8
19 changed files with 172 additions and 41 deletions

View File

@@ -1,15 +1,20 @@
use embassy_time::{Duration, Instant, Timer};
use embedded_graphics::{
mono_font::{MonoTextStyleBuilder, ascii::FONT_6X10},
pixelcolor::BinaryColor,
prelude::Point,
text::{Baseline, Text},
draw_target::DrawTarget,
Drawable,
Drawable, draw_target::DrawTarget, image::{Image, ImageRaw}, mono_font::{MonoTextStyleBuilder, iso_8859_1::FONT_10X20}, pixelcolor::BinaryColor, prelude::Point, text::{Baseline, Text}
};
use ssd1306::{prelude::*, I2CDisplayInterface, Ssd1306, mode::BufferedGraphicsMode};
use embassy_rp::i2c::I2c;
use core::fmt::Write;
use crate::wpm::WPM_CHANNEL;
// Helper enum for all possible animation states
enum PuppyAnim {
Sleep,
Idle,
Happy,
}
/// Display type encapsulating the I2C interface, its size and graphics mode
pub type Display = Ssd1306<
I2CInterface<I2c<'static, embassy_rp::peripherals::I2C0, embassy_rp::i2c::Async>>,
@@ -33,39 +38,91 @@ pub fn init_display(bus: I2c<'static, embassy_rp::peripherals::I2C0, embassy_rp:
}
}
/// Temp function which updates the display with currently pressed keys
pub fn update_display(
display: &mut Option<Display>,
pressed_keys: &[(usize, usize, crate::keymap::Action)],
) -> Option<()> {
pub async fn run_display(display: &mut Option<Display>) -> Option<()> {
if let Some(display) = display {
display.clear(BinaryColor::Off).ok()?;
let text_style = MonoTextStyleBuilder::new()
.font(&FONT_6X10)
.text_color(BinaryColor::On)
.build();
if pressed_keys.is_empty() {
Text::with_baseline("No keys", Point::zero(), text_style, Baseline::Top)
let mut wpm = 0;
let mut frame = 0;
let mut blink_frame = 0;
let mut last_frame_ms = Instant::now();
loop {
// Choose state based on wpm
let (state, frame_delay_ms) = if wpm == 0 {
(PuppyAnim::Sleep, 250)
} else if wpm < 60 {
// faster tail wagging, connected to wpm
let delay = (3000 / wpm.max(10)).clamp(100, 300);
(PuppyAnim::Idle, delay)
} else {
// even faster tail wagging!!
let delay = (4000 / wpm).clamp(16, 100);
(PuppyAnim::Happy, delay)
};
if last_frame_ms.elapsed().as_millis() >= frame_delay_ms {
// used for determining when to blink
blink_frame += 1;
// switch between tail left and right
frame = (frame + 1) % 2;
last_frame_ms = Instant::now();
}
// Choose image based on current state and tail position
let image_data = match (state, frame) {
(PuppyAnim::Sleep, 0) => include_bytes!("../assets/sleep-1.raw"),
(PuppyAnim::Sleep, 1) => include_bytes!("../assets/sleep-2.raw"),
(PuppyAnim::Idle, 0) => {
// blink every once in a while
if blink_frame % 20 < 5 {
include_bytes!("../assets/idle-blink-1.raw")
} else {
include_bytes!("../assets/idle-normal-1.raw")
}
},
(PuppyAnim::Idle, 1) => {
if blink_frame % 20 < 5 {
include_bytes!("../assets/idle-blink-2.raw")
} else {
include_bytes!("../assets/idle-normal-2.raw")
}
},
(PuppyAnim::Happy, 0) => include_bytes!("../assets/happy-1.raw"),
(PuppyAnim::Happy, 1) => include_bytes!("../assets/happy-2.raw"),
_ => include_bytes!("../assets/idle-normal-1.raw"),
};
display.clear(BinaryColor::Off).ok()?;
// Load and draw the chosen image
let raw: ImageRaw<BinaryColor> = ImageRaw::new(image_data, 32);
let im = Image::new(&raw, Point::new(0, 0));
im.draw(display).ok()?;
let text_style = MonoTextStyleBuilder::new()
.font(&FONT_10X20)
.text_color(BinaryColor::On)
.build();
match WPM_CHANNEL.try_receive() {
Ok(current_wpm) => {
wpm = current_wpm;
},
Err(_) => {},
}
let mut wpm_str = heapless::String::<32>::new();
write!(&mut wpm_str, "WPM: {}", wpm).ok();
// Print current WPM
Text::with_baseline(&wpm_str, Point::new(32 + 4, 6), text_style, Baseline::Top)
.draw(display)
.ok()?;
} else {
let mut y_pos = 0;
for (row_idx, col_idx, keycode) in pressed_keys.iter().take(3) {
let mut msg = heapless::String::<32>::new();
write!(&mut msg, "R{}C{}: 0x{:?}", row_idx, col_idx, keycode).ok();
Text::with_baseline(&msg, Point::new(0, y_pos), text_style, Baseline::Top)
.draw(display)
.ok()?;
y_pos += 10;
}
display.flush().ok()?;
Timer::after(Duration::from_millis(16)).await;
}
display.flush().ok()?;
Some(())
} else {
None
}