diff --git a/embedded-examples/stm32h7-rtic/Cargo.lock b/embedded-examples/stm32h7-rtic/Cargo.lock index eea4a31..3436bc9 100644 --- a/embedded-examples/stm32h7-rtic/Cargo.lock +++ b/embedded-examples/stm32h7-rtic/Cargo.lock @@ -315,6 +315,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bfb9eb618601c89945a70e254898da93b13be0388091d42117462b265bb3fad" dependencies = [ + "defmt", "hash32", "stable_deref_trait", ] @@ -601,6 +602,7 @@ dependencies = [ "bitflags", "byteorder", "cfg-if", + "defmt", "heapless", "managed", ] diff --git a/embedded-examples/stm32h7-rtic/Cargo.toml b/embedded-examples/stm32h7-rtic/Cargo.toml index 44a7079..c09b999 100644 --- a/embedded-examples/stm32h7-rtic/Cargo.toml +++ b/embedded-examples/stm32h7-rtic/Cargo.toml @@ -25,7 +25,7 @@ stm32h7xx-hal = { version="0.16", features= ["stm32h743v", "ethernet"] } [dependencies.smoltcp] version = "0.11.0" default-features = false -features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4"] +features = ["medium-ethernet", "proto-ipv4", "socket-raw", "socket-dhcpv4", "socket-udp", "defmt"] [dependencies.rtic] version = "2" diff --git a/embedded-examples/stm32h7-rtic/src/main.rs b/embedded-examples/stm32h7-rtic/src/main.rs index 2965c7a..7bad1f7 100644 --- a/embedded-examples/stm32h7-rtic/src/main.rs +++ b/embedded-examples/stm32h7-rtic/src/main.rs @@ -4,15 +4,16 @@ use rtic::app; use rtic_monotonics::systick::Systick; use rtic_monotonics::Monotonic; -use satrs_stm32h7_nucleo_rtic as _; // global logger + panicking-behavior + memory layout +use satrs_stm32h7_nucleo_rtic as _; +use smoltcp::socket::{dhcpv4, udp}; // global logger + panicking-behavior + memory layout use core::mem::MaybeUninit; -use smoltcp::iface::{Config, Interface, SocketSet, SocketStorage}; -use smoltcp::time::Instant; +use smoltcp::iface::{Config, Interface, SocketHandle, SocketSet, SocketStorage}; use smoltcp::wire::{HardwareAddress, IpAddress, IpCidr}; use stm32h7xx_hal::ethernet; const DEFAULT_BLINK_FREQ_MS: u32 = 1000; +const PORT: u16 = 7301; /// Ethernet descriptor rings are a global singleton #[link_section = ".sram3.eth"] @@ -26,6 +27,11 @@ pub struct NetStorageStatic<'a> { // initialised by the runtime static mut STORE: MaybeUninit = MaybeUninit::uninit(); +static mut UDP_RX_META: [udp::PacketMetadata; 4] = [udp::PacketMetadata::EMPTY; 4]; +static mut UDP_RX: [u8; 2048] = [0; 2048]; +static mut UDP_TX_META: [udp::PacketMetadata; 4] = [udp::PacketMetadata::EMPTY; 4]; +static mut UDP_TX: [u8; 2048] = [0; 2048]; + /// Locally administered MAC address const MAC_ADDRESS: [u8; 6] = [0x02, 0x00, 0x11, 0x22, 0x33, 0x44]; @@ -33,6 +39,8 @@ pub struct Net<'a> { iface: Interface, ethdev: ethernet::EthernetDMA<4, 4>, sockets: SocketSet<'a>, + dhcp_handle: SocketHandle, + udp_handle: SocketHandle, } impl<'a> Net<'a> { @@ -40,21 +48,39 @@ impl<'a> Net<'a> { store: &'a mut NetStorageStatic<'a>, mut ethdev: ethernet::EthernetDMA<4, 4>, ethernet_addr: HardwareAddress, - now: Instant, ) -> Self { let config = Config::new(ethernet_addr); - let mut iface = Interface::new(config, &mut ethdev, now); - // Set IP address + let mut iface = Interface::new( + config, + &mut ethdev, + smoltcp::time::Instant::from_millis((Systick::now() - Systick::ZERO).to_millis()), + ); + let udp_rx_buffer = + smoltcp::socket::udp::PacketBuffer::new(unsafe { &mut UDP_RX_META[..] }, unsafe { + &mut UDP_RX[..] + }); + let udp_tx_buffer = + smoltcp::socket::udp::PacketBuffer::new(unsafe { &mut UDP_TX_META[..] }, unsafe { + &mut UDP_TX[..] + }); + let udp_socket = smoltcp::socket::udp::Socket::new(udp_rx_buffer, udp_tx_buffer); + + // Create sockets + let dhcp_socket = dhcpv4::Socket::new(); + iface.update_ip_addrs(|addrs| { let _ = addrs.push(IpCidr::new(IpAddress::v4(192, 168, 1, 99), 0)); }); - let sockets = SocketSet::new(&mut store.socket_storage[..]); - + let mut sockets = SocketSet::new(&mut store.socket_storage[..]); + let dhcp_handle = sockets.add(dhcp_socket); + let udp_handle = sockets.add(udp_socket); Net::<'a> { iface, ethdev, sockets, + dhcp_handle, + udp_handle, } } @@ -67,6 +93,73 @@ impl<'a> Net<'a> { self.iface .poll(timestamp, &mut self.ethdev, &mut self.sockets) } + + pub fn poll_dhcp(&mut self) -> Option { + let opt_event = self + .sockets + .get_mut::(self.dhcp_handle) + .poll(); + if let Some(event) = &opt_event { + match event { + dhcpv4::Event::Deconfigured => { + defmt::info!("DHCP lost configuration"); + self.iface.update_ip_addrs(|addrs| addrs.clear()); + self.iface.routes_mut().remove_default_ipv4_route(); + } + dhcpv4::Event::Configured(config) => { + defmt::info!("DHCP configuration acquired"); + defmt::info!("IP address: {}", config.address); + self.iface.update_ip_addrs(|addrs| { + addrs.clear(); + addrs.push(IpCidr::Ipv4(config.address)).unwrap(); + }); + + if let Some(router) = config.router { + defmt::debug!("Default gateway: {}", router); + self.iface + .routes_mut() + .add_default_ipv4_route(router) + .unwrap(); + } else { + defmt::debug!("Default gateway: None"); + self.iface.routes_mut().remove_default_ipv4_route(); + } + } + } + } + opt_event + } + + pub fn poll_udp(&mut self) { + let socket = self.sockets.get_mut::(self.udp_handle); + if !socket.is_open() { + if let Err(e) = socket.bind(PORT) { + defmt::warn!("binding UDP socket failed"); + } + } + loop { + match socket.recv() { + Ok((data, client)) => { + /*defmt::info!("UDP: rx {} bytes from {}", data.len(), endpoint); + if let Ok(recv_str) = str::from_utf8(data) { + defmt::info!("recv: {}", recv_str); + } + */ + // TODO: Implement packet wiretapping. + // TODO: Store last endpoint. + // TODO: Send packet to PUS/CCSDS distributor via message queue. + } + Err(e) => match e { + udp::RecvError::Exhausted => { + break; + } + udp::RecvError::Truncated => { + defmt::warn!("UDP packet was truncacted"); + } + }, + }; + } + } } #[app(device = stm32h7xx_hal::stm32, peripherals = true)] @@ -205,7 +298,7 @@ mod app { STORE.assume_init_mut() }; - let net = Net::new(store, eth_dma, mac_addr.into(), Instant::ZERO); + let net = Net::new(store, eth_dma, mac_addr.into()); eth_link_check::spawn().expect("eth link check failed"); blink::spawn().expect("spawning blink task failed"); @@ -261,6 +354,10 @@ mod app { unsafe { ethernet::interrupt_handler(); } + // TODO: I am not fully sure whether we should do everything here. Mabye we should + // offload this to a regular task? cx.local.net.poll(); + cx.local.net.poll_dhcp(); + cx.local.net.poll_udp(); } }