Skip to content

Commit 36dcf6b

Browse files
committed
fix(cli): add Wi-Fi discovery recovery steps
1 parent 738f367 commit 36dcf6b

6 files changed

Lines changed: 79 additions & 20 deletions

File tree

bridge/src/cmd_doctor.rs

Lines changed: 1 addition & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -341,12 +341,7 @@ pub async fn check_discover() -> CheckResult {
341341
),
342342
Err(_) => CheckResult::Fail(
343343
"no Pico replied within 3 s on UDP/4242".into(),
344-
"Confirm the Pico is powered, joined your Wi-Fi, and on the same \
345-
LAN as this PC. Common causes: AP isolation enabled on the router \
346-
(see wiki/Troubleshooting.md), Pico on a different SSID, or this \
347-
PC is multi-homed and the broadcast went out the wrong NIC. If \
348-
unsure, run `couchlink configure-wifi` to re-provision."
349-
.into(),
344+
support::no_pico_wifi_short_hint().into(),
350345
),
351346
}
352347
}

bridge/src/cmd_home.rs

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,9 @@ use anyhow::{Context, Result};
77
use dialoguer::theme::ColorfulTheme;
88
use dialoguer::{Confirm, MultiSelect, Select};
99

10-
use crate::{cmd_configure_wifi, cmd_doctor, cmd_flash, cmd_run, cmd_setup, config, xinput};
10+
use crate::{
11+
cmd_configure_wifi, cmd_doctor, cmd_flash, cmd_run, cmd_setup, config, support, xinput,
12+
};
1113

1214
pub async fn run() -> Result<()> {
1315
loop {
@@ -43,11 +45,19 @@ async fn start_routing() -> Result<()> {
4345
println!("Looking for running Pico boards on Wi-Fi...");
4446
let picos = cmd_run::discover_picos(Duration::from_secs(5)).await?;
4547
if picos.is_empty() {
46-
println!("No Pico replied on Wi-Fi.");
47-
let choices = vec!["Try again", "Flash/update firmware", "Back"];
48+
support::print_no_pico_wifi_help(5);
49+
let choices = vec![
50+
"Try discovery again",
51+
"Set or change Wi-Fi",
52+
"Run diagnostics",
53+
"Flash/update firmware",
54+
"Back",
55+
];
4856
match select("Next step", &choices, 0).await? {
4957
0 => continue,
50-
1 => flash_menu().await?,
58+
1 => cmd_configure_wifi::run().await?,
59+
2 => cmd_doctor::run().await?,
60+
3 => flash_menu().await?,
5161
_ => return Ok(()),
5262
}
5363
continue;

bridge/src/cmd_run.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ use tokio::sync::mpsc;
1313
use tokio::time::{interval, MissedTickBehavior};
1414

1515
use crate::protocol::{self, GamepadState, Packet, PacketKind, FLAG_PARSEC_CONNECTED};
16-
use crate::{config, discovery, journal, xinput};
16+
use crate::{config, discovery, journal, support, xinput};
1717

1818
const DEFAULT_DISCOVER_SECONDS: u64 = 5;
1919
const DEFAULT_STATUS_SECONDS: u64 = 2;
@@ -144,10 +144,7 @@ pub async fn run(options: RunOptions) -> Result<()> {
144144

145145
let picos = discover_picos(Duration::from_secs(options.discover_seconds)).await?;
146146
if picos.is_empty() {
147-
bail!(
148-
"no Pico replied within {} s. Run `couchlink` for the guided menu or `couchlink test discover --all` for details.",
149-
options.discover_seconds
150-
);
147+
bail!("{}", support::no_pico_wifi_help(options.discover_seconds));
151148
}
152149

153150
let routes = if !options.routes.is_empty() {

bridge/src/cmd_test.rs

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use crate::cmd_doctor::{
1010
check_24ghz_warning, check_cdc, check_discover, check_firewall, check_paths,
1111
check_startup_shortcut, check_xinput, CheckResult,
1212
};
13-
use crate::{discovery, protocol};
13+
use crate::{discovery, support};
1414

1515
pub async fn run(which: &str, all: bool, reboot_to_run: bool) -> Result<()> {
1616
if reboot_to_run && which != "cdc" {
@@ -168,10 +168,7 @@ async fn run_discover_all() -> Result<()> {
168168
let socket = UdpSocket::bind("0.0.0.0:0").await?;
169169
let replies = discovery::collect(&socket, Duration::from_secs(8)).await?;
170170
if replies.is_empty() {
171-
return Err(anyhow!(
172-
"no Pico replied within 8 s on UDP/{}",
173-
protocol::PORT
174-
));
171+
return Err(anyhow!("{}", support::no_pico_wifi_help(8)));
175172
}
176173

177174
for (peer, info) in &replies {

bridge/src/support.rs

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,3 +21,47 @@ pub fn print_help_footer() {
2121
eprintln!(" bundle: couchlink bundle");
2222
eprintln!(" report: {}", issue_url());
2323
}
24+
25+
pub fn no_pico_wifi_help(timeout_seconds: u64) -> String {
26+
format!(
27+
"No Pico replied on Wi-Fi within {timeout_seconds} s.\n\
28+
This is a Pico/network discovery problem, not a controller problem.\n\
29+
Try this in order:\n\
30+
1. Confirm the Pico is powered and not sitting in BOOTSEL. If it appears as setup-mode USB, use `couchlink configure-wifi` first.\n\
31+
2. Run `couchlink test discover --all` to retry discovery.\n\
32+
3. If the router, SSID, or password changed, run `couchlink configure-wifi`.\n\
33+
4. If configure-wifi cannot find setup mode, use the BOOTSEL-button credential wipe from the wiki, then run configure-wifi again.\n\
34+
5. Run `couchlink doctor`; check Windows Firewall, router client isolation, and whether the PC and Pico are on the same LAN.\n\
35+
6. Run `couchlink bundle` and send the zip if it still fails."
36+
)
37+
}
38+
39+
pub fn print_no_pico_wifi_help(timeout_seconds: u64) {
40+
println!("{}", no_pico_wifi_help(timeout_seconds));
41+
}
42+
43+
pub fn no_pico_wifi_short_hint() -> &'static str {
44+
"Confirm the Pico is powered, not in BOOTSEL, and on the same LAN, then run `couchlink test discover --all`. If Wi-Fi changed or the Pico is in setup-mode USB, run `couchlink configure-wifi`. If it still fails, run `couchlink doctor` and `couchlink bundle`."
45+
}
46+
47+
#[cfg(test)]
48+
mod tests {
49+
use super::{no_pico_wifi_help, no_pico_wifi_short_hint};
50+
51+
#[test]
52+
fn no_pico_wifi_help_points_to_recovery_commands() {
53+
let help = no_pico_wifi_help(5);
54+
assert!(help.contains("No Pico replied on Wi-Fi within 5 s."));
55+
assert!(help.contains("not a controller problem"));
56+
assert!(help.contains("couchlink configure-wifi"));
57+
assert!(help.contains("couchlink doctor"));
58+
assert!(help.contains("couchlink bundle"));
59+
}
60+
61+
#[test]
62+
fn no_pico_wifi_short_hint_is_actionable() {
63+
let hint = no_pico_wifi_short_hint();
64+
assert!(hint.contains("same LAN"));
65+
assert!(hint.contains("couchlink test discover --all"));
66+
}
67+
}

wiki/Troubleshooting.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,22 @@ Run:
7575
.\couchlink.exe test discover
7676
```
7777

78+
If the guided menu says `No Pico replied on Wi-Fi`, that is not a
79+
controller problem. It means the PC did not receive a UDP discovery reply
80+
from a running Pico.
81+
82+
Try this in order:
83+
84+
1. Confirm the Pico is powered and not sitting in BOOTSEL. If it appears
85+
as setup-mode USB, use `configure-wifi` first.
86+
2. Run `.\couchlink.exe test discover --all`.
87+
3. If the router name or password changed, run `.\couchlink.exe
88+
configure-wifi`.
89+
4. If `configure-wifi` cannot find the Pico, use the credential-wipe
90+
recovery in [[Setup and Flashing]], then run `configure-wifi` again.
91+
5. Run `.\couchlink.exe doctor`.
92+
6. Run `.\couchlink.exe bundle` and attach the ZIP to a bug report.
93+
7894
Common causes:
7995

8096
- Windows Firewall blocks UDP broadcast. The bridge will print a

0 commit comments

Comments
 (0)