earlephilhower / arduino-pico

Raspberry Pi Pico Arduino core, for all RP2040 and RP2350 boards
GNU Lesser General Public License v2.1
2.03k stars 422 forks source link

If Access point is turned on after calling WiFi.begin(ssid, pass), Rpi Pico W never connects to AP. #906

Closed MrGreensWorkshop closed 1 year ago

MrGreensWorkshop commented 2 years ago

If Access point is turned on after calling WiFi.begin(ssid, pass), Rpi Pico W never connects to AP. I checked the AP web interface and Rpi Pico wasn't connected. (btw I tested on 2 different APs)

Steps to reproduce

  1. Set the ssid and pass compile flash and run.
  2. Let Rpi Pico W connect to the AP.
  3. After 10 sec, Power off the AP.
  4. Wait 5 sec and Power on the AP. (Meanwhile Rpi Pico W reboots and tries to connect to the AP)
  5. Rpi Pico W never connects to AP. (waited 8 min or more)
  6. If you reboot Rpi Pico W by re-plug Usb, wifi connection succeeds.

Use case: Reconnect to the AP after connection fails.

test code

#include <WiFi.h>

const char* WIFI_SSID = "..............";
const char* WIFI_PASS = "..............";  

bool wifiSetup(const char* ssid, const char* pass) {
  bool connected = false;
  int tryCount = 10;
  // Set WIFI module to STA mode
  WiFi.mode(WIFI_STA);

  // Connect
  Serial.printf("[WIFI] Connecting to %s ", ssid);
  WiFi.begin(ssid, pass);

  // Wait for wifi connection
  while (1) {
    //check wifi connection
    static unsigned long last = millis();
    if (millis() - last > 500) {
      last = millis();
      if (WiFi.status() == WL_CONNECTED) {
        // Connected!
        Serial.printf("\n[WIFI] STATION Mode, SSID: %s, IP address: %s\n", \
                          WiFi.SSID(), WiFi.localIP().toString().c_str());
        connected = true;
        break;
      }
      Serial.print(".");
    }
    delay(100);
  }
  Serial.println("");
  return connected;
}

void setup() {
  Serial.begin(115200);
  Serial.println("");
  delay(1000);

  // to disable soft AP after restart
  WiFi.mode(WIFI_OFF);
  delay(100);

  wifiSetup(WIFI_SSID, WIFI_PASS);
}

void loop() {
  static unsigned long last = millis();
  if (millis() - last > 5000) {
    Serial.println("Wifi check");
    last = millis();
    if (WiFi.status() != WL_CONNECTED) {
      WiFi.disconnect();
      Serial.println("Wifi disconnected. Rebooting");
      delay(100);
      rp2040.restart();
    }
  }
  delay(100);
}
earlephilhower commented 2 years ago

If I understand you correctly, then I think there is nothing to be done here.

If the binary BLOB CYW43 firmware doesn't connect in some unspecified time, then it doesn't seem to keep trying to connect., A WiFi.begin() ends up calling cyw43_wifi_join which sets the connection params in the blob. but there is no particular call in the core here which says "shut down wifi" unless you do WiFi.end()

MrGreensWorkshop commented 2 years ago

Yeah, It doesn't seem to keep trying to connect.

earlephilhower commented 2 years ago

One thing we can check, just to verify this, is after you power-cycle the AP{, do you see the PICOW ever appear in the STA/WiFi client list on the AP? If the link layer doesn't ever connect like this, then the upper IP layer can't, either. IF it does connect, but just does not grab an IP, then it's something else and we may be able to do something here...

MrGreensWorkshop commented 2 years ago

At first I thought so, but I don't think that's the case. AP that I'm using is has responsive interface to check connected devices (dd-wrt) unfortunately after power-cycle, I didn't see the PicoW on the list, but I will try again.

I'm trying to understand the call chain, cyw43_wifi_join -> cyw43_ll_wifi_join -> BLOB CYW43 right? non blocking calls, we set the required wifi params to registers then we check the wifi status..

I wonder what happens with picosdk, if we do the same thing? I mean does it also happen there too.. since both uses BLOB CYW43 too right?

earlephilhower commented 2 years ago

Yes, the blob is the blob is the blob. :laughing: The same codepath is used in the SDK as well. My fork of the thin wrapper only includes some changes to avoid filtering out multicast packets (lack of which completely breaks IPv6).

So, if your power-cycled AP isn't seeing the PicoW show up as a client, then your code will need to attempt to reconnect explicitly before it does something that requires WiFi connectivity...

MrGreensWorkshop commented 2 years ago

https://github.com/raspberrypi/pico-sdk/issues/912 could it be the issue? power save mode setting messes up?

earlephilhower commented 2 years ago

You could try adding a WiFi.noLowPowerMode() before you WiFi.begin(). I don't have any way of grabbing the WiFi connection sequence with my routers, so can't really say if it changes anything but it's a simple thing to test.

MrGreensWorkshop commented 2 years ago

I will try that thanks. I was lucky to have spare wifi router. 😄 Btw you can test with setting up wifi hotspot with smartphone. It gives you same result.

MrGreensWorkshop commented 2 years ago

At first I thought so, but I don't think that's the case. AP that I'm using is has responsive interface to check connected devices (dd-wrt) unfortunately after power-cycle, I didn't see the PicoW on the list, but I will try again.

Tested again, it is definitely as I explained. I don't see PicoW in Client list.

WiFi.noLowPowerMode()

Tested but unfortunately, it didn't change anything. 😞

earlephilhower commented 2 years ago

You could try editing the noLowPower call to use CYW43_PERFORMANCE_PM instead of the default setting. Again, this is just spitballing but easy to test.

MrGreensWorkshop commented 2 years ago
earlephilhower commented 2 years ago

Good debug, but it unfortunately means that we can't do anything here and we need something upstream (either pico-sdk or more likely the cyw43-driver repo) to actually attack this. OTW, your workaround seems like it works, yes? I believe others have been doing that, just by default.

MrGreensWorkshop commented 2 years ago

Good debug, but it unfortunately means that we can't do anything here and we need something upstream (either pico-sdk or more likely the cyw43-driver repo) to actually attack this.

cyw43 driver calls CYW43::begin -> cyw43_arch_wifi_connect_timeout_ms

I think we need to wait and see what pico-sdk will come up with. if cyw43_arch_wifi_connect_timeout_ms fixed, then wifi.begin should work as expected. If they make a workaround outside of the function we need to bring into CYW43::begin.

OTW, your workaround seems like it works, yes?

needs bit testing.

I believe others have been doing that, just by default.

I believe they don't even realize it. Because Arduino pico using same calls as ESP so, they will think it should try until wifi connects.

earlephilhower commented 2 years ago

The SDK moves a little slow as they try and be extra stable. Since this includes the CYW43 driver (seems like it's completely in the driver + blob) that probably will make it even more difficult. I've added a tag now but don't see anything we can do in this repo so we'll just sit on it...

revell1 commented 1 year ago

I will add a few notes from my observations with my ongoing code development using Arduino IDE. No solutions as such, but I think probably useful to make a note for future use.

I have currently goto V2.6.1 on my boards. But had been observing things with 2.6.0, so some things may have been fixed or shifted with your reworking of the interrupts.

But my current observations appear to suggest to me that:

Once the code connects to a router, WiFi.status() returns WL_CONNECTED.

If the router is rebooted, or the Pico drops the connection, my software currently is unaware of the loss, and the driver code does not try to auto reconnect.

I have modified my code to add a check for WiFi.status() returning WL_CONNECTED in the loop() hoping to atleast get a Serial.print() to indicate a detected failure of link, but sofar have not seen a case where WL_CONNECTED is not reported, but I also have not lost a connection since adding the check!!!

I think in both our cases, we need to have a periodic test function that checks WiFi.status(), and if detects a loss of connection, then proceeds to try to reconnect to the lost connection (in my case my code is ending up doing a watchdog reset).

I am at present unclear how best to do detect and recover the link. As the link could fail at any time, I do not know what impact the disconnection has on any other WiFi activity, i.e. if in middle of sending or receiving WiFi data, will the driver recover or atleast gracefully terminate existing connection states, or will there be a messy cleanup needed in any users application code, (I suspect the latter).

At present I have a very lazy option of using the WATCHDOG, which I kick regularly, and will stop kicking if the link status fails (assuming WiFi.Status() actually indicates this), the other advantage of the WATCHDOG is that when I get other stalled conditions where my loop() takes longer than 2seconds, the code get a reboot, my debug output suggests my loop() typically averages 2.6ms over a 1000loop average. Where the loop appears to die for longer than 2 seconds, I suspect this is caused by Wifi and USB serial.print activities, my code will reboot. Which is atleast somewhat safer than loosing control of a Wifi controlled robot running straight into a wall.

My loop() only has these random delays/stalls when I have the USB cable connected, hence my thinking that there is still some interrupt issue when both Wifi and USB are busy.

As I say, these are just my observations, which may be of some use now or in the future, so am not expecting a fix any time soon, but if I mention them now, then they may be useful.

MrGreensWorkshop commented 1 year ago

@earlephilhower Hi, since the issue on PicoSDK was fixed and released with PicoSDK v1.5.0. I think we can close this. What do you think?

earlephilhower commented 1 year ago

Let's close it with #1167 merge, @MrGreensWorkshop

It should have been a simple update, but unfortunately The RPi guys had to rewrite the whole CYW43 WiFi infrastructure and it's pretty much busted WiFi here. So, it may be a few weekends worth of work before we actually can move to 1.5.0...

MrGreensWorkshop commented 1 year ago

@MrGreensWorkshop would you be able to try out #1167 and see if the open connections work now? It took a ton of work, but I think I'm at a stage where WiFi is working pretty stably. I don't have any HW around that will do an open AP (all my phones require WPA2 in their hotspot mode) so can't test and see if we're good.

So didn't you know the Raspberry Pi Pico W could connect to a WPA2 AP? I just don't get it. Anyway, I tested it for you. What I did was;

Sorry, but I will not be around for a while. I hope you can figure it out. Good luck.