//! CDC-ACM serial port example using embassy. //! //! This example should be built in release mode. //! //! The following wiring is assumed: //! - DP => GPIO20 //! - DM => GPIO19 //% CHIPS: esp32s2 esp32s3 //% FEATURES: embassy esp-hal/unstable #![no_std] #![no_main] use embassy_executor::Spawner; use embassy_futures::join::join; use embassy_usb::{ Builder, class::cdc_acm::{CdcAcmClass, State}, driver::EndpointError, }; use esp_backtrace as _; use esp_hal::{ otg_fs::{ Usb, asynch::{Config, Driver}, }, timer::timg::TimerGroup, }; esp_bootloader_esp_idf::esp_app_desc!(); #[esp_hal_embassy::main] async fn main(_spawner: Spawner) { esp_println::println!("Init!"); let peripherals = esp_hal::init(esp_hal::Config::default()); let timg0 = TimerGroup::new(peripherals.TIMG0); esp_hal_embassy::init(timg0.timer0); let usb = Usb::new(peripherals.USB0, peripherals.GPIO20, peripherals.GPIO19); // Create the driver, from the HAL. let mut ep_out_buffer = [0u8; 1024]; let config = Config::default(); let driver = Driver::new(usb, &mut ep_out_buffer, config); // Create embassy-usb Config let mut config = embassy_usb::Config::new(0x303A, 0x3001); config.manufacturer = Some("Espressif"); config.product = Some("USB-serial example"); 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; // Create embassy-usb DeviceBuilder using the driver and config. // It needs some buffers for building the descriptors. let mut config_descriptor = [0; 256]; let mut bos_descriptor = [0; 256]; let mut control_buf = [0; 64]; let mut state = State::new(); let mut builder = Builder::new( driver, config, &mut config_descriptor, &mut bos_descriptor, &mut [], // no msos descriptors &mut control_buf, ); // Create classes on the builder. let mut class = CdcAcmClass::new(&mut builder, &mut state, 64); // Build the builder. let mut usb = builder.build(); // Run the USB device. let usb_fut = usb.run(); // Do stuff with the class! let echo_fut = async { loop { class.wait_connection().await; esp_println::println!("Connected"); let _ = echo(&mut class).await; esp_println::println!("Disconnected"); } }; // Run everything concurrently. // If we had made everything `'static` above instead, we could do this using // separate tasks instead. join(usb_fut, echo_fut).await; } async fn echo<'d>(class: &mut CdcAcmClass<'d, Driver<'d>>) -> Result<(), Disconnected> { let mut buf = [0; 64]; loop { let n = class.read_packet(&mut buf).await?; // Echo back in upper case for c in buf[0..n].iter_mut() { if 0x61 <= *c && *c <= 0x7a { *c &= !0x20; } } let data = &buf[..n]; class.write_packet(data).await?; } } struct Disconnected {} impl From for Disconnected { fn from(val: EndpointError) -> Self { match val { EndpointError::BufferOverflow => panic!("Buffer overflow"), EndpointError::Disabled => Disconnected {}, } } }