From c6c13aa70f63f5d269fdfd086878fe690193e63b Mon Sep 17 00:00:00 2001 From: issy Date: Sat, 18 Apr 2026 20:48:45 +0100 Subject: [PATCH 01/57] Refactor button creation --- firmware/simulator/src/lib.rs | 62 ++++++++++++++++------------------- 1 file changed, 29 insertions(+), 33 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 9b6366c..c77a658 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -41,10 +41,22 @@ static DISPLAY_3: StaticCell> = StaticCell::new(); static DISPLAY_4: StaticCell> = StaticCell::new(); static DISPLAYS: StaticCell>> = StaticCell::new(); -pub fn init_logging() { +fn init_logging() { console_log::init_with_level(Level::Debug).expect("logger init failed"); } +fn create_button_element(root_element: &Element) -> Result { + root_element + .append_child( + &window() + .unwrap() + .document() + .unwrap() + .create_element("button")?, + ) + .and_then(|el| Ok(el.unchecked_into::())) +} + #[wasm_bindgen] pub fn teardown() { let document = window() @@ -93,22 +105,14 @@ pub fn main() { root_element .set_attribute("style", "display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 2em auto 2em; gap: 1rem;") .unwrap(); - let _button_1_element = root_element - .append_child(&document.create_element("button").unwrap()) - .and_then(|el| Ok(el.unchecked_into::())) - .expect("Failed to create button 1 element"); - let _button_3_element = root_element - .append_child(&document.create_element("button").unwrap()) - .and_then(|el| Ok(el.unchecked_into::())) - .expect("Failed to create button 3 element"); - let _button_5_element = root_element - .append_child(&document.create_element("button").unwrap()) - .and_then(|el| Ok(el.unchecked_into::())) - .expect("Failed to create button 5 element"); - let _button_6_element = root_element - .append_child(&document.create_element("button").unwrap()) - .and_then(|el| Ok(el.unchecked_into::())) - .expect("Failed to create button 6 element"); + let _button_1_element = + create_button_element(&root_element).expect("Failed to create button 1 element"); + let _button_3_element = + create_button_element(&root_element).expect("Failed to create button 3 element"); + let _button_5_element = + create_button_element(&root_element).expect("Failed to create button 5 element"); + let _button_7_element = + create_button_element(&root_element).expect("Failed to create button 7 element"); let display_1_element = root_element .append_child(&document.create_element("div").unwrap()) @@ -143,22 +147,14 @@ pub fn main() { }) .expect("Failed to create display-4 element"); - let _button_2_element = root_element - .append_child(&document.create_element("button").unwrap()) - .and_then(|el| Ok(el.unchecked_into::())) - .expect("Failed to create button 2 element"); - let _button_4_element = root_element - .append_child(&document.create_element("button").unwrap()) - .and_then(|el| Ok(el.unchecked_into::())) - .expect("Failed to create button 4 element"); - let _button_6_element = root_element - .append_child(&document.create_element("button").unwrap()) - .and_then(|el| Ok(el.unchecked_into::())) - .expect("Failed to create button 6 element"); - let _button_8_element = root_element - .append_child(&document.create_element("button").unwrap()) - .and_then(|el| Ok(el.unchecked_into::())) - .expect("Failed to create button 8 element"); + let _button_2_element = + create_button_element(&root_element).expect("Failed to create button 2 element"); + let _button_4_element = + create_button_element(&root_element).expect("Failed to create button 4 element"); + let _button_6_element = + create_button_element(&root_element).expect("Failed to create button 6 element"); + let _button_8_element = + create_button_element(&root_element).expect("Failed to create button 8 element"); let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_ORANGE); let display_output_settings = OutputSettingsBuilder::new() From 760de75f87e60fa149223299ef6c84548d1fbd8e Mon Sep 17 00:00:00 2001 From: issy Date: Sat, 18 Apr 2026 20:55:11 +0100 Subject: [PATCH 02/57] Fix document access --- firmware/simulator/src/lib.rs | 43 ++++++++++++++++++++--------------- 1 file changed, 25 insertions(+), 18 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index c77a658..7c59868 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -24,7 +24,7 @@ use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; -use web_sys::{Element, HtmlButtonElement, Storage, window}; +use web_sys::{Document, Element, HtmlButtonElement, HtmlElement, Storage, window}; const STORAGE_KEY_PRESETS: &str = "presets"; const STORAGE_KEY_PRESET_ID: &str = "preset_id"; @@ -45,18 +45,25 @@ fn init_logging() { console_log::init_with_level(Level::Debug).expect("logger init failed"); } -fn create_button_element(root_element: &Element) -> Result { - root_element - .append_child( - &window() - .unwrap() - .document() - .unwrap() - .create_element("button")?, - ) +fn create_button_element( + document: &Document, + parent: &Element, +) -> Result { + parent + .append_child(&document.create_element("button")?) .and_then(|el| Ok(el.unchecked_into::())) } +fn create_display_element(document: &Document, parent: &Element) -> Result { + parent + .append_child(&document.create_element("div")?) + .and_then(|el| Ok(el.dyn_into::()?)) + .and_then(|el| { + el.set_attribute("style", "display: flex; justify-content: center;")?; + Ok(el) + }) +} + #[wasm_bindgen] pub fn teardown() { let document = window() @@ -106,13 +113,13 @@ pub fn main() { .set_attribute("style", "display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 2em auto 2em; gap: 1rem;") .unwrap(); let _button_1_element = - create_button_element(&root_element).expect("Failed to create button 1 element"); + create_button_element(&document, &root_element).expect("Failed to create button 1 element"); let _button_3_element = - create_button_element(&root_element).expect("Failed to create button 3 element"); + create_button_element(&document, &root_element).expect("Failed to create button 3 element"); let _button_5_element = - create_button_element(&root_element).expect("Failed to create button 5 element"); + create_button_element(&document, &root_element).expect("Failed to create button 5 element"); let _button_7_element = - create_button_element(&root_element).expect("Failed to create button 7 element"); + create_button_element(&document, &root_element).expect("Failed to create button 7 element"); let display_1_element = root_element .append_child(&document.create_element("div").unwrap()) @@ -148,13 +155,13 @@ pub fn main() { .expect("Failed to create display-4 element"); let _button_2_element = - create_button_element(&root_element).expect("Failed to create button 2 element"); + create_button_element(&document, &root_element).expect("Failed to create button 2 element"); let _button_4_element = - create_button_element(&root_element).expect("Failed to create button 4 element"); + create_button_element(&document, &root_element).expect("Failed to create button 4 element"); let _button_6_element = - create_button_element(&root_element).expect("Failed to create button 6 element"); + create_button_element(&document, &root_element).expect("Failed to create button 6 element"); let _button_8_element = - create_button_element(&root_element).expect("Failed to create button 8 element"); + create_button_element(&document, &root_element).expect("Failed to create button 8 element"); let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_ORANGE); let display_output_settings = OutputSettingsBuilder::new() From 3d522c714c017f6aae708c532e22f81e6d73499f Mon Sep 17 00:00:00 2001 From: issy Date: Sat, 18 Apr 2026 20:56:30 +0100 Subject: [PATCH 03/57] Refactor display element creation --- firmware/simulator/src/lib.rs | 40 +++++++---------------------------- 1 file changed, 8 insertions(+), 32 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 7c59868..bf407cc 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -121,38 +121,14 @@ pub fn main() { let _button_7_element = create_button_element(&document, &root_element).expect("Failed to create button 7 element"); - let display_1_element = root_element - .append_child(&document.create_element("div").unwrap()) - .and_then(|el| Ok(el.dyn_into::()?)) - .and_then(|el| { - el.set_attribute("style", "display: flex; justify-content: center;")?; - Ok(el) - }) - .expect("Failed to create display-1 element"); - let display_2_element = root_element - .append_child(&document.create_element("div").unwrap()) - .and_then(|el| Ok(el.dyn_into::()?)) - .and_then(|el| { - el.set_attribute("style", "display: flex; justify-content: center;")?; - Ok(el) - }) - .expect("Failed to create display-2 element"); - let display_3_element = root_element - .append_child(&document.create_element("div").unwrap()) - .and_then(|el| Ok(el.dyn_into::()?)) - .and_then(|el| { - el.set_attribute("style", "display: flex; justify-content: center;")?; - Ok(el) - }) - .expect("Failed to create display-3 element"); - let display_4_element = root_element - .append_child(&document.create_element("div").unwrap()) - .and_then(|el| Ok(el.dyn_into::()?)) - .and_then(|el| { - el.set_attribute("style", "display: flex; justify-content: center;")?; - Ok(el) - }) - .expect("Failed to create display-4 element"); + let display_1_element = create_display_element(&document, &root_element) + .expect("Failed to create display 1 element"); + let display_2_element = create_display_element(&document, &root_element) + .expect("Failed to create display 2 element"); + let display_3_element = create_display_element(&document, &root_element) + .expect("Failed to create display 3 element"); + let display_4_element = create_display_element(&document, &root_element) + .expect("Failed to create display 4 element"); let _button_2_element = create_button_element(&document, &root_element).expect("Failed to create button 2 element"); From 12e1d847f77239438a9e8276a38703f0eddcd95a Mon Sep 17 00:00:00 2001 From: issy Date: Sat, 18 Apr 2026 20:57:44 +0100 Subject: [PATCH 04/57] Comment explaining layout --- firmware/simulator/src/lib.rs | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index bf407cc..09e23c8 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -109,9 +109,14 @@ pub fn main() { }) .expect("Could not find root element with id 'simulator-root'"); + // Buttons 1 3 5 7 + // Displays 1 2 3 4 + // Buttons 2 4 6 8 + root_element .set_attribute("style", "display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 2em auto 2em; gap: 1rem;") .unwrap(); + let _button_1_element = create_button_element(&document, &root_element).expect("Failed to create button 1 element"); let _button_3_element = From 6a4e5b0390acc104f0f3fd0f4b3b2f2ffcb6bf83 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 13:01:58 +0100 Subject: [PATCH 05/57] Use enum to identify targeted display --- firmware/foundation/src/application/channels.rs | 9 ++++++++- firmware/foundation/src/application/state.rs | 15 +++++++-------- 2 files changed, 15 insertions(+), 9 deletions(-) diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index 3cc2c0f..22e54cf 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -52,8 +52,15 @@ impl AppChannel { pub type MidiOutChannel = AppChannel; +pub enum DisplayIdentifier { + DisplayOne, + DisplayTwo, + DisplayThree, + DisplayFour, +} + pub struct DisplayStateUpdateMessage { - pub(crate) display_index: i8, + pub(crate) display_identifier: DisplayIdentifier, pub(crate) top_row_text: DisplayText, pub(crate) top_row_color: Colour, pub(crate) bottom_row_text: DisplayText, diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 9f4898d..e165fa5 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -1,6 +1,6 @@ use crate::application::channels::{ - ButtonEventChannel, DisplayStateUpdateChannel, MidiOutChannel, StorageStateEvent, - StorageStateUpdateChannel, + ButtonEventChannel, DisplayIdentifier, DisplayStateUpdateChannel, MidiOutChannel, + StorageStateEvent, StorageStateUpdateChannel, }; use crate::layout::DisplayLayout; use crate::midi::{MidiReader, MidiWriter}; @@ -120,12 +120,11 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, loop { let update_message = self.channels.display_state_update.receive().await; - let target = match update_message.display_index { - 0 => &mut display_1_layout, - 1 => &mut display_2_layout, - 2 => &mut display_3_layout, - 3 => &mut display_4_layout, - _ => continue, // Invalid display index, ignore the message + let target = match update_message.display_identifier { + DisplayIdentifier::DisplayOne => &mut display_1_layout, + DisplayIdentifier::DisplayTwo => &mut display_2_layout, + DisplayIdentifier::DisplayThree => &mut display_3_layout, + DisplayIdentifier::DisplayFour => &mut display_4_layout, }; // TODO: Update layout for display } From faf4807aeedb47143a485b5927697cc2105f8837 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 18:22:29 +0100 Subject: [PATCH 06/57] Update display layout in task --- firmware/foundation/src/application/state.rs | 8 +++++++- firmware/foundation/src/protocol.rs | 16 ++++++++++++++++ 2 files changed, 23 insertions(+), 1 deletion(-) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index e165fa5..46b5947 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -126,7 +126,13 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, DisplayIdentifier::DisplayThree => &mut display_3_layout, DisplayIdentifier::DisplayFour => &mut display_4_layout, }; - // TODO: Update layout for display + target.set_top_box_colour(update_message.top_row_color.into()); + target.set_bottom_box_colour(update_message.bottom_row_color.into()); + + target.set_top_text(update_message.top_row_text); + target.set_bottom_text(update_message.bottom_row_text); + + target.draw().unwrap(); } } diff --git a/firmware/foundation/src/protocol.rs b/firmware/foundation/src/protocol.rs index 1f7aebe..5b63d12 100644 --- a/firmware/foundation/src/protocol.rs +++ b/firmware/foundation/src/protocol.rs @@ -3,6 +3,7 @@ use crate::generated::device_v1::Envelope; use alloc::string::String; use alloc::vec::Vec; use core::fmt::Debug; +use embedded_graphics::pixelcolor::{Rgb565, WebColors}; use serde::{Deserialize, Serialize}; const PROTOCOL_VERSION: u32 = 1; @@ -40,6 +41,21 @@ pub enum Colour { White = 8, } +impl Into for Colour { + fn into(self) -> Rgb565 { + match self { + Colour::Red => Rgb565::CSS_RED, + Colour::Green => Rgb565::CSS_GREEN, + Colour::Blue => Rgb565::CSS_BLUE, + Colour::Yellow => Rgb565::CSS_YELLOW, + Colour::Orange => Rgb565::CSS_ORANGE, + Colour::Purple => Rgb565::CSS_MAGENTA, + Colour::Cyan => Rgb565::CSS_CYAN, + Colour::White => Rgb565::CSS_WHITE, + } + } +} + impl From> for pb::Colour { fn from(colour: Option) -> Self { match colour { From 96aaafd5b3c0a0d9d7517414263cde042026231d Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 19:03:10 +0100 Subject: [PATCH 07/57] Add ButtonIdentifier enum --- .../foundation/src/application/channels.rs | 23 ++++++++++++++----- firmware/foundation/src/application/state.rs | 8 +++---- 2 files changed, 21 insertions(+), 10 deletions(-) diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index 22e54cf..bdd6b00 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -53,10 +53,10 @@ impl AppChannel { pub type MidiOutChannel = AppChannel; pub enum DisplayIdentifier { - DisplayOne, - DisplayTwo, - DisplayThree, - DisplayFour, + Display1, + Display2, + Display3, + Display4, } pub struct DisplayStateUpdateMessage { @@ -69,9 +69,20 @@ pub struct DisplayStateUpdateMessage { pub type DisplayStateUpdateChannel = AppChannel; +pub enum ButtonIdentifier { + Button1, + Button2, + Button3, + Button4, + Button5, + Button6, + Button7, + Button8, +} + pub enum ButtonEvent { - Pressed { button_index: i8 }, - Released { button_index: i8 }, + Pressed { button_identifier: ButtonIdentifier }, + Released { button_identifier: ButtonIdentifier }, } pub type ButtonEventChannel = AppChannel; diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 46b5947..8ccf5f7 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -121,10 +121,10 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, loop { let update_message = self.channels.display_state_update.receive().await; let target = match update_message.display_identifier { - DisplayIdentifier::DisplayOne => &mut display_1_layout, - DisplayIdentifier::DisplayTwo => &mut display_2_layout, - DisplayIdentifier::DisplayThree => &mut display_3_layout, - DisplayIdentifier::DisplayFour => &mut display_4_layout, + DisplayIdentifier::Display1 => &mut display_1_layout, + DisplayIdentifier::Display2 => &mut display_2_layout, + DisplayIdentifier::Display3 => &mut display_3_layout, + DisplayIdentifier::Display4 => &mut display_4_layout, }; target.set_top_box_colour(update_message.top_row_color.into()); target.set_bottom_box_colour(update_message.bottom_row_color.into()); From c73568dda8608a6e97eaf107a4934bec3f1cecd8 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 20:13:50 +0100 Subject: [PATCH 08/57] Inject external button event channel --- firmware/foundation/src/application/state.rs | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 8ccf5f7..9e9bbf6 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -58,6 +58,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, midi_reader: &'a mut MR, midi_writer: &'a mut MW, storage_manager: &'a mut SM, + button_event_channel: ButtonEventChannel, ) -> Self { Self { midi_streams: MidiStreams { @@ -68,7 +69,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, midi_out: MidiOutChannel::new(), display_state_update: DisplayStateUpdateChannel::new(), storage_state_update: StorageStateUpdateChannel::new(), - button_event: ButtonEventChannel::new(), + button_event: button_event_channel, }, storage_manager, } @@ -157,6 +158,7 @@ pub struct ApplicationBuilder<'a, MR: MidiReader, MW: MidiWriter, SM: StorageMan midi_reader: Option<&'a mut MR>, midi_writer: Option<&'a mut MW>, storage_manager: Option<&'a mut SM>, + button_event_channel: Option, } impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder<'a, MR, MW, SM> { @@ -165,6 +167,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< midi_reader: None, midi_writer: None, storage_manager: None, + button_event_channel: None, } } @@ -183,11 +186,18 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< self } + pub fn with_button_event_channel(mut self, button_event_channel: ButtonEventChannel) -> Self { + self.button_event_channel = Some(button_event_channel); + self + } + pub fn build(self) -> Application<'a, MR, MW, SM> { Application::new( self.midi_reader.expect("MIDI reader is required"), self.midi_writer.expect("MIDI writer is required"), self.storage_manager.expect("Storage manager is required"), + self.button_event_channel + .expect("Button event channel is required"), ) } } From 553daa2dc8f1e8db9c4ac244cbbbce69a6404d46 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 20:16:44 +0100 Subject: [PATCH 09/57] Restrict drawable type --- firmware/foundation/src/application/state.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 9e9bbf6..82ba58f 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -113,7 +113,10 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, pub async fn display_task>( &self, displays: &mut Displays<'_, D>, - ) -> ! { + ) -> ! + where + ::Error: core::fmt::Debug, + { let mut display_1_layout = DisplayLayout::new(displays.display_1); let mut display_2_layout = DisplayLayout::new(displays.display_2); let mut display_3_layout = DisplayLayout::new(displays.display_3); From d6de978491e677c7e1974da48b67dd320cc1ec26 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 20:17:51 +0100 Subject: [PATCH 10/57] Initialise button event channel externally --- firmware/firmware/src/main.rs | 4 ++++ firmware/simulator/src/lib.rs | 6 ++++-- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/firmware/firmware/src/main.rs b/firmware/firmware/src/main.rs index 1076f08..57a2bee 100644 --- a/firmware/firmware/src/main.rs +++ b/firmware/firmware/src/main.rs @@ -42,6 +42,7 @@ use esp_hal::time::Rate; use esp_hal::timer::timg::TimerGroup; use esp_hal::uart::{Config as UartConfig, DataBits, Parity, StopBits, Uart, UartRx, UartTx}; use esp_hal::{Async, Blocking}; +use foundation::application::channels::ButtonEventChannel; use foundation::application::state::{Application, ApplicationBuilder, Displays}; use log::info; use midi::{UartMidiReader, UartMidiWriter}; @@ -76,6 +77,7 @@ static DISPLAYS: StaticCell> = StaticCell::new(); static UART_MIDI_READER: StaticCell = StaticCell::new(); static UART_MIDI_WRITER: StaticCell = StaticCell::new(); static STORAGE_MANAGER: StaticCell = StaticCell::new(); +static BUTTON_EVENT_CHANNEL: StaticCell = StaticCell::new(); static SPI_BUS: StaticCell>>> = StaticCell::new(); static APP: StaticCell = StaticCell::new(); @@ -217,12 +219,14 @@ async fn main(spawner: Spawner) -> ! { let midi_reader = UART_MIDI_READER.init(UartMidiReader::new(rx)); let midi_writer = UART_MIDI_WRITER.init(UartMidiWriter::new(tx)); let storage_manager = STORAGE_MANAGER.init(FakeStorageManager::default()); + let button_event_channel = BUTTON_EVENT_CHANNEL.init(ButtonEventChannel::new()); let app = APP.init( ApplicationBuilder::new() .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) + .with_button_event_channel(*button_event_channel) .build(), ); diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 09e23c8..3faa567 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -50,13 +50,13 @@ fn create_button_element( parent: &Element, ) -> Result { parent - .append_child(&document.create_element("button")?) + .append_child(&document.create_element("button")?.into()) .and_then(|el| Ok(el.unchecked_into::())) } fn create_display_element(document: &Document, parent: &Element) -> Result { parent - .append_child(&document.create_element("div")?) + .append_child(&document.create_element("div")?.into()) .and_then(|el| Ok(el.dyn_into::()?)) .and_then(|el| { el.set_attribute("style", "display: flex; justify-content: center;")?; @@ -198,12 +198,14 @@ pub fn main() { let midi_reader = MIDI_READER.init(FakeMidiReader::default()); let midi_writer = MIDI_WRITER.init(FakeMidiWriter::default()); let storage_manager = STORAGE_MANAGER.init(LocalStorageManager::new(local_storage)); + let button_event_channel = foundation::application::channels::ButtonEventChannel::new(); let app = APP.init( ApplicationBuilder::new() .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) + .with_button_event_channel(button_event_channel) .build(), ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); From 88544d7e21c92ba5c368cf409e961fee996fe7cf Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 22:03:22 +0100 Subject: [PATCH 11/57] derive Copy, Clone for enums --- firmware/foundation/src/application/channels.rs | 2 ++ 1 file changed, 2 insertions(+) diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index bdd6b00..f912f10 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -69,6 +69,7 @@ pub struct DisplayStateUpdateMessage { pub type DisplayStateUpdateChannel = AppChannel; +#[derive(Copy, Clone)] pub enum ButtonIdentifier { Button1, Button2, @@ -80,6 +81,7 @@ pub enum ButtonIdentifier { Button8, } +#[derive(Copy, Clone)] pub enum ButtonEvent { Pressed { button_identifier: ButtonIdentifier }, Released { button_identifier: ButtonIdentifier }, From d52aefad68aed29e74631c47c7012cbe9bdcc309 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 22:03:35 +0100 Subject: [PATCH 12/57] Trying to implement click event handlers --- firmware/simulator/src/lib.rs | 111 ++++++++++++++++++++++++---------- 1 file changed, 79 insertions(+), 32 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 3faa567..4edb76f 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -19,11 +19,13 @@ use embedded_graphics::{ use embedded_graphics_web_simulator::{ display::WebSimulatorDisplay, output_settings::OutputSettingsBuilder, }; +use foundation::application::channels::{ButtonEvent, ButtonEventChannel, ButtonIdentifier}; use foundation::application::state::{Application, ApplicationBuilder, Displays}; use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; +use web_sys::js_sys::futures::spawn_local; use web_sys::{Document, Element, HtmlButtonElement, HtmlElement, Storage, window}; const STORAGE_KEY_PRESETS: &str = "presets"; @@ -48,10 +50,25 @@ fn init_logging() { fn create_button_element( document: &Document, parent: &Element, + button_identifier: ButtonIdentifier, + button_event_channel: &ButtonEventChannel, ) -> Result { - parent + let element = parent .append_child(&document.create_element("button")?.into()) - .and_then(|el| Ok(el.unchecked_into::())) + .and_then(|el| Ok(el.unchecked_into::()))?; + let sender = button_event_channel.clone(); + let button_identifier_new = button_identifier.clone(); + let closure = Closure::wrap(Box::new(move || { + spawn_local(async move { + sender + .send(ButtonEvent::Pressed { + button_identifier: button_identifier_new, + }) + .await; + }); + }) as Box); + element.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())?; + Ok(element) } fn create_display_element(document: &Document, parent: &Element) -> Result { @@ -88,15 +105,10 @@ pub fn main() { .expect("Failed to access localStorage") .expect("No localStorage"), ); - - let _initial_preset_id: u8 = local_storage - .get_item(STORAGE_KEY_PRESET_ID) - .expect("Failed to get item from localStorage") - .map(|v| { - v.parse::() - .expect("Failed to parse item from localStorage") - }) - .unwrap_or(0); + let midi_reader = MIDI_READER.init(FakeMidiReader::default()); + let midi_writer = MIDI_WRITER.init(FakeMidiWriter::default()); + let storage_manager = STORAGE_MANAGER.init(LocalStorageManager::new(local_storage)); + let button_event_channel = foundation::application::channels::ButtonEventChannel::new(); let document = window() .and_then(|win| win.document()) @@ -117,14 +129,34 @@ pub fn main() { .set_attribute("style", "display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 2em auto 2em; gap: 1rem;") .unwrap(); - let _button_1_element = - create_button_element(&document, &root_element).expect("Failed to create button 1 element"); - let _button_3_element = - create_button_element(&document, &root_element).expect("Failed to create button 3 element"); - let _button_5_element = - create_button_element(&document, &root_element).expect("Failed to create button 5 element"); - let _button_7_element = - create_button_element(&document, &root_element).expect("Failed to create button 7 element"); + let _button_1_element = create_button_element( + &document, + &root_element, + ButtonIdentifier::Button1, + &button_event_channel, + ) + .expect("Failed to create button 1 element"); + let _button_3_element = create_button_element( + &document, + &root_element, + ButtonIdentifier::Button3, + &button_event_channel, + ) + .expect("Failed to create button 3 element"); + let _button_5_element = create_button_element( + &document, + &root_element, + ButtonIdentifier::Button5, + &button_event_channel, + ) + .expect("Failed to create button 5 element"); + let _button_7_element = create_button_element( + &document, + &root_element, + ButtonIdentifier::Button7, + &button_event_channel, + ) + .expect("Failed to create button 7 element"); let display_1_element = create_display_element(&document, &root_element) .expect("Failed to create display 1 element"); @@ -135,14 +167,34 @@ pub fn main() { let display_4_element = create_display_element(&document, &root_element) .expect("Failed to create display 4 element"); - let _button_2_element = - create_button_element(&document, &root_element).expect("Failed to create button 2 element"); - let _button_4_element = - create_button_element(&document, &root_element).expect("Failed to create button 4 element"); - let _button_6_element = - create_button_element(&document, &root_element).expect("Failed to create button 6 element"); - let _button_8_element = - create_button_element(&document, &root_element).expect("Failed to create button 8 element"); + let _button_2_element = create_button_element( + &document, + &root_element, + ButtonIdentifier::Button2, + &button_event_channel, + ) + .expect("Failed to create button 2 element"); + let _button_4_element = create_button_element( + &document, + &root_element, + ButtonIdentifier::Button4, + &button_event_channel, + ) + .expect("Failed to create button 4 element"); + let _button_6_element = create_button_element( + &document, + &root_element, + ButtonIdentifier::Button6, + &button_event_channel, + ) + .expect("Failed to create button 6 element"); + let _button_8_element = create_button_element( + &document, + &root_element, + ButtonIdentifier::Button8, + &button_event_channel, + ) + .expect("Failed to create button 8 element"); let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_ORANGE); let display_output_settings = OutputSettingsBuilder::new() @@ -195,11 +247,6 @@ pub fn main() { display_1.flush().unwrap(); display_2.flush().unwrap(); - let midi_reader = MIDI_READER.init(FakeMidiReader::default()); - let midi_writer = MIDI_WRITER.init(FakeMidiWriter::default()); - let storage_manager = STORAGE_MANAGER.init(LocalStorageManager::new(local_storage)); - let button_event_channel = foundation::application::channels::ButtonEventChannel::new(); - let app = APP.init( ApplicationBuilder::new() .with_midi_reader(midi_reader) From 9c209b16bd18db1a567b416176102b86f289ecb5 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 22:05:07 +0100 Subject: [PATCH 13/57] This still doesn't work --- firmware/simulator/src/lib.rs | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 4edb76f..f0c6d55 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -51,16 +51,15 @@ fn create_button_element( document: &Document, parent: &Element, button_identifier: ButtonIdentifier, - button_event_channel: &ButtonEventChannel, + button_event_channel: ButtonEventChannel, ) -> Result { let element = parent .append_child(&document.create_element("button")?.into()) .and_then(|el| Ok(el.unchecked_into::()))?; - let sender = button_event_channel.clone(); let button_identifier_new = button_identifier.clone(); let closure = Closure::wrap(Box::new(move || { spawn_local(async move { - sender + button_event_channel .send(ButtonEvent::Pressed { button_identifier: button_identifier_new, }) From 1ae8481b3d292a7892bf573e9e1013d8540705b1 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 22:06:40 +0100 Subject: [PATCH 14/57] Start again --- firmware/simulator/src/lib.rs | 90 +++++++---------------------------- 1 file changed, 18 insertions(+), 72 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index f0c6d55..3261081 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -50,24 +50,10 @@ fn init_logging() { fn create_button_element( document: &Document, parent: &Element, - button_identifier: ButtonIdentifier, - button_event_channel: ButtonEventChannel, ) -> Result { - let element = parent + parent .append_child(&document.create_element("button")?.into()) - .and_then(|el| Ok(el.unchecked_into::()))?; - let button_identifier_new = button_identifier.clone(); - let closure = Closure::wrap(Box::new(move || { - spawn_local(async move { - button_event_channel - .send(ButtonEvent::Pressed { - button_identifier: button_identifier_new, - }) - .await; - }); - }) as Box); - element.add_event_listener_with_callback("click", closure.as_ref().unchecked_ref())?; - Ok(element) + .and_then(|el| Ok(el.unchecked_into::())) } fn create_display_element(document: &Document, parent: &Element) -> Result { @@ -128,34 +114,14 @@ pub fn main() { .set_attribute("style", "display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 2em auto 2em; gap: 1rem;") .unwrap(); - let _button_1_element = create_button_element( - &document, - &root_element, - ButtonIdentifier::Button1, - &button_event_channel, - ) - .expect("Failed to create button 1 element"); - let _button_3_element = create_button_element( - &document, - &root_element, - ButtonIdentifier::Button3, - &button_event_channel, - ) - .expect("Failed to create button 3 element"); - let _button_5_element = create_button_element( - &document, - &root_element, - ButtonIdentifier::Button5, - &button_event_channel, - ) - .expect("Failed to create button 5 element"); - let _button_7_element = create_button_element( - &document, - &root_element, - ButtonIdentifier::Button7, - &button_event_channel, - ) - .expect("Failed to create button 7 element"); + let _button_1_element = + create_button_element(&document, &root_element).expect("Failed to create button 1 element"); + let _button_3_element = + create_button_element(&document, &root_element).expect("Failed to create button 3 element"); + let _button_5_element = + create_button_element(&document, &root_element).expect("Failed to create button 5 element"); + let _button_7_element = + create_button_element(&document, &root_element).expect("Failed to create button 7 element"); let display_1_element = create_display_element(&document, &root_element) .expect("Failed to create display 1 element"); @@ -166,34 +132,14 @@ pub fn main() { let display_4_element = create_display_element(&document, &root_element) .expect("Failed to create display 4 element"); - let _button_2_element = create_button_element( - &document, - &root_element, - ButtonIdentifier::Button2, - &button_event_channel, - ) - .expect("Failed to create button 2 element"); - let _button_4_element = create_button_element( - &document, - &root_element, - ButtonIdentifier::Button4, - &button_event_channel, - ) - .expect("Failed to create button 4 element"); - let _button_6_element = create_button_element( - &document, - &root_element, - ButtonIdentifier::Button6, - &button_event_channel, - ) - .expect("Failed to create button 6 element"); - let _button_8_element = create_button_element( - &document, - &root_element, - ButtonIdentifier::Button8, - &button_event_channel, - ) - .expect("Failed to create button 8 element"); + let _button_2_element = + create_button_element(&document, &root_element).expect("Failed to create button 2 element"); + let _button_4_element = + create_button_element(&document, &root_element).expect("Failed to create button 4 element"); + let _button_6_element = + create_button_element(&document, &root_element).expect("Failed to create button 6 element"); + let _button_8_element = + create_button_element(&document, &root_element).expect("Failed to create button 8 element"); let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_ORANGE); let display_output_settings = OutputSettingsBuilder::new() From fb680cc231592fe6d7fe3aeba56be835d854ae64 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 22:08:11 +0100 Subject: [PATCH 15/57] Working closure --- firmware/simulator/src/lib.rs | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 3261081..e2f9018 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -138,9 +138,17 @@ pub fn main() { create_button_element(&document, &root_element).expect("Failed to create button 4 element"); let _button_6_element = create_button_element(&document, &root_element).expect("Failed to create button 6 element"); - let _button_8_element = + let button_8_element = create_button_element(&document, &root_element).expect("Failed to create button 8 element"); + let closure = Closure::wrap(Box::new(move || { + web_sys::console::log_1(&"Clicked!".into()); + }) as Box); + button_8_element + .add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()) + .expect("Failed to add event listener to button 8"); + closure.forget(); + let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_ORANGE); let display_output_settings = OutputSettingsBuilder::new() .scale(1) From f2a5e1386b32667f1c7e9fdbbc3fb6038d8a8a64 Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 22:09:22 +0100 Subject: [PATCH 16/57] Nope --- firmware/simulator/src/lib.rs | 10 +--------- 1 file changed, 1 insertion(+), 9 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index e2f9018..3261081 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -138,17 +138,9 @@ pub fn main() { create_button_element(&document, &root_element).expect("Failed to create button 4 element"); let _button_6_element = create_button_element(&document, &root_element).expect("Failed to create button 6 element"); - let button_8_element = + let _button_8_element = create_button_element(&document, &root_element).expect("Failed to create button 8 element"); - let closure = Closure::wrap(Box::new(move || { - web_sys::console::log_1(&"Clicked!".into()); - }) as Box); - button_8_element - .add_event_listener_with_callback("click", closure.as_ref().unchecked_ref()) - .expect("Failed to add event listener to button 8"); - closure.forget(); - let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_ORANGE); let display_output_settings = OutputSettingsBuilder::new() .scale(1) From b59335df91bdc4f8851c77dde135ade938d3c30e Mon Sep 17 00:00:00 2001 From: issy Date: Sun, 19 Apr 2026 22:15:38 +0100 Subject: [PATCH 17/57] Tidying --- firmware/Cargo.lock | 1 + firmware/simulator/Cargo.toml | 1 + firmware/simulator/src/lib.rs | 7 ++----- 3 files changed, 4 insertions(+), 5 deletions(-) diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index 4aea81c..77f69a2 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -4542,6 +4542,7 @@ dependencies = [ "static_cell", "trunk", "wasm-bindgen", + "wasm-bindgen-futures", "web-sys", ] diff --git a/firmware/simulator/Cargo.toml b/firmware/simulator/Cargo.toml index 9d81dfb..afa1d24 100644 --- a/firmware/simulator/Cargo.toml +++ b/firmware/simulator/Cargo.toml @@ -9,6 +9,7 @@ crate-type = ["cdylib", "rlib"] [dependencies] foundation = { path = "../foundation" } wasm-bindgen = { version = "0.2.114" } +wasm-bindgen-futures = { version = "0.4.68" } async_wasm_task = { version = "0.2.3" } web-sys = { version = "0.3.91", features = ["console", "CanvasRenderingContext2d", "Document", "HtmlButtonElement", "Element", "HtmlCanvasElement", "Window", "Text", "Storage"] } console_error_panic_hook = { version = "0.1.7" } diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 3261081..fd7f2f5 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -3,12 +3,11 @@ mod sleep; mod storage; use crate::midi::{FakeMidiReader, FakeMidiWriter}; -use crate::storage::{LocalStorageManager, Preset}; +use crate::storage::LocalStorageManager; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::prelude::Primitive; use embedded_graphics::prelude::RgbColor; -use embedded_graphics::primitives::{PrimitiveStyleBuilder, StyledDrawable}; use embedded_graphics::{ Drawable, mono_font::MonoTextStyle, @@ -19,14 +18,12 @@ use embedded_graphics::{ use embedded_graphics_web_simulator::{ display::WebSimulatorDisplay, output_settings::OutputSettingsBuilder, }; -use foundation::application::channels::{ButtonEvent, ButtonEventChannel, ButtonIdentifier}; use foundation::application::state::{Application, ApplicationBuilder, Displays}; use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; -use web_sys::js_sys::futures::spawn_local; -use web_sys::{Document, Element, HtmlButtonElement, HtmlElement, Storage, window}; +use web_sys::{Document, Element, HtmlButtonElement, Storage, window}; const STORAGE_KEY_PRESETS: &str = "presets"; const STORAGE_KEY_PRESET_ID: &str = "preset_id"; From f9ea3a5a68a8ad7f1c8e47027bcd06a4fb70c8f1 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:05:32 +0100 Subject: [PATCH 18/57] Event listener working Tidy --- firmware/simulator/Cargo.toml | 2 +- firmware/simulator/src/lib.rs | 20 +++++++++++++++++--- 2 files changed, 18 insertions(+), 4 deletions(-) diff --git a/firmware/simulator/Cargo.toml b/firmware/simulator/Cargo.toml index afa1d24..cd08647 100644 --- a/firmware/simulator/Cargo.toml +++ b/firmware/simulator/Cargo.toml @@ -11,7 +11,7 @@ foundation = { path = "../foundation" } wasm-bindgen = { version = "0.2.114" } wasm-bindgen-futures = { version = "0.4.68" } async_wasm_task = { version = "0.2.3" } -web-sys = { version = "0.3.91", features = ["console", "CanvasRenderingContext2d", "Document", "HtmlButtonElement", "Element", "HtmlCanvasElement", "Window", "Text", "Storage"] } +web-sys = { version = "0.3.91", features = ["console", "CanvasRenderingContext2d", "Document", "HtmlButtonElement", "Element", "HtmlCanvasElement", "Window", "Text", "Storage", "EventListener", "EventListenerOptions", "Event", "EventTarget"] } console_error_panic_hook = { version = "0.1.7" } embedded-graphics = { workspace = true } diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index fd7f2f5..0d60bd8 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -23,7 +23,8 @@ use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; -use web_sys::{Document, Element, HtmlButtonElement, Storage, window}; +use wasm_bindgen_futures::spawn_local; +use web_sys::{Document, Element, EventListener, HtmlButtonElement, Storage, window}; const STORAGE_KEY_PRESETS: &str = "presets"; const STORAGE_KEY_PRESET_ID: &str = "preset_id"; @@ -75,6 +76,8 @@ pub fn teardown() { } } +async fn bloop() {} + #[wasm_bindgen] pub fn main() { console_error_panic_hook::set_once(); @@ -108,8 +111,8 @@ pub fn main() { // Buttons 2 4 6 8 root_element - .set_attribute("style", "display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 2em auto 2em; gap: 1rem;") - .unwrap(); + .set_attribute("style", "display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 2em auto 2em; gap: 1rem;") + .unwrap(); let _button_1_element = create_button_element(&document, &root_element).expect("Failed to create button 1 element"); @@ -138,6 +141,17 @@ pub fn main() { let _button_8_element = create_button_element(&document, &root_element).expect("Failed to create button 8 element"); + let l = EventListener::new(); + l.set_handle_event(move || { + spawn_local(async { + bloop().await; + info!("Hello from the event listener!"); + }); + }); + _button_8_element + .add_event_listener_with_event_listener("click", &l) + .unwrap(); + let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_ORANGE); let display_output_settings = OutputSettingsBuilder::new() .scale(1) From b4c1fc18ec55b0829ae05ba8d80b82596355145c Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:12:33 +0100 Subject: [PATCH 19/57] Working --- firmware/simulator/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 0d60bd8..0ef94d9 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -23,7 +23,6 @@ use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; -use wasm_bindgen_futures::spawn_local; use web_sys::{Document, Element, EventListener, HtmlButtonElement, Storage, window}; const STORAGE_KEY_PRESETS: &str = "presets"; @@ -142,12 +141,13 @@ pub fn main() { create_button_element(&document, &root_element).expect("Failed to create button 8 element"); let l = EventListener::new(); - l.set_handle_event(move || { - spawn_local(async { - bloop().await; - info!("Hello from the event listener!"); - }); - }); + l.set_handle_event( + Closure::wrap(Box::new(move |_event: web_sys::Event| { + info!("Button clicked!"); + }) as Box) + .as_ref() + .unchecked_ref(), + ); _button_8_element .add_event_listener_with_event_listener("click", &l) .unwrap(); From 8186ec9eabaa2180f6a1d860d0530bd624c48425 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:13:04 +0100 Subject: [PATCH 20/57] Spawn async task --- firmware/simulator/src/lib.rs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 0ef94d9..c4a5e05 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -23,6 +23,7 @@ use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; +use web_sys::js_sys::futures::spawn_local; use web_sys::{Document, Element, EventListener, HtmlButtonElement, Storage, window}; const STORAGE_KEY_PRESETS: &str = "presets"; @@ -143,6 +144,9 @@ pub fn main() { let l = EventListener::new(); l.set_handle_event( Closure::wrap(Box::new(move |_event: web_sys::Event| { + spawn_local(async move { + bloop().await; + }); info!("Button clicked!"); }) as Box) .as_ref() From e617e6f86277ae69f0e14caa56d60adc581b9b1b Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:13:50 +0100 Subject: [PATCH 21/57] Add task content --- firmware/simulator/src/lib.rs | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index c4a5e05..30fc5d7 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -3,6 +3,7 @@ mod sleep; mod storage; use crate::midi::{FakeMidiReader, FakeMidiWriter}; +use crate::sleep::sleep; use crate::storage::LocalStorageManager; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::mono_font::ascii::FONT_10X20; @@ -23,6 +24,7 @@ use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; +use web_sys::console::info; use web_sys::js_sys::futures::spawn_local; use web_sys::{Document, Element, EventListener, HtmlButtonElement, Storage, window}; @@ -76,7 +78,11 @@ pub fn teardown() { } } -async fn bloop() {} +async fn bloop() { + info!("Starting bloop..."); + sleep(1000).await; + info!("Bloop complete!"); +} #[wasm_bindgen] pub fn main() { From 55a520a491de12912758b2e48250e0669c27d8b5 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:20:19 +0100 Subject: [PATCH 22/57] Take shared reference --- firmware/firmware/src/main.rs | 3 ++- firmware/foundation/src/application/state.rs | 15 +++++++++------ firmware/simulator/src/lib.rs | 2 +- 3 files changed, 12 insertions(+), 8 deletions(-) diff --git a/firmware/firmware/src/main.rs b/firmware/firmware/src/main.rs index 57a2bee..69fa2fe 100644 --- a/firmware/firmware/src/main.rs +++ b/firmware/firmware/src/main.rs @@ -31,6 +31,7 @@ use display_interface_spi::SPIInterface; use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::{Mutex, raw::NoopRawMutex}; +use embassy_sync::channel::{Receiver, Sender}; use embassy_time::Delay; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; @@ -226,7 +227,7 @@ async fn main(spawner: Spawner) -> ! { .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) - .with_button_event_channel(*button_event_channel) + .with_button_event_channel(button_event_channel) .build(), ); diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 82ba58f..e5e2f0f 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -38,16 +38,16 @@ pub(crate) struct MidiStreams<'a, MR: MidiReader, MW: MidiWriter> { writer: RefCell<&'a mut MW>, } -pub(crate) struct InternalChannels { +pub(crate) struct InternalChannels<'a> { midi_out: MidiOutChannel, display_state_update: DisplayStateUpdateChannel, storage_state_update: StorageStateUpdateChannel, - button_event: ButtonEventChannel, + button_event: &'a ButtonEventChannel, } pub struct Application<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> { pub(crate) midi_streams: MidiStreams<'a, MR, MW>, - pub(crate) channels: InternalChannels, + pub(crate) channels: InternalChannels<'a>, pub(crate) storage_manager: &'a mut SM, // TODO: Add protocol streams // TODO: Add buttons @@ -58,7 +58,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, midi_reader: &'a mut MR, midi_writer: &'a mut MW, storage_manager: &'a mut SM, - button_event_channel: ButtonEventChannel, + button_event_channel: &'a ButtonEventChannel, ) -> Self { Self { midi_streams: MidiStreams { @@ -161,7 +161,7 @@ pub struct ApplicationBuilder<'a, MR: MidiReader, MW: MidiWriter, SM: StorageMan midi_reader: Option<&'a mut MR>, midi_writer: Option<&'a mut MW>, storage_manager: Option<&'a mut SM>, - button_event_channel: Option, + button_event_channel: Option<&'a ButtonEventChannel>, } impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder<'a, MR, MW, SM> { @@ -189,7 +189,10 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< self } - pub fn with_button_event_channel(mut self, button_event_channel: ButtonEventChannel) -> Self { + pub fn with_button_event_channel( + mut self, + button_event_channel: &'a ButtonEventChannel, + ) -> Self { self.button_event_channel = Some(button_event_channel); self } diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 30fc5d7..978b4aa 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -218,7 +218,7 @@ pub fn main() { .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) - .with_button_event_channel(button_event_channel) + .with_button_event_channel(&button_event_channel) .build(), ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); From d4bfe44b162c8e364bfb498be2d263035004f741 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:21:38 +0100 Subject: [PATCH 23/57] Do not drop --- firmware/simulator/src/lib.rs | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 978b4aa..5bc6b02 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -148,16 +148,15 @@ pub fn main() { create_button_element(&document, &root_element).expect("Failed to create button 8 element"); let l = EventListener::new(); - l.set_handle_event( - Closure::wrap(Box::new(move |_event: web_sys::Event| { - spawn_local(async move { - bloop().await; - }); - info!("Button clicked!"); - }) as Box) - .as_ref() - .unchecked_ref(), - ); + let f = Closure::wrap(Box::new(move |_event: web_sys::Event| { + spawn_local(async move { + bloop().await; + }); + info!("Button clicked!"); + }) as Box); + l.set_handle_event(f.as_ref().unchecked_ref()); + f.forget(); + _button_8_element .add_event_listener_with_event_listener("click", &l) .unwrap(); From 4b1e949ac74c51c664a211842c897577352fa711 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:23:52 +0100 Subject: [PATCH 24/57] Fix lifetime --- firmware/simulator/src/lib.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 5bc6b02..fedafd8 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -19,6 +19,7 @@ use embedded_graphics::{ use embedded_graphics_web_simulator::{ display::WebSimulatorDisplay, output_settings::OutputSettingsBuilder, }; +use foundation::application::channels::ButtonEventChannel; use foundation::application::state::{Application, ApplicationBuilder, Displays}; use log::{Level, info}; use static_cell::StaticCell; @@ -35,6 +36,7 @@ static LOCAL_STORAGE: StaticCell = StaticCell::new(); static MIDI_READER: StaticCell = StaticCell::new(); static MIDI_WRITER: StaticCell = StaticCell::new(); static STORAGE_MANAGER: StaticCell = StaticCell::new(); +static BUTTON_EVENT_CHANNEL: StaticCell = StaticCell::new(); static APP: StaticCell> = StaticCell::new(); static DISPLAY_1: StaticCell> = StaticCell::new(); @@ -99,7 +101,7 @@ pub fn main() { let midi_reader = MIDI_READER.init(FakeMidiReader::default()); let midi_writer = MIDI_WRITER.init(FakeMidiWriter::default()); let storage_manager = STORAGE_MANAGER.init(LocalStorageManager::new(local_storage)); - let button_event_channel = foundation::application::channels::ButtonEventChannel::new(); + let button_event_channel = BUTTON_EVENT_CHANNEL.init(ButtonEventChannel::new()); let document = window() .and_then(|win| win.document()) @@ -217,7 +219,7 @@ pub fn main() { .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) - .with_button_event_channel(&button_event_channel) + .with_button_event_channel(button_event_channel) .build(), ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); From 9e695e3a25318f740db8f7f574f73b9d90102c91 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:36:23 +0100 Subject: [PATCH 25/57] Accept mutable reference --- firmware/foundation/src/application/state.rs | 6 +++--- firmware/simulator/src/lib.rs | 7 ++++++- 2 files changed, 9 insertions(+), 4 deletions(-) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index e5e2f0f..65217c1 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -58,7 +58,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, midi_reader: &'a mut MR, midi_writer: &'a mut MW, storage_manager: &'a mut SM, - button_event_channel: &'a ButtonEventChannel, + button_event_channel: &'a mut ButtonEventChannel, ) -> Self { Self { midi_streams: MidiStreams { @@ -161,7 +161,7 @@ pub struct ApplicationBuilder<'a, MR: MidiReader, MW: MidiWriter, SM: StorageMan midi_reader: Option<&'a mut MR>, midi_writer: Option<&'a mut MW>, storage_manager: Option<&'a mut SM>, - button_event_channel: Option<&'a ButtonEventChannel>, + button_event_channel: Option<&'a mut ButtonEventChannel>, } impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder<'a, MR, MW, SM> { @@ -191,7 +191,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< pub fn with_button_event_channel( mut self, - button_event_channel: &'a ButtonEventChannel, + button_event_channel: &'a mut ButtonEventChannel, ) -> Self { self.button_event_channel = Some(button_event_channel); self diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index fedafd8..6595bca 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -19,7 +19,7 @@ use embedded_graphics::{ use embedded_graphics_web_simulator::{ display::WebSimulatorDisplay, output_settings::OutputSettingsBuilder, }; -use foundation::application::channels::ButtonEventChannel; +use foundation::application::channels::{ButtonEvent, ButtonEventChannel, ButtonIdentifier}; use foundation::application::state::{Application, ApplicationBuilder, Displays}; use log::{Level, info}; use static_cell::StaticCell; @@ -152,6 +152,11 @@ pub fn main() { let l = EventListener::new(); let f = Closure::wrap(Box::new(move |_event: web_sys::Event| { spawn_local(async move { + // button_event_channel + // .send(ButtonEvent::Pressed { + // button_identifier: ButtonIdentifier::Button8, + // }) + // .await; bloop().await; }); info!("Button clicked!"); From f304fe7fad4590d5541d4a4e2345c3a9e4b39d87 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:40:29 +0100 Subject: [PATCH 26/57] Does this work? --- firmware/simulator/src/lib.rs | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 6595bca..dc76fa6 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -150,13 +150,13 @@ pub fn main() { create_button_element(&document, &root_element).expect("Failed to create button 8 element"); let l = EventListener::new(); - let f = Closure::wrap(Box::new(move |_event: web_sys::Event| { - spawn_local(async move { - // button_event_channel - // .send(ButtonEvent::Pressed { - // button_identifier: ButtonIdentifier::Button8, - // }) - // .await; + let f = Closure::wrap(Box::new(|_event: web_sys::Event| { + spawn_local(async { + button_event_channel + .send(ButtonEvent::Pressed { + button_identifier: ButtonIdentifier::Button8, + }) + .await; bloop().await; }); info!("Button clicked!"); From 2b7af8fe7393230faeb8870e2f353188f7e39420 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:42:55 +0100 Subject: [PATCH 27/57] Logging inside task --- firmware/foundation/src/application/channels.rs | 4 ++-- firmware/foundation/src/application/state.rs | 7 ++++++- 2 files changed, 8 insertions(+), 3 deletions(-) diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index f912f10..e0259dc 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -69,7 +69,7 @@ pub struct DisplayStateUpdateMessage { pub type DisplayStateUpdateChannel = AppChannel; -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub enum ButtonIdentifier { Button1, Button2, @@ -81,7 +81,7 @@ pub enum ButtonIdentifier { Button8, } -#[derive(Copy, Clone)] +#[derive(Debug, Copy, Clone)] pub enum ButtonEvent { Pressed { button_identifier: ButtonIdentifier }, Released { button_identifier: ButtonIdentifier }, diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 65217c1..24f4e5b 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -1,5 +1,5 @@ use crate::application::channels::{ - ButtonEventChannel, DisplayIdentifier, DisplayStateUpdateChannel, MidiOutChannel, + ButtonEvent, ButtonEventChannel, DisplayIdentifier, DisplayStateUpdateChannel, MidiOutChannel, StorageStateEvent, StorageStateUpdateChannel, }; use crate::layout::DisplayLayout; @@ -106,6 +106,11 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, pub async fn button_task(&self) -> ! { loop { let button_event = self.channels.button_event.receive().await; + match button_event { + ButtonEvent::Pressed { button_identifier } => { + info!("Button {:?} pressed", button_identifier); + } + } } } From de43cdaac573edbe60c7e44ebd35ccfc5c7471aa Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:44:50 +0100 Subject: [PATCH 28/57] Cover default branch --- firmware/foundation/src/application/state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 24f4e5b..d9bfe4b 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -110,6 +110,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, ButtonEvent::Pressed { button_identifier } => { info!("Button {:?} pressed", button_identifier); } + ButtonEvent::Released { .. } => todo!(), } } } From c9d9505573a7d47c60d5e31b138357f708eab036 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 20:59:06 +0100 Subject: [PATCH 29/57] Rename --- firmware/firmware/src/main.rs | 2 +- firmware/foundation/src/application/state.rs | 2 +- firmware/simulator/src/lib.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/firmware/firmware/src/main.rs b/firmware/firmware/src/main.rs index 69fa2fe..bc1bdf1 100644 --- a/firmware/firmware/src/main.rs +++ b/firmware/firmware/src/main.rs @@ -227,7 +227,7 @@ async fn main(spawner: Spawner) -> ! { .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) - .with_button_event_channel(button_event_channel) + .with_button_event_receiver(button_event_channel) .build(), ); diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index d9bfe4b..d1ec62f 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -195,7 +195,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< self } - pub fn with_button_event_channel( + pub fn with_button_event_receiver( mut self, button_event_channel: &'a mut ButtonEventChannel, ) -> Self { diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index dc76fa6..7aa7817 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -224,7 +224,7 @@ pub fn main() { .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) - .with_button_event_channel(button_event_channel) + .with_button_event_receiver(button_event_channel) .build(), ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); From 2bc8decd2323e3eb8efa2797379fc148681805fc Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:12:18 +0100 Subject: [PATCH 30/57] Share `async-channel` in workspace --- firmware/Cargo.lock | 1 + firmware/Cargo.toml | 1 + firmware/foundation/Cargo.toml | 2 +- firmware/simulator/Cargo.toml | 1 + 4 files changed, 4 insertions(+), 1 deletion(-) diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index 77f69a2..f2a9b20 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -4529,6 +4529,7 @@ checksum = "e3a9fe34e3e7a50316060351f37187a3f546bce95496156754b601a5fa71b76e" name = "simulator" version = "0.1.0" dependencies = [ + "async-channel", "async_wasm_task", "console_error_panic_hook", "console_log", diff --git a/firmware/Cargo.toml b/firmware/Cargo.toml index 7dbd477..de5b1b0 100644 --- a/firmware/Cargo.toml +++ b/firmware/Cargo.toml @@ -30,3 +30,4 @@ heapless = { version = "0.9.2" } static_cell = { version = "2.1.1" } serde = { version = "1.0.228", default-features = false } embassy-sync = { version = "0.7.2" } +async-channel = { version = "2.5.0" } diff --git a/firmware/foundation/Cargo.toml b/firmware/foundation/Cargo.toml index c63cfb2..1f02c2a 100644 --- a/firmware/foundation/Cargo.toml +++ b/firmware/foundation/Cargo.toml @@ -19,4 +19,4 @@ log = { workspace = true } embassy-sync = { workspace = true, features = ["log"] } [target.'cfg(target_arch = "wasm32")'.dependencies] -async-channel = { version = "2.5.0" } +async-channel = { workspace = true } diff --git a/firmware/simulator/Cargo.toml b/firmware/simulator/Cargo.toml index cd08647..901c46d 100644 --- a/firmware/simulator/Cargo.toml +++ b/firmware/simulator/Cargo.toml @@ -11,6 +11,7 @@ foundation = { path = "../foundation" } wasm-bindgen = { version = "0.2.114" } wasm-bindgen-futures = { version = "0.4.68" } async_wasm_task = { version = "0.2.3" } +async-channel = { workspace = true } web-sys = { version = "0.3.91", features = ["console", "CanvasRenderingContext2d", "Document", "HtmlButtonElement", "Element", "HtmlCanvasElement", "Window", "Text", "Storage", "EventListener", "EventListenerOptions", "Event", "EventTarget"] } console_error_panic_hook = { version = "0.1.7" } From c802275c69b1b9e37c6650af3c6be78846097e08 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:12:30 +0100 Subject: [PATCH 31/57] Use separate button event receiver --- firmware/firmware/src/main.rs | 12 ++++++---- .../foundation/src/application/channels.rs | 8 ++++++- firmware/foundation/src/application/state.rs | 23 +++++++++++-------- firmware/simulator/src/lib.rs | 14 +++++++---- 4 files changed, 38 insertions(+), 19 deletions(-) diff --git a/firmware/firmware/src/main.rs b/firmware/firmware/src/main.rs index bc1bdf1..53acb74 100644 --- a/firmware/firmware/src/main.rs +++ b/firmware/firmware/src/main.rs @@ -31,7 +31,7 @@ use display_interface_spi::SPIInterface; use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::{Mutex, raw::NoopRawMutex}; -use embassy_sync::channel::{Receiver, Sender}; +use embassy_sync::channel::{Channel, Receiver, Sender}; use embassy_time::Delay; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; @@ -43,7 +43,7 @@ use esp_hal::time::Rate; use esp_hal::timer::timg::TimerGroup; use esp_hal::uart::{Config as UartConfig, DataBits, Parity, StopBits, Uart, UartRx, UartTx}; use esp_hal::{Async, Blocking}; -use foundation::application::channels::ButtonEventChannel; +use foundation::application::channels::{ButtonEvent, ButtonEventReceiver}; use foundation::application::state::{Application, ApplicationBuilder, Displays}; use log::info; use midi::{UartMidiReader, UartMidiWriter}; @@ -78,7 +78,7 @@ static DISPLAYS: StaticCell> = StaticCell::new(); static UART_MIDI_READER: StaticCell = StaticCell::new(); static UART_MIDI_WRITER: StaticCell = StaticCell::new(); static STORAGE_MANAGER: StaticCell = StaticCell::new(); -static BUTTON_EVENT_CHANNEL: StaticCell = StaticCell::new(); +static BUTTON_EVENT_CHANNEL: StaticCell = StaticCell::new(); static SPI_BUS: StaticCell>>> = StaticCell::new(); static APP: StaticCell = StaticCell::new(); @@ -220,14 +220,16 @@ async fn main(spawner: Spawner) -> ! { let midi_reader = UART_MIDI_READER.init(UartMidiReader::new(rx)); let midi_writer = UART_MIDI_WRITER.init(UartMidiWriter::new(tx)); let storage_manager = STORAGE_MANAGER.init(FakeStorageManager::default()); - let button_event_channel = BUTTON_EVENT_CHANNEL.init(ButtonEventChannel::new()); + let button_event_channel = Channel::::new(); + let button_event_sender = button_event_channel.sender(); + let button_event_receiver = button_event_channel.receiver(); let app = APP.init( ApplicationBuilder::new() .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) - .with_button_event_receiver(button_event_channel) + .with_button_event_receiver(button_event_receiver) .build(), ); diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index e0259dc..005eb98 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -87,7 +87,13 @@ pub enum ButtonEvent { Released { button_identifier: ButtonIdentifier }, } -pub type ButtonEventChannel = AppChannel; +#[cfg(target_arch = "wasm32")] +type Receiver = async_channel::Receiver; +#[cfg(not(target_arch = "wasm32"))] +type Receiver = + embassy_sync::channel::Receiver; + +pub type ButtonEventReceiver = Receiver; // TODO: Add channel for state updates pub enum StorageStateEvent { diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index d1ec62f..4776af4 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -1,11 +1,13 @@ use crate::application::channels::{ - ButtonEvent, ButtonEventChannel, DisplayIdentifier, DisplayStateUpdateChannel, MidiOutChannel, + ButtonEvent, ButtonEventReceiver, DisplayIdentifier, DisplayStateUpdateChannel, MidiOutChannel, StorageStateEvent, StorageStateUpdateChannel, }; use crate::layout::DisplayLayout; use crate::midi::{MidiReader, MidiWriter}; use crate::storage::StorageManager; use core::cell::RefCell; +use embassy_sync::blocking_mutex::raw::NoopRawMutex; +use embassy_sync::channel::Receiver; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; use log::info; @@ -42,7 +44,7 @@ pub(crate) struct InternalChannels<'a> { midi_out: MidiOutChannel, display_state_update: DisplayStateUpdateChannel, storage_state_update: StorageStateUpdateChannel, - button_event: &'a ButtonEventChannel, + button_event: &'a ButtonEventReceiver, } pub struct Application<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> { @@ -58,7 +60,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, midi_reader: &'a mut MR, midi_writer: &'a mut MW, storage_manager: &'a mut SM, - button_event_channel: &'a mut ButtonEventChannel, + button_event_receiver: &'a mut ButtonEventReceiver, ) -> Self { Self { midi_streams: MidiStreams { @@ -69,7 +71,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, midi_out: MidiOutChannel::new(), display_state_update: DisplayStateUpdateChannel::new(), storage_state_update: StorageStateUpdateChannel::new(), - button_event: button_event_channel, + button_event: button_event_receiver, }, storage_manager, } @@ -105,6 +107,9 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, pub async fn button_task(&self) -> ! { loop { + #[cfg(target_arch = "wasm32")] + let button_event = self.channels.button_event.recv().await.unwrap(); + #[cfg(not(target_arch = "wasm32"))] let button_event = self.channels.button_event.receive().await; match button_event { ButtonEvent::Pressed { button_identifier } => { @@ -167,7 +172,7 @@ pub struct ApplicationBuilder<'a, MR: MidiReader, MW: MidiWriter, SM: StorageMan midi_reader: Option<&'a mut MR>, midi_writer: Option<&'a mut MW>, storage_manager: Option<&'a mut SM>, - button_event_channel: Option<&'a mut ButtonEventChannel>, + button_event_receiver: Option<&'a mut ButtonEventReceiver>, } impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder<'a, MR, MW, SM> { @@ -176,7 +181,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< midi_reader: None, midi_writer: None, storage_manager: None, - button_event_channel: None, + button_event_receiver: None, } } @@ -197,9 +202,9 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< pub fn with_button_event_receiver( mut self, - button_event_channel: &'a mut ButtonEventChannel, + button_event_receiver: Receiver, ) -> Self { - self.button_event_channel = Some(button_event_channel); + self.button_event_receiver = Some(button_event_receiver); self } @@ -208,7 +213,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< self.midi_reader.expect("MIDI reader is required"), self.midi_writer.expect("MIDI writer is required"), self.storage_manager.expect("Storage manager is required"), - self.button_event_channel + self.button_event_receiver .expect("Button event channel is required"), ) } diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 7aa7817..a36fc87 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -19,7 +19,7 @@ use embedded_graphics::{ use embedded_graphics_web_simulator::{ display::WebSimulatorDisplay, output_settings::OutputSettingsBuilder, }; -use foundation::application::channels::{ButtonEvent, ButtonEventChannel, ButtonIdentifier}; +use foundation::application::channels::{ButtonEvent, ButtonEventReceiver, ButtonIdentifier}; use foundation::application::state::{Application, ApplicationBuilder, Displays}; use log::{Level, info}; use static_cell::StaticCell; @@ -32,11 +32,14 @@ use web_sys::{Document, Element, EventListener, HtmlButtonElement, Storage, wind const STORAGE_KEY_PRESETS: &str = "presets"; const STORAGE_KEY_PRESET_ID: &str = "preset_id"; +type ButtonEventSender = async_channel::Sender; + static LOCAL_STORAGE: StaticCell = StaticCell::new(); static MIDI_READER: StaticCell = StaticCell::new(); static MIDI_WRITER: StaticCell = StaticCell::new(); static STORAGE_MANAGER: StaticCell = StaticCell::new(); -static BUTTON_EVENT_CHANNEL: StaticCell = StaticCell::new(); +static BUTTON_EVENT_SENDER: StaticCell = StaticCell::new(); +static BUTTON_EVENT_RECEIVER: StaticCell = StaticCell::new(); static APP: StaticCell> = StaticCell::new(); static DISPLAY_1: StaticCell> = StaticCell::new(); @@ -101,7 +104,10 @@ pub fn main() { let midi_reader = MIDI_READER.init(FakeMidiReader::default()); let midi_writer = MIDI_WRITER.init(FakeMidiWriter::default()); let storage_manager = STORAGE_MANAGER.init(LocalStorageManager::new(local_storage)); - let button_event_channel = BUTTON_EVENT_CHANNEL.init(ButtonEventChannel::new()); + + let (_button_event_sender, _button_event_receiver) = async_channel::bounded(64); + let button_event_sender = BUTTON_EVENT_SENDER.init(_button_event_sender); + let button_event_receiver = BUTTON_EVENT_RECEIVER.init(_button_event_receiver); let document = window() .and_then(|win| win.document()) @@ -224,7 +230,7 @@ pub fn main() { .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) - .with_button_event_receiver(button_event_channel) + .with_button_event_receiver(button_event_receiver) .build(), ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); From 3d03be7a9d4abadd7eae715fffdbf9f53cb0fcfe Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:17:42 +0100 Subject: [PATCH 32/57] Ugh --- firmware/firmware/src/main.rs | 14 ++++++++++---- firmware/foundation/src/application/channels.rs | 12 ++++++++---- firmware/foundation/src/application/state.rs | 4 ++-- 3 files changed, 20 insertions(+), 10 deletions(-) diff --git a/firmware/firmware/src/main.rs b/firmware/firmware/src/main.rs index 53acb74..6eee290 100644 --- a/firmware/firmware/src/main.rs +++ b/firmware/firmware/src/main.rs @@ -30,6 +30,7 @@ use core::cell::RefCell; use display_interface_spi::SPIInterface; use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; use embassy_executor::Spawner; +use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::{Mutex, raw::NoopRawMutex}; use embassy_sync::channel::{Channel, Receiver, Sender}; use embassy_time::Delay; @@ -68,6 +69,8 @@ type FirmwareApplication = Application< FakeStorageManager, >; +type ButtonEventChannel = Channel; + static RX: StaticCell> = StaticCell::new(); static TX: StaticCell> = StaticCell::new(); static DISPLAY_1: StaticCell = StaticCell::new(); @@ -78,7 +81,9 @@ static DISPLAYS: StaticCell> = StaticCell::new(); static UART_MIDI_READER: StaticCell = StaticCell::new(); static UART_MIDI_WRITER: StaticCell = StaticCell::new(); static STORAGE_MANAGER: StaticCell = StaticCell::new(); -static BUTTON_EVENT_CHANNEL: StaticCell = StaticCell::new(); +static BUTTON_EVENT_CHANNEL: StaticCell = StaticCell::new(); +static BUTTON_EVENT_SENDER: StaticCell> = StaticCell::new(); +static BUTTON_EVENT_RECEIVER: StaticCell = StaticCell::new(); static SPI_BUS: StaticCell>>> = StaticCell::new(); static APP: StaticCell = StaticCell::new(); @@ -220,9 +225,10 @@ async fn main(spawner: Spawner) -> ! { let midi_reader = UART_MIDI_READER.init(UartMidiReader::new(rx)); let midi_writer = UART_MIDI_WRITER.init(UartMidiWriter::new(tx)); let storage_manager = STORAGE_MANAGER.init(FakeStorageManager::default()); - let button_event_channel = Channel::::new(); - let button_event_sender = button_event_channel.sender(); - let button_event_receiver = button_event_channel.receiver(); + let button_event_channel = + BUTTON_EVENT_CHANNEL.init(Channel::::new()); + let button_event_sender = BUTTON_EVENT_SENDER.init(button_event_channel.sender()); + let button_event_receiver = BUTTON_EVENT_RECEIVER.init(button_event_channel.receiver()); let app = APP.init( ApplicationBuilder::new() diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index 005eb98..eae7b5c 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -88,12 +88,16 @@ pub enum ButtonEvent { } #[cfg(target_arch = "wasm32")] -type Receiver = async_channel::Receiver; +type Receiver<'a, T, const N: usize> = async_channel::Receiver<'a, T>; #[cfg(not(target_arch = "wasm32"))] -type Receiver = - embassy_sync::channel::Receiver; +type Receiver<'a, T, const N: usize> = embassy_sync::channel::Receiver< + 'a, + embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex, + T, + N, +>; -pub type ButtonEventReceiver = Receiver; +pub type ButtonEventReceiver<'a> = Receiver<'a, ButtonEvent, 64>; // TODO: Add channel for state updates pub enum StorageStateEvent { diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 4776af4..2a8b847 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -44,7 +44,7 @@ pub(crate) struct InternalChannels<'a> { midi_out: MidiOutChannel, display_state_update: DisplayStateUpdateChannel, storage_state_update: StorageStateUpdateChannel, - button_event: &'a ButtonEventReceiver, + button_event: &'a ButtonEventReceiver<'a>, } pub struct Application<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> { @@ -172,7 +172,7 @@ pub struct ApplicationBuilder<'a, MR: MidiReader, MW: MidiWriter, SM: StorageMan midi_reader: Option<&'a mut MR>, midi_writer: Option<&'a mut MW>, storage_manager: Option<&'a mut SM>, - button_event_receiver: Option<&'a mut ButtonEventReceiver>, + button_event_receiver: Option<&'a mut ButtonEventReceiver<'a>>, } impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder<'a, MR, MW, SM> { From 1cb1393abfb9faa45fcc40c4e1fb2649dcee5a2e Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:19:16 +0100 Subject: [PATCH 33/57] Fix --- firmware/foundation/src/application/channels.rs | 2 +- firmware/foundation/src/application/state.rs | 2 +- firmware/simulator/src/lib.rs | 5 +++-- 3 files changed, 5 insertions(+), 4 deletions(-) diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index eae7b5c..68bd6ab 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -5,7 +5,7 @@ use crate::protocol::Colour; #[cfg(target_arch = "wasm32")] pub struct Inner { tx: async_channel::Sender, - rx: async_channel::Receiver, + rx: async_channel::Receiver, } #[cfg(target_arch = "wasm32")] diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 2a8b847..8468c43 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -202,7 +202,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< pub fn with_button_event_receiver( mut self, - button_event_receiver: Receiver, + button_event_receiver: &mut ButtonEventReceiver, ) -> Self { self.button_event_receiver = Some(button_event_receiver); self diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index a36fc87..caeb594 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -158,11 +158,12 @@ pub fn main() { let l = EventListener::new(); let f = Closure::wrap(Box::new(|_event: web_sys::Event| { spawn_local(async { - button_event_channel + button_event_sender .send(ButtonEvent::Pressed { button_identifier: ButtonIdentifier::Button8, }) - .await; + .await + .unwrap(); bloop().await; }); info!("Button clicked!"); From d98e06835f186d6181549ea29c33cf25b54ee086 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:20:56 +0100 Subject: [PATCH 34/57] Fix --- firmware/foundation/src/application/channels.rs | 4 ++-- firmware/foundation/src/application/state.rs | 6 ++---- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index 68bd6ab..ba6211c 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -5,7 +5,7 @@ use crate::protocol::Colour; #[cfg(target_arch = "wasm32")] pub struct Inner { tx: async_channel::Sender, - rx: async_channel::Receiver, + rx: async_channel::Receiver, } #[cfg(target_arch = "wasm32")] @@ -88,7 +88,7 @@ pub enum ButtonEvent { } #[cfg(target_arch = "wasm32")] -type Receiver<'a, T, const N: usize> = async_channel::Receiver<'a, T>; +type Receiver<'a, T, const N: usize> = async_channel::Receiver; #[cfg(not(target_arch = "wasm32"))] type Receiver<'a, T, const N: usize> = embassy_sync::channel::Receiver< 'a, diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 8468c43..7130ea7 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -6,8 +6,6 @@ use crate::layout::DisplayLayout; use crate::midi::{MidiReader, MidiWriter}; use crate::storage::StorageManager; use core::cell::RefCell; -use embassy_sync::blocking_mutex::raw::NoopRawMutex; -use embassy_sync::channel::Receiver; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; use log::info; @@ -126,7 +124,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, displays: &mut Displays<'_, D>, ) -> ! where - ::Error: core::fmt::Debug, + ::Error: core::fmt::Debug, { let mut display_1_layout = DisplayLayout::new(displays.display_1); let mut display_2_layout = DisplayLayout::new(displays.display_2); @@ -202,7 +200,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< pub fn with_button_event_receiver( mut self, - button_event_receiver: &mut ButtonEventReceiver, + button_event_receiver: &'a mut ButtonEventReceiver<'a>, ) -> Self { self.button_event_receiver = Some(button_event_receiver); self From e50ba543eb3aa1fdecabdc827c83a2e1d6232e52 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:21:57 +0100 Subject: [PATCH 35/57] Fix --- firmware/firmware/src/main.rs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/firmware/firmware/src/main.rs b/firmware/firmware/src/main.rs index 6eee290..6ac27fb 100644 --- a/firmware/firmware/src/main.rs +++ b/firmware/firmware/src/main.rs @@ -82,7 +82,8 @@ static UART_MIDI_READER: StaticCell = StaticCell::new(); static UART_MIDI_WRITER: StaticCell = StaticCell::new(); static STORAGE_MANAGER: StaticCell = StaticCell::new(); static BUTTON_EVENT_CHANNEL: StaticCell = StaticCell::new(); -static BUTTON_EVENT_SENDER: StaticCell> = StaticCell::new(); +static BUTTON_EVENT_SENDER: StaticCell> = + StaticCell::new(); static BUTTON_EVENT_RECEIVER: StaticCell = StaticCell::new(); static SPI_BUS: StaticCell>>> = StaticCell::new(); static APP: StaticCell = StaticCell::new(); From 5bf902df42cda8034f560ddff6c7be3f1cffa879 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:39:10 +0100 Subject: [PATCH 36/57] Remove log lines --- firmware/simulator/src/lib.rs | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index caeb594..e7f119f 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -164,15 +164,13 @@ pub fn main() { }) .await .unwrap(); - bloop().await; }); - info!("Button clicked!"); }) as Box); l.set_handle_event(f.as_ref().unchecked_ref()); f.forget(); _button_8_element - .add_event_listener_with_event_listener("click", &l) + .add_event_listener_with_event_listener("mousedown", &l) .unwrap(); let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_ORANGE); From 403c48e3c2c348d4f5330129787c6ac9c72f781e Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:39:30 +0100 Subject: [PATCH 37/57] Remove dummy function --- firmware/simulator/src/lib.rs | 7 ------- 1 file changed, 7 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index e7f119f..40cf5ef 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -3,7 +3,6 @@ mod sleep; mod storage; use crate::midi::{FakeMidiReader, FakeMidiWriter}; -use crate::sleep::sleep; use crate::storage::LocalStorageManager; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::mono_font::ascii::FONT_10X20; @@ -83,12 +82,6 @@ pub fn teardown() { } } -async fn bloop() { - info!("Starting bloop..."); - sleep(1000).await; - info!("Bloop complete!"); -} - #[wasm_bindgen] pub fn main() { console_error_panic_hook::set_once(); From c8850fd6d04e19ac6d1c08c16279c16e962c885d Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:44:32 +0100 Subject: [PATCH 38/57] Add util to create event listeners --- firmware/simulator/src/lib.rs | 33 ++++++++++++++++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 40cf5ef..fb3e609 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -70,6 +70,37 @@ fn create_display_element(document: &Document, parent: &Element) -> Result (EventListener, EventListener) { + let press_listener = EventListener::new(); + let press_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { + spawn_local(async { + button_event_sender + .send(ButtonEvent::Pressed { button_identifier }) + .await + .unwrap(); + }); + }) as Box); + press_listener.set_handle_event(press_handler.as_ref().unchecked_ref()); + press_handler.forget(); + + let release_listener = EventListener::new(); + let release_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { + spawn_local(async { + button_event_sender + .send(ButtonEvent::Pressed { button_identifier }) + .await + .unwrap(); + }); + }) as Box); + release_listener.set_handle_event(release_handler.as_ref().unchecked_ref()); + release_handler.forget(); + + (press_listener, release_listener) +} + #[wasm_bindgen] pub fn teardown() { let document = window() @@ -100,7 +131,7 @@ pub fn main() { let (_button_event_sender, _button_event_receiver) = async_channel::bounded(64); let button_event_sender = BUTTON_EVENT_SENDER.init(_button_event_sender); - let button_event_receiver = BUTTON_EVENT_RECEIVER.init(_button_event_receiver); + let button_event_receiver = BUTTON_EVENT_RECEIVER.init(_button_event_receiver.into()); let document = window() .and_then(|win| win.document()) From 1efa1568a92d8135ae27d24803cadee8caf00796 Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 21:48:08 +0100 Subject: [PATCH 39/57] Add button event listeners --- firmware/simulator/src/lib.rs | 85 +++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 28 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index fb3e609..b690edd 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -70,10 +70,11 @@ fn create_display_element(document: &Document, parent: &Element) -> Result (EventListener, EventListener) { +) { let press_listener = EventListener::new(); let press_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { spawn_local(async { @@ -98,7 +99,12 @@ fn create_event_listeners( release_listener.set_handle_event(release_handler.as_ref().unchecked_ref()); release_handler.forget(); - (press_listener, release_listener) + button_element + .add_event_listener_with_event_listener("mousedown", &press_listener) + .unwrap(); + button_element + .add_event_listener_with_event_listener("mouseup", &release_listener) + .unwrap(); } #[wasm_bindgen] @@ -152,13 +158,13 @@ pub fn main() { .set_attribute("style", "display: grid; grid-template-columns: repeat(4, 1fr); grid-template-rows: 2em auto 2em; gap: 1rem;") .unwrap(); - let _button_1_element = + let button_1_element = create_button_element(&document, &root_element).expect("Failed to create button 1 element"); - let _button_3_element = + let button_3_element = create_button_element(&document, &root_element).expect("Failed to create button 3 element"); - let _button_5_element = + let button_5_element = create_button_element(&document, &root_element).expect("Failed to create button 5 element"); - let _button_7_element = + let button_7_element = create_button_element(&document, &root_element).expect("Failed to create button 7 element"); let display_1_element = create_display_element(&document, &root_element) @@ -170,32 +176,55 @@ pub fn main() { let display_4_element = create_display_element(&document, &root_element) .expect("Failed to create display 4 element"); - let _button_2_element = + let button_2_element = create_button_element(&document, &root_element).expect("Failed to create button 2 element"); - let _button_4_element = + let button_4_element = create_button_element(&document, &root_element).expect("Failed to create button 4 element"); - let _button_6_element = + let button_6_element = create_button_element(&document, &root_element).expect("Failed to create button 6 element"); - let _button_8_element = + let button_8_element = create_button_element(&document, &root_element).expect("Failed to create button 8 element"); - let l = EventListener::new(); - let f = Closure::wrap(Box::new(|_event: web_sys::Event| { - spawn_local(async { - button_event_sender - .send(ButtonEvent::Pressed { - button_identifier: ButtonIdentifier::Button8, - }) - .await - .unwrap(); - }); - }) as Box); - l.set_handle_event(f.as_ref().unchecked_ref()); - f.forget(); - - _button_8_element - .add_event_listener_with_event_listener("mousedown", &l) - .unwrap(); + setup_event_listeners( + button_event_sender, + &button_1_element, + ButtonIdentifier::Button1, + ); + setup_event_listeners( + button_event_sender, + &button_2_element, + ButtonIdentifier::Button2, + ); + setup_event_listeners( + button_event_sender, + &button_3_element, + ButtonIdentifier::Button3, + ); + setup_event_listeners( + button_event_sender, + &button_4_element, + ButtonIdentifier::Button4, + ); + setup_event_listeners( + button_event_sender, + &button_5_element, + ButtonIdentifier::Button5, + ); + setup_event_listeners( + button_event_sender, + &button_6_element, + ButtonIdentifier::Button6, + ); + setup_event_listeners( + button_event_sender, + &button_7_element, + ButtonIdentifier::Button7, + ); + setup_event_listeners( + button_event_sender, + &button_8_element, + ButtonIdentifier::Button8, + ); let text_style = MonoTextStyle::new(&FONT_10X20, Rgb565::CSS_ORANGE); let display_output_settings = OutputSettingsBuilder::new() From 5e5a96f194a9e3d5ae368d86d9b936fd88721c8f Mon Sep 17 00:00:00 2001 From: issy Date: Mon, 20 Apr 2026 22:00:54 +0100 Subject: [PATCH 40/57] Fix lifetimes --- firmware/simulator/src/lib.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index b690edd..32926d3 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -77,9 +77,12 @@ fn setup_event_listeners( ) { let press_listener = EventListener::new(); let press_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { - spawn_local(async { + let id = button_identifier.clone(); + spawn_local(async move { button_event_sender - .send(ButtonEvent::Pressed { button_identifier }) + .send(ButtonEvent::Pressed { + button_identifier: id, + }) .await .unwrap(); }); @@ -89,7 +92,8 @@ fn setup_event_listeners( let release_listener = EventListener::new(); let release_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { - spawn_local(async { + let id = button_identifier.clone(); + spawn_local(async move { button_event_sender .send(ButtonEvent::Pressed { button_identifier }) .await From 9fd22362b1589049c4401411152134184a1345c4 Mon Sep 17 00:00:00 2001 From: issy Date: Wed, 22 Apr 2026 20:42:26 +0100 Subject: [PATCH 41/57] init logger for foundation logging --- firmware/Cargo.lock | 1 + firmware/foundation/Cargo.toml | 1 + firmware/foundation/src/application/state.rs | 5 +++++ firmware/simulator/src/lib.rs | 3 ++- 4 files changed, 9 insertions(+), 1 deletion(-) diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index f2a9b20..a579861 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -1877,6 +1877,7 @@ name = "foundation" version = "0.1.0" dependencies = [ "async-channel", + "console_log", "embassy-sync 0.7.2", "embedded-graphics", "heapless 0.9.2", diff --git a/firmware/foundation/Cargo.toml b/firmware/foundation/Cargo.toml index 1f02c2a..51ddbf3 100644 --- a/firmware/foundation/Cargo.toml +++ b/firmware/foundation/Cargo.toml @@ -20,3 +20,4 @@ embassy-sync = { workspace = true, features = ["log"] } [target.'cfg(target_arch = "wasm32")'.dependencies] async-channel = { workspace = true } +console_log = { version = "1.0.0", features = ["wasm-bindgen"] } diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 7130ea7..8fa0e0d 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -75,6 +75,11 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, } } + pub fn init_logger(&self) { + #[cfg(target_arch = "wasm32")] + console_log::init_with_level(log::Level::Trace).expect("Failed to initialize logger"); + } + pub async fn midi_thru_task(&self) -> ! { loop { if let Some(packet) = self diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 32926d3..6387632 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -24,7 +24,6 @@ use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; -use web_sys::console::info; use web_sys::js_sys::futures::spawn_local; use web_sys::{Document, Element, EventListener, HtmlButtonElement, Storage, window}; @@ -291,6 +290,8 @@ pub fn main() { ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); + app.init_logger(); + info!("Hello world from main"); async_wasm_task::spawn(async { app.storage_read_task().await; From 5419aa7f69bd04429b3938b291b710f9972713d7 Mon Sep 17 00:00:00 2001 From: issy Date: Wed, 22 Apr 2026 20:47:01 +0100 Subject: [PATCH 42/57] Revert "init logger for foundation logging" This reverts commit 9fd22362b1589049c4401411152134184a1345c4. --- firmware/Cargo.lock | 1 - firmware/foundation/Cargo.toml | 1 - firmware/foundation/src/application/state.rs | 5 ----- firmware/simulator/src/lib.rs | 3 +-- 4 files changed, 1 insertion(+), 9 deletions(-) diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index a579861..f2a9b20 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -1877,7 +1877,6 @@ name = "foundation" version = "0.1.0" dependencies = [ "async-channel", - "console_log", "embassy-sync 0.7.2", "embedded-graphics", "heapless 0.9.2", diff --git a/firmware/foundation/Cargo.toml b/firmware/foundation/Cargo.toml index 51ddbf3..1f02c2a 100644 --- a/firmware/foundation/Cargo.toml +++ b/firmware/foundation/Cargo.toml @@ -20,4 +20,3 @@ embassy-sync = { workspace = true, features = ["log"] } [target.'cfg(target_arch = "wasm32")'.dependencies] async-channel = { workspace = true } -console_log = { version = "1.0.0", features = ["wasm-bindgen"] } diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 8fa0e0d..7130ea7 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -75,11 +75,6 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, } } - pub fn init_logger(&self) { - #[cfg(target_arch = "wasm32")] - console_log::init_with_level(log::Level::Trace).expect("Failed to initialize logger"); - } - pub async fn midi_thru_task(&self) -> ! { loop { if let Some(packet) = self diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 6387632..32926d3 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -24,6 +24,7 @@ use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; +use web_sys::console::info; use web_sys::js_sys::futures::spawn_local; use web_sys::{Document, Element, EventListener, HtmlButtonElement, Storage, window}; @@ -290,8 +291,6 @@ pub fn main() { ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); - app.init_logger(); - info!("Hello world from main"); async_wasm_task::spawn(async { app.storage_read_task().await; From 73fd4aba357cfd2cde4d3e02f684b1341eaaf2be Mon Sep 17 00:00:00 2001 From: issy Date: Wed, 22 Apr 2026 20:54:42 +0100 Subject: [PATCH 43/57] Try again with a different crate --- firmware/Cargo.lock | 12 ++++++++++++ firmware/simulator/Cargo.toml | 1 + firmware/simulator/src/lib.rs | 3 ++- 3 files changed, 15 insertions(+), 1 deletion(-) diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index f2a9b20..f34b499 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -4544,6 +4544,7 @@ dependencies = [ "trunk", "wasm-bindgen", "wasm-bindgen-futures", + "wasm-logger", "web-sys", ] @@ -5493,6 +5494,17 @@ dependencies = [ "wasmparser", ] +[[package]] +name = "wasm-logger" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "074649a66bb306c8f2068c9016395fa65d8e08d2affcbf95acf3c24c3ab19718" +dependencies = [ + "log", + "wasm-bindgen", + "web-sys", +] + [[package]] name = "wasm-metadata" version = "0.244.0" diff --git a/firmware/simulator/Cargo.toml b/firmware/simulator/Cargo.toml index 901c46d..0c7f174 100644 --- a/firmware/simulator/Cargo.toml +++ b/firmware/simulator/Cargo.toml @@ -26,6 +26,7 @@ static_cell = { workspace = true } console_log = { version = "1.0.0", features = ["wasm-bindgen"] } log = { workspace = true } +wasm-logger = { version = "0.2.0" } [dev-dependencies] trunk = { version = "0.21.14" } diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 32926d3..bff71b8 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -48,7 +48,8 @@ static DISPLAY_4: StaticCell> = StaticCell::new(); static DISPLAYS: StaticCell>> = StaticCell::new(); fn init_logging() { - console_log::init_with_level(Level::Debug).expect("logger init failed"); + wasm_logger::init(wasm_logger::Config::default()); + // console_log::init_with_level(Level::Debug).expect("logger init failed"); } fn create_button_element( From ff4a84a8a01c5af0986c4b78bd9fce2cb26fd1a6 Mon Sep 17 00:00:00 2001 From: issy Date: Wed, 22 Apr 2026 21:05:20 +0100 Subject: [PATCH 44/57] Remove unused crate --- firmware/Cargo.lock | 12 ------------ firmware/simulator/Cargo.toml | 1 - firmware/simulator/src/lib.rs | 1 - 3 files changed, 14 deletions(-) diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index f34b499..311c6b5 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -602,17 +602,6 @@ dependencies = [ "wasm-bindgen", ] -[[package]] -name = "console_log" -version = "1.0.0" -source = "registry+https://github.com/rust-lang/crates.io-index" -checksum = "be8aed40e4edbf4d3b4431ab260b63fdc40f5780a4766824329ea0f1eefe3c0f" -dependencies = [ - "log", - "wasm-bindgen", - "web-sys", -] - [[package]] name = "const-default" version = "1.0.0" @@ -4532,7 +4521,6 @@ dependencies = [ "async-channel", "async_wasm_task", "console_error_panic_hook", - "console_log", "embedded-graphics", "embedded-graphics-web-simulator", "foundation", diff --git a/firmware/simulator/Cargo.toml b/firmware/simulator/Cargo.toml index 0c7f174..2bf3dc9 100644 --- a/firmware/simulator/Cargo.toml +++ b/firmware/simulator/Cargo.toml @@ -24,7 +24,6 @@ serde_json = { version = "1.0.149" } heapless = { workspace = true } static_cell = { workspace = true } -console_log = { version = "1.0.0", features = ["wasm-bindgen"] } log = { workspace = true } wasm-logger = { version = "0.2.0" } diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index bff71b8..d4e91df 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -49,7 +49,6 @@ static DISPLAYS: StaticCell>> = StaticCell: fn init_logging() { wasm_logger::init(wasm_logger::Config::default()); - // console_log::init_with_level(Level::Debug).expect("logger init failed"); } fn create_button_element( From 329b354478010dfa93498c35a97e37c6c6ca75ee Mon Sep 17 00:00:00 2001 From: issy Date: Wed, 22 Apr 2026 21:06:46 +0100 Subject: [PATCH 45/57] Add extra debug messages --- firmware/simulator/src/lib.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index d4e91df..2f99aca 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -79,6 +79,7 @@ fn setup_event_listeners( let press_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { let id = button_identifier.clone(); spawn_local(async move { + info!("Button {:?} pressed", id); button_event_sender .send(ButtonEvent::Pressed { button_identifier: id, @@ -94,8 +95,11 @@ fn setup_event_listeners( let release_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { let id = button_identifier.clone(); spawn_local(async move { + info!("Button {:?} released", id); button_event_sender - .send(ButtonEvent::Pressed { button_identifier }) + .send(ButtonEvent::Pressed { + button_identifier: id, + }) .await .unwrap(); }); From 7611fd7448ad06cfa7fa812f37e5631d8d202250 Mon Sep 17 00:00:00 2001 From: issy Date: Wed, 22 Apr 2026 21:10:35 +0100 Subject: [PATCH 46/57] Another log line --- firmware/foundation/src/application/state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 7130ea7..fb8e0bc 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -104,6 +104,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, } pub async fn button_task(&self) -> ! { + info!("Starting button task (inside task)"); loop { #[cfg(target_arch = "wasm32")] let button_event = self.channels.button_event.recv().await.unwrap(); From 31fa7a1802298a3ee63ae6ba73ac6e8bcf254f9b Mon Sep 17 00:00:00 2001 From: issy Date: Wed, 22 Apr 2026 21:14:41 +0100 Subject: [PATCH 47/57] Fix this --- firmware/simulator/src/lib.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 2f99aca..b532e13 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -97,7 +97,7 @@ fn setup_event_listeners( spawn_local(async move { info!("Button {:?} released", id); button_event_sender - .send(ButtonEvent::Pressed { + .send(ButtonEvent::Released { button_identifier: id, }) .await From 78bb2fb3c28638b6f04e14aa094d5430c98baea3 Mon Sep 17 00:00:00 2001 From: issy Date: Wed, 22 Apr 2026 21:16:21 +0100 Subject: [PATCH 48/57] Logging... --- firmware/foundation/src/application/state.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index fb8e0bc..508e923 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -106,6 +106,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, pub async fn button_task(&self) -> ! { info!("Starting button task (inside task)"); loop { + info!("Waiting for button event"); #[cfg(target_arch = "wasm32")] let button_event = self.channels.button_event.recv().await.unwrap(); #[cfg(not(target_arch = "wasm32"))] From 508fe9120ee7003c2fd9d643e4c3bdd5db4fb3c4 Mon Sep 17 00:00:00 2001 From: issy Date: Thu, 23 Apr 2026 19:22:46 +0100 Subject: [PATCH 49/57] Start to refactor button events --- .../foundation/src/application/channels.rs | 20 +++++++++++++++++-- firmware/foundation/src/application/state.rs | 7 ++++++- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index ba6211c..76b946d 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -83,8 +83,24 @@ pub enum ButtonIdentifier { #[derive(Debug, Copy, Clone)] pub enum ButtonEvent { - Pressed { button_identifier: ButtonIdentifier }, - Released { button_identifier: ButtonIdentifier }, + SinglePress { + button_identifier: ButtonIdentifier, + }, + LongPress { + button_identifier: ButtonIdentifier, + }, + DoublePress { + button_identifier: ButtonIdentifier, + }, + + #[deprecated] + Pressed { + button_identifier: ButtonIdentifier, + }, + #[deprecated] + Released { + button_identifier: ButtonIdentifier, + }, } #[cfg(target_arch = "wasm32")] diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 508e923..33054f4 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -115,7 +115,12 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, ButtonEvent::Pressed { button_identifier } => { info!("Button {:?} pressed", button_identifier); } - ButtonEvent::Released { .. } => todo!(), + ButtonEvent::Released { .. } => { + info!("Button released"); + } + ButtonEvent::SinglePress { .. } + | ButtonEvent::LongPress { .. } + | ButtonEvent::DoublePress { .. } => todo!(), } } } From 4b6dde9bafd6f90812d52bab548073c1ce937191 Mon Sep 17 00:00:00 2001 From: issy Date: Thu, 23 Apr 2026 19:31:53 +0100 Subject: [PATCH 50/57] Remove unnecessary stuff --- firmware/foundation/src/application/state.rs | 3 ++- firmware/simulator/src/lib.rs | 2 +- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 33054f4..7357dd3 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -111,6 +111,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, let button_event = self.channels.button_event.recv().await.unwrap(); #[cfg(not(target_arch = "wasm32"))] let button_event = self.channels.button_event.receive().await; + info!("Received button event: {:?}", button_event); match button_event { ButtonEvent::Pressed { button_identifier } => { info!("Button {:?} pressed", button_identifier); @@ -207,7 +208,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< pub fn with_button_event_receiver( mut self, - button_event_receiver: &'a mut ButtonEventReceiver<'a>, + button_event_receiver: &'a mut ButtonEventReceiver, ) -> Self { self.button_event_receiver = Some(button_event_receiver); self diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index b532e13..d245857 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -145,7 +145,7 @@ pub fn main() { let (_button_event_sender, _button_event_receiver) = async_channel::bounded(64); let button_event_sender = BUTTON_EVENT_SENDER.init(_button_event_sender); - let button_event_receiver = BUTTON_EVENT_RECEIVER.init(_button_event_receiver.into()); + let button_event_receiver = BUTTON_EVENT_RECEIVER.init(_button_event_receiver); let document = window() .and_then(|win| win.document()) From fb28bd56fd291fed285e4897deb7d3d052db50d6 Mon Sep 17 00:00:00 2001 From: issy Date: Thu, 23 Apr 2026 19:46:41 +0100 Subject: [PATCH 51/57] Remove log lines, add test task --- firmware/foundation/src/application/state.rs | 9 ++------- firmware/simulator/src/lib.rs | 16 +++++++++++++--- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 7357dd3..74aa200 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -106,19 +106,14 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, pub async fn button_task(&self) -> ! { info!("Starting button task (inside task)"); loop { - info!("Waiting for button event"); #[cfg(target_arch = "wasm32")] let button_event = self.channels.button_event.recv().await.unwrap(); #[cfg(not(target_arch = "wasm32"))] let button_event = self.channels.button_event.receive().await; info!("Received button event: {:?}", button_event); match button_event { - ButtonEvent::Pressed { button_identifier } => { - info!("Button {:?} pressed", button_identifier); - } - ButtonEvent::Released { .. } => { - info!("Button released"); - } + ButtonEvent::Pressed { .. } => {} + ButtonEvent::Released { .. } => {} ButtonEvent::SinglePress { .. } | ButtonEvent::LongPress { .. } | ButtonEvent::DoublePress { .. } => todo!(), diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index d245857..1ef8d01 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -3,6 +3,7 @@ mod sleep; mod storage; use crate::midi::{FakeMidiReader, FakeMidiWriter}; +use crate::sleep::sleep; use crate::storage::LocalStorageManager; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::mono_font::ascii::FONT_10X20; @@ -24,7 +25,6 @@ use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; -use web_sys::console::info; use web_sys::js_sys::futures::spawn_local; use web_sys::{Document, Element, EventListener, HtmlButtonElement, Storage, window}; @@ -79,7 +79,6 @@ fn setup_event_listeners( let press_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { let id = button_identifier.clone(); spawn_local(async move { - info!("Button {:?} pressed", id); button_event_sender .send(ButtonEvent::Pressed { button_identifier: id, @@ -95,7 +94,6 @@ fn setup_event_listeners( let release_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { let id = button_identifier.clone(); spawn_local(async move { - info!("Button {:?} released", id); button_event_sender .send(ButtonEvent::Released { button_identifier: id, @@ -295,6 +293,18 @@ pub fn main() { ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); + async_wasm_task::spawn(async { + loop { + sleep(1_000).await; + button_event_sender + .send(ButtonEvent::Pressed { + button_identifier: ButtonIdentifier::Button1, + }) + .await + .unwrap(); + } + }); + info!("Hello world from main"); async_wasm_task::spawn(async { app.storage_read_task().await; From a5a9e3b10005e854854c06169d187d83cdc86d73 Mon Sep 17 00:00:00 2001 From: issy Date: Thu, 23 Apr 2026 19:50:51 +0100 Subject: [PATCH 52/57] Solved by moving --- firmware/simulator/src/lib.rs | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 1ef8d01..41b7632 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -3,7 +3,6 @@ mod sleep; mod storage; use crate::midi::{FakeMidiReader, FakeMidiWriter}; -use crate::sleep::sleep; use crate::storage::LocalStorageManager; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::mono_font::ascii::FONT_10X20; @@ -76,9 +75,9 @@ fn setup_event_listeners( button_identifier: ButtonIdentifier, ) { let press_listener = EventListener::new(); - let press_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { - let id = button_identifier.clone(); + let press_handler = Closure::wrap(Box::new(move |_event: web_sys::Event| { spawn_local(async move { + let id = button_identifier.clone(); button_event_sender .send(ButtonEvent::Pressed { button_identifier: id, @@ -91,7 +90,7 @@ fn setup_event_listeners( press_handler.forget(); let release_listener = EventListener::new(); - let release_handler = Closure::wrap(Box::new(|_event: web_sys::Event| { + let release_handler = Closure::wrap(Box::new(move |_event: web_sys::Event| { let id = button_identifier.clone(); spawn_local(async move { button_event_sender @@ -293,18 +292,6 @@ pub fn main() { ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); - async_wasm_task::spawn(async { - loop { - sleep(1_000).await; - button_event_sender - .send(ButtonEvent::Pressed { - button_identifier: ButtonIdentifier::Button1, - }) - .await - .unwrap(); - } - }); - info!("Hello world from main"); async_wasm_task::spawn(async { app.storage_read_task().await; From 1db61b0a8464b7678c48e2aad290dbedf182bada Mon Sep 17 00:00:00 2001 From: issy Date: Tue, 28 Apr 2026 21:57:07 +0100 Subject: [PATCH 53/57] Add time source dependency for application --- firmware/firmware/src/main.rs | 11 ++++-- firmware/firmware/src/time.rs | 10 ++++++ firmware/foundation/src/application/mod.rs | 1 + firmware/foundation/src/application/state.rs | 35 ++++++++++++++++---- firmware/foundation/src/application/time.rs | 9 +++++ firmware/simulator/src/lib.rs | 6 ++++ firmware/simulator/src/time.rs | 11 ++++++ 7 files changed, 74 insertions(+), 9 deletions(-) create mode 100644 firmware/firmware/src/time.rs create mode 100644 firmware/foundation/src/application/time.rs create mode 100644 firmware/simulator/src/time.rs diff --git a/firmware/firmware/src/main.rs b/firmware/firmware/src/main.rs index 6ac27fb..f71d94b 100644 --- a/firmware/firmware/src/main.rs +++ b/firmware/firmware/src/main.rs @@ -10,6 +10,7 @@ extern crate alloc; mod midi; mod storage; +mod time; include!(concat!(env!("OUT_DIR"), "/version.rs")); @@ -26,13 +27,14 @@ use esp_backtrace as _; use crate::storage::FakeStorageManager; +use crate::time::EmbassyTimeSource; use core::cell::RefCell; use display_interface_spi::SPIInterface; use embassy_embedded_hal::shared_bus::blocking::spi::SpiDevice; use embassy_executor::Spawner; use embassy_sync::blocking_mutex::raw::CriticalSectionRawMutex; use embassy_sync::blocking_mutex::{Mutex, raw::NoopRawMutex}; -use embassy_sync::channel::{Channel, Receiver, Sender}; +use embassy_sync::channel::{Channel, Sender}; use embassy_time::Delay; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; @@ -67,6 +69,7 @@ type FirmwareApplication = Application< UartMidiReader<'static, 'static>, UartMidiWriter<'static, 'static>, FakeStorageManager, + EmbassyTimeSource, >; type ButtonEventChannel = Channel; @@ -228,15 +231,19 @@ async fn main(spawner: Spawner) -> ! { let storage_manager = STORAGE_MANAGER.init(FakeStorageManager::default()); let button_event_channel = BUTTON_EVENT_CHANNEL.init(Channel::::new()); - let button_event_sender = BUTTON_EVENT_SENDER.init(button_event_channel.sender()); + // TODO: Use this in tasks later + let _button_event_sender = BUTTON_EVENT_SENDER.init(button_event_channel.sender()); let button_event_receiver = BUTTON_EVENT_RECEIVER.init(button_event_channel.receiver()); + let time_source = EmbassyTimeSource::default(); + let app = APP.init( ApplicationBuilder::new() .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) .with_button_event_receiver(button_event_receiver) + .with_time_source(&time_source) .build(), ); diff --git a/firmware/firmware/src/time.rs b/firmware/firmware/src/time.rs new file mode 100644 index 0000000..c1c9444 --- /dev/null +++ b/firmware/firmware/src/time.rs @@ -0,0 +1,10 @@ +use foundation::application::time::TimeSource; + +#[derive(Default)] +pub struct EmbassyTimeSource; + +impl TimeSource for EmbassyTimeSource { + fn now(&self) -> u64 { + embassy_time::Instant::now().as_millis() + } +} diff --git a/firmware/foundation/src/application/mod.rs b/firmware/foundation/src/application/mod.rs index 9290e5e..43d14f6 100644 --- a/firmware/foundation/src/application/mod.rs +++ b/firmware/foundation/src/application/mod.rs @@ -1,2 +1,3 @@ pub mod channels; pub mod state; +pub mod time; diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index 74aa200..ef8d1f6 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -2,6 +2,7 @@ use crate::application::channels::{ ButtonEvent, ButtonEventReceiver, DisplayIdentifier, DisplayStateUpdateChannel, MidiOutChannel, StorageStateEvent, StorageStateUpdateChannel, }; +use crate::application::time::TimeSource; use crate::layout::DisplayLayout; use crate::midi::{MidiReader, MidiWriter}; use crate::storage::StorageManager; @@ -45,20 +46,23 @@ pub(crate) struct InternalChannels<'a> { button_event: &'a ButtonEventReceiver<'a>, } -pub struct Application<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> { +pub struct Application<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager, TS: TimeSource> { pub(crate) midi_streams: MidiStreams<'a, MR, MW>, pub(crate) channels: InternalChannels<'a>, pub(crate) storage_manager: &'a mut SM, - // TODO: Add protocol streams - // TODO: Add buttons + pub(crate) time_source: &'a TS, // TODO: Add protocol streams + // TODO: Add buttons } -impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, MW, SM> { +impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager, TS: TimeSource> + Application<'a, MR, MW, SM, TS> +{ pub fn new( midi_reader: &'a mut MR, midi_writer: &'a mut MW, storage_manager: &'a mut SM, button_event_receiver: &'a mut ButtonEventReceiver, + time_source: &'a TS, ) -> Self { Self { midi_streams: MidiStreams { @@ -72,6 +76,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, button_event: button_event_receiver, }, storage_manager, + time_source, } } @@ -169,20 +174,30 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> Application<'a, MR, } #[derive(Default)] -pub struct ApplicationBuilder<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> { +pub struct ApplicationBuilder< + 'a, + MR: MidiReader, + MW: MidiWriter, + SM: StorageManager, + TS: TimeSource, +> { midi_reader: Option<&'a mut MR>, midi_writer: Option<&'a mut MW>, storage_manager: Option<&'a mut SM>, button_event_receiver: Option<&'a mut ButtonEventReceiver<'a>>, + time_source: Option<&'a TS>, } -impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder<'a, MR, MW, SM> { +impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager, TS: TimeSource> + ApplicationBuilder<'a, MR, MW, SM, TS> +{ pub fn new() -> Self { Self { midi_reader: None, midi_writer: None, storage_manager: None, button_event_receiver: None, + time_source: None, } } @@ -209,13 +224,19 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager> ApplicationBuilder< self } - pub fn build(self) -> Application<'a, MR, MW, SM> { + pub fn with_time_source(mut self, time_source: &'a TS) -> Self { + self.time_source = Some(time_source); + self + } + + pub fn build(self) -> Application<'a, MR, MW, SM, TS> { Application::new( self.midi_reader.expect("MIDI reader is required"), self.midi_writer.expect("MIDI writer is required"), self.storage_manager.expect("Storage manager is required"), self.button_event_receiver .expect("Button event channel is required"), + self.time_source.expect("Time source is required"), ) } } diff --git a/firmware/foundation/src/application/time.rs b/firmware/foundation/src/application/time.rs new file mode 100644 index 0000000..28b9d35 --- /dev/null +++ b/firmware/foundation/src/application/time.rs @@ -0,0 +1,9 @@ +use core::time::Duration; + +pub trait TimeSource { + fn now(&self) -> u64; + + fn duration(&self, from: u64, to: u64) -> Duration { + Duration::from_millis(to - from) + } +} diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 41b7632..e641362 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -1,9 +1,11 @@ mod midi; mod sleep; mod storage; +mod time; use crate::midi::{FakeMidiReader, FakeMidiWriter}; use crate::storage::LocalStorageManager; +use crate::time::BrowserTimeSource; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::mono_font::ascii::FONT_10X20; use embedded_graphics::prelude::Primitive; @@ -24,6 +26,7 @@ use log::{Level, info}; use static_cell::StaticCell; use wasm_bindgen::JsCast; use wasm_bindgen::prelude::*; +use web_sys::js_sys::Date; use web_sys::js_sys::futures::spawn_local; use web_sys::{Document, Element, EventListener, HtmlButtonElement, Storage, window}; @@ -282,12 +285,15 @@ pub fn main() { display_1.flush().unwrap(); display_2.flush().unwrap(); + let time_source = BrowserTimeSource::default(); + let app = APP.init( ApplicationBuilder::new() .with_midi_reader(midi_reader) .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) .with_button_event_receiver(button_event_receiver) + .with_time_source(&time_source) .build(), ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); diff --git a/firmware/simulator/src/time.rs b/firmware/simulator/src/time.rs new file mode 100644 index 0000000..882a5dc --- /dev/null +++ b/firmware/simulator/src/time.rs @@ -0,0 +1,11 @@ +use foundation::application::time::TimeSource; +use web_sys::js_sys::Date; + +#[derive(Default)] +pub struct BrowserTimeSource; + +impl TimeSource for BrowserTimeSource { + fn now(&self) -> u64 { + Date::now() as u64 + } +} From 69c179380f9b8864ea21f00b1d30fe250c73feee Mon Sep 17 00:00:00 2001 From: issy Date: Tue, 28 Apr 2026 22:00:20 +0100 Subject: [PATCH 54/57] Fix types --- firmware/foundation/src/application/state.rs | 4 ++-- firmware/simulator/src/lib.rs | 5 +++-- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index ef8d1f6..d07a3ef 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -61,7 +61,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager, TS: TimeSource> midi_reader: &'a mut MR, midi_writer: &'a mut MW, storage_manager: &'a mut SM, - button_event_receiver: &'a mut ButtonEventReceiver, + button_event_receiver: &'a mut ButtonEventReceiver<'a>, time_source: &'a TS, ) -> Self { Self { @@ -218,7 +218,7 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager, TS: TimeSource> pub fn with_button_event_receiver( mut self, - button_event_receiver: &'a mut ButtonEventReceiver, + button_event_receiver: &'a mut ButtonEventReceiver<'a>, ) -> Self { self.button_event_receiver = Some(button_event_receiver); self diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index e641362..07a7292 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -41,8 +41,9 @@ static MIDI_WRITER: StaticCell = StaticCell::new(); static STORAGE_MANAGER: StaticCell = StaticCell::new(); static BUTTON_EVENT_SENDER: StaticCell = StaticCell::new(); static BUTTON_EVENT_RECEIVER: StaticCell = StaticCell::new(); -static APP: StaticCell> = - StaticCell::new(); +static APP: StaticCell< + Application, +> = StaticCell::new(); static DISPLAY_1: StaticCell> = StaticCell::new(); static DISPLAY_2: StaticCell> = StaticCell::new(); static DISPLAY_3: StaticCell> = StaticCell::new(); From 7f9a9d465ec49f86fd6ae83a8a40835614824bc5 Mon Sep 17 00:00:00 2001 From: issy Date: Tue, 28 Apr 2026 22:03:25 +0100 Subject: [PATCH 55/57] Fix time source lifetimes --- firmware/firmware/src/main.rs | 5 +++-- firmware/simulator/src/lib.rs | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/firmware/firmware/src/main.rs b/firmware/firmware/src/main.rs index f71d94b..7bf1706 100644 --- a/firmware/firmware/src/main.rs +++ b/firmware/firmware/src/main.rs @@ -89,6 +89,7 @@ static BUTTON_EVENT_SENDER: StaticCell = StaticCell::new(); static SPI_BUS: StaticCell>>> = StaticCell::new(); +static TIME_SOURCE: StaticCell = StaticCell::new(); static APP: StaticCell = StaticCell::new(); #[embassy_executor::task] @@ -235,7 +236,7 @@ async fn main(spawner: Spawner) -> ! { let _button_event_sender = BUTTON_EVENT_SENDER.init(button_event_channel.sender()); let button_event_receiver = BUTTON_EVENT_RECEIVER.init(button_event_channel.receiver()); - let time_source = EmbassyTimeSource::default(); + let time_source = TIME_SOURCE.init(EmbassyTimeSource::default()); let app = APP.init( ApplicationBuilder::new() @@ -243,7 +244,7 @@ async fn main(spawner: Spawner) -> ! { .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) .with_button_event_receiver(button_event_receiver) - .with_time_source(&time_source) + .with_time_source(time_source) .build(), ); diff --git a/firmware/simulator/src/lib.rs b/firmware/simulator/src/lib.rs index 07a7292..cfc5767 100644 --- a/firmware/simulator/src/lib.rs +++ b/firmware/simulator/src/lib.rs @@ -41,6 +41,7 @@ static MIDI_WRITER: StaticCell = StaticCell::new(); static STORAGE_MANAGER: StaticCell = StaticCell::new(); static BUTTON_EVENT_SENDER: StaticCell = StaticCell::new(); static BUTTON_EVENT_RECEIVER: StaticCell = StaticCell::new(); +static TIME_SOURCE: StaticCell = StaticCell::new(); static APP: StaticCell< Application, > = StaticCell::new(); @@ -286,7 +287,7 @@ pub fn main() { display_1.flush().unwrap(); display_2.flush().unwrap(); - let time_source = BrowserTimeSource::default(); + let time_source = TIME_SOURCE.init(BrowserTimeSource::default()); let app = APP.init( ApplicationBuilder::new() @@ -294,7 +295,7 @@ pub fn main() { .with_midi_writer(midi_writer) .with_storage_manager(storage_manager) .with_button_event_receiver(button_event_receiver) - .with_time_source(&time_source) + .with_time_source(time_source) .build(), ); let displays = DISPLAYS.init(Displays::new(display_1, display_2, display_3, display_4)); From 94154e2f3dfef86ac05a356c85da972cb66a9ac6 Mon Sep 17 00:00:00 2001 From: issy Date: Tue, 28 Apr 2026 22:05:22 +0100 Subject: [PATCH 56/57] Formatting --- firmware/firmware/src/main.rs | 1 - 1 file changed, 1 deletion(-) diff --git a/firmware/firmware/src/main.rs b/firmware/firmware/src/main.rs index 7bf1706..feae5da 100644 --- a/firmware/firmware/src/main.rs +++ b/firmware/firmware/src/main.rs @@ -235,7 +235,6 @@ async fn main(spawner: Spawner) -> ! { // TODO: Use this in tasks later let _button_event_sender = BUTTON_EVENT_SENDER.init(button_event_channel.sender()); let button_event_receiver = BUTTON_EVENT_RECEIVER.init(button_event_channel.receiver()); - let time_source = TIME_SOURCE.init(EmbassyTimeSource::default()); let app = APP.init( From 21a39945500ea429d912af28fbdb0a54b565c783 Mon Sep 17 00:00:00 2001 From: issy Date: Tue, 28 Apr 2026 22:35:52 +0100 Subject: [PATCH 57/57] Add some logic for the button task --- firmware/Cargo.lock | 35 +++++++++++++++++-- firmware/foundation/Cargo.toml | 1 + .../foundation/src/application/channels.rs | 22 ++---------- firmware/foundation/src/application/state.rs | 33 +++++++++++++---- 4 files changed, 63 insertions(+), 28 deletions(-) diff --git a/firmware/Cargo.lock b/firmware/Cargo.lock index 311c6b5..081c4f8 100644 --- a/firmware/Cargo.lock +++ b/firmware/Cargo.lock @@ -70,6 +70,12 @@ dependencies = [ "as-slice", ] +[[package]] +name = "allocator-api2" +version = "0.2.21" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" + [[package]] name = "allocator-api2" version = "0.3.1" @@ -1478,7 +1484,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "641e43d6a60244429117ef2fa7a47182120c7561336ea01f6fb08d634f46bae1" dependencies = [ - "allocator-api2", + "allocator-api2 0.3.1", "cfg-if", "document-features", "enumset", @@ -1852,6 +1858,12 @@ version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" +[[package]] +name = "foldhash" +version = "0.2.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "77ce24cb58228fbb8aa041425bb1050850ac19177686ea6e0f41a70416f56fdb" + [[package]] name = "form_urlencoded" version = "1.2.2" @@ -1868,6 +1880,7 @@ dependencies = [ "async-channel", "embassy-sync 0.7.2", "embedded-graphics", + "hashbrown 0.17.0", "heapless 0.9.2", "log", "prost", @@ -2164,7 +2177,7 @@ version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ - "foldhash", + "foldhash 0.1.5", ] [[package]] @@ -2173,6 +2186,18 @@ version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" +[[package]] +name = "hashbrown" +version = "0.17.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "4f467dd6dccf739c208452f8014c75c18bb8301b050ad1cfb27153803edb0f51" +dependencies = [ + "allocator-api2 0.2.21", + "equivalent", + "foldhash 0.2.0", + "rustc-std-workspace-alloc", +] + [[package]] name = "heapless" version = "0.8.0" @@ -4140,6 +4165,12 @@ version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" +[[package]] +name = "rustc-std-workspace-alloc" +version = "1.0.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "f9d441c3b2ebf55cebf796bfdc265d67fa09db17b7bb6bd4be75c509e1e8fec3" + [[package]] name = "rustc_version" version = "0.4.1" diff --git a/firmware/foundation/Cargo.toml b/firmware/foundation/Cargo.toml index 1f02c2a..675d4cb 100644 --- a/firmware/foundation/Cargo.toml +++ b/firmware/foundation/Cargo.toml @@ -14,6 +14,7 @@ prost = { version = "0.14.3", default-features = false, features = ["derive"] } heapless = { workspace = true, features = ["serde"] } serde = { workspace = true, features = ["derive"] } log = { workspace = true } +hashbrown = { version = "0.17.0", features = ["alloc"] } [target.'cfg(not(target_arch = "wasm32"))'.dependencies] embassy-sync = { workspace = true, features = ["log"] } diff --git a/firmware/foundation/src/application/channels.rs b/firmware/foundation/src/application/channels.rs index 76b946d..594fd49 100644 --- a/firmware/foundation/src/application/channels.rs +++ b/firmware/foundation/src/application/channels.rs @@ -69,7 +69,7 @@ pub struct DisplayStateUpdateMessage { pub type DisplayStateUpdateChannel = AppChannel; -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)] pub enum ButtonIdentifier { Button1, Button2, @@ -83,24 +83,8 @@ pub enum ButtonIdentifier { #[derive(Debug, Copy, Clone)] pub enum ButtonEvent { - SinglePress { - button_identifier: ButtonIdentifier, - }, - LongPress { - button_identifier: ButtonIdentifier, - }, - DoublePress { - button_identifier: ButtonIdentifier, - }, - - #[deprecated] - Pressed { - button_identifier: ButtonIdentifier, - }, - #[deprecated] - Released { - button_identifier: ButtonIdentifier, - }, + Pressed { button_identifier: ButtonIdentifier }, + Released { button_identifier: ButtonIdentifier }, } #[cfg(target_arch = "wasm32")] diff --git a/firmware/foundation/src/application/state.rs b/firmware/foundation/src/application/state.rs index d07a3ef..c4f3c28 100644 --- a/firmware/foundation/src/application/state.rs +++ b/firmware/foundation/src/application/state.rs @@ -1,6 +1,6 @@ use crate::application::channels::{ - ButtonEvent, ButtonEventReceiver, DisplayIdentifier, DisplayStateUpdateChannel, MidiOutChannel, - StorageStateEvent, StorageStateUpdateChannel, + ButtonEvent, ButtonEventReceiver, ButtonIdentifier, DisplayIdentifier, + DisplayStateUpdateChannel, MidiOutChannel, StorageStateEvent, StorageStateUpdateChannel, }; use crate::application::time::TimeSource; use crate::layout::DisplayLayout; @@ -9,6 +9,7 @@ use crate::storage::StorageManager; use core::cell::RefCell; use embedded_graphics::draw_target::DrawTarget; use embedded_graphics::pixelcolor::Rgb565; +use hashbrown::HashMap; use log::info; pub struct Displays<'a, D: DrawTarget> { @@ -110,6 +111,11 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager, TS: TimeSource> pub async fn button_task(&self) -> ! { info!("Starting button task (inside task)"); + + // TODO: Maybe create an enum to represent a single button's state machine + let mut button_pressed_at = HashMap::::with_capacity(8); + let mut button_released_at = HashMap::::with_capacity(8); + loop { #[cfg(target_arch = "wasm32")] let button_event = self.channels.button_event.recv().await.unwrap(); @@ -117,11 +123,24 @@ impl<'a, MR: MidiReader, MW: MidiWriter, SM: StorageManager, TS: TimeSource> let button_event = self.channels.button_event.receive().await; info!("Received button event: {:?}", button_event); match button_event { - ButtonEvent::Pressed { .. } => {} - ButtonEvent::Released { .. } => {} - ButtonEvent::SinglePress { .. } - | ButtonEvent::LongPress { .. } - | ButtonEvent::DoublePress { .. } => todo!(), + ButtonEvent::Pressed { button_identifier } => { + // TODO: We can do some logic here to register double-presses + button_released_at.remove(&button_identifier); + button_pressed_at.insert(button_identifier, self.time_source.now()); + // TODO: Trigger momentary button pressed event? + } + ButtonEvent::Released { button_identifier } => { + if let Some(pressed_at) = button_pressed_at.remove(&button_identifier) { + let duration_pressed = self + .time_source + .duration(pressed_at, self.time_source.now()); + if duration_pressed.as_secs().ge(&1) { + // TODO: Long press? + } + } + // TODO: Trigger momentary button released event? + // TODO: Trigger regular button press event? + } } } }