// Device implementation struct TTYPhy { rx_size: usize, rx_buffer: [u8; 1536], tx_size: usize, tx_buffer: [u8; 1536], } impl TTYPhy { fn new() -> TTYPhy { TTYPhy { rx_size: 0, rx_buffer: [0; 1536], tx_size: 0, tx_buffer: [0; 1536], } } fn put(&mut self, frame: &[u8]) { if self.rx_size != 0 { cortex_m_semihosting::heprintln!("rx collision"); } self.rx_size = frame.len(); self.rx_buffer[..frame.len()].copy_from_slice(frame); } fn get(&mut self, f: F) where F: FnOnce(&[u8]), { f(&self.tx_buffer[..self.tx_size]); self.tx_size = 0; } } impl smoltcp::phy::Device for TTYPhy { type RxToken<'a> = TTYPhyRxToken<'a> where Self: 'a; type TxToken<'a> = TTYPhyTxToken<'a> where Self: 'a; fn receive( &mut self, _timestamp: smoltcp::time::Instant, ) -> Option<(Self::RxToken<'_>, Self::TxToken<'_>)> { if self.rx_size > 0 && self.tx_size == 0 { Some(( TTYPhyRxToken(&mut self.rx_buffer[..self.rx_size], &mut self.rx_size), TTYPhyTxToken(&mut self.tx_buffer[..], &mut self.tx_size), )) } else { None } } fn transmit(&mut self, _timestamp: smoltcp::time::Instant) -> Option> { if self.tx_size == 0 { Some(TTYPhyTxToken(&mut self.tx_buffer[..], &mut self.tx_size)) } else { None } } fn capabilities(&self) -> smoltcp::phy::DeviceCapabilities { let mut caps = smoltcp::phy::DeviceCapabilities::default(); caps.max_transmission_unit = 1536; caps.max_burst_size = Some(1); caps.medium = smoltcp::phy::Medium::Ip; caps } } struct TTYPhyRxToken<'a>(&'a mut [u8], &'a mut usize); impl<'a> smoltcp::phy::RxToken for TTYPhyRxToken<'a> { fn consume(mut self, f: F) -> R where F: FnOnce(&mut [u8]) -> R, { // cortex_m_semihosting::heprintln!("{}", smoltcp::wire::PrettyPrinter::>::new("r ", &self.0)); let result = f(&mut self.0); *self.1 = 0; result } } struct TTYPhyTxToken<'a>(&'a mut [u8], &'a mut usize); impl<'a> smoltcp::phy::TxToken for TTYPhyTxToken<'a> { fn consume(self, len: usize, f: F) -> R where F: FnOnce(&mut [u8]) -> R, { let result = f(&mut self.0[..len]); // cortex_m_semihosting::heprintln!("{}", smoltcp::wire::PrettyPrinter::>::new("t ", &&self.0[..len])); *self.1 = len; result } } // somewhere in serial handler rtic task, after getting bytes from network modem_state.ip_device.put(&big_buf[1..(big_at - 2)]); modem_task::spawn().ok(); // rtic task periodically/on events #[task(shared = [modem_comm, rtc, rng])] fn modem_task(mut ctx: modem_task::Context) { ctx.shared.modem_comm.lock( |( // resources )| { // switching depending on state let ts = ctx.shared.rtc.lock(|(rtc, overflows)| { let us = read_timer(rtc, *overflows).duration_since_epoch().to_micros(); smoltcp::time::Instant::from_micros_const(us as i64) }); modem_state.ip_iface.poll(ts, &mut modem_state.ip_device, &mut modem_state.ip_sockets); modem_state.ip_device.get(|b| { if b.len() == 0 { return; } let mut big_buf = [0; 1536]; big_buf[0] = 0x21; big_buf[1..(1 + b.len())].copy_from_slice(b); write_ppp_packet(&big_buf[0..(1 + b.len())], modem_comm_spi, modem_comm_nselect_pin); }); let sock = modem_state.ip_sockets.get_mut::(modem_state.ip_tcp_socket_handle); modem_state.http_state = match modem_state.http_state { HttpState::Connect if !sock.is_active() => { let random = ctx.shared.rng.lock(|rng| {rng.random_u16()}); let local_port = 49152 + random % 16384; use core::str::FromStr; sock .connect(modem_state.ip_iface.context(), (smoltcp::wire::IpAddress::from_str("0.0.0.0").unwrap(), 8000), local_port) .unwrap(); sock.set_ack_delay(Some(smoltcp::time::Duration::from_millis(999))); sock.set_keep_alive(Some(smoltcp::time::Duration::from_millis(9999))); HttpState::Request } HttpState::Request if sock.may_send() => { sock.send_slice( b"GET /ws HTTP/1.1\r\n\ Host: host.tld\r\n\ Upgrade: websocket\r\n\ Connection: Upgrade\r\n\ Sec-WebSocket-Key: x3JJHMbDL1EzLkh9GBhXDw==\r\n\ Sec-WebSocket-Protocol: parcel-v1\r\n\ Sec-WebSocket-Version: 13\r\n\ Origin: http://host.tld\r\n\ \r\n").unwrap(); HttpState::Upgrade } HttpState::Upgrade if sock.can_recv() => { sock .recv(|data| { let mut r = (data.len(), HttpState::Closing); const END_OF_HEADERS: &[u8] = b"\r\n\r\n"; if data.starts_with(b"HTTP/1.1 101 Switching Protocols\r\n") { r.1 = HttpState::Websocket; } if let Some(p) = data.windows(END_OF_HEADERS.len()).position(|w| w == END_OF_HEADERS) { r.0 = p + END_OF_HEADERS.len(); } r }) .unwrap() } HttpState::Websocket if sock.can_recv() => { enum Decision { TryLater, Continue, Close, } let mut ns = HttpState::Websocket; while sock.can_recv() { match sock.recv(|data| { let mut header_length = 2; let mut payload_length = 0; // websocket things (header_length + payload_length, Decision::Continue) }) .unwrap() { Decision::TryLater => break, Decision::Continue => continue, Decision::Close => { ns = HttpState::Closing; break; } } } ns } HttpState::Websocket if !sock.may_recv() => { *network_required = false; HttpState::Done } HttpState::Closing if sock.is_active() => { sock.close(); HttpState::Closing } HttpState::Closing if !sock.may_recv() => { *network_required = false; HttpState::Done } other => other, }; if let Some(delay) = modem_state.ip_iface.poll_delay(ts, &modem_state.ip_sockets) { let ms = delay.total_millis(); reschedule_respawn(modem_task_spawn_handle, ms as u32); } } ); }