Files
bioz-firmware-rs/src/communication.rs
2025-08-06 11:21:31 +02:00

127 lines
3.8 KiB
Rust

use defmt::{info, error};
use embassy_executor::Spawner;
use embassy_sync::blocking_mutex::raw::ThreadModeRawMutex;
use embassy_stm32::usb::Driver;
use embassy_stm32::{peripherals, uid, usb};
use postcard_rpc::{
define_dispatch,
header::VarHeader,
server::{
impls::embassy_usb_v0_4::{
dispatch_impl::{WireRxBuf, WireRxImpl, WireSpawnImpl, WireStorage, WireTxImpl},
PacketBuffers,
},
Dispatch, Server,
},
};
use bioz_icd_rs::{PingEndpoint, GetUniqueIdEndpoint, ENDPOINT_LIST, TOPICS_IN_LIST, TOPICS_OUT_LIST};
pub struct Context {
pub unique_id: [u8; 12],
}
type AppDriver = usb::Driver<'static, peripherals::USB>;
type AppStorage = WireStorage<ThreadModeRawMutex, AppDriver, 256, 256, 64, 256>;
type BufStorage = PacketBuffers<1024, 1024>;
type AppTx = WireTxImpl<ThreadModeRawMutex, AppDriver>;
type AppRx = WireRxImpl<AppDriver>;
type AppServer = Server<AppTx, AppRx, WireRxBuf, MyApp>;
use static_cell::ConstStaticCell;
static PBUFS: ConstStaticCell<BufStorage> = ConstStaticCell::new(BufStorage::new());
static STORAGE: AppStorage = AppStorage::new();
define_dispatch! {
app: MyApp;
spawn_fn: spawn_fn;
tx_impl: AppTx;
spawn_impl: WireSpawnImpl;
context: Context;
endpoints: {
list: ENDPOINT_LIST;
| EndpointTy | kind | handler |
| ---------- | ---- | ------- |
| PingEndpoint | blocking | ping_handler |
| GetUniqueIdEndpoint | blocking | get_unique_id_handler |
};
topics_in: {
list: TOPICS_IN_LIST;
| TopicTy | kind | handler |
| ---------- | ---- | ------- |
};
topics_out: {
list: TOPICS_OUT_LIST;
};
}
fn usb_config() -> embassy_usb::Config<'static> {
let mut config = embassy_usb::Config::new(0x16c0, 0x27DD);
config.manufacturer = Some("Hubald Verzijl");
config.product = Some("Bioz Amplifier");
config.serial_number = Some("12345678");
// Required for windows compatibility.
// https://developer.nordicsemi.com/nRF_Connect_SDK/doc/1.9.1/kconfig/CONFIG_CDC_ACM_IAD.html#help
config.device_class = 0xEF;
config.device_sub_class = 0x02;
config.device_protocol = 0x01;
config.composite_with_iads = true;
config
}
/// This handles the low level USB management
#[embassy_executor::task]
pub async fn usb_task(mut usb: embassy_usb::UsbDevice<'static, AppDriver>) {
usb.run().await;
}
#[embassy_executor::task]
async fn server_run(mut server: AppServer) {
loop {
// If the host disconnects, we'll return an error here.
// If this happens, just wait until the host reconnects
let _ = server.run().await;
}
}
// ---
pub fn init_communication(usb_driver: Driver<'static, peripherals::USB>, spawner: Spawner) {
// Initialize communication peripherals
let pbufs = PBUFS.take();
let config = usb_config();
let context = Context {
unique_id: *uid::uid(),
};
let (device, tx_impl, rx_impl) = STORAGE.init(usb_driver, config, pbufs.tx_buf.as_mut_slice());
let dispatcher = MyApp::new(context, spawner.into());
let vkk = dispatcher.min_key_len();
let server: AppServer = Server::new(
tx_impl,
rx_impl,
pbufs.rx_buf.as_mut_slice(),
dispatcher,
vkk,
);
spawner.must_spawn(usb_task(device));
spawner.must_spawn(server_run(server));
}
fn ping_handler(_context: &mut Context, _header: VarHeader, rqst: u32) -> u32 {
info!("ping");
rqst
}
fn get_unique_id_handler(context: &mut Context, _header: VarHeader, _rqst: ()) -> [u8; 12] {
info!("get_unique_id");
context.unique_id
}