SoftAP rejects my wifi password always

@friedl_1977, I was able to compile without modification. I’m glad you found a fix though I’m not sure why running TE would not work along with your change to SEMI_AUTOMATIC. Did you test that (I will be shortly).

1 Like

HI @peekay123 -

Sorry if I was not clear. It did compile, the problem came when running the code. After entering Listen mode via the interrupt, one was able to access SoftAP but after entering new WiFi credentials, the Photon just proceeded to Listen mode again and then as stuck.

The only way to resolve it, was to flash Tinker software again. Not even re-entering WiFi credentials via Particle app worked, it presented an error.

I removed TE and at some stage also changed to SEMI_AUTOMATIC, I suppose @plusmartin can just change back to MANUAL as well. I have found on a precious occasion that TE caused some challenges for me, especially working with BLYNK.

Hope this is more clear :slight_smile:

Thanks Again!!

HI Maritn -

hmm... Do you mean pressing the button now does not put the device in Listen mode? That is strange as tested this.

I suppose you can change it back ti Manual Mode, if I recall correctly your code included couple of particle.process(); calls so it should be fine. When in doubt, first see whether SEMI_AUTOMATIC mode does not work :slight_smile:

As I mentioned to Peekay, I have had some challenges with TE mode in the past, more specifically when using Blynk. Even on fairly simple sketches, changing to TE later can have some undesired effects. Best is to keep TE in mind from the get go if you are going to need it.

Please keep in mind, I am by no means a seasoned coder, so my advice is from the viewpoint of a novice :relaxed:

Let me know how things go, I will be able to help a bit more tomorrow, quite late here now. Now that you have @peekay123 also looking into it, you should be on your way soon!!

Regards,
Friedl.

Sorry if I was not clear. It did compile, the problem came when running the code. After entering Listen mode via the interrupt, one was able to access SoftAP but after entering new WiFi credentials, the Photon just proceeded to Listen mode again and then as stuck.

Yes @friedl_1977, this is the same behavior mine had.
I will test in SEMI AUTO and AUTO as mentioned by @peekay123. I dont remember why I set TE but I hope it is not needed.

As for the button, every time I press it long enough to enter WiFi.listen the photon finally lets me input the wifi/password and it blinks green then cyan, but once it is in breathing cyan the button is ignored. So the interrupt call is probably disabled? Only after I hit reset it responds to the button. I will try to debug this, but with the help from both of you I have new hope and energy !
best,
Martin

I remember now why I was using SYSTEM_THREAD(ENABLED)…

If the wifi or the internet fails, the photon stops working immediately. I can see this because every second I am serial printing the status of the sensor. When the wifi fails, it just stops printing until it is back.
My sensor needs to be constantly monitored with or without wifi.
with SYSTEM_THREAD(ENABLED) the photon continues to print while blinking green waiting for wifi.
The fact that the photon relies so much on wifi is quite annoying and idealistic.

I still dont know what option or solution o have to NOT use SYSTEM_THREAD(ENABLED) and still have the photon report data when no wifi is available…

SYSTEM_THREAD(ENABLED) consumes about 7 o 8 kb of additional ram, this is probably why softAP finally worked when not enabled.

any thoughts?

@plusmartin, looking at the interruptcheck() code, I can’t help but to think the logic doesn’t quite work. When the button is pushed, the button interrupt is not disabled, allowing the button bounce to keep firing the interrupt and affect the value of interruptflag, possibly while it is being used. You also assume that when the interrupt will set interruptflag to 1 if the button is kept pressed, which is not the case (the interrupt fires only on a falling edged). I am not sure why you use an interrupt in the first place since you sample interruptflag in loop() anyway to do your debounce/long press detect. You could use a polled button library which would work better. There are some great Arduino libraries which you could use including one I haven’t tried yet but looks good:

BTW, when sharing a global variable between an ISR and the main code, it is recommended to declare that variable volatile.

I encourage you to think about using non-blocking (no delay() calls) code by using Finite State Machine programming. This will also help in keeping your code logic and flow clear.

My last comment is to avoid the use of String variables which can create heap fragmentation. You can use c-strings (char[]) instead.

The comments you made regarding SYSTEM_THREAD() and the extra RAM impacting SoftAP are correct. By NOT using String variables, which are dynamically allocated on the HEAP at runtime, and using c-strings instead, which are allocated on the heap at compile time, will make it easier to see how much RAM is being used by the code. I’ll be playing with your code tomorrow to test these ideas.

2 Likes

This can be one suspect as it will initialise 200*16 bytes and once you add strings longer than 16 bytes the string will double in size and be relocated, then when it exceeds 32 bytes it will again double and be relocated and so forth.
But clearing the strings will not reclaim the used memory.
So your code may start of with a small footprint, but that can massively change over time.

We usually advise agains the use of String wherever possible.

You can also try to remove all global variables that are not really needed globally.

BTW, I'm not sure why you are using SparkTime - the inbuilt Time object should do all the tricks. And where do you need stdio?

Also when dealing with millis() you should not mix signed and unsigned datatypes (e.g. int timeout_milli_seconds).

2 Likes

@plusmartin -

Ok, now the main band is playing, time for the opening act to get off stage :relaxed:

You are in good hands with @ScruffR and @peekay123. Hope I kept you entertained at least if nothing else, haha.

I will also look at the code again, but doubt I will have more to add than these two gentleman. If the button ‘stops working’ after connected once, it seems to me like it is only called in the setup (which only run once) and never again unless a restart occurs. Having said this, I have only used interrupts once, so new to this.

With regards to getting the button to work:

Have a look at this simple sketch… Placed just after SoftAP code, it allows me to pull A2 (in your case D6) to GND which puts the device in listen mode. This works whether or not it has been connected, is connected or any other situation I could through at it.

SYSTEM_MODE(SEMI_AUTOMATIC);

boolean connectToCloud = true;

void setup() {

    attachInterrupt(A2, connect, FALLING);
    WiFi.setListenTimeout(300);

// your other void setup code goes here //

}

void loop() {
    
     if(connectToCloud) {
        if(Particle.connected() == false) {
             Particle.connect();
             connectToCloud = false;

        }
    }

// your other void loop() code goes here //

}

 void connect() {
     connectToCloud = true;
     WiFi.listen();
    }

It also exists listen mode after a while.

I’m not saying it is perfect, but it is working for me. At least from here you can add the rest of your code :slight_smile:

Regards,
Friedl.

1 Like

@peekay123 I will include a disable for the interrupt once the button is pushed and the script is running.
In loop I run interruptcheck(); which checks wether the interruptflag is 1, which can only change to 1 when the button is pushed, pulling its input to GND and executing the interrupt called interruptbutton();

BTW, when sharing a global variable between an ISR and the main code, it is recommended to declare that variable volatile.

As for the ISR and volatile variables, can you comment a bit more? should the structure of the interrupt be different?

I encourage you to think about using non-blocking (no delay() calls) code by using Finite State Machine programming. This will also help in keeping your code logic and flow clear.

Are you referring to a specific delay() or in general? how would you replace them and still keep some time sensitive processes such as the reading of the sensor and the speed of publishing backups to the cloud?

My last comment is to avoid the use of String variables which can create heap fragmentation. You can use c-strings (char[]) instead.

Im very happy to use char instead of string, I just want to make sure that the syntax and the way I am handling the data is correct. Should I just declare

char backup[ARRAYSIZE];

instead of

String backup[ARRAYSIZE];

@ScruffR Understood, so what is the best way to make backups of data when there is no wifi in a way that makes better use of my RAM?

BTW, I’m not sure why you are using  `SparkTime`  - the inbuilt  `Time`  object should do all the tricks. And where do you need  `stdio` ?

removed stdio, thanks for that.
How do I use the 'Time' object? for example here:

currentTime = rtc.nowEpoch();

Also when dealing with millis() you should not mix signed and unsigned datatypes (e.g. int timeout_milli_seconds ).

yes, I deleted that, it is no longed used and forgot to remove it.

@friedl_1977
What happens after the WiFi.setListenTimeout(300); times out? it just goes back to flashing green/cyan?
if there are no wifi credentials stored what happens? foe example if a customer wants the device to connect to a certain wifi and NOT to another? This case is not vey probable, my interest is that the user can easily and reliably press the button until WiFi.listen() starts and input a new wifi via softAP, then back to normal. By The way, I still dont understand when is the softAP script run? is it only launched at STARTUP? why does it then run when WiFi.listen() is executed?

Thanks a lot for your help and support! this is helping me a lot.

I suggest you don't use an interrupt at all and instead use a button polling library you call from loop(). However, this requires that you not block anywhere in your code so you can poll quickly. It may also be possible to run the polling from a Software Timer though I have not tried that before.

Simply declare interruptflag as a volatile int, which tells the compiler to not apply any optimizations that might affect the handling of the variable.

Using delay() stops the flow of your code for the specified time. The purpose of a Finite State Machine is to eliminate the use of delays through the use of program "states". If you have never used FSMs before, you may want to learn how. Here is a good start:

I am not clear on how or why you use backup[]. If you are trying to buffer publish statements then you should consider using @rickkas7 PublishQueueAsyncRK library on the Web IDE.

For the record, String backup[200] creates an array of 200 Strings, each with a starting value of 16 bytes as @ScruffR pointed out, with a starting size of 3200 bytes. However, char backup[200] creates a fixed-sized array of 200 chars (or bytes). So these two arrays are NOT the same.

For timezone adjusted epoch time (assuming you set the time zone), you would use Time.local().

2 Likes

I appreciate these great advices, thanks @peekay123

I am incorporating your recommendations, starting with the interruptbutton which indeed was useless to have if the code runs through loop relatively often.

The part about PublishQueueAsyncRK

this looks very interesting and could potentially solve all of my issues, I just need a bit of guidance on how to implement it.
My 200 string array was intended to store up to 200 logs of offline data, now I see that my approach was innocently wrong, since my string is much longer than 16 bytes.
this is the payload that I was trying to log (up to 200 times haha):

payload = String::format(  "{\"Timestamp_Device\":\"" + String(currentTime) + "\",\"device_id\":\"" + String(id.c_str()) +  "\",\"temp\":\"" + String(flowtemp1) + "\",\"flowshort\":\"" + String(flowtotal) +  "\",\"flowacum\":\"" + String(flowacum) + "\",\"vbat\":\"" + String(vbat) + "\",\"counterstatus\":\"" + String(counterstatus)+"\"}");

There is a lot of repetitive text that I could avoid saving. So my question now is how to implement a better backup using this library?
This question is indirectly related to the softAP issue, so I dont know if I should post another question or keep the conversation here?

Thanks a lot!
Martin

1 Like

While the PublishQueue library will make things easier it may not reduce memory impact that much as it will also buffer strings.
I'd rather buffer the raw numbers (e.g. in a struct) which will take much less space and only create the event strings temporaily when able to actualy publish.

Something along the line of this post

2 Likes

Hi @plusmartin -

I had WiFi.setListenTimeout(300); initially only implemented as I had a waterproof enclosure for a project and therefor no external ports or buttons. Was also one of my first projects and was not familiar with Reed switches which now, based on the advice @ScruffR gave, might be an even better solution than using GPIO pins. The function of this was then to exist Listen mode after some time and to try and reconnect to WiFi automatically. With the help of some other code If no wifi was detected or no valid credentials was stored, it will re-enter Listen mode... rinse repeat :slight_smile:

I initially had it commented out when I tested your code, but quickly saw the need to use it again. In your case what it should do is;

  1. You pull A2 (or whatever pin you are using) to GND and the device enter Listen Mode.

  2. After specified time (in the case 300 seconds) the device will exit Listen Mode and try to reconnect. If you pull A2 to GND, it will enter listen mode again.

  3. Once in listen mode, you can enter appropriate detail for the Network you want to connect to.

The real world scenario for me here is what if someone accidentally presses the button (or change their mind while in Listen mode). If implemented, they can simply wait couple of seconds and the device will reconnect (if details are stored) or simply will flash green trying to connect. To re-enter Listen mode, simply press the button then :slight_smile: If of course you want to clear credentials, simply ad your long-press code implant this feature. I just wanted to get you started with the basic SoftAP code and some code to enter/exit Listen Mode successfully.

This is preference though.

My understanding of this is that with SoftAP you are setting the Photon up as a WebServer, meaning this code is 'run' all the time. Wherever 192.168.0.1 is accessed you will see that page. Of course you can only access this page once connected to the photon.

Think of it as you would access the admin page of your Wifi Router or Access point. Whether is is exactly the same I don't know, but it acts the same.

I need to take my dog for a walk, we only have 1 hour left before we are not allowed outside due to hard lockdown, if you have more questions, I will gladly assists when I am back :slight_smile:

Hope this helps.
Friedl.

Actually it’s not really running until you enter Listening Mode - LM does spin up the software WiFi access point provided by the WICED framework which will then deal with requests directed at it.
The softap_set_application_page_hanlder() call merely hooks up a custom callback function on that AP which by default would only serve a “hello.txt” file whenever you send a generic request to the SoftAP (the special setup requests used by the mobile app are undergoing special treatment tho’).

Also to note, without SYSTEM_THREAD(ENABLED) your loop() will not be executed while in LM and all the work you want to do with SoftAP has to be done in the callback.
However, with SYSTEM_THREAD(ENABLED) your loop() can still monitor and do things - even call WiFi.listen(false) to end LM when your code deems it needed - as long it does not do anything networky.

1 Like

Hi @plusmartin

@ScruffR gave a better explanation about the SoftAp functionality. Comes down to the fact than when in Listen mode and you are connected to the Photon, you can access 192.168.0.1 and you will see the SoftAp page. At least, this is my experience. If you implement the code as is, you should have no problems.

The way I am using the interrupt is exactly for this reason you mentioned.

Scenario:
Device reaches new client with no WiFi credentials installed (or only my WiFi details if I forget to clear them). Client switches device on, it starts up and will proceed flashing green as it will not be able to connect to new WiFi. When button is pressed, the device will enter listen mode. Client can then connect to the AP of the Photon by connecting to the Photon WiFi and visit 192.168.0.1

The SoftAP page will be deployed and client can successfully enter their details. The device will restart, connect to correct WiFi and resume its function.

Let say after some time, clients details change due to new ISP or new Password or he replaces the router, AP or extender the Photon was connected to, with a new one with new details. At this stage, the device will proceed flashing GREEN as it will be able to connect to the new WiFi. The client can proceed to press the button and Listen Mode will be entered. The client can again connect to the Photon WiFi, access 192.168.0.1 and use the SoftAP page to enter new details. After this, the device will restart, connect to the new WiFi and resume function.

In the even that client pressed the button by accident and unintentionally enters listen mode, with WiFi.setListenTimeout(*whatever_time_you_deiced*); in the code, the client can simply leave the device and after set time, it will exist Listen Mode, connect to current WiFi and resume function. It will not interfere with what you normal operation. If you DO NO use this and client accidentally presses the button and enters listen mode, the client will need to go through the setup on the Particle app to clear Listen mode.

While not probable, it is most certainly possible. I think my many years in IT and support made me realise when it comes to clients, ANYTHING becomes a probability :rofl:

I sincerely hope this helps, but please, if anything I say seem to contradict any advice from @ScruffR or @peekay123 (which I not my intention), rather go with their advice, they are much more experienced than I am and very helpful.

Pay attention to the SYSTEM_THREAD(ENABLED) advice form Scruff, if you need to collect data even while in Listen Mode, you will have to find a way to implement SYSTEM_THREAD(ENABLED); All depends on how critical the data is for that time period I suppose. I my cases so far, not needed. If you are doing IoT black boxes for Airbus, I suppose it is critical :relaxed:

Best of Luck!!
Friedl.

1 Like

thanks again @friedl_1977

I hope your dog is happy.

The use of softAP for clients is the same in my case and probably for almost everybody using it.
I will not clear credentials for now and leave a timer just in case, good idea.

@ScruffR I really dont mind working without SYSTEM_THREAD(ENABLED) while in LM, since it will be hopefully a short time and almost never used anyways.
My concern is that if I dont enable it the photon just stops working when there is no wifi.
I tested this with my phone hotspot and the photon printing its status via serial. Whenever I turned hotspot off the photon stopped printing.
So apparently It must be always enabled, which brings the problem of using it together with softAP (as someone mentioned it was not the best to do and because it uses several kb of extra RAM).

Could it have something to do with SEMI_AUTOMATIC or MANUAL mode? I guess if done properly I could even use AUTOMATIC and forget about it. As long as it keeps working without wifi and then reconnecting and uploading whatever backup it has.

Also, thanks for your recommendation for using struct, I will take a look at it in detail since it is reaching the limit of my coding skills, thankfully you are helping me push it further =).

1 Like

Hi @plusmartin

He is indeed :slight_smile:

As far as I know and if I understand your need correctly, you do not need SYSTEM_THREAD(ENABLED) for this, either SYSTEM_MODE(SEMI_AUTOMATIC); or SYSTEM_MODE(MANUAL); will allow your code to run without being connected to WiFi.

My understanding is though, that if you need code to run while in LISTEN MODE, you would need SYSTEM_THREAD(ENABLED). Also, note the differences between SYSTEM_MODE(SEMI_AUTOMATIC); and SYSTEM_MODE(MANUAL); when implementing your code.

Regards,
Friedl

You are right about not needing the SYSTEM_THREAD(ENABLED),
I almost got it to work the way I want, maybe @rickkas7 or @ScruffR can comment on this, since it should be a simple fix:

My device works as intended when connected to wifi. CHECK.
When I remove the wifi it starts blinking deep blue and after a while it shuts down the wifi (white LED). During all this process it is still sensing and responsive.
Then, at some point, apparently when I loop again it tries to Particle.connnect() and blinks green. This is when it stops printing serial and ignoring the pushbutton. This happens for a few seconds (around 20) and then it starts responding again while blinking green until it goes back again through loop and freezes while trying to connect.

I am currently using SYSTEM_MODE(MANUAL);

  1. Is there a way to have the device do the Particle.connnect() and remain responsive without the need for SYSTEM_THREAD(ENABLED)?

  2. How can I ensure that while there is no wifi it will keep trying to connect forever until it does and not give up? Right now it changes from blinking green to white after a while..

thanks!

Once your code realises it has lost connection (!Wifi.ready()) you should explicitly call WiFi.disconnect() and then - from time to time - do a WiFi.scan() to see whether your WiFi has come back - only then call WiFi.connect() (or Particle.connect()).

If you let the device OS try to auto-reconnect it will keep doing what you describe above.

1 Like

Hi @plusmartin -

I am not sure it will help in this case, but here is my golden rule with system modes. Please keep in mind I am still learning C++

  1. Always try Automatic first…
  2. Then Semi
  3. if, and only if ABSOLUTELY NEEDED, Manual mode

Then,If I am really feeling brave, I attempt Threads :rofl:

My understanding is in Manual mode you all need to call Particle.process(); at the correct times or you will have undesired results, whereas this is being handled for you in Semi_Automatic mode. In Semi_Automatic your code will run without being connected to WiFi so you should be able to enter Listen mode via Interrupt pin at any given time. Your code will not be blocked due to not being connected to WiFi but you won’t be taking any readings while in listen mode. For this you will need Threads.

I applied this last night on a pump control project and am getting the desired results. @ScruffR please feel free to correct me :see_no_evil:

Regards,
F