aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorDario Nieuwenhuis <[email protected]>2023-05-31 00:55:16 +0200
committerDario Nieuwenhuis <[email protected]>2023-05-31 00:55:16 +0200
commit82d765689aa0f922d0b43e402eaa3d911b2d461d (patch)
tree62247897621cf874a4cd4f71b720366d8d102b28
parent3f90620343a5413423da8b84008cf468ea957d54 (diff)
parent7f0e778145e7a0f7281d9b37e59473fddf232097 (diff)
Merge remote-tracking branch 'w5500/main' into w5500
-rw-r--r--embassy-net-w5500/Cargo.toml26
-rw-r--r--embassy-net-w5500/README.md7
-rw-r--r--embassy-net-w5500/src/device.rs144
-rw-r--r--embassy-net-w5500/src/lib.rs114
-rw-r--r--embassy-net-w5500/src/socket.rs103
-rw-r--r--embassy-net-w5500/src/spi.rs37
-rw-r--r--examples/rp/src/bin/ethernet_w5500_multisocket.rs152
-rw-r--r--examples/rp/src/bin/ethernet_w5500_tcp_client.rs132
-rw-r--r--examples/rp/src/bin/ethernet_w5500_tcp_server.rs141
-rw-r--r--examples/rp/src/bin/ethernet_w5500_udp.rs127
10 files changed, 983 insertions, 0 deletions
diff --git a/embassy-net-w5500/Cargo.toml b/embassy-net-w5500/Cargo.toml
new file mode 100644
index 000000000..1921e812d
--- /dev/null
+++ b/embassy-net-w5500/Cargo.toml
@@ -0,0 +1,26 @@
1[package]
2name = "embassy-net-w5500"
3version = "0.1.0"
4description = "embassy-net driver for the W5500 ethernet chip"
5keywords = ["embedded", "w5500", "embassy-net", "embedded-hal-async", "ethernet", "async"]
6categories = ["embedded", "hardware-support", "no-std", "network-programming", "async"]
7license = "MIT OR Apache-2.0"
8edition = "2021"
9
10[dependencies]
11embedded-hal = { version = "1.0.0-alpha.10" }
12embedded-hal-async = { version = "=0.2.0-alpha.1" }
13embassy-net-driver-channel = { version = "0.1.0" }
14embassy-time = { version = "0.1.0" }
15embassy-futures = { version = "0.1.0" }
16defmt = { version = "0.3", optional = true }
17
18[patch.crates-io]
19embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" }
20embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" }
21embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" }
22embassy-sync = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" }
23embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" }
24embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" }
25embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" }
26embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "e179e7cf85810f0aa7ef8027d8d48f6d21f64dac" }
diff --git a/embassy-net-w5500/README.md b/embassy-net-w5500/README.md
new file mode 100644
index 000000000..9eaf4b700
--- /dev/null
+++ b/embassy-net-w5500/README.md
@@ -0,0 +1,7 @@
1# WIZnet W5500 `embassy-net` integration
2
3[`embassy-net`](https://crates.io/crates/embassy-net) integration for the WIZnet W5500 SPI ethernet chip, operating in MACRAW mode.
4
5Supports any SPI driver implementing [`embedded-hal-async`](https://crates.io/crates/embedded-hal-async)
6
7See [`examples`](https://github.com/kalkyl/embassy-net-w5500/tree/main/examples) directory for usage examples with the rp2040 [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) module. \ No newline at end of file
diff --git a/embassy-net-w5500/src/device.rs b/embassy-net-w5500/src/device.rs
new file mode 100644
index 000000000..8158bc98e
--- /dev/null
+++ b/embassy-net-w5500/src/device.rs
@@ -0,0 +1,144 @@
1use crate::socket;
2use crate::spi::SpiInterface;
3use embedded_hal_async::spi::SpiDevice;
4
5pub const MODE: u16 = 0x00;
6pub const MAC: u16 = 0x09;
7pub const SOCKET_INTR: u16 = 0x18;
8pub const PHY_CFG: u16 = 0x2E;
9
10#[repr(u8)]
11pub enum RegisterBlock {
12 Common = 0x00,
13 Socket0 = 0x01,
14 TxBuf = 0x02,
15 RxBuf = 0x03,
16}
17
18/// W5500 in MACRAW mode
19#[derive(Debug)]
20#[cfg_attr(feature = "defmt", derive(defmt::Format))]
21pub struct W5500<SPI> {
22 bus: SpiInterface<SPI>,
23}
24
25impl<SPI: SpiDevice> W5500<SPI> {
26 /// Create and initialize the W5500 driver
27 pub async fn new(spi: SPI, mac_addr: [u8; 6]) -> Result<W5500<SPI>, SPI::Error> {
28 let mut bus = SpiInterface(spi);
29 // Reset device
30 bus.write_frame(RegisterBlock::Common, MODE, &[0x80])
31 .await?;
32
33 // Enable interrupt pin
34 bus.write_frame(RegisterBlock::Common, SOCKET_INTR, &[0x01])
35 .await?;
36 // Enable receive interrupt
37 bus.write_frame(
38 RegisterBlock::Socket0,
39 socket::SOCKET_INTR_MASK,
40 &[socket::Interrupt::Receive as u8],
41 )
42 .await?;
43
44 // Set MAC address
45 bus.write_frame(RegisterBlock::Common, MAC, &mac_addr)
46 .await?;
47
48 // Set the raw socket RX/TX buffer sizes to 16KB
49 bus.write_frame(RegisterBlock::Socket0, socket::TXBUF_SIZE, &[16])
50 .await?;
51 bus.write_frame(RegisterBlock::Socket0, socket::RXBUF_SIZE, &[16])
52 .await?;
53
54 // MACRAW mode with MAC filtering.
55 let mode: u8 = (1 << 2) | (1 << 7);
56 bus.write_frame(RegisterBlock::Socket0, socket::MODE, &[mode])
57 .await?;
58 socket::command(&mut bus, socket::Command::Open).await?;
59
60 Ok(Self { bus })
61 }
62
63 /// Read bytes from the RX buffer. Returns the number of bytes read.
64 async fn read_bytes(&mut self, buffer: &mut [u8], offset: u16) -> Result<usize, SPI::Error> {
65 let rx_size = socket::get_rx_size(&mut self.bus).await? as usize;
66
67 let read_buffer = if rx_size > buffer.len() + offset as usize {
68 buffer
69 } else {
70 &mut buffer[..rx_size - offset as usize]
71 };
72
73 let read_ptr = socket::get_rx_read_ptr(&mut self.bus)
74 .await?
75 .wrapping_add(offset);
76 self.bus
77 .read_frame(RegisterBlock::RxBuf, read_ptr, read_buffer)
78 .await?;
79 socket::set_rx_read_ptr(
80 &mut self.bus,
81 read_ptr.wrapping_add(read_buffer.len() as u16),
82 )
83 .await?;
84
85 Ok(read_buffer.len())
86 }
87
88 /// Read an ethernet frame from the device. Returns the number of bytes read.
89 pub async fn read_frame(&mut self, frame: &mut [u8]) -> Result<usize, SPI::Error> {
90 let rx_size = socket::get_rx_size(&mut self.bus).await? as usize;
91 if rx_size == 0 {
92 return Ok(0);
93 }
94
95 socket::reset_interrupt(&mut self.bus, socket::Interrupt::Receive).await?;
96
97 // First two bytes gives the size of the received ethernet frame
98 let expected_frame_size: usize = {
99 let mut frame_bytes = [0u8; 2];
100 assert!(self.read_bytes(&mut frame_bytes[..], 0).await? == 2);
101 u16::from_be_bytes(frame_bytes) as usize - 2
102 };
103
104 // Read the ethernet frame
105 let read_buffer = if frame.len() > expected_frame_size {
106 &mut frame[..expected_frame_size]
107 } else {
108 frame
109 };
110
111 let recvd_frame_size = self.read_bytes(read_buffer, 2).await?;
112
113 // Register RX as completed
114 socket::command(&mut self.bus, socket::Command::Receive).await?;
115
116 // If the whole frame wasn't read, drop it
117 if recvd_frame_size < expected_frame_size {
118 Ok(0)
119 } else {
120 Ok(recvd_frame_size)
121 }
122 }
123
124 /// Write an ethernet frame to the device. Returns number of bytes written
125 pub async fn write_frame(&mut self, frame: &[u8]) -> Result<usize, SPI::Error> {
126 while socket::get_tx_free_size(&mut self.bus).await? < frame.len() as u16 {}
127 let write_ptr = socket::get_tx_write_ptr(&mut self.bus).await?;
128 self.bus
129 .write_frame(RegisterBlock::TxBuf, write_ptr, frame)
130 .await?;
131 socket::set_tx_write_ptr(&mut self.bus, write_ptr.wrapping_add(frame.len() as u16)).await?;
132 socket::command(&mut self.bus, socket::Command::Send).await?;
133 Ok(frame.len())
134 }
135
136 pub async fn is_link_up(&mut self) -> bool {
137 let mut link = [0];
138 self.bus
139 .read_frame(RegisterBlock::Common, PHY_CFG, &mut link)
140 .await
141 .ok();
142 link[0] & 1 == 1
143 }
144}
diff --git a/embassy-net-w5500/src/lib.rs b/embassy-net-w5500/src/lib.rs
new file mode 100644
index 000000000..bf14b05b4
--- /dev/null
+++ b/embassy-net-w5500/src/lib.rs
@@ -0,0 +1,114 @@
1#![no_std]
2/// [`embassy-net`](crates.io/crates/embassy-net) driver for the WIZnet W5500 ethernet chip.
3mod device;
4mod socket;
5mod spi;
6
7use crate::device::W5500;
8use embassy_futures::select::{select, Either};
9use embassy_net_driver_channel as ch;
10use embassy_net_driver_channel::driver::LinkState;
11use embassy_time::{Duration, Timer};
12use embedded_hal::digital::OutputPin;
13use embedded_hal_async::digital::Wait;
14use embedded_hal_async::spi::SpiDevice;
15const MTU: usize = 1514;
16
17/// Type alias for the embassy-net driver for W5500
18pub type Device<'d> = embassy_net_driver_channel::Device<'d, MTU>;
19
20/// Internal state for the embassy-net integration.
21pub struct State<const N_RX: usize, const N_TX: usize> {
22 ch_state: ch::State<MTU, N_RX, N_TX>,
23}
24
25impl<const N_RX: usize, const N_TX: usize> State<N_RX, N_TX> {
26 /// Create a new `State`.
27 pub const fn new() -> Self {
28 Self {
29 ch_state: ch::State::new(),
30 }
31 }
32}
33
34/// Background runner for the W5500.
35///
36/// You must call `.run()` in a background task for the W5500 to operate.
37pub struct Runner<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> {
38 mac: W5500<SPI>,
39 ch: ch::Runner<'d, MTU>,
40 int: INT,
41 _reset: RST,
42}
43
44/// You must call this in a background task for the W5500 to operate.
45impl<'d, SPI: SpiDevice, INT: Wait, RST: OutputPin> Runner<'d, SPI, INT, RST> {
46 pub async fn run(mut self) -> ! {
47 let (state_chan, mut rx_chan, mut tx_chan) = self.ch.split();
48 loop {
49 if self.mac.is_link_up().await {
50 state_chan.set_link_state(LinkState::Up);
51 loop {
52 match select(
53 async {
54 self.int.wait_for_low().await.ok();
55 rx_chan.rx_buf().await
56 },
57 tx_chan.tx_buf(),
58 )
59 .await
60 {
61 Either::First(p) => {
62 if let Ok(n) = self.mac.read_frame(p).await {
63 rx_chan.rx_done(n);
64 }
65 }
66 Either::Second(p) => {
67 self.mac.write_frame(p).await.ok();
68 tx_chan.tx_done();
69 }
70 }
71 }
72 } else {
73 state_chan.set_link_state(LinkState::Down);
74 }
75 }
76 }
77}
78
79/// Obtain a driver for using the W5500 with [`embassy-net`](crates.io/crates/embassy-net).
80pub async fn new<
81 'a,
82 const N_RX: usize,
83 const N_TX: usize,
84 SPI: SpiDevice,
85 INT: Wait,
86 RST: OutputPin,
87>(
88 mac_addr: [u8; 6],
89 state: &'a mut State<N_RX, N_TX>,
90 spi_dev: SPI,
91 int: INT,
92 mut reset: RST,
93) -> (Device<'a>, Runner<'a, SPI, INT, RST>) {
94 // Reset the W5500.
95 reset.set_low().ok();
96 // Ensure the reset is registered.
97 Timer::after(Duration::from_millis(1)).await;
98 reset.set_high().ok();
99 // Wait for the W5500 to achieve PLL lock.
100 Timer::after(Duration::from_millis(2)).await;
101
102 let mac = W5500::new(spi_dev, mac_addr).await.unwrap();
103
104 let (runner, device) = ch::new(&mut state.ch_state, mac_addr);
105 (
106 device,
107 Runner {
108 ch: runner,
109 mac,
110 int,
111 _reset: reset,
112 },
113 )
114}
diff --git a/embassy-net-w5500/src/socket.rs b/embassy-net-w5500/src/socket.rs
new file mode 100644
index 000000000..3f64d04d1
--- /dev/null
+++ b/embassy-net-w5500/src/socket.rs
@@ -0,0 +1,103 @@
1use crate::device::RegisterBlock;
2use crate::spi::SpiInterface;
3use embedded_hal_async::spi::SpiDevice;
4
5pub const MODE: u16 = 0x00;
6pub const COMMAND: u16 = 0x01;
7pub const RXBUF_SIZE: u16 = 0x1E;
8pub const TXBUF_SIZE: u16 = 0x1F;
9pub const TX_FREE_SIZE: u16 = 0x20;
10pub const TX_DATA_WRITE_PTR: u16 = 0x24;
11pub const RECVD_SIZE: u16 = 0x26;
12pub const RX_DATA_READ_PTR: u16 = 0x28;
13pub const SOCKET_INTR_MASK: u16 = 0x2C;
14
15#[repr(u8)]
16pub enum Command {
17 Open = 0x01,
18 Send = 0x20,
19 Receive = 0x40,
20}
21
22pub const INTR: u16 = 0x02;
23#[repr(u8)]
24pub enum Interrupt {
25 Receive = 0b00100_u8,
26}
27
28pub async fn reset_interrupt<SPI: SpiDevice>(
29 bus: &mut SpiInterface<SPI>,
30 code: Interrupt,
31) -> Result<(), SPI::Error> {
32 let data = [code as u8];
33 bus.write_frame(RegisterBlock::Socket0, INTR, &data).await
34}
35
36pub async fn get_tx_write_ptr<SPI: SpiDevice>(
37 bus: &mut SpiInterface<SPI>,
38) -> Result<u16, SPI::Error> {
39 let mut data = [0u8; 2];
40 bus.read_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &mut data)
41 .await?;
42 Ok(u16::from_be_bytes(data))
43}
44
45pub async fn set_tx_write_ptr<SPI: SpiDevice>(
46 bus: &mut SpiInterface<SPI>,
47 ptr: u16,
48) -> Result<(), SPI::Error> {
49 let data = ptr.to_be_bytes();
50 bus.write_frame(RegisterBlock::Socket0, TX_DATA_WRITE_PTR, &data)
51 .await
52}
53
54pub async fn get_rx_read_ptr<SPI: SpiDevice>(
55 bus: &mut SpiInterface<SPI>,
56) -> Result<u16, SPI::Error> {
57 let mut data = [0u8; 2];
58 bus.read_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &mut data)
59 .await?;
60 Ok(u16::from_be_bytes(data))
61}
62
63pub async fn set_rx_read_ptr<SPI: SpiDevice>(
64 bus: &mut SpiInterface<SPI>,
65 ptr: u16,
66) -> Result<(), SPI::Error> {
67 let data = ptr.to_be_bytes();
68 bus.write_frame(RegisterBlock::Socket0, RX_DATA_READ_PTR, &data)
69 .await
70}
71
72pub async fn command<SPI: SpiDevice>(
73 bus: &mut SpiInterface<SPI>,
74 command: Command,
75) -> Result<(), SPI::Error> {
76 let data = [command as u8];
77 bus.write_frame(RegisterBlock::Socket0, COMMAND, &data)
78 .await
79}
80
81pub async fn get_rx_size<SPI: SpiDevice>(bus: &mut SpiInterface<SPI>) -> Result<u16, SPI::Error> {
82 loop {
83 // Wait until two sequential reads are equal
84 let mut res0 = [0u8; 2];
85 bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res0)
86 .await?;
87 let mut res1 = [0u8; 2];
88 bus.read_frame(RegisterBlock::Socket0, RECVD_SIZE, &mut res1)
89 .await?;
90 if res0 == res1 {
91 break Ok(u16::from_be_bytes(res0));
92 }
93 }
94}
95
96pub async fn get_tx_free_size<SPI: SpiDevice>(
97 bus: &mut SpiInterface<SPI>,
98) -> Result<u16, SPI::Error> {
99 let mut data = [0; 2];
100 bus.read_frame(RegisterBlock::Socket0, TX_FREE_SIZE, &mut data)
101 .await?;
102 Ok(u16::from_be_bytes(data))
103}
diff --git a/embassy-net-w5500/src/spi.rs b/embassy-net-w5500/src/spi.rs
new file mode 100644
index 000000000..55d311888
--- /dev/null
+++ b/embassy-net-w5500/src/spi.rs
@@ -0,0 +1,37 @@
1use crate::device::RegisterBlock;
2use embedded_hal_async::spi::{Operation, SpiDevice};
3
4#[derive(Debug)]
5#[cfg_attr(feature = "defmt", derive(defmt::Format))]
6pub struct SpiInterface<SPI>(pub SPI);
7
8impl<SPI: SpiDevice> SpiInterface<SPI> {
9 pub async fn read_frame(
10 &mut self,
11 block: RegisterBlock,
12 address: u16,
13 data: &mut [u8],
14 ) -> Result<(), SPI::Error> {
15 let address_phase = address.to_be_bytes();
16 let control_phase = [(block as u8) << 3];
17 let operations = &mut [
18 Operation::Write(&address_phase),
19 Operation::Write(&control_phase),
20 Operation::TransferInPlace(data),
21 ];
22 self.0.transaction(operations).await
23 }
24
25 pub async fn write_frame(
26 &mut self,
27 block: RegisterBlock,
28 address: u16,
29 data: &[u8],
30 ) -> Result<(), SPI::Error> {
31 let address_phase = address.to_be_bytes();
32 let control_phase = [(block as u8) << 3 | 0b0000_0100];
33 let data_phase = data;
34 let operations = &[&address_phase[..], &control_phase, &data_phase];
35 self.0.write_transaction(operations).await
36 }
37}
diff --git a/examples/rp/src/bin/ethernet_w5500_multisocket.rs b/examples/rp/src/bin/ethernet_w5500_multisocket.rs
new file mode 100644
index 000000000..eb3b8de81
--- /dev/null
+++ b/examples/rp/src/bin/ethernet_w5500_multisocket.rs
@@ -0,0 +1,152 @@
1//! This example shows how you can allow multiple simultaneous TCP connections, by having multiple sockets listening on the same port.
2//!
3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board.
4
5#![no_std]
6#![no_main]
7#![feature(type_alias_impl_trait)]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_futures::yield_now;
12use embassy_net::{Stack, StackResources};
13use embassy_net_w5500::*;
14use embassy_rp::clocks::RoscRng;
15use embassy_rp::gpio::{Input, Level, Output, Pull};
16use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0};
17use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
18use embedded_hal_async::spi::ExclusiveDevice;
19use embedded_io::asynch::Write;
20use rand::RngCore;
21use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _};
23
24macro_rules! singleton {
25 ($val:expr) => {{
26 type T = impl Sized;
27 static STATIC_CELL: StaticCell<T> = StaticCell::new();
28 let (x,) = STATIC_CELL.init(($val,));
29 x
30 }};
31}
32
33#[embassy_executor::task]
34async fn ethernet_task(
35 runner: Runner<
36 'static,
37 ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>>,
38 Input<'static, PIN_21>,
39 Output<'static, PIN_20>,
40 >,
41) -> ! {
42 runner.run().await
43}
44
45#[embassy_executor::task]
46async fn net_task(stack: &'static Stack<Device<'static>>) -> ! {
47 stack.run().await
48}
49
50#[embassy_executor::main]
51async fn main(spawner: Spawner) {
52 let p = embassy_rp::init(Default::default());
53 let mut rng = RoscRng;
54
55 let mut spi_cfg = SpiConfig::default();
56 spi_cfg.frequency = 50_000_000;
57 let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);
58 let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg);
59 let cs = Output::new(p.PIN_17, Level::High);
60 let w5500_int = Input::new(p.PIN_21, Pull::Up);
61 let w5500_reset = Output::new(p.PIN_20, Level::High);
62
63 let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
64 let state = singleton!(State::<8, 8>::new());
65 let (device, runner) = embassy_net_w5500::new(
66 mac_addr,
67 state,
68 ExclusiveDevice::new(spi, cs),
69 w5500_int,
70 w5500_reset,
71 )
72 .await;
73 unwrap!(spawner.spawn(ethernet_task(runner)));
74
75 // Generate random seed
76 let seed = rng.next_u64();
77
78 // Init network stack
79 let stack = &*singleton!(Stack::new(
80 device,
81 embassy_net::Config::Dhcp(Default::default()),
82 singleton!(StackResources::<3>::new()),
83 seed
84 ));
85
86 // Launch network task
87 unwrap!(spawner.spawn(net_task(&stack)));
88
89 info!("Waiting for DHCP...");
90 let cfg = wait_for_config(stack).await;
91 let local_addr = cfg.address.address();
92 info!("IP address: {:?}", local_addr);
93
94 // Create two sockets listening to the same port, to handle simultaneous connections
95 unwrap!(spawner.spawn(listen_task(&stack, 0, 1234)));
96 unwrap!(spawner.spawn(listen_task(&stack, 1, 1234)));
97}
98
99#[embassy_executor::task(pool_size = 2)]
100async fn listen_task(stack: &'static Stack<Device<'static>>, id: u8, port: u16) {
101 let mut rx_buffer = [0; 4096];
102 let mut tx_buffer = [0; 4096];
103 let mut buf = [0; 4096];
104 loop {
105 let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
106 socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
107
108 info!("SOCKET {}: Listening on TCP:{}...", id, port);
109 if let Err(e) = socket.accept(port).await {
110 warn!("accept error: {:?}", e);
111 continue;
112 }
113 info!(
114 "SOCKET {}: Received connection from {:?}",
115 id,
116 socket.remote_endpoint()
117 );
118
119 loop {
120 let n = match socket.read(&mut buf).await {
121 Ok(0) => {
122 warn!("read EOF");
123 break;
124 }
125 Ok(n) => n,
126 Err(e) => {
127 warn!("SOCKET {}: {:?}", id, e);
128 break;
129 }
130 };
131 info!(
132 "SOCKET {}: rxd {}",
133 id,
134 core::str::from_utf8(&buf[..n]).unwrap()
135 );
136
137 if let Err(e) = socket.write_all(&buf[..n]).await {
138 warn!("write error: {:?}", e);
139 break;
140 }
141 }
142 }
143}
144
145async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfig {
146 loop {
147 if let Some(config) = stack.config() {
148 return config.clone();
149 }
150 yield_now().await;
151 }
152}
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_client.rs b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
new file mode 100644
index 000000000..e166e0f35
--- /dev/null
+++ b/examples/rp/src/bin/ethernet_w5500_tcp_client.rs
@@ -0,0 +1,132 @@
1//! This example implements a TCP client that attempts to connect to a host on port 1234 and send it some data once per second.
2//!
3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board.
4
5#![no_std]
6#![no_main]
7#![feature(type_alias_impl_trait)]
8
9use core::str::FromStr;
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_futures::yield_now;
13use embassy_net::{Stack, StackResources};
14use embassy_net_w5500::*;
15use embassy_rp::clocks::RoscRng;
16use embassy_rp::gpio::{Input, Level, Output, Pull};
17use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0};
18use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
19use embassy_time::{Duration, Timer};
20use embedded_hal_async::spi::ExclusiveDevice;
21use embedded_io::asynch::Write;
22use rand::RngCore;
23use static_cell::StaticCell;
24use {defmt_rtt as _, panic_probe as _};
25
26macro_rules! singleton {
27 ($val:expr) => {{
28 type T = impl Sized;
29 static STATIC_CELL: StaticCell<T> = StaticCell::new();
30 let (x,) = STATIC_CELL.init(($val,));
31 x
32 }};
33}
34
35#[embassy_executor::task]
36async fn ethernet_task(
37 runner: Runner<
38 'static,
39 ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>>,
40 Input<'static, PIN_21>,
41 Output<'static, PIN_20>,
42 >,
43) -> ! {
44 runner.run().await
45}
46
47#[embassy_executor::task]
48async fn net_task(stack: &'static Stack<Device<'static>>) -> ! {
49 stack.run().await
50}
51
52#[embassy_executor::main]
53async fn main(spawner: Spawner) {
54 let p = embassy_rp::init(Default::default());
55 let mut rng = RoscRng;
56 let mut led = Output::new(p.PIN_25, Level::Low);
57
58 let mut spi_cfg = SpiConfig::default();
59 spi_cfg.frequency = 50_000_000;
60 let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);
61 let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg);
62 let cs = Output::new(p.PIN_17, Level::High);
63 let w5500_int = Input::new(p.PIN_21, Pull::Up);
64 let w5500_reset = Output::new(p.PIN_20, Level::High);
65
66 let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
67 let state = singleton!(State::<8, 8>::new());
68 let (device, runner) = embassy_net_w5500::new(
69 mac_addr,
70 state,
71 ExclusiveDevice::new(spi, cs),
72 w5500_int,
73 w5500_reset,
74 )
75 .await;
76 unwrap!(spawner.spawn(ethernet_task(runner)));
77
78 // Generate random seed
79 let seed = rng.next_u64();
80
81 // Init network stack
82 let stack = &*singleton!(Stack::new(
83 device,
84 embassy_net::Config::Dhcp(Default::default()),
85 singleton!(StackResources::<2>::new()),
86 seed
87 ));
88
89 // Launch network task
90 unwrap!(spawner.spawn(net_task(&stack)));
91
92 info!("Waiting for DHCP...");
93 let cfg = wait_for_config(stack).await;
94 let local_addr = cfg.address.address();
95 info!("IP address: {:?}", local_addr);
96
97 let mut rx_buffer = [0; 4096];
98 let mut tx_buffer = [0; 4096];
99 loop {
100 let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
101 socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
102
103 led.set_low();
104 info!("Connecting...");
105 let host_addr = embassy_net::Ipv4Address::from_str("192.168.1.110").unwrap();
106 if let Err(e) = socket.connect((host_addr, 1234)).await {
107 warn!("connect error: {:?}", e);
108 continue;
109 }
110 info!("Connected to {:?}", socket.remote_endpoint());
111 led.set_high();
112
113 let msg = b"Hello world!\n";
114 loop {
115 if let Err(e) = socket.write_all(msg).await {
116 warn!("write error: {:?}", e);
117 break;
118 }
119 info!("txd: {}", core::str::from_utf8(msg).unwrap());
120 Timer::after(Duration::from_secs(1)).await;
121 }
122 }
123}
124
125async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfig {
126 loop {
127 if let Some(config) = stack.config() {
128 return config.clone();
129 }
130 yield_now().await;
131 }
132}
diff --git a/examples/rp/src/bin/ethernet_w5500_tcp_server.rs b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
new file mode 100644
index 000000000..ffd664d15
--- /dev/null
+++ b/examples/rp/src/bin/ethernet_w5500_tcp_server.rs
@@ -0,0 +1,141 @@
1//! This example implements a TCP echo server on port 1234 and using DHCP.
2//! Send it some data, you should see it echoed back and printed in the console.
3//!
4//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board.
5
6#![no_std]
7#![no_main]
8#![feature(type_alias_impl_trait)]
9
10use defmt::*;
11use embassy_executor::Spawner;
12use embassy_futures::yield_now;
13use embassy_net::{Stack, StackResources};
14use embassy_net_w5500::*;
15use embassy_rp::clocks::RoscRng;
16use embassy_rp::gpio::{Input, Level, Output, Pull};
17use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0};
18use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
19use embedded_hal_async::spi::ExclusiveDevice;
20use embedded_io::asynch::Write;
21use rand::RngCore;
22use static_cell::StaticCell;
23use {defmt_rtt as _, panic_probe as _};
24
25macro_rules! singleton {
26 ($val:expr) => {{
27 type T = impl Sized;
28 static STATIC_CELL: StaticCell<T> = StaticCell::new();
29 let (x,) = STATIC_CELL.init(($val,));
30 x
31 }};
32}
33
34#[embassy_executor::task]
35async fn ethernet_task(
36 runner: Runner<
37 'static,
38 ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>>,
39 Input<'static, PIN_21>,
40 Output<'static, PIN_20>,
41 >,
42) -> ! {
43 runner.run().await
44}
45
46#[embassy_executor::task]
47async fn net_task(stack: &'static Stack<Device<'static>>) -> ! {
48 stack.run().await
49}
50
51#[embassy_executor::main]
52async fn main(spawner: Spawner) {
53 let p = embassy_rp::init(Default::default());
54 let mut rng = RoscRng;
55 let mut led = Output::new(p.PIN_25, Level::Low);
56
57 let mut spi_cfg = SpiConfig::default();
58 spi_cfg.frequency = 50_000_000;
59 let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);
60 let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg);
61 let cs = Output::new(p.PIN_17, Level::High);
62 let w5500_int = Input::new(p.PIN_21, Pull::Up);
63 let w5500_reset = Output::new(p.PIN_20, Level::High);
64
65 let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
66 let state = singleton!(State::<8, 8>::new());
67 let (device, runner) = embassy_net_w5500::new(
68 mac_addr,
69 state,
70 ExclusiveDevice::new(spi, cs),
71 w5500_int,
72 w5500_reset,
73 )
74 .await;
75 unwrap!(spawner.spawn(ethernet_task(runner)));
76
77 // Generate random seed
78 let seed = rng.next_u64();
79
80 // Init network stack
81 let stack = &*singleton!(Stack::new(
82 device,
83 embassy_net::Config::Dhcp(Default::default()),
84 singleton!(StackResources::<2>::new()),
85 seed
86 ));
87
88 // Launch network task
89 unwrap!(spawner.spawn(net_task(&stack)));
90
91 info!("Waiting for DHCP...");
92 let cfg = wait_for_config(stack).await;
93 let local_addr = cfg.address.address();
94 info!("IP address: {:?}", local_addr);
95
96 let mut rx_buffer = [0; 4096];
97 let mut tx_buffer = [0; 4096];
98 let mut buf = [0; 4096];
99 loop {
100 let mut socket = embassy_net::tcp::TcpSocket::new(stack, &mut rx_buffer, &mut tx_buffer);
101 socket.set_timeout(Some(embassy_net::SmolDuration::from_secs(10)));
102
103 led.set_low();
104 info!("Listening on TCP:1234...");
105 if let Err(e) = socket.accept(1234).await {
106 warn!("accept error: {:?}", e);
107 continue;
108 }
109 info!("Received connection from {:?}", socket.remote_endpoint());
110 led.set_high();
111
112 loop {
113 let n = match socket.read(&mut buf).await {
114 Ok(0) => {
115 warn!("read EOF");
116 break;
117 }
118 Ok(n) => n,
119 Err(e) => {
120 warn!("{:?}", e);
121 break;
122 }
123 };
124 info!("rxd {}", core::str::from_utf8(&buf[..n]).unwrap());
125
126 if let Err(e) = socket.write_all(&buf[..n]).await {
127 warn!("write error: {:?}", e);
128 break;
129 }
130 }
131 }
132}
133
134async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfig {
135 loop {
136 if let Some(config) = stack.config() {
137 return config.clone();
138 }
139 yield_now().await;
140 }
141}
diff --git a/examples/rp/src/bin/ethernet_w5500_udp.rs b/examples/rp/src/bin/ethernet_w5500_udp.rs
new file mode 100644
index 000000000..08ffeb244
--- /dev/null
+++ b/examples/rp/src/bin/ethernet_w5500_udp.rs
@@ -0,0 +1,127 @@
1//! This example implements a UDP server listening on port 1234 and echoing back the data.
2//!
3//! Example written for the [`WIZnet W5500-EVB-Pico`](https://www.wiznet.io/product-item/w5500-evb-pico/) board.
4
5#![no_std]
6#![no_main]
7#![feature(type_alias_impl_trait)]
8
9use defmt::*;
10use embassy_executor::Spawner;
11use embassy_futures::yield_now;
12use embassy_net::udp::UdpSocket;
13use embassy_net::{PacketMetadata, Stack, StackResources};
14use embassy_net_w5500::*;
15use embassy_rp::clocks::RoscRng;
16use embassy_rp::gpio::{Input, Level, Output, Pull};
17use embassy_rp::peripherals::{PIN_17, PIN_20, PIN_21, SPI0};
18use embassy_rp::spi::{Async, Config as SpiConfig, Spi};
19use embedded_hal_async::spi::ExclusiveDevice;
20use rand::RngCore;
21use static_cell::StaticCell;
22use {defmt_rtt as _, panic_probe as _};
23
24macro_rules! singleton {
25 ($val:expr) => {{
26 type T = impl Sized;
27 static STATIC_CELL: StaticCell<T> = StaticCell::new();
28 let (x,) = STATIC_CELL.init(($val,));
29 x
30 }};
31}
32
33#[embassy_executor::task]
34async fn ethernet_task(
35 runner: Runner<
36 'static,
37 ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static, PIN_17>>,
38 Input<'static, PIN_21>,
39 Output<'static, PIN_20>,
40 >,
41) -> ! {
42 runner.run().await
43}
44
45#[embassy_executor::task]
46async fn net_task(stack: &'static Stack<Device<'static>>) -> ! {
47 stack.run().await
48}
49
50#[embassy_executor::main]
51async fn main(spawner: Spawner) {
52 let p = embassy_rp::init(Default::default());
53 let mut rng = RoscRng;
54
55 let mut spi_cfg = SpiConfig::default();
56 spi_cfg.frequency = 50_000_000;
57 let (miso, mosi, clk) = (p.PIN_16, p.PIN_19, p.PIN_18);
58 let spi = Spi::new(p.SPI0, clk, mosi, miso, p.DMA_CH0, p.DMA_CH1, spi_cfg);
59 let cs = Output::new(p.PIN_17, Level::High);
60 let w5500_int = Input::new(p.PIN_21, Pull::Up);
61 let w5500_reset = Output::new(p.PIN_20, Level::High);
62
63 let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x00];
64 let state = singleton!(State::<8, 8>::new());
65 let (device, runner) = embassy_net_w5500::new(
66 mac_addr,
67 state,
68 ExclusiveDevice::new(spi, cs),
69 w5500_int,
70 w5500_reset,
71 )
72 .await;
73 unwrap!(spawner.spawn(ethernet_task(runner)));
74
75 // Generate random seed
76 let seed = rng.next_u64();
77
78 // Init network stack
79 let stack = &*singleton!(Stack::new(
80 device,
81 embassy_net::Config::Dhcp(Default::default()),
82 singleton!(StackResources::<2>::new()),
83 seed
84 ));
85
86 // Launch network task
87 unwrap!(spawner.spawn(net_task(&stack)));
88
89 info!("Waiting for DHCP...");
90 let cfg = wait_for_config(stack).await;
91 let local_addr = cfg.address.address();
92 info!("IP address: {:?}", local_addr);
93
94 // Then we can use it!
95 let mut rx_buffer = [0; 4096];
96 let mut tx_buffer = [0; 4096];
97 let mut rx_meta = [PacketMetadata::EMPTY; 16];
98 let mut tx_meta = [PacketMetadata::EMPTY; 16];
99 let mut buf = [0; 4096];
100 loop {
101 let mut socket = UdpSocket::new(
102 stack,
103 &mut rx_meta,
104 &mut rx_buffer,
105 &mut tx_meta,
106 &mut tx_buffer,
107 );
108 socket.bind(1234).unwrap();
109
110 loop {
111 let (n, ep) = socket.recv_from(&mut buf).await.unwrap();
112 if let Ok(s) = core::str::from_utf8(&buf[..n]) {
113 info!("rxd from {}: {}", ep, s);
114 }
115 socket.send_to(&buf[..n], ep).await.unwrap();
116 }
117 }
118}
119
120async fn wait_for_config(stack: &'static Stack<Device<'static>>) -> embassy_net::StaticConfig {
121 loop {
122 if let Some(config) = stack.config() {
123 return config.clone();
124 }
125 yield_now().await;
126 }
127}