#![no_std] #![no_main] #![feature(impl_trait_in_assoc_type)] #![feature(try_blocks)] extern crate alloc; use core::net::{ IpAddr, Ipv4Addr, }; use embassy_net::tcp::client::TcpClientState; use molybdos::{ embassy_embedded_hal::shared_bus::asynch::{ i2c::I2cDevice, spi::SpiDevice, }, embassy_net::StackResources, embassy_rp, embassy_rp::{ i2c::{ self, I2c, }, peripherals::{ I2C0, I2C1, PIO0, SPI0, SPI1, }, spi::{ self, Spi, }, }, embassy_sync::{ blocking_mutex::raw::{ CriticalSectionRawMutex, ThreadModeRawMutex, }, pipe::Pipe, pubsub, pubsub::PubSubBehavior, }, embassy_time, embassy_usb, embassy_usb::class::{ cdc_ncm, cdc_ncm::embassy_net::Device, }, embedded_hal_async, embedded_hal_async::spi::{ Operation, SpiDevice as _, }, embedded_io_async::Write, embedded_sdmmc_async::{ VolumeIdx, VolumeManager, }, genlog, genlog::{ defmt, defmt::unwrap, }, heapless, pal::StaticOutput, static_cell::{ ConstStaticCell, StaticCell, }, }; use ocularium_common::{ Downlink, Sensor, }; use reqwless::request::Method; use crate::usb::COBS_DOWNLINK; mod bringup; mod usb; pub type BMESpi = Spi<'static, SPI1, spi::Async>; pub type BMESpiDev = SpiDevice<'static, CriticalSectionRawMutex, BMESpi, StaticOutput>; pub type SdSpi = Spi<'static, SPI0, spi::Async>; pub type SdSpiDev = SpiDevice<'static, CriticalSectionRawMutex, SdSpi, StaticOutput>; pub type SensorI2c = I2c<'static, I2C1, i2c::Async>; pub static I2S_PIPE: Pipe = Pipe::new(); #[::embassy_executor::main] #[inline] async fn main(spawner: embassy_executor::Spawner) { _main(spawner).await } async fn _main(spawner: embassy_executor::Spawner) { molybdos::pal::heap::init(); defmt::info!("boot"); let bringup::Split { sd_spi, sd_cs, bme_spi, bme_cs, wdt, usb, i2s, i2s_dma, .. } = bringup::split(Default::default()); defmt::info!("split ok"); // spawner.must_spawn(molybdos::pal::watchdog(wdt)); const MTU: usize = 1514; const MAC: [u8; 6] = [0, 0, 0, 0, 0, 0]; let (acm, ncm) = molybdos::lib::usb::bringup!( spawn, usb, molybdos::pal::UsbDriver, molybdos::lib::usb::config("npry", "ocularium", 0x8888, 0x0011), endpoints = { acm => |builder| molybdos::lib::usb::acm!(builder), ncm => |builder| { static STATE: StaticCell = StaticCell::new(); let state = StaticCell::init(&STATE, cdc_ncm::State::new()); cdc_ncm::CdcNcmClass::new(builder, state, MAC, 64) }, } ); #[embassy_executor::task] async fn ncm_task( runner: cdc_ncm::embassy_net::Runner< 'static, embassy_rp::usb::Driver<'static, embassy_rp::peripherals::USB>, MTU, >, ) -> ! { runner.run().await } molybdos::lib::usb::start!(molybdos::pal::UsbDriver, acm, &usb::UPLINK, &usb::DOWNLINK); static NET_STATE: ConstStaticCell> = ConstStaticCell::new(cdc_ncm::embassy_net::State::new()); let (runner, dev) = ncm.into_embassy_net_device::(NET_STATE.take(), MAC); spawner.must_spawn(ncm_task(runner)); let config = molybdos::embassy_net::Config::dhcpv4(Default::default()); static RESOURCES: StaticCell> = StaticCell::new(); let (stack, runner) = molybdos::embassy_net::new(dev, config, RESOURCES.init(StackResources::new()), 0); #[embassy_executor::task] async fn net_task( mut runner: molybdos::embassy_net::Runner<'static, Device<'static, MTU>>, ) -> ! { runner.run().await } spawner.must_spawn(net_task(runner)); molybdos::lib::util::cobs::start!( ocularium_common::Uplink, &usb::UPLINK, &usb::COBS_UPLINK, ocularium_common::Downlink, &usb::DOWNLINK, &usb::COBS_DOWNLINK ); // let mut vemldev = I2cDevice::new(&); let mut spidev = SpiDevice::new(bme_spi, bme_cs); manually_check_bme_id(&mut spidev).await; let config = drogue_bme680_async::Configuration::default(); let bme = drogue_bme680_async::Sensor::new_spi(spidev).await.unwrap(); let mut bme = drogue_bme680_async::Controller::new( bme, molybdos::embassy_time::Delay, config, drogue_bme680_async::StaticProvider(23), ) .await .unwrap(); let meas = bme.measure_default().await.unwrap().unwrap(); defmt::info!("got bme measurement: {}", defmt::Debug2Format(&meas)); #[cfg(not(feature = "disable_sd"))] { let vm = molybdos::rt::sd::bringup!(sd_spi, SdSpi, sd_cs); { let vol = match VolumeManager::open_volume(vm, VolumeIdx(0)).await { Ok(x) => x, Err(e) => { defmt::error!("opening volume: {}", defmt::Debug2Format(&e)); loop { molybdos::embassy_time::Timer::after( molybdos::embassy_time::Duration::from_secs(1), ) .await; } }, }; } } #[cfg(not(feature = "disable_i2s"))] { spawner.must_spawn(drain_pipe()); spawner.must_spawn(run_i2s(i2s, i2s_dma)); } let state = TcpClientState::new(); let client = embassy_net::tcp::client::TcpClient::<1>::new(stack, &state); let dns = embassy_net::dns::DnsSocket::new(stack); let mut client = reqwless::client::HttpClient::new(&client, &dns); let mut headers_buf = [0; 512]; loop { let _result: Result<(), reqwless::Error> = try { let mut req = client.request(Method::GET, "http://httpbin.org/anything").await?; let resp = req.send(&mut headers_buf).await?; let body = resp.body().read_to_end().await?; let body_str = core::str::from_utf8(&body).unwrap(); defmt::info!("got response: {}", body_str); }; embassy_time::Timer::after(embassy_time::Duration::from_secs(1)).await; } } async fn manually_check_bme_id(spidev: &mut impl embedded_hal_async::spi::SpiDevice) { let data = &mut [0u8]; let result = spidev .transaction(&mut [Operation::Write(&[0x50 | (1 << 7)]), Operation::Read(data)]) .await; if let Err(e) = result { defmt::error!("reading spi dev: {}", defmt::Debug2Format(&e)); } else { defmt::info!("read chipid: {:x}", data[0]); } } #[cfg(not(feature = "disable_i2s"))] #[::embassy_executor::task] async fn run_i2s(mut i2s: bringup::StaticI2S, dma: embassy_rp::peripherals::DMA_CH5) { i2s.run::<960>(dma, &I2S_PIPE).await } #[cfg(not(feature = "disable_i2s"))] #[::embassy_executor::task] async fn drain_pipe() { let publ = COBS_DOWNLINK.publisher().expect("getting publisher"); loop { let buf = &mut [0u8; 960]; let n = I2S_PIPE.read(buf).await; for elem in (&buf[..n]).chunks(48) { publ.publish_immediate(Downlink::Sensor(Sensor::Pdm(alloc::vec::Vec::from(elem)))); } } }