diff --git a/left/src/main.rs b/left/src/main.rs index 7a550be..eb8f73c 100644 --- a/left/src/main.rs +++ b/left/src/main.rs @@ -11,7 +11,7 @@ extern crate cortex_m_rt; use alloc::vec::Vec; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output, Pin}; -use embassy_time::{Duration, Timer}; +use embassy_time::Timer; use log::error; use tangentbord1::{ board::Board, @@ -20,7 +20,7 @@ use tangentbord1::{ keyboard::KeyboardConfig, logger::Logger, rgb::Rgb, - util::{stall, wheel}, + util::stall, ws2812::Ws2812, {allocator, rtt, uart, usb}, }; @@ -103,10 +103,16 @@ async fn main(_spawner: Spawner) { neopixel.write(&[Rgb::new(0x00, 0x99, 0x99)]).await; usb::setup_logger_and_keyboard(board.USB, events1).await; - neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await; - for w in 0usize.. { - neopixel.write(&[wheel(w as u8) * 0.15]).await; - Timer::after(Duration::from_millis(10)).await; + neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await; + Timer::after_secs(5).await; + + for b in (0u8..0xff).rev() { + neopixel.write(&[Rgb::new(0, 0, b)]).await; + Timer::after_millis(10).await; + } + + loop { + Timer::after_secs(5).await; } } diff --git a/lib/src/keyboard/lights.rs b/lib/src/keyboard/lights.rs index 438abf0..82d3d9b 100644 --- a/lib/src/keyboard/lights.rs +++ b/lib/src/keyboard/lights.rs @@ -1,8 +1,14 @@ +use core::future::pending; + use embassy_time::{Duration, Instant, Timer}; use futures::{select_biased, FutureExt}; use tgnt::button::Button; -use crate::{rgb::Rgb, util::wheel}; +use crate::{ + rgb::Rgb, + usb::{UsbEvent, USB_EVENTS}, + util::wheel, +}; use super::{Event, EventKind, KbEvents, State, SWITCH_COUNT}; @@ -36,7 +42,21 @@ pub(super) async fn task(mut events: KbEvents, state: &'static State) { let mut lights: [LightState; SWITCH_COUNT] = [LightState::None; SWITCH_COUNT]; let mut next_frame = Instant::now(); let mut idle_at = Instant::now() + UNTIL_IDLE; + let mut usb_enabled: bool = false; + let mut usb_events = USB_EVENTS + .subscriber() + .expect("USB_EVENTS: out of subscribers"); + loop { + let wait_for_idle = async { + if usb_enabled { + Timer::at(idle_at).await + } else { + // if usb is disabled, we never want to start the idle animation + pending().await + } + }; + select_biased! { event = events.recv().fuse() => { handle_event(event, state, &mut lights).await; @@ -46,17 +66,24 @@ pub(super) async fn task(mut events: KbEvents, state: &'static State) { tick(state, &mut lights).await; next_frame = Instant::now() + Duration::from_millis(16); } - _ = Timer::at(idle_at).fuse() => { + event = usb_events.next_message_pure().fuse() => { + handle_usb_event(event, &mut usb_enabled, &mut lights).await; + idle_at = Instant::now() + UNTIL_IDLE; + } + _ = wait_for_idle.fuse() => { select_biased! { event = events.recv().fuse() => { state.lights.update(|lights| { lights.iter_mut().for_each(|rgb| *rgb = Rgb::new(0, 0, 0)); }).await; handle_event(event, state, &mut lights).await; - idle_at = Instant::now() + UNTIL_IDLE; + } + event = usb_events.next_message_pure().fuse() => { + handle_usb_event(event, &mut usb_enabled, &mut lights).await; } _ = idle_animation(state).fuse() => {} } + idle_at = Instant::now() + UNTIL_IDLE; } } } @@ -196,3 +223,31 @@ async fn handle_event( }; *light = rgb; } + +async fn handle_usb_event( + event: UsbEvent, + is_enabled: &mut bool, + lights: &mut [LightState; SWITCH_COUNT], +) { + match event { + UsbEvent::Suspended(false) | UsbEvent::Configured(true) => { + let new_state = LightState::SolidThenFade { + color: Rgb::new(0, 255, 0), + solid_until: Instant::now() + Duration::from_millis(200), + fade_by: 0.85, + }; + lights.iter_mut().for_each(|state| *state = new_state); + *is_enabled = true; + } + UsbEvent::Configured(false) | UsbEvent::Suspended(true) | UsbEvent::Reset => { + let new_state = LightState::SolidThenFade { + color: Rgb::new(255, 0, 0), + solid_until: Instant::now() + Duration::from_millis(200), + fade_by: 0.85, + }; + lights.iter_mut().for_each(|state| *state = new_state); + *is_enabled = false; + } + _ => {} + } +} diff --git a/lib/src/uart.rs b/lib/src/uart.rs index 36b523e..6fa0e20 100644 --- a/lib/src/uart.rs +++ b/lib/src/uart.rs @@ -5,6 +5,8 @@ use crc_any::CRCu16; use embassy_executor::Spawner; use embassy_rp::peripherals::{PIN_0, PIN_1, UART0}; use embassy_rp::uart::{self, BufferedUart, DataBits, Parity, StopBits}; +use embassy_sync::channel::Channel; +use embassy_sync::pubsub::PubSubBehavior; use embedded_io_async::{Read, Write}; use futures::{select_biased, FutureExt}; use heapless::Vec; @@ -14,10 +16,16 @@ use static_cell::StaticCell; use crate::event::{switch, Half}; use crate::interrupts::Irqs; use crate::keyboard::KbEvents; +use crate::usb::{UsbEvent, USB_EVENTS}; +use crate::util::CS; + +/// Channel for [UsbEvent]s to be sent on the uart line. +pub static UART_USB_EVENTS_OUT: Channel = Channel::new(); #[derive(Clone, Debug, Serialize, Deserialize)] enum Message { KeyboardEvent(switch::Event), + UsbEvent(UsbEvent), } pub async fn start(tx: PIN_0, rx: PIN_1, uart: UART0, board: Half, events: KbEvents) { @@ -106,6 +114,7 @@ async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut even match &message { Message::KeyboardEvent(event) => events_tx.send(event.clone()), + &Message::UsbEvent(event) => USB_EVENTS.publish_immediate(event), } log::info!("got msg: {:?}", message); @@ -130,14 +139,19 @@ async fn uart_task(uart: BufferedUart<'static, UART0>, this_half: Half, mut even let tx_task = async { let mut buf = [0u8; 256 + HEADER_LEN]; let mut counter = 0u8; - loop { - // forward messages to the other keyboard half - let event = events_rx.recv().await; - if event.source != this_half { - continue; // do not forward messages from the other half back to it - } - let message = Message::KeyboardEvent(event); + // forward messages to the other keyboard half + loop { + let message = select_biased! { + event = events_rx.recv().fuse() => { + if event.source != this_half { + continue; // do not forward messages from the other half back to it + } + Message::KeyboardEvent(event) + } + event = UART_USB_EVENTS_OUT.receive().fuse() => Message::UsbEvent(event), + }; + let (buf_header, body) = buf.split_array_mut(); let serialized = match postcard::to_slice(&message, body) { Ok(s) => s, diff --git a/lib/src/usb.rs b/lib/src/usb.rs index 18a1aa8..66f0a38 100644 --- a/lib/src/usb.rs +++ b/lib/src/usb.rs @@ -1,27 +1,43 @@ use embassy_executor::Spawner; use embassy_rp::{peripherals::USB, usb::Driver}; -use embassy_usb::{Builder, Config, UsbDevice}; +use embassy_sync::pubsub::{PubSubBehavior, PubSubChannel}; +use embassy_usb::{Builder, Config, Handler, UsbDevice}; +use serde::{Deserialize, Serialize}; use static_cell::StaticCell; -use crate::{interrupts::Irqs, keyboard::KbEvents}; +use crate::{interrupts::Irqs, keyboard::KbEvents, uart::UART_USB_EVENTS_OUT, util::CS}; pub mod keyboard; pub mod logger; pub const MAX_PACKET_SIZE: u8 = 64; +#[derive(Clone, Copy, Debug, PartialEq, Eq, Serialize, Deserialize)] +pub enum UsbEvent { + Enabled(bool), + Suspended(bool), + Configured(bool), + Addressed(u8), + Reset, +} + +pub type UsbEventChannel = PubSubChannel; +pub static USB_EVENTS: UsbEventChannel = UsbEventChannel::new(); + struct State { device_descriptor: [u8; 256], config_descriptor: [u8; 256], bos_descriptor: [u8; 256], msos_descriptor: [u8; 256], control_buf: [u8; 64], + handler: UsbHandler, } static STATE: StaticCell = StaticCell::new(); pub async fn setup_logger_and_keyboard(usb: USB, events: KbEvents) { let mut builder = builder(usb); + //logger::setup(&mut builder).await; keyboard::setup(&mut builder, events).await; @@ -41,6 +57,7 @@ pub fn builder(usb: USB) -> Builder<'static, Driver<'static, USB>> { bos_descriptor: [0; 256], msos_descriptor: [0; 256], control_buf: [0; 64], + handler: UsbHandler, }); // Create embassy-usb Config @@ -60,7 +77,7 @@ pub fn builder(usb: USB) -> Builder<'static, Driver<'static, USB>> { let driver = Driver::new(usb, Irqs); - Builder::new( + let mut builder = Builder::new( driver, config, &mut state.device_descriptor, @@ -68,7 +85,11 @@ pub fn builder(usb: USB) -> Builder<'static, Driver<'static, USB>> { &mut state.bos_descriptor, &mut state.msos_descriptor, &mut state.control_buf, - ) + ); + + builder.handler(&mut state.handler); + + builder } #[embassy_executor::task] @@ -76,3 +97,37 @@ pub async fn run(mut device: UsbDevice<'static, Driver<'static, USB>>) { log::info!("running usb device"); device.run().await } + +struct UsbHandler; + +impl Handler for UsbHandler { + fn enabled(&mut self, enabled: bool) { + USB_EVENTS.publish_immediate(UsbEvent::Enabled(enabled)); + let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Enabled(enabled)); + log::debug!("usb enabled({enabled})"); + } + + fn reset(&mut self) { + USB_EVENTS.publish_immediate(UsbEvent::Reset); + let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Reset); + log::debug!("usb reset()"); + } + + fn addressed(&mut self, addr: u8) { + USB_EVENTS.publish_immediate(UsbEvent::Addressed(addr)); + let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Addressed(addr)); + log::debug!("usb addressed({addr})"); + } + + fn configured(&mut self, configured: bool) { + USB_EVENTS.publish_immediate(UsbEvent::Configured(configured)); + let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Configured(configured)); + log::debug!("usb configured({configured})"); + } + + fn suspended(&mut self, suspended: bool) { + USB_EVENTS.publish_immediate(UsbEvent::Suspended(suspended)); + let _ = UART_USB_EVENTS_OUT.try_send(UsbEvent::Suspended(suspended)); + log::debug!("usb suspended({suspended})"); + } +} diff --git a/right/src/main.rs b/right/src/main.rs index 7d313b3..66bacbe 100644 --- a/right/src/main.rs +++ b/right/src/main.rs @@ -10,7 +10,7 @@ extern crate cortex_m_rt; use alloc::vec::Vec; use embassy_executor::Spawner; use embassy_rp::gpio::{Level, Output, Pin}; -use embassy_time::{Duration, Timer}; +use embassy_time::Timer; use log::error; use tangentbord1::{ board::Board, @@ -19,7 +19,7 @@ use tangentbord1::{ keyboard::KeyboardConfig, logger::Logger, rgb::Rgb, - util::{stall, wheel}, + util::stall, ws2812::Ws2812, {allocator, rtt, uart, usb}, }; @@ -101,10 +101,16 @@ async fn main(_spawner: Spawner) { uart::start(board.tx, board.rx, board.UART0, half, events2).await; usb::setup_logger_and_keyboard(board.USB, events1).await; - neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await; - for w in 0usize.. { - neopixel.write(&[wheel(w as u8) * 0.15]).await; - Timer::after(Duration::from_millis(10)).await; + neopixel.write(&[Rgb::new(0x00, 0x00, 0xFF)]).await; + Timer::after_secs(5).await; + + for b in (0u8..0xff).rev() { + neopixel.write(&[Rgb::new(0, 0, b)]).await; + Timer::after_millis(10).await; + } + + loop { + Timer::after_secs(5).await; } }