diff --git a/embassy/Cargo.toml b/embassy/Cargo.toml
index d90a643de22e8dcdc8e3c9f59a3f63ea62cf450d..296a6f10907deed6b19d4f6de2e2737833aa957b 100644
--- a/embassy/Cargo.toml
+++ b/embassy/Cargo.toml
@@ -13,3 +13,8 @@ pin-utils = { version = "0.1" }
 sunset = { path = "../", features = ["embedded-io"] }
 
 log = { version = "0.4" }
+
+defmt = { version = "0.3", optional = true }
+
+[features]
+defmt = ["dep:defmt"]
diff --git a/embassy/demos/common/Cargo.toml b/embassy/demos/common/Cargo.toml
index f4b791f577c72166fffd514559dd35e1f863a87a..01e3aa5a8825231799ad53c32998b80b1ec8d2bf 100644
--- a/embassy/demos/common/Cargo.toml
+++ b/embassy/demos/common/Cargo.toml
@@ -36,6 +36,6 @@ defmt = ["dep:defmt", "embedded-io/defmt"]
 log = ["embassy-net/log"]
 
 [patch.crates-io]
-embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
diff --git a/embassy/demos/picow/Cargo.lock b/embassy/demos/picow/Cargo.lock
index ba87631decbc360d2524218b32a586dd55f1d644..afd580401bba3ac0a326e40eeaa240e3bc40d3db 100644
--- a/embassy/demos/picow/Cargo.lock
+++ b/embassy/demos/picow/Cargo.lock
@@ -298,6 +298,15 @@ dependencies = [
  "libc",
 ]
 
+[[package]]
+name = "crc-any"
+version = "2.4.3"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "774646b687f63643eb0f4bf13dc263cb581c8c9e57973b6ddf78bda3994d88df"
+dependencies = [
+ "debug-helper",
+]
+
 [[package]]
 name = "critical-section"
 version = "0.2.8"
@@ -342,7 +351,7 @@ dependencies = [
 [[package]]
 name = "cyw43"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/cyw43/?rev=6b5d9642d583bc034ee35b88c903545e7f423b4e#6b5d9642d583bc034ee35b88c903545e7f423b4e"
+source = "git+https://github.com/embassy-rs/cyw43/?rev=3cc0ec654a1e46fe7b8fa168942452303343cd84#3cc0ec654a1e46fe7b8fa168942452303343cd84"
 dependencies = [
  "atomic-polyfill 0.1.11",
  "cortex-m",
@@ -350,7 +359,7 @@ dependencies = [
  "defmt",
  "embassy-futures",
  "embassy-net-driver-channel",
- "embassy-sync 0.1.0",
+ "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "embassy-time",
  "embedded-hal 1.0.0-alpha.10",
  "futures",
@@ -360,10 +369,11 @@ dependencies = [
 [[package]]
 name = "cyw43-pio"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/cyw43/?rev=6b5d9642d583bc034ee35b88c903545e7f423b4e#6b5d9642d583bc034ee35b88c903545e7f423b4e"
+source = "git+https://github.com/embassy-rs/cyw43/?rev=3cc0ec654a1e46fe7b8fa168942452303343cd84#3cc0ec654a1e46fe7b8fa168942452303343cd84"
 dependencies = [
  "cyw43",
  "embassy-rp",
+ "fixed",
  "pio",
  "pio-proc",
 ]
@@ -403,6 +413,12 @@ dependencies = [
  "syn",
 ]
 
+[[package]]
+name = "debug-helper"
+version = "0.3.13"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "f578e8e2c440e7297e008bb5486a3a8a194775224bbc23729b0dbdfaeebf162e"
+
 [[package]]
 name = "defmt"
 version = "0.3.2"
@@ -504,7 +520,7 @@ checksum = "7fcaabb2fef8c910e7f4c7ce9f67a1283a1715879a7c230ca9d6d1ae31f16d91"
 [[package]]
 name = "embassy-cortex-m"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "cfg-if",
@@ -513,15 +529,16 @@ dependencies = [
  "embassy-executor",
  "embassy-hal-common",
  "embassy-macros",
- "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f)",
 ]
 
 [[package]]
 name = "embassy-embedded-hal"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
- "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embassy-futures",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f)",
  "embedded-hal 0.2.7",
  "embedded-hal 1.0.0-alpha.10",
  "embedded-hal-async",
@@ -533,7 +550,7 @@ dependencies = [
 [[package]]
 name = "embassy-executor"
 version = "0.2.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "cortex-m",
@@ -548,12 +565,12 @@ dependencies = [
 [[package]]
 name = "embassy-futures"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 
 [[package]]
 name = "embassy-hal-common"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "defmt",
  "num-traits",
@@ -562,7 +579,7 @@ dependencies = [
 [[package]]
 name = "embassy-macros"
 version = "0.2.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "darling",
  "proc-macro2",
@@ -573,16 +590,16 @@ dependencies = [
 [[package]]
 name = "embassy-net"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "as-slice 0.2.1",
  "atomic-polyfill 1.0.1",
  "atomic-pool",
  "embassy-hal-common",
  "embassy-net-driver",
- "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f)",
  "embassy-time",
- "embedded-io 0.4.0",
+ "embedded-io",
  "embedded-nal-async",
  "futures",
  "generic-array 0.14.6",
@@ -595,22 +612,22 @@ dependencies = [
 [[package]]
 name = "embassy-net-driver"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 
 [[package]]
 name = "embassy-net-driver-channel"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "embassy-futures",
  "embassy-net-driver",
- "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f)",
 ]
 
 [[package]]
 name = "embassy-rp"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "cfg-if",
@@ -623,14 +640,14 @@ dependencies = [
  "embassy-executor",
  "embassy-futures",
  "embassy-hal-common",
- "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f)",
  "embassy-time",
  "embassy-usb-driver",
  "embedded-hal 0.2.7",
  "embedded-hal 1.0.0-alpha.10",
  "embedded-hal-async",
  "embedded-hal-nb",
- "embedded-io 0.4.0",
+ "embedded-io",
  "embedded-storage",
  "fixed",
  "futures",
@@ -640,20 +657,7 @@ dependencies = [
  "pio-proc",
  "rand_core",
  "rp-pac",
-]
-
-[[package]]
-name = "embassy-sync"
-version = "0.1.0"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "b6ea38e6ea5d0361d087680f786c19a1454becb06174790280534a3be05ed839"
-dependencies = [
- "atomic-polyfill 1.0.1",
- "cfg-if",
- "critical-section 1.1.1",
- "embedded-io 0.3.1",
- "futures-util",
- "heapless",
+ "rp2040-boot2",
 ]
 
 [[package]]
@@ -664,7 +668,7 @@ checksum = "a0dad296a6f70bfdc32ef52442a31f98c28e1608893c1cecc9b6f419bab005a0"
 dependencies = [
  "cfg-if",
  "critical-section 1.1.1",
- "embedded-io 0.4.0",
+ "embedded-io",
  "futures-util",
  "heapless",
 ]
@@ -672,11 +676,11 @@ dependencies = [
 [[package]]
 name = "embassy-sync"
 version = "0.2.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "cfg-if",
  "critical-section 1.1.1",
- "embedded-io 0.4.0",
+ "embedded-io",
  "futures-util",
  "heapless",
 ]
@@ -684,7 +688,7 @@ dependencies = [
 [[package]]
 name = "embassy-time"
 version = "0.1.1"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "atomic-polyfill 1.0.1",
  "cfg-if",
@@ -698,12 +702,12 @@ dependencies = [
 [[package]]
 name = "embassy-usb"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "defmt",
  "embassy-futures",
  "embassy-net-driver-channel",
- "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e)",
+ "embassy-sync 0.2.0 (git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f)",
  "embassy-usb-driver",
  "heapless",
  "ssmarshal",
@@ -713,7 +717,7 @@ dependencies = [
 [[package]]
 name = "embassy-usb-driver"
 version = "0.1.0"
-source = "git+https://github.com/embassy-rs/embassy?rev=3e730aa8b06401003202bf9e21a9c83ec6b21b0e#3e730aa8b06401003202bf9e21a9c83ec6b21b0e"
+source = "git+https://github.com/embassy-rs/embassy?rev=1d34078fa11839f88dd2e47a9355c6b35755128f#1d34078fa11839f88dd2e47a9355c6b35755128f"
 dependencies = [
  "defmt",
 ]
@@ -753,12 +757,6 @@ dependencies = [
  "nb 1.0.0",
 ]
 
-[[package]]
-name = "embedded-io"
-version = "0.3.1"
-source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "33dfba9e6c113f2fd8537c943780a7345945e66c86972e356b1152e19481bcf5"
-
 [[package]]
 name = "embedded-io"
 version = "0.4.0"
@@ -785,7 +783,7 @@ version = "0.4.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
 checksum = "27ce84f518ca912777ec143db235f4d615e3bf8d4e46d507d6ef12daf5b1df98"
 dependencies = [
- "embedded-io 0.4.0",
+ "embedded-io",
  "embedded-nal",
  "heapless",
  "no-std-net 0.6.0",
@@ -1495,14 +1493,23 @@ checksum = "f162c6dd7b008981e4d40210aca20b4bd0f9b60ca9271061b07f78537722f2e1"
 
 [[package]]
 name = "rp-pac"
-version = "2.0.0"
+version = "4.0.0"
 source = "registry+https://github.com/rust-lang/crates.io-index"
-checksum = "75de4bf19a348bd73e49e0476b74a1a19c305de1289c87a7f5049f939246c927"
+checksum = "a76e426cd8377db668fba1fe885028788b126b7cef91059cd478de8b076c2915"
 dependencies = [
  "cortex-m",
  "cortex-m-rt",
 ]
 
+[[package]]
+name = "rp2040-boot2"
+version = "0.3.0"
+source = "registry+https://github.com/rust-lang/crates.io-index"
+checksum = "7c92f344f63f950ee36cf4080050e4dce850839b9175da38f9d2ffb69b4dbb21"
+dependencies = [
+ "crc-any",
+]
+
 [[package]]
 name = "rustc_version"
 version = "0.2.3"
@@ -1725,7 +1732,7 @@ dependencies = [
  "ctr",
  "defmt",
  "digest",
- "embedded-io 0.4.0",
+ "embedded-io",
  "futures",
  "getrandom",
  "heapless",
@@ -1754,7 +1761,7 @@ dependencies = [
  "embassy-net-driver",
  "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
  "embassy-time",
- "embedded-io 0.4.0",
+ "embedded-io",
  "heapless",
  "hmac",
  "log",
@@ -1789,7 +1796,7 @@ dependencies = [
  "embassy-usb-driver",
  "embedded-hal 1.0.0-alpha.10",
  "embedded-hal-async",
- "embedded-io 0.4.0",
+ "embedded-io",
  "futures",
  "getrandom",
  "heapless",
@@ -1813,9 +1820,10 @@ name = "sunset-embassy"
 version = "0.2.0-alpha"
 dependencies = [
  "atomic-polyfill 1.0.1",
+ "defmt",
  "embassy-futures",
  "embassy-sync 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)",
- "embedded-io 0.4.0",
+ "embedded-io",
  "log",
  "pin-utils",
  "sunset",
diff --git a/embassy/demos/picow/Cargo.toml b/embassy/demos/picow/Cargo.toml
index c8c14f450dded32f99b90d06c3e0572c86572bb3..694ecab24516be4858a69083f5ffadc02926e7b9 100644
--- a/embassy/demos/picow/Cargo.toml
+++ b/embassy/demos/picow/Cargo.toml
@@ -12,8 +12,8 @@ sunset = { path = "../../.." }
 sunset-sshwire-derive = { version = "0.1", path = "../../../sshwire-derive" }
 sunset-demo-embassy-common= { path = "../common" }
 
-cyw43 = { git = "https://github.com/embassy-rs/cyw43/", rev = "6b5d9642d583bc034ee35b88c903545e7f423b4e", features = ["defmt"]}
-cyw43-pio = { git = "https://github.com/embassy-rs/cyw43/", rev = "6b5d9642d583bc034ee35b88c903545e7f423b4e" }
+cyw43 = { git = "https://github.com/embassy-rs/cyw43/", rev = "3cc0ec654a1e46fe7b8fa168942452303343cd84", features = ["defmt"]}
+cyw43-pio = { git = "https://github.com/embassy-rs/cyw43/", rev = "3cc0ec654a1e46fe7b8fa168942452303343cd84" }
 # cyw43 = { path = "/home/matt/3rd/rs/cyw43", features = ["defmt"] }
 # cyw43-pio = { path = "/home/matt/3rd/rs/cyw43/cyw43-pio" }
 
@@ -64,24 +64,27 @@ smoltcp = { default-features = false }
 
 [features]
 default = ["defmt", "sunset-demo-embassy-common/defmt", "embassy-usb/defmt"]
-defmt = ["dep:defmt", "sunset/defmt", "smoltcp/defmt"]
+defmt = ["dep:defmt", "sunset/defmt", "sunset-embassy/defmt", "smoltcp/defmt"]
 
 # Use cyw43 firmware already on flash. This saves time when developing.
 # probe-rs-cli download firmware/43439A0.bin --format bin --chip RP2040 --base-address 0x10100000
 # probe-rs-cli download firmware/43439A0_clm.bin --format bin --chip RP2040 --base-address 0x10140000
 romfw = []
 
+# Add a UART on tx pin1, rx pin2
+serial1 = []
+
 [patch.crates-io]
-embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-usb = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-usb-driver = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-executor = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-rp = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-usb = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-time = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-usb-driver = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
 # for cyw43
-embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-net-driver-channel = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
 
 # embassy-net = { path = "/home/matt/3rd/rs/embassy/embassy-net" }
 # embassy-net-driver = { path = "/home/matt/3rd/rs/embassy/embassy-net-driver" }
diff --git a/embassy/demos/picow/src/flashconfig.rs b/embassy/demos/picow/src/flashconfig.rs
index afd59894c476c4b39a8284ae527f795115b3cb9f..7a82ef9a85d1241394c55d9f24e7e72775ba358c 100644
--- a/embassy/demos/picow/src/flashconfig.rs
+++ b/embassy/demos/picow/src/flashconfig.rs
@@ -79,11 +79,11 @@ pub fn load(flash: &mut Flash<'_, FLASH, FLASH_SIZE>) -> Result<SSHConfig> {
     let mut buf = [0u8; FlashConfig::BUF_SIZE];
     flash.read(CONFIG_OFFSET, &mut buf).map_err(|_| Error::msg("flash error"))?;
 
-    use pretty_hex::PrettyHex;
-    use core::fmt::Write;
-    let mut b = demo_common::BufOutput::default();
-    writeln!(b, "load {:?}", buf.hex_dump());
-    info!("{}", &b.s);
+    // use pretty_hex::PrettyHex;
+    // use core::fmt::Write;
+    // let mut b = demo_common::BufOutput::default();
+    // writeln!(b, "load {:?}", buf.hex_dump());
+    // info!("{}", &b.s);
 
     let s: FlashConfig = sshwire::read_ssh(&buf, None)?;
 
@@ -114,11 +114,11 @@ pub fn save(flash: &mut Flash<'_, FLASH, FLASH_SIZE>, config: &SSHConfig) -> Res
     let l = sshwire::write_ssh(&mut buf, &sc)?;
     let buf = &buf[..l];
 
-    use pretty_hex::PrettyHex;
-    use core::fmt::Write;
-    let mut b = demo_common::BufOutput::default();
-    writeln!(b, "save {:?}", buf.hex_dump());
-    info!("{}", &b.s);
+    // use pretty_hex::PrettyHex;
+    // use core::fmt::Write;
+    // let mut b = demo_common::BufOutput::default();
+    // writeln!(b, "save {:?}", buf.hex_dump());
+    // info!("{}", &b.s);
 
     trace!("flash erase");
     flash.erase(CONFIG_OFFSET, CONFIG_OFFSET + ERASE_SIZE as u32)
diff --git a/embassy/demos/picow/src/main.rs b/embassy/demos/picow/src/main.rs
index a294d07fcd07f6f54c597d2ae1e0f44fd280b900..aa5bb9463a7688938829d3beb2ab8a091bc3234b 100644
--- a/embassy/demos/picow/src/main.rs
+++ b/embassy/demos/picow/src/main.rs
@@ -22,7 +22,6 @@ use embassy_futures::join::join;
 use embassy_futures::select::select;
 use embassy_net::Stack;
 use embassy_rp::peripherals::FLASH;
-use embassy_rp::{interrupt, pio::PioPeripheral};
 use embassy_time::Duration;
 use embedded_io::asynch::Write as _;
 use embedded_io::{asynch, Io};
@@ -44,13 +43,15 @@ pub(crate) use sunset_demo_embassy_common as demo_common;
 
 mod flashconfig;
 mod picowmenu;
+#[cfg(feature = "serial1")]
+mod serial;
 mod takepipe;
 mod usbserial;
 mod wifi;
 
 use demo_common::{demo_menu, SSHConfig, Shell};
 
-use takepipe::TakeBase;
+use takepipe::TakePipe;
 
 const NUM_LISTENERS: usize = 4;
 // +1 for dhcp. referenced directly by wifi_stack() function
@@ -82,28 +83,54 @@ async fn main(spawner: Spawner) {
         (c.wifi_net.clone(), c.wifi_pw.clone())
     };
     // spawn the wifi stack
-    let (_, sm, _, _, _) = p.PIO0.split();
     let (stack, wifi_control) = wifi::wifi_stack(
-        &spawner, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, sm, wifi_net,
-        wifi_pw,
+        &spawner, p.PIN_23, p.PIN_24, p.PIN_25, p.PIN_29, p.DMA_CH0, p.PIO0,
+        wifi_net, wifi_pw,
     )
     .await;
     let stack = &*singleton!(stack);
     let wifi_control = singleton!(SunsetMutex::new(wifi_control));
     spawner.spawn(net_task(&stack)).unwrap();
 
-    let usb_pipe = singleton!(takepipe::TakePipe::new());
-    let usb_pipe = singleton!(usb_pipe.base());
+    let usb_pipe = {
+        let p = singleton!(takepipe::TakePipeStorage::new());
+        singleton!(p.pipe())
+    };
+
+    #[cfg(feature = "serial1")]
+    let serial1_pipe = {
+        let s = singleton!(takepipe::TakePipeStorage::new());
+        singleton!(s.pipe())
+    };
+    #[cfg(feature = "serial1")]
+    spawner
+        .spawn(serial::task(
+            p.UART0,
+            p.PIN_0,
+            p.PIN_1,
+            p.PIN_2,
+            p.PIN_3,
+            serial1_pipe,
+        ))
+        .unwrap();
 
     let watchdog = singleton!(SunsetMutex::new(
         embassy_rp::watchdog::Watchdog::new(p.WATCHDOG)
     ));
 
-    let state = GlobalState { usb_pipe, wifi_control, config, flash, watchdog };
+    let state = GlobalState {
+        usb_pipe,
+        #[cfg(feature = "serial1")]
+        serial1_pipe,
+
+        wifi_control,
+        config,
+        flash,
+        watchdog,
+    };
     let state = singleton!(state);
 
-    let usb_irq = interrupt::take!(USBCTRL_IRQ);
-    spawner.spawn(usb_serial_task(p.USB, usb_irq, state)).unwrap();
+    spawner.spawn(usbserial::task(p.USB, state)).unwrap();
 
     for _ in 0..NUM_LISTENERS {
         spawner.spawn(listener(&stack, config, state)).unwrap();
@@ -115,14 +142,17 @@ async fn main(spawner: Spawner) {
 async fn listener(
     stack: &'static Stack<cyw43::NetDriver<'static>>,
     config: &'static SunsetMutex<SSHConfig>,
-    ctx: &'static GlobalState,
+    global: &'static GlobalState,
 ) -> ! {
-    demo_common::listener::<_, DemoShell>(stack, config, ctx).await
+    demo_common::listener::<_, DemoShell>(stack, config, global).await
 }
 
 pub(crate) struct GlobalState {
     // If taking multiple mutexes, lock in the order below avoid inversion.
-    pub usb_pipe: &'static TakeBase<'static>,
+    pub usb_pipe: &'static TakePipe<'static>,
+    #[cfg(feature = "serial1")]
+    pub serial1_pipe: &'static TakePipe<'static>,
+
     pub wifi_control: &'static SunsetMutex<cyw43::Control<'static>>,
     pub config: &'static SunsetMutex<SSHConfig>,
     pub flash: &'static SunsetMutex<
@@ -133,7 +163,7 @@ pub(crate) struct GlobalState {
 
 struct DemoShell {
     notify: Signal<NoopRawMutex, ChanHandle>,
-    ctx: &'static GlobalState,
+    global: &'static GlobalState,
 
     // Mutex is a bit of a bodge
     username: SunsetMutex<String<20>>,
@@ -141,8 +171,8 @@ struct DemoShell {
 
 // `local` is set for usb serial menus which require different auth
 async fn menu<R, W>(
-    mut chanr: R,
-    mut chanw: W,
+    chanr: &mut R,
+    chanw: &mut W,
     local: bool,
     state: &'static GlobalState,
 ) -> Result<()>
@@ -151,7 +181,7 @@ where
     W: asynch::Write + Io<Error = sunset::Error>,
 {
     let mut menu_buf = [0u8; 64];
-    let menu_ctx = picowmenu::MenuCtx::new(state);
+    let menu_ctx = picowmenu::MenuCtx::new(state, local);
 
     // let echo = !local;
     let echo = true;
@@ -165,7 +195,7 @@ where
         for c in "help\r\n".bytes() {
             menu.input_byte(c);
         }
-        menu.context.out.flush(&mut chanw).await?;
+        menu.context.out.flush(chanw).await?;
     }
 
     'io: loop {
@@ -178,70 +208,27 @@ where
 
         for c in b.iter() {
             menu.input_byte(*c);
-            menu.context.out.flush(&mut chanw).await?;
-
-            // TODO: move this to a function or something
-            if menu.context.switch_usb1 {
-                menu.context.switch_usb1 = false;
-                if local {
-                    writeln!(menu.context.out, "serial can't loop");
-                } else {
-                    if state.usb_pipe.is_in_use() {
-                        writeln!(
-                            menu.context.out,
-                            "Opening usb1, stealing existing session"
-                        );
-                    } else {
-                        writeln!(menu.context.out, "Opening usb1");
-                    }
-                    serial(chanr, chanw, state).await?;
-                    // TODO we could return to the menu on serial error?
-                    break 'io;
-                }
-            }
+            menu.context.out.flush(chanw).await?;
 
-            if menu.context.need_save {
-                info!("needs save");
-                // clear regardless of success, don't want a tight loop.
-                menu.context.need_save = false;
-
-                let conf = state.config.lock().await;
-                let mut fl = state.flash.lock().await;
-                if let Err(_e) = flashconfig::save(&mut fl, &conf) {
-                    warn!("Error writing flash");
-                }
-            }
-
-            if menu.context.logout {
+            if menu.context.progress(chanr, chanw).await? {
                 break 'io;
             }
-
-            if menu.context.reset {
-                let _ = chanw.write_all(b"Resetting\r\n").await;
-                let mut wd = state.watchdog.lock().await;
-                wd.start(Duration::from_millis(200));
-                loop {
-                    embassy_time::Timer::after(Duration::from_secs(1)).await;
-                }
-            }
-
-            // messages from handling
-            menu.context.out.flush(&mut chanw).await?;
         }
     }
     Ok(())
 }
 
-async fn serial<R, W>(
-    mut chanr: R,
-    mut chanw: W,
-    state: &'static GlobalState,
+pub(crate) async fn serial<R, W>(
+    chanr: &mut R,
+    chanw: &mut W,
+    serial_pipe: &'static TakePipe<'static>,
 ) -> Result<()>
 where
     R: asynch::Read + Io<Error = sunset::Error>,
     W: asynch::Write + Io<Error = sunset::Error>,
 {
-    let (mut rx, mut tx) = state.usb_pipe.take().await;
+    info!("start serial");
+    let (mut rx, mut tx) = serial_pipe.take().await;
     let r = async {
         // TODO: could have a single buffer to translate in-place.
         const DOUBLE: usize = 2 * takepipe::READ_SIZE;
@@ -291,10 +278,10 @@ where
 impl Shell for DemoShell {
     type Init = &'static GlobalState;
 
-    fn new(ctx: &Self::Init) -> Self {
+    fn new(global: &Self::Init) -> Self {
         Self {
             notify: Default::default(),
-            ctx,
+            global,
             username: SunsetMutex::new(String::new()),
         }
     }
@@ -316,12 +303,19 @@ impl Shell for DemoShell {
         let session = async {
             // wait for a shell to start
             let chan_handle = self.notify.wait().await;
-            let stdio = serv.stdio(chan_handle).await?;
-
-            if *self.username.lock().await == "config" {
-                menu(stdio.clone(), stdio, false, self.ctx).await
-            } else {
-                serial(stdio.clone(), stdio, self.ctx).await
+            let mut stdio = serv.stdio(chan_handle).await?;
+
+            #[cfg(feature = "serial1")]
+            let default_pipe = self.global.serial1_pipe;
+            #[cfg(not(feature = "serial1"))]
+            let default_pipe = self.global.usb_pipe;
+
+            let username = self.username.lock().await;
+            let mut stdio2 = stdio.clone();
+            match username.as_str() {
+                "config" => menu(&mut stdio, &mut stdio2, false, self.global).await,
+                "usb" => serial(&mut stdio, &mut stdio2, self.global.usb_pipe).await,
+                _ => serial(&mut stdio, &mut stdio2, default_pipe).await,
             }
         };
 
@@ -333,13 +327,3 @@ impl Shell for DemoShell {
 async fn net_task(stack: &'static Stack<cyw43::NetDriver<'static>>) -> ! {
     stack.run().await
 }
-
-#[embassy_executor::task]
-async fn usb_serial_task(
-    usb: embassy_rp::peripherals::USB,
-    irq: embassy_rp::interrupt::USBCTRL_IRQ,
-    global: &'static GlobalState,
-) -> ! {
-    usbserial::usb_serial(usb, irq, global).await;
-    todo!("shoudln't exit");
-}
diff --git a/embassy/demos/picow/src/picowmenu.rs b/embassy/demos/picow/src/picowmenu.rs
index 304497b865a0747144348d5cef7f97a8b5c8722b..3770c7dc26d271440484b0b88544de8f6fa22b61 100644
--- a/embassy/demos/picow/src/picowmenu.rs
+++ b/embassy/demos/picow/src/picowmenu.rs
@@ -1,21 +1,32 @@
+#[allow(unused_imports)]
+#[cfg(not(feature = "defmt"))]
+pub use log::{debug, error, info, log, trace, warn};
+
+#[allow(unused_imports)]
+#[cfg(feature = "defmt")]
+pub use defmt::{debug, error, info, panic, trace, warn};
+
 use core::fmt::Write;
 use core::future::{poll_fn, Future};
 use core::ops::DerefMut;
 use core::sync::atomic::Ordering::{Relaxed, SeqCst};
 
-use embedded_io::asynch;
+use embedded_io::{asynch, Io};
 use embedded_io::asynch::Write as _;
 
 use embassy_sync::waitqueue::MultiWakerRegistration;
+use embassy_time::Duration;
 
 use heapless::{String, Vec};
 
+use crate::flashconfig;
 use crate::demo_common;
 use crate::GlobalState;
 use demo_common::{BufOutput, SSHConfig};
 
 use demo_common::menu::*;
 
+use sunset::*;
 use sunset::packets::Ed25519PubKey;
 
 // arbitrary in bytes, for sizing buffers
@@ -23,25 +34,31 @@ const MAX_PW_LEN: usize = 50;
 
 pub(crate) struct MenuCtx {
     pub out: BufOutput,
-    pub state: &'static GlobalState,
+    state: &'static GlobalState,
+    local: bool,
 
     // flags to be handled by the calling async loop
-    pub switch_usb1: bool,
-    pub need_save: bool,
+    switch_usb1: bool,
+    switch_serial1: bool,
+    need_save: bool,
 
-    pub logout: bool,
-    pub reset: bool,
+    logout: bool,
+    reset: bool,
+    bootsel: bool,
 }
 
 impl MenuCtx {
-    pub fn new(state: &'static GlobalState) -> Self {
+    pub fn new(state: &'static GlobalState, local: bool) -> Self {
         Self {
             state,
+            local,
             out: Default::default(),
             switch_usb1: false,
+            switch_serial1: false,
             need_save: false,
             logout: false,
             reset: false,
+            bootsel: false,
         }
     }
 
@@ -59,6 +76,83 @@ impl MenuCtx {
         f(c.deref_mut(), &mut self.out);
         true
     }
+
+    // Returns `Ok(true)` to exit the menu
+    pub(crate) async fn progress<R, W>(
+        &mut self,
+        mut chanr: &mut R,
+        mut chanw: &mut W,
+    ) -> Result<bool>
+    where
+        R: asynch::Read + Io<Error = sunset::Error>,
+        W: asynch::Write + Io<Error = sunset::Error>,
+    {
+        if self.switch_usb1 {
+            self.switch_usb1 = false;
+            if self.local {
+                writeln!(self.out, "serial can't loop");
+            } else {
+                if self.state.usb_pipe.is_in_use() {
+                    writeln!(self.out, "Opening usb1, stealing existing session");
+                } else {
+                    writeln!(self.out, "Opening usb1");
+                }
+                crate::serial(chanr, chanw, self.state.usb_pipe).await?;
+                // TODO we could return to the menu on serial error?
+                return Ok(true);
+            }
+        }
+
+        #[cfg(feature = "serial1")]
+        if self.switch_serial1 {
+            self.switch_serial1 = false;
+            if self.local {
+                writeln!(self.out, "serial can't loop");
+            } else {
+                if self.state.serial1_pipe.is_in_use() {
+                    writeln!(self.out, "Opening serial1, stealing existing session");
+                } else {
+                    writeln!(self.out, "Opening serial1");
+                }
+                crate::serial(chanr, chanw, self.state.serial1_pipe).await?;
+                // TODO we could return to the menu on serial error?
+                return Ok(true);
+            }
+        }
+
+        if self.need_save {
+            info!("needs save");
+            // clear regardless of success, don't want a tight loop.
+            self.need_save = false;
+
+            let conf = self.state.config.lock().await;
+            let mut fl = self.state.flash.lock().await;
+            if let Err(_e) = flashconfig::save(&mut fl, &conf) {
+                warn!("Error writing flash");
+            }
+        }
+
+        if self.logout {
+            return Ok(true);
+        }
+
+        if self.reset {
+            let _ = chanw.write_all(b"Resetting\r\n").await;
+            let mut wd = self.state.watchdog.lock().await;
+            wd.start(Duration::from_millis(200));
+            loop {
+                embassy_time::Timer::after(Duration::from_secs(1)).await;
+            }
+        }
+
+        if self.bootsel {
+            embassy_rp::rom_data::reset_to_usb_boot::ptr()(0, 0);
+        }
+
+        // write messages from handling
+        self.out.flush(&mut chanw).await?;
+        Ok(false)
+    }
 }
 
 impl core::fmt::Write for MenuCtx {
@@ -84,6 +178,11 @@ pub(crate) const SETUP_MENU: Menu<MenuCtx> = Menu {
             help: Some("Reset picow. Will log out."),
             item_type: ItemType::Callback { function: do_reset, parameters: &[] },
         },
+        &Item {
+            command: "bootsel",
+            help: Some("Resets in rp2040 bootsel mode, use with picotool"),
+            item_type: ItemType::Callback { function: do_bootsel, parameters: &[] },
+        },
         &Item {
             command: "erase_config",
             item_type: ItemType::Callback {
@@ -123,7 +222,9 @@ const AUTH_ITEM: Item<MenuCtx> = Item {
                 item_type: ItemType::Callback {
                     parameters: &[Parameter::Mandatory {
                         parameter_name: "yesno",
-                        help: Some("Set yes for SSH to serial with no auth. Take care!"),
+                        help: Some(
+                            "Set yes for SSH to serial with no auth. Take care!",
+                        ),
                     }],
                     function: do_console_noauth,
                 },
@@ -326,11 +427,22 @@ const SERIAL_ITEM: Item<MenuCtx> = Item {
     command: "serial",
     item_type: ItemType::Menu(&Menu {
         label: "serial",
-        items: &[&Item {
-            command: "usb0",
-            item_type: ItemType::Callback { parameters: &[], function: do_usb1 },
-            help: Some("Connect to if00 serial port. Disconnect to exit."),
-        }],
+        items: &[
+            &Item {
+                command: "usb0",
+                item_type: ItemType::Callback { parameters: &[], function: do_usb1 },
+                help: Some("Connect to if00 serial port. Disconnect to exit."),
+            },
+            #[cfg(feature = "serial1")]
+            &Item {
+                command: "serial1",
+                item_type: ItemType::Callback {
+                    parameters: &[],
+                    function: do_serial1,
+                },
+                help: Some("Connect to uart0 serial port. Disconnect to exit."),
+            },
+        ],
         entry: None,
         exit: None,
     }),
@@ -417,11 +529,11 @@ fn do_console_pw(_item: &Item<MenuCtx>, args: &[&str], context: &mut MenuCtx) {
 fn do_console_noauth(_item: &Item<MenuCtx>, args: &[&str], context: &mut MenuCtx) {
     context.with_config(|c, out| {
         c.console_noauth = args[0] == "yes";
-        let _ = writeln!(out, "Set console noauth {}", if c.console_noauth {
-            "yes"
-        } else {
-            "no"
-        });
+        let _ = writeln!(
+            out,
+            "Set console noauth {}",
+            if c.console_noauth { "yes" } else { "no" }
+        );
     });
     context.need_save = true;
 }
@@ -483,6 +595,10 @@ fn do_reset(_item: &Item<MenuCtx>, args: &[&str], context: &mut MenuCtx) {
     context.reset = true;
 }
 
+fn do_bootsel(_item: &Item<MenuCtx>, args: &[&str], context: &mut MenuCtx) {
+    context.bootsel = true;
+}
+
 fn do_about(_item: &Item<MenuCtx>, _args: &[&str], context: &mut MenuCtx) {
     let _ = writeln!(
         context,
@@ -495,6 +611,11 @@ fn do_usb1(_item: &Item<MenuCtx>, _args: &[&str], context: &mut MenuCtx) {
     context.switch_usb1 = true;
 }
 
+fn do_serial1(_item: &Item<MenuCtx>, _args: &[&str], context: &mut MenuCtx) {
+    writeln!(context, "serial1");
+    context.switch_serial1 = true;
+}
+
 fn wifi_entry(context: &mut MenuCtx) {
     context.with_config(|c, out| {
         write!(out, "Wifi net {} ", c.wifi_net);
diff --git a/embassy/demos/picow/src/serial.rs b/embassy/demos/picow/src/serial.rs
new file mode 100644
index 0000000000000000000000000000000000000000..25e38e0fc553631d169d422af775c276b20d50c4
--- /dev/null
+++ b/embassy/demos/picow/src/serial.rs
@@ -0,0 +1,76 @@
+//! Serial on rp2040 pins with a `BufferedUart`.
+
+#[allow(unused_imports)]
+#[cfg(not(feature = "defmt"))]
+pub use log::{debug, error, info, log, trace, warn};
+
+#[allow(unused_imports)]
+#[cfg(feature = "defmt")]
+pub use defmt::{debug, error, info, panic, trace, warn};
+
+use embassy_rp::peripherals::*;
+use embassy_rp::interrupt::UART0_IRQ;
+use embassy_rp::{bind_interrupts, Peripheral};
+use embassy_rp::uart::{
+    self as rp_uart, BufferedInterruptHandler, BufferedUart, BufferedUartRx, CtsPin,
+    RtsPin, RxPin, TxPin,
+};
+
+use embassy_rp::interrupt::{Interrupt, InterruptExt};
+
+use sunset::*;
+use sunset_embassy::*;
+
+use crate::*;
+
+bind_interrupts!(struct Irqs {
+    UART0_IRQ => BufferedInterruptHandler<UART0>;
+});
+
+#[embassy_executor::task]
+pub(crate) async fn task(
+    uart: UART0,
+    pin_tx: PIN_0,
+    pin_rx: PIN_1,
+    pin_cts: PIN_2,
+    pin_rts: PIN_3,
+    pipe: &'static TakePipe<'static>,
+) -> ! {
+    let tx_buf = singleton!([0u8; 16]).as_mut_slice();
+    let rx_buf = singleton!([0u8; 300]).as_mut_slice();
+    let uart = BufferedUart::new_with_rtscts(
+        uart,
+        Irqs,
+        pin_tx,
+        pin_rx,
+        pin_rts,
+        pin_cts,
+        tx_buf,
+        rx_buf,
+        rp_uart::Config::default(),
+    );
+
+    // let uart = BufferedUart::new(
+    //     uart,
+    //     Irqs,
+    //     pin_tx,
+    //     pin_rx,
+    //     tx_buf,
+    //     rx_buf,
+    //     rp_uart::Config::default(),
+    // );
+
+    let (mut rx, mut tx) = uart.split();
+
+    // console via SSH
+    let (mut chan_rx, mut chan_tx) = pipe.split();
+    let chan_rx = &mut chan_rx;
+    let chan_tx = &mut chan_tx;
+    info!("serial task copying");
+    let io_tx = io_buf_copy_noreaderror(&mut rx, chan_tx);
+    let io_rx = io_copy_nowriteerror::<64, _, _>(chan_rx, &mut tx);
+
+    let _ = select(io_rx, io_tx).await;
+    panic!("serial finished");
+}
+
diff --git a/embassy/demos/picow/src/takepipe.rs b/embassy/demos/picow/src/takepipe.rs
index c2bd835493c30fd8265b11205c2d7a4f4e8e1f8a..d14a94bc88f2e0c516080d227c402a7d7375941d 100644
--- a/embassy/demos/picow/src/takepipe.rs
+++ b/embassy/demos/picow/src/takepipe.rs
@@ -26,27 +26,28 @@ pub const WRITE_SIZE: usize = 64;
 /// Allows a bidirectional pipe to be shared by many endpoints
 ///
 /// One end of the pipe is fixed (attached to eg a physical/virtual
-/// uart), used with `.split()`.
+/// uart), used with `.split()`. `TakePipeStorage` is the backing store,
+/// the `TakePipe` struct returned by `.pipe()` has the functionality.
 ///
 /// The other end can be used by many clients, one at a time.
 /// When a subsequent client takes the pipe (with `.take()`), the existing
 /// client loses the pipe and gets EOF.
 ///
 /// It works a bit like `screen -r -d`.
-pub(crate) struct TakePipe {
+pub(crate) struct TakePipeStorage {
 	fanout: Pipe<SunsetRawMutex, READ_SIZE>,
     fanin: Pipe<SunsetRawMutex, WRITE_SIZE>,
     wake: Signal<SunsetRawMutex, ()>,
     counter: u64,
 }
 
-impl TakePipe {
+impl TakePipeStorage {
     pub fn new() -> Self {
         Default::default()
     }
 
-    pub fn base(&self) -> TakeBase {
-        TakeBase {
+    pub fn pipe(&self) -> TakePipe {
+        TakePipe {
             shared_read: Mutex::new((0, self.fanout.reader())),
             shared_write: Mutex::new((0, self.fanin.writer())),
             pipe: self,
@@ -54,7 +55,7 @@ impl TakePipe {
     }
 }
 
-impl Default for TakePipe {
+impl Default for TakePipeStorage {
     fn default() -> Self {
         Self {
             fanout: Pipe::new(),
@@ -65,13 +66,13 @@ impl Default for TakePipe {
     }
 }
 
-pub(crate) struct TakeBase<'a> {
+pub(crate) struct TakePipe<'a> {
     shared_read: Mutex<SunsetRawMutex, (u64, pipe::Reader<'a, SunsetRawMutex, READ_SIZE>)>,
     shared_write: Mutex<SunsetRawMutex, (u64, pipe::Writer<'a, SunsetRawMutex, WRITE_SIZE>)>,
-    pipe: &'a TakePipe,
+    pipe: &'a TakePipeStorage,
 }
 
-impl<'a> TakeBase<'a> {
+impl<'a> TakePipe<'a> {
     pub async fn take(&'a self) -> (TakeRead<'a>, TakeWrite<'a>) {
 
         self.pipe.wake.signal(());
@@ -105,47 +106,49 @@ impl<'a> TakeBase<'a> {
         self.shared_read.try_lock().is_err()
     }
 
-    pub fn split(&'a self) -> (TakeBaseRead<'a>, TakeBaseWrite<'a>) {
-        let r = TakeBaseRead {
+    pub fn split(&'a self) -> (TakePipeRead<'a>, TakePipeWrite<'a>) {
+        let r = TakePipeRead {
             pipe: self.pipe,
         };
-        let w = TakeBaseWrite {
+        let w = TakePipeWrite {
             pipe: self.pipe,
         };
         (r, w)
     }
 }
 
-pub(crate) struct TakeBaseRead<'a> {
-    pipe: &'a TakePipe,
+pub(crate) struct TakePipeRead<'a> {
+    pipe: &'a TakePipeStorage,
 }
 
-pub(crate) struct TakeBaseWrite<'a> {
-    pipe: &'a TakePipe,
+pub(crate) struct TakePipeWrite<'a> {
+    pipe: &'a TakePipeStorage,
 }
 
-impl<'a> asynch::Read for TakeBaseRead<'a> {
+impl<'a> asynch::Read for TakePipeRead<'a> {
     async fn read(&mut self, buf: &mut [u8]) -> sunset::Result<usize> {
-        Ok(self.pipe.fanin.read(buf).await)
+        let r = self.pipe.fanin.read(buf).await;
+        Ok(r)
     }
 }
 
-impl<'a> asynch::Write for TakeBaseWrite<'a> {
+impl<'a> asynch::Write for TakePipeWrite<'a> {
     async fn write(&mut self, buf: &[u8]) -> sunset::Result<usize> {
-        Ok(self.pipe.fanout.write(buf).await)
+        let r = self.pipe.fanout.write(buf).await;
+        Ok(r)
     }
 }
 
-impl Io for TakeBaseRead<'_> {
+impl Io for TakePipeRead<'_> {
     type Error = sunset::Error;
 }
 
-impl Io for TakeBaseWrite<'_> {
+impl Io for TakePipeWrite<'_> {
     type Error = sunset::Error;
 }
 
 pub(crate) struct TakeRead<'a> {
-    pipe: &'a TakePipe,
+    pipe: &'a TakePipeStorage,
     shared: Option<&'a SunsetMutex<(u64, pipe::Reader<'a, SunsetRawMutex, READ_SIZE>)>>,
     counter: u64,
 }
@@ -186,7 +189,7 @@ impl Io for TakeRead<'_> {
 }
 
 pub(crate) struct TakeWrite<'a> {
-    pipe: &'a TakePipe,
+    pipe: &'a TakePipeStorage,
     shared: Option<&'a SunsetMutex<(u64, pipe::Writer<'a, SunsetRawMutex, WRITE_SIZE>)>>,
     counter: u64,
 }
diff --git a/embassy/demos/picow/src/usbserial.rs b/embassy/demos/picow/src/usbserial.rs
index 02ee0c9199017664a83774cf671e7f2c6e19c46d..04fc999f902a895a641f09451ebbfd91d9ec255d 100644
--- a/embassy/demos/picow/src/usbserial.rs
+++ b/embassy/demos/picow/src/usbserial.rs
@@ -7,7 +7,9 @@ pub use log::{debug, error, info, log, trace, warn};
 pub use defmt::{debug, error, info, panic, trace, warn};
 
 use embassy_futures::join::{join, join3};
-use embassy_rp::usb::Instance;
+use embassy_rp::usb::{Instance, InterruptHandler};
+use embassy_rp::bind_interrupts;
+use embassy_rp::peripherals::USB;
 use embassy_usb::class::cdc_acm::{self, CdcAcmClass, State};
 use embassy_usb::Builder;
 use embassy_usb_driver::Driver;
@@ -22,13 +24,17 @@ use sunset_embassy::*;
 use crate::*;
 use picowmenu::request_pw;
 
-pub(crate) async fn usb_serial(
+bind_interrupts!(struct Irqs {
+    USBCTRL_IRQ => InterruptHandler<USB>;
+});
+
+#[embassy_executor::task]
+pub(crate) async fn task(
     usb: embassy_rp::peripherals::USB,
-    irq: embassy_rp::interrupt::USBCTRL_IRQ,
     global: &'static GlobalState,
-)
+) -> !
 {
-    let driver = embassy_rp::usb::Driver::new(usb, irq);
+    let driver = embassy_rp::usb::Driver::new(usb, Irqs);
 
     let mut config = embassy_usb::Config::new(0xf055, 0x6053);
     config.manufacturer = Some("Sunset SSH");
@@ -127,11 +133,12 @@ pub(crate) async fn usb_serial(
                 }
             }
 
-            let _ = menu(cdc2_rx, cdc2_tx, true, global).await;
+            let _ = menu(&mut cdc2_rx, &mut cdc2_tx, true, global).await;
         }
     };
 
     join3(usb_fut, io0, setup).await;
+    unreachable!()
 }
 
 pub struct CDCRead<'a, 'p, D: Driver<'a>> {
diff --git a/embassy/demos/picow/src/wifi.rs b/embassy/demos/picow/src/wifi.rs
index ca39d9f172f2abff41b64801f5a745d4497ff4e2..98a8ad4736d47715e4c0951cd93183301a28a4f5 100644
--- a/embassy/demos/picow/src/wifi.rs
+++ b/embassy/demos/picow/src/wifi.rs
@@ -13,7 +13,7 @@ pub use {
 pub use defmt::{debug, info, warn, panic, error, trace};
 
 use embassy_rp::gpio::{Level, Output};
-use embassy_rp::pio::{PioStateMachineInstance, Sm0, Pio0};
+use embassy_rp::pio::Pio;
 use embassy_rp::peripherals::*;
 use embassy_executor::Spawner;
 use embassy_net::{Stack, StackResources};
@@ -33,7 +33,7 @@ async fn wifi_task(
     runner: cyw43::Runner<
         'static,
         Output<'static, PIN_23>,
-        PioSpi<PIN_25, PioStateMachineInstance<Pio0, Sm0>, DMA_CH0>,
+        PioSpi<'static, PIN_25, PIO0, 0, DMA_CH0>,
     >,
 ) -> ! {
     runner.run().await
@@ -42,7 +42,7 @@ async fn wifi_task(
 // It would be nice to make Pio0, Sm0, DMA_CH0 generic, but wifi_task can't have generics.
 pub(crate) async fn wifi_stack(spawner: &Spawner,
     p23: PIN_23, p24: PIN_24, p25: PIN_25, p29: PIN_29, dma: DMA_CH0,
-    sm: PioStateMachineInstance<Pio0, Sm0>,
+    pio0: PIO0,
     wifi_net: String<32>, wpa_password: Option<String<63>>,
 
     ) -> (embassy_net::Stack<cyw43::NetDriver<'static>>, cyw43::Control<'static>)
@@ -52,7 +52,8 @@ pub(crate) async fn wifi_stack(spawner: &Spawner,
 
     let pwr = Output::new(p23, Level::Low);
     let cs = Output::new(p25, Level::High);
-    let spi = PioSpi::new(sm, cs, p24, p29, dma);
+    let mut pio = Pio::new(pio0);
+    let spi = PioSpi::new(&mut pio.common, pio.sm0, pio.irq0, cs, p24, p29, dma);
 
     let state = singleton!(cyw43::State::new());
     let (net_device, mut control, runner) = cyw43::new(state, pwr, spi, fw).await;
@@ -68,7 +69,7 @@ pub(crate) async fn wifi_stack(spawner: &Spawner,
     let mut status = Ok(());
     for i in 0..2 {
         status = if let Some(ref pw) = wpa_password {
-            info!("wifi net {} wpa2 {}", wifi_net, &pw);
+            info!("wifi net {} wpa2", wifi_net);
             control.join_wpa2(&wifi_net, &pw).await
         } else {
             info!("wifi net {} open", wifi_net);
diff --git a/embassy/demos/std/Cargo.toml b/embassy/demos/std/Cargo.toml
index 266bcf90b6b3b376f3cd108a303ce9fe936094ab..0af5512475a6b9ce586d04e191296309b8db22db 100644
--- a/embassy/demos/std/Cargo.toml
+++ b/embassy/demos/std/Cargo.toml
@@ -39,10 +39,10 @@ rand = { version = "0.8", default-features = false, features = ["getrandom"] }
 sha2 = { version = "0.10", default-features = false }
 
 [patch.crates-io]
-embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
-embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-futures = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
+embassy-net = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
 # for tuntap
-embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "3e730aa8b06401003202bf9e21a9c83ec6b21b0e" }
+embassy-net-driver = { git = "https://github.com/embassy-rs/embassy", rev = "1d34078fa11839f88dd2e47a9355c6b35755128f" }
 
 [profile.dev]
 debug = 2
diff --git a/embassy/src/embassy_sunset.rs b/embassy/src/embassy_sunset.rs
index 7746c8bad658f741cb371a3e8594e8653106d5fb..62eea99c3f2508c208d0a079c52e974fab2e0fa4 100644
--- a/embassy/src/embassy_sunset.rs
+++ b/embassy/src/embassy_sunset.rs
@@ -1,7 +1,10 @@
 #[allow(unused_imports)]
-use {
-    log::{debug, error, info, log, trace, warn},
-};
+#[cfg(not(feature = "defmt"))]
+pub use log::{debug, error, info, log, trace, warn};
+
+#[allow(unused_imports)]
+#[cfg(feature = "defmt")]
+pub use defmt::{debug, error, info, panic, trace, warn};
 
 use core::future::{poll_fn, Future};
 use core::task::{Poll, Context};
@@ -138,7 +141,7 @@ impl<'a, C: CliBehaviour, S: ServBehaviour> EmbassySunset<'a, C, S> {
                 let l = self.output(&mut buf).await?;
                 wsock.write_all(&buf[..l]).await
                 .map_err(|e| {
-                    info!("socket write error: {e:?}");
+                    info!("socket write error");
                     Error::ChannelEOF
                 })?;
             }
@@ -153,7 +156,7 @@ impl<'a, C: CliBehaviour, S: ServBehaviour> EmbassySunset<'a, C, S> {
                 let mut buf = [0; 1024];
                 let l = rsock.read(&mut buf).await
                 .map_err(|e| {
-                    info!("socket read error: {e:?}");
+                    info!("socket read error");
                     Error::ChannelEOF
                 })?;
                 if l == 0 {
@@ -531,6 +534,25 @@ pub async fn io_copy<const B: usize, R, W>(r: &mut R, w: &mut W) -> Result<()>
     Ok::<_, Error>(())
 }
 
+pub async fn io_copy_nowriteerror<const B: usize, R, W>(r: &mut R, w: &mut W) -> Result<()>
+    where R: asynch::Read+Io<Error=sunset::Error>,
+        W: asynch::Write,
+{
+    let mut b = [0u8; B];
+    loop {
+        let n = r.read(&mut b).await?;
+        if n == 0 {
+            return sunset::error::ChannelEOF.fail();
+        }
+        let b = &b[..n];
+        if let Err(e) = w.write_all(b).await {
+            info!("write error");
+        }
+    }
+    #[allow(unreachable_code)]
+    Ok::<_, Error>(())
+}
+
 pub async fn io_buf_copy<R, W>(r: &mut R, w: &mut W) -> Result<()>
     where R: asynch::BufRead+Io<Error=sunset::Error>,
         W: asynch::Write+Io<Error=sunset::Error>
@@ -547,3 +569,27 @@ pub async fn io_buf_copy<R, W>(r: &mut R, w: &mut W) -> Result<()>
     #[allow(unreachable_code)]
     Ok::<_, Error>(())
 }
+
+pub async fn io_buf_copy_noreaderror<R, W>(r: &mut R, w: &mut W) -> Result<()>
+    where R: asynch::BufRead,
+        W: asynch::Write+Io<Error=sunset::Error>
+{
+    loop {
+        let b = match r.fill_buf().await {
+            Ok(b) => b,
+            Err(e) => {
+                info!("read error");
+                embassy_futures::yield_now().await;
+                continue;
+            }
+        };
+        if b.len() == 0 {
+            return sunset::error::ChannelEOF.fail();
+        }
+        let n = b.len();
+        w.write_all(b).await?;
+        r.consume(n)
+    }
+    #[allow(unreachable_code)]
+    Ok::<_, Error>(())
+}
diff --git a/embassy/src/lib.rs b/embassy/src/lib.rs
index 79c292b2a578b912fa3c49c9c00d095a20ebe4b5..d29e242456bff54373c50237363b7de993bae31a 100644
--- a/embassy/src/lib.rs
+++ b/embassy/src/lib.rs
@@ -20,3 +20,4 @@ pub use client::SSHClient;
 pub use embassy_channel::{ChanInOut, ChanIn, ChanOut};
 
 pub use embassy_sunset::{SunsetMutex, SunsetRawMutex, io_copy, io_buf_copy};
+pub use embassy_sunset::{io_copy_nowriteerror, io_buf_copy_noreaderror};