P1 freezes when it can’t connect to one of the saved networks after 67 calls of WiFi.connect() set on a timer. I know it is associated with the number of function calls because adjusting the interval of the timer has a proportional affect on the time between freezes. With the below code the freezing is occurring about every 5 minutes. The loop runs in under 1 second until the freeze occurs. I also assume it’s associated with the number of failed attempts to connect because the time before freezing is consistently set by the time since connection was lost, not the system time. If the device starts up and there’s no network, it will freeze in ~5 minutes. If the device starts up and connects, it will run continuously with no problem. When the network is lost, such as turning off the hotspot on my phone, the ~5 minute time to freeze will begin. This happens every time.
Setup is:
Threading enabled
System mode manual
v1.4.4
You should not call WiFi.connect() from a timer handler. The issue is probably due to stack running out - hence why I asked about the reset reason - need to enable and capture in restart.
The P1 doesn’t reset or give the stack overflow indication of flashing red. Instead, it just keeps flashing green continuously. If I move the connect call out of the timer and instead just use the timer to set a flag to call in the loop, it does the same thing. If it’s the memory as you suggest, what’s the best way to verify this? Fix it?
Does anybody know why this is freezing and how to prevent it? Turning off the WiFi wouldn’t affect the memory of the MCU, would it? Why would calling WiFi.connect() a certain number of times ever cause the application and timers to both freeze when threading?
To try and answer your question - if you ran the full SerialLogHandler when the Photon or P1 is trying to connect to an AP you would see that there is considerable use of memory (RAM) from the heap. Using softAP (which itself uses a ton of memory) with a WiFi.connect which can’t connect to any AP can possibly run a device out of memory. When this occurs odd stuff happens - it hangs. If you can check how much memory there is with System.freememory(); and output this whilst trying to connect. You could also implement an application watchdog and in that handler capture the memory to eeprom before calling reset and then output that on startup if it is non-zero?
Is there anything can be done to prevent the memory from being used up in the scenario? I system reset is undesirable due to other operations in the application.
I understand that each call to connect() will consume some stack space and since the wifi modem hasn't had time to process it (and the default listen timeout is not set i.e. listen forever), this just increases stack usage with each call until it crashes with no more stack (or heap) available.
I implemented the following code to determine if the free memory was being used up and, while it’s steadily decreasing, the application freezes and the watchdog performs a reset:
The serial output for the above print of free memory starts at 37,568 bytes and decreases by ~40 bytes/second when no network is available. I am calling the connect function every 1 second now. At 35,968 bytes and ~60 calls to connect, the application freezes.
@B_Mac So now you know that the application freezes when it gets to ~36K bytes free RAM (heap) - I suggest you make your checks less frequent when there is no AP available.
@shanevanj Thanks for the solution, it seems to have fixed the problem of the application freezing when no AP is available; however, I would still like to know what is causing the crash so that I can be confident that this won’t happen again for another reason. Checking the freeMemory at multiple points throughout my code, I don’t see it getting low at all and the freeMemory gets lower when connected than it seems to do when not connected as all Cloud functions are conditional of Particle.connected. Can anybody help me find why it’s freezing? I don’t have a lot going on that would consume 36k and I can only see the memory fluctuate around 2k during operation.
So my understanding (others please correct me if I am wrong) - is that every time you call any function it will allocate some memory for the stack and when that function returns, it will generally free that memory from the stack. So if you make multiple calls that don’t return, you will steadily consume the stack unit the system has no more memory to work in a will freeze. (remember system function in DeviceOS are also calling functions all the time to maintain itself - your app is not the only thing running in the device). In your example code you posted, you are calling WiFi.connect() every 5s regardless of if its connected or not. Obviously if its connected it returns and all is well. If its not connected it will start a connection sequence that will be waiting for response from an AP - if this response does not arrive and the connection is completed in your allocated 5s, then you issue another call (consuming more stack) - now since the first call to connect hasn’t completed, then next one cannot start and therefore waits, and in 5s you again issue another connection call and so on until there is no stack left and it freezes.
So in summary - you need to as a principle, check the status of any connection (active or busy trying) before using it. Secondly communications generally always has setup delays and in the case of WiFi connections can be unto 30s or more, depending on the AP configuration. A 5s reconnect is generally to short for anything except a wired connection to a LAN
In principle correct. However, stack size is limited by a system setting and not by the available RAM. IIRC the default stack size is 3K (or was it 6K ) and that is not reflected via System.freeMemory().
If you are not able to share your code with us - to help you determine from static analysis where your RAM might being consumed then you could try conditional compile statements in your code using something like this to remove things to determine where the RAM is being used. This is most likely to be an array of some sort possibly in a library.
#define BUILD_THIS false
and #if BUILD_THIS #endif