Skip to content

Commit 4a93adc

Browse files
committed
feat(w6100): Add support for the W6100 ethernet driver
1 parent 7855c3d commit 4a93adc

File tree

5 files changed

+74
-30
lines changed

5 files changed

+74
-30
lines changed

CLAUDE.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -20,14 +20,18 @@ The bootloader enables safe over-the-air firmware updates by maintaining separat
2020
cargo xtask build
2121

2222
# Build individual components
23-
cargo xtask build --bootloader # Build bootloader only
24-
cargo xtask build --application # Build main PDU controller application
23+
cargo xtask build --bootloader # Build bootloader only
24+
cargo xtask build --application # Build main PDU controller application
25+
cargo xtask build --application --w6100 # Build main PDU controller application for WIZnet W6100
2526

2627
# Produce combined UF2 (build + combine)
2728
cargo xtask dist
29+
# Produce combined UF2 (build + combine) for WIZnet W6100 variant
30+
cargo xtask dist --w6100
2831

2932
# Flash to device
3033
cargo xtask flash # Flash combined UF2 via BOOTSEL drag-and-drop
34+
cargo xtask flash --w6100 # Flash combined UF2 via BOOTSEL drag-and-drop (for WIZnet W6100)
3135
cargo xtask flash --bootloader # Flash bootloader only
3236
cargo xtask flash --application # Flash application only
3337
cargo xtask flash --probe # Flash via probe-rs with RTT logging (recommended for dev)
@@ -67,7 +71,7 @@ All build outputs are placed in the `build/` directory:
6771

6872
## Hardware Configuration
6973

70-
### W5500 Ethernet Module (SPI)
74+
### W5500/W6100 Ethernet Module (SPI)
7175
- MISO: GPIO 16
7276
- MOSI: GPIO 19
7377
- CLK: GPIO 18
@@ -165,7 +169,7 @@ Hard faults trigger system reset to retry boot.
165169
### Application Structure
166170

167171
The application uses Embassy async runtime with multiple concurrent tasks:
168-
- **ethernet_task**: Manages W5500 hardware and link layer
172+
- **ethernet_task**: Manages W5500/W6100 hardware and link layer
169173
- **net_task**: Runs the embassy-net network stack (TCP/IP, DHCP)
170174
- **gpio_task**: Handles GPIO control commands via Signal primitive (8 pins)
171175
- **sensor_task**: Reads RP2040 internal ADC temperature every 5s
@@ -206,7 +210,7 @@ The `archive/` directory contains the original PIC18-based firmware tooling (Mak
206210
Application dependencies:
207211
- `embassy-executor`: Async task executor
208212
- `embassy-net`: Network stack with DHCP support
209-
- `embassy-net-wiznet`: Driver for W5500 Ethernet chip
213+
- `embassy-net-wiznet`: Driver for W5500/W6100 Ethernet chip
210214
- `embassy-rp`: RP2040 HAL with flash support
211215
- `embassy-boot-rp`: Bootloader and firmware updater
212216
- `embassy-embedded-hal`: Async adapters for blocking peripherals

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ Raspberry Pi Pico (RP2040), replacing the original PIC18-based firmware.
66
## Features
77

88
- 8 relay-controlled power outlets (GPIO 0-7)
9-
- W5500 Ethernet with DHCP
9+
- W5500 or W6100 Ethernet with DHCP
1010
- HTTP server with single-page web UI
1111
- REST API for GPIO control, sensors, user management
1212
- HTTP Basic Auth with multi-user support and per-port ACL

application/Cargo.toml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,8 @@ heapless = "0.8"
4545
default = ["panic-reset"]
4646
debug = ["panic-probe"]
4747
skip-include = []
48+
# Select the WIZnet W6100, defaults to W5500 if not enabled
49+
w6100 = []
4850

4951
# 'panic-reset' and 'panic-probe' are mutually exclusive.
5052
# Build with --no-default-features --features debug to use panic-probe.

application/src/main.rs

Lines changed: 21 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,10 @@ use defmt::*;
2020
use defmt_rtt as _;
2121
use embassy_executor::Spawner;
2222
use embassy_net::StackResources;
23+
#[cfg(not(feature = "w6100"))]
2324
use embassy_net_wiznet::chip::W5500;
25+
#[cfg(feature = "w6100")]
26+
use embassy_net_wiznet::chip::W6100;
2427
use embassy_net_wiznet::*;
2528
use embassy_rp::adc::{Adc, Channel, Config as AdcConfig};
2629
use embassy_rp::bind_interrupts;
@@ -67,6 +70,7 @@ bind_interrupts!(struct Irqs {
6770
// ── Ethernet task ──────────────────────────────────────────────────────────────
6871

6972
/// Type alias for the W5500 runner to avoid repeating the full generic signature.
73+
#[cfg(not(feature = "w6100"))]
7074
type EthernetRunner = Runner<
7175
'static,
7276
W5500,
@@ -75,6 +79,16 @@ type EthernetRunner = Runner<
7579
Output<'static>,
7680
>;
7781

82+
/// Type alias for the W6100 runner to avoid repeating the full generic signature.
83+
#[cfg(feature = "w6100")]
84+
type EthernetRunner = Runner<
85+
'static,
86+
W6100,
87+
ExclusiveDevice<Spi<'static, SPI0, Async>, Output<'static>, embassy_time::Delay>,
88+
Input<'static>,
89+
Output<'static>,
90+
>;
91+
7892
#[embassy_executor::task]
7993
async fn ethernet_task(runner: EthernetRunner) -> ! {
8094
runner.run().await
@@ -135,7 +149,7 @@ async fn main(spawner: Spawner) {
135149
// 6. Spawn GPIO task
136150
spawner.spawn(gpio_task(relay_pins).unwrap());
137151

138-
// 7. Init W5500 Ethernet (SPI0, DMA_CH0 TX, DMA_CH1 RX)
152+
// 7. Init Ethernet (SPI0, DMA_CH0 TX, DMA_CH1 RX)
139153
let mut spi_cfg = SpiConfig::default();
140154
spi_cfg.frequency = SPI_FREQ_HZ;
141155
let spi = Spi::new(
@@ -145,18 +159,18 @@ async fn main(spawner: Spawner) {
145159
p.DMA_CH0, p.DMA_CH1, Irqs, spi_cfg,
146160
);
147161
let cs = Output::new(p.PIN_17, Level::High);
148-
let w5500_int = Input::new(p.PIN_21, Pull::Up);
149-
let w5500_reset = Output::new(p.PIN_20, Level::High);
162+
let eth_int = Input::new(p.PIN_21, Pull::Up);
163+
let eth_reset = Output::new(p.PIN_20, Level::High);
150164

151165
let mac_addr = [0x02, 0x00, 0x00, 0x00, 0x00, 0x01];
152-
static W5500_STATE: StaticCell<embassy_net_wiznet::State<8, 8>> = StaticCell::new();
153-
let state = W5500_STATE.init(embassy_net_wiznet::State::<8, 8>::new());
166+
static ETH_STATE: StaticCell<embassy_net_wiznet::State<8, 8>> = StaticCell::new();
167+
let state = ETH_STATE.init(embassy_net_wiznet::State::<8, 8>::new());
154168
let (device, runner) = embassy_net_wiznet::new(
155169
mac_addr,
156170
state,
157171
ExclusiveDevice::new(spi, cs, embassy_time::Delay),
158-
w5500_int,
159-
w5500_reset,
172+
eth_int,
173+
eth_reset,
160174
)
161175
.await
162176
.unwrap();

xtask/src/main.rs

Lines changed: 41 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,18 @@ enum Command {
2323
/// Build only the application
2424
#[arg(long)]
2525
application: bool,
26+
/// Build the application for the WIZnet W6100 instead of W5500
27+
#[arg(long)]
28+
w6100: bool,
2629
},
2730
/// Combine bootloader + application ELFs into build/combined.uf2
2831
Combine,
2932
/// Build everything and produce the combined UF2 (build + combine)
30-
Dist,
33+
Dist {
34+
/// Build the application for the WIZnet W6100 instead of W5500
35+
#[arg(long)]
36+
w6100: bool,
37+
},
3138
/// Flash firmware to the device
3239
Flash {
3340
/// Flash only the bootloader
@@ -36,6 +43,9 @@ enum Command {
3643
/// Flash only the application
3744
#[arg(long)]
3845
application: bool,
46+
/// Build the application for the WIZnet W6100 instead of W5500
47+
#[arg(long)]
48+
w6100: bool,
3949
/// Use probe-rs instead of UF2 drag-and-drop (attaches RTT for live logging)
4050
#[arg(long)]
4151
probe: bool,
@@ -112,13 +122,17 @@ fn build_bootloader(sh: &Shell, root: &Path) -> Result<()> {
112122
Ok(())
113123
}
114124

115-
fn build_application(sh: &Shell, root: &Path) -> Result<()> {
125+
fn build_application(sh: &Shell, root: &Path, w6100: bool) -> Result<()> {
116126
eprintln!("→ Building application…");
117127
let build_dir = build_dir(root);
118128
std::fs::create_dir_all(&build_dir)?;
119129

120130
let _dir = sh.push_dir(root.join("application"));
121-
cmd!(sh, "cargo build --release").run()?;
131+
if w6100 {
132+
cmd!(sh, "cargo build --release --features w6100").run()?;
133+
} else {
134+
cmd!(sh, "cargo build --release").run()?;
135+
}
122136

123137
let src = root.join("target/thumbv6m-none-eabi/release/pdu-rp-application");
124138
let dst = application_elf(root);
@@ -135,16 +149,24 @@ fn build_application(sh: &Shell, root: &Path) -> Result<()> {
135149
/// Build the application with the `debug` feature (panic-probe + full defmt RTT logging).
136150
/// The resulting ELF is stored separately as `build/application-debug.elf` so it
137151
/// doesn't overwrite the production UF2 artefacts.
138-
fn build_application_debug(sh: &Shell, root: &Path) -> Result<()> {
152+
fn build_application_debug(sh: &Shell, root: &Path, w6100: bool) -> Result<()> {
139153
eprintln!("→ Building application (debug / probe-rs)…");
140154
std::fs::create_dir_all(build_dir(root))?;
141155

142156
let _dir = sh.push_dir(root.join("application"));
143-
cmd!(
144-
sh,
145-
"cargo build --release --no-default-features --features debug"
146-
)
147-
.run()?;
157+
if w6100 {
158+
cmd!(
159+
sh,
160+
"cargo build --release --no-default-features --features debug,w6100"
161+
)
162+
.run()?;
163+
} else {
164+
cmd!(
165+
sh,
166+
"cargo build --release --no-default-features --features debug"
167+
)
168+
.run()?;
169+
}
148170

149171
let src = root.join("target/thumbv6m-none-eabi/release/pdu-rp-application");
150172
let dst = application_debug_elf(root);
@@ -559,29 +581,31 @@ fn main() -> Result<()> {
559581
Command::Build {
560582
bootloader,
561583
application,
584+
w6100,
562585
} => {
563586
let both = !bootloader && !application;
564587
if both || bootloader {
565588
build_bootloader(&sh, &root)?;
566589
}
567590
if both || application {
568-
build_application(&sh, &root)?;
591+
build_application(&sh, &root, w6100)?;
569592
}
570593
}
571594

572595
Command::Combine => {
573596
combine(&sh, &root)?;
574597
}
575598

576-
Command::Dist => {
599+
Command::Dist { w6100 } => {
577600
build_bootloader(&sh, &root)?;
578-
build_application(&sh, &root)?;
601+
build_application(&sh, &root, w6100)?;
579602
combine(&sh, &root)?;
580603
}
581604

582605
Command::Flash {
583606
bootloader,
584607
application,
608+
w6100,
585609
probe,
586610
debug,
587611
ota,
@@ -595,7 +619,7 @@ fn main() -> Result<()> {
595619
let uf2 = application_uf2(&root);
596620
if !uf2.exists() {
597621
eprintln!("Application UF2 not found — building first…");
598-
build_application(&sh, &root)?;
622+
build_application(&sh, &root, w6100)?;
599623
}
600624
flash_ota(&uf2, ip)?;
601625
} else if probe {
@@ -610,9 +634,9 @@ fn main() -> Result<()> {
610634
};
611635
let build_app = |sh: &Shell, root: &Path| {
612636
if use_debug_build {
613-
build_application_debug(sh, root)
637+
build_application_debug(sh, root, w6100)
614638
} else {
615-
build_application(sh, root)
639+
build_application(sh, root, w6100)
616640
}
617641
};
618642

@@ -677,7 +701,7 @@ fn main() -> Result<()> {
677701
let uf2 = combined_uf2(&root);
678702
if !uf2.exists() {
679703
build_bootloader(&sh, &root)?;
680-
build_application(&sh, &root)?;
704+
build_application(&sh, &root, w6100)?;
681705
combine(&sh, &root)?;
682706
}
683707
flash_uf2(&uf2)?;
@@ -692,7 +716,7 @@ fn main() -> Result<()> {
692716
if application {
693717
let uf2 = application_uf2(&root);
694718
if !uf2.exists() {
695-
build_application(&sh, &root)?;
719+
build_application(&sh, &root, w6100)?;
696720
}
697721
flash_uf2(&uf2)?;
698722
}

0 commit comments

Comments
 (0)