Photon/P1 loop()/user code blocking in SEMI_AUTOMATIC mode

I finally had time today to finish my time-out, reconnect after some time code. It works pretty well. In general, the down time is [Spark.connect() duration]/[period to reconnect]. For me that would be 30s/30m roughly 2% downtime when the cloud connection is lost. For my application, it is definitely more important to always run the user code than always being connected to cloud. @ScruffR, I still don’t know why Spark.connect() takes such a long time on my Photons. The P1s I am using definitely spend shorter time trying to connect.

I decide to turn WiFi completely off to save power while the reconnect timer is running. Here’s my code in case anyone would like to use it :smile: : Again, to be able to constantly run the user code in loop() is much more important than being connected to the cloud at all times for my application.

I look forward to the multi-threading release!

SYSTEM_MODE(SEMI_AUTOMATIC);

unsigned long discTimer;
unsigned long connTimer;
unsigned long discTimerLimit = 2000UL;
unsigned long connTimerLimit = 60000UL; // retry connection every 60 seconds

bool flag = FALSE;
bool disconnected = FALSE;
bool tryConnect = FALSE;

void setup(){
    Serial.begin(9600);
    pinMode(D7, OUTPUT);
    Time.zone(-7);
    discTimer = millis(); // one time reset, in case the P1 never connects to Cloud in the first place
    Spark.connect();
}

void loop(){

/*the following line is replaced by user code*/
digitalWrite(D7, (millis() >> 2) & 0x88);

if(!Spark.connected()){
    
    // first if to flag and start timer to disconnect
    if(!flag){
        flag = TRUE; // one time flag for starting the disconnect timer
        Serial.println(Time.timeStr()); // time stamp
        Serial.println("Connection lost! Flag and timer to disconnect starts");
    }  
    // second if to disconnect and start timer to reconnect 
    if(flag && (millis()-discTimer >= discTimerLimit) && !disconnected){
        Spark.disconnect();
        WiFi.off();
        disconnected = TRUE; // disconnect using code done
        Serial.println(Time.timeStr()); // time stamp
        Serial.println("Time's up, disconnect and retry in 30 min.");
        tryConnect = TRUE; 
        connTimer = millis(); // start the reconnect timer
    }
    // third if to reconnect and clear flags
    if(disconnected && (millis()-connTimer >= connTimerLimit) && tryConnect){
        flag = FALSE;
        tryConnect = FALSE;
        disconnected = FALSE;
        Serial.println(Time.timeStr()); // time stamp
        Serial.println("Retrying connection now.");
        WiFi.on();
        Spark.connect();
        discTimer = millis(); // reset the timer to disconnect
    }
} else {
    flag = FALSE; disconnected = FALSE; tryConnect = FALSE; // clear flags if connected
    discTimer = millis(); // timer to disconnect
    connTimer = millis(); // timer to reconnect
}

}
1 Like

It is a puzzle where these differences come from.
Just to help narrowing things down:

  • We already have established that they are running the same firmware version on all devices
  • You do have only one set of WiFi credentials stored in each of your devices (if not sure, try clearing the credentials)
  • All your devices are round about the same spot when you're testing
  • All your devices are using the same kind of antenna
  • You are testing them all at approximately the same time (changing interference levels due to time of day, neighbours, girl friend being online, ...)
  • The behaviour for each individual device is consistent (always slow vs. always fast) - even with another router/network
  • You have not set fix IPs for some devices and free DHCP for others, and you are not running into DHCP limitations of your router

If you could answer all these, maybe @bko, @Dave or @BDub might have an idea.

1 Like

Thanks for the debug ideas! I could say the following for each of your questions (for just the Photons):

Yes, all Photons are using firmware release 0.4.3. I actually loaded my own factory reset firmware on all the Photons using the 0x80E0000 flash address in dfu but don't think that would make any difference for this discussion.

Yes, only a single set of (same) WiFi credentials for each Photon. They were all set using USB instead of in code (much faster to load). The distance from my router are the same but I can't comment on the strength of signal vs day. I did however, try to use a uFL coax PCB antenna and got consistently faster connection for all the Photons probably due to the higher gain. I do most of my testings in the afternoon. Though typically I have around 40 browsers opened on my computer so maybe that degraded the network response time? I have definitely not set fix IPs for any devices and I am not running into any DHCP issues on the router.

On average with the absence of network each Spark.connect() would take about 32 seconds before giving up. I time stamped the connection and checked using serial terminal. Is that normal?

Is there anything new here re providing a timeout for the wifi connection?

I have a sensor system that needs to move from location to location but does not have the physical buttons on the board exposed. As it is now, if a connection fails, currently unusable as user codes are blocked. Ideally, if connection cannot be acheived after a defined period I would like to have it automatically enter listening mode so that new credentials could be suppled via a smartphone.

SYSTEM_THREAD(ENABLED) would be the first suggestion that comes to mind.

ScruffR - thanks. A few additional questions. Will calling WiFi.disconnect() cancel an on going attempt at connecting? If not, will calling WiFi.listen() override a stuck connection effort and switch modes? Is there another strategy? If not the separate system thread may not be as useful as I imagined. Alternatively I was thinking about running in SEMI_AUTOMATIC, and checking the stored credentials against the available networks before issuing a connect. If there is no match then call WiFi.listen. Is that reasonable? Is it better to do that with threading enabled?

Yes to both and for additional ways you can use WiFi.off() (after disconnect) or System.sleep(SLEEP_MODE_DEEP, timeout) or even System.reset().

And if you have no explicit reason against multi threading I'd always go with it especially when also using Listening Mode (to enable your own code to leave LM on demand via WiFi.listen(false))

ScruffR - I seem to have done myself in playing with code for this. The code cleared the credentials and then went into listening mode as expected, however I haven't been able to load new credentials using the Particle CLI, even though it is blinking blue. It shows up on the wifi as Photon-XXX. If I first go into safe mode which then automatically changes to listening mode, it can scan for networks, but fails

Requesting public key from the device...
Setting the magical cloud claim code...
Telling the Photon to apply your Wi-Fi configuration...
The Photon will now attempt to connect to your Wi-Fi network...
! It doesn't look like your Photon has made it to the cloud yet.

Trying the reconfigure after this doesn't help. Any suggestions would be appreciated

Clearing credentials needs to be done with care since it by default causes the device to enter Listening Mode when trying to connect to the cloud and without SYSTEM_THREAD(ENABLED) you’ll lose control over the code.

And clearing credentials won’t require you to reclaim the device, so with CLI you’d not use particle setup but particle serial wifi
But personally I’d rather include SoftAP code in my own project and use that instead of CLI.

when I try with particle serial wifi I get the following error on auto detecting the WiFi security.

^C[DAALCM:~] agard% sudo particle serial wifi
? Should I scan for nearby Wi-Fi networks? Yes
? Select the Wi-Fi network with which you wish to connect your device: DAALCMn
? Should I try to auto-detect the wireless security type? Yes
> Detected WPA(PSK/TKIP/TKIP) WPA2(PSK/AES,TKIP/TKIP) security
Attempting to configure Wi-Fi on /dev/cu.usbmodem1411
/usr/local/lib/node_modules/particle-cli/commands/SerialCommand.js:1231
				return cb(security + '\n', startTimeout.bind(self, 10000));
				       ^

TypeError: cb is not a function
    at parseSecurityType (/usr/local/lib/node_modules/particle-cli/commands/SerialCommand.js:1231:12)
    at SerialTrigger.serialDataCallback (/usr/local/lib/node_modules/particle-cli/oldlib/SerialTrigger.js:66:5)
    at emitOne (events.js:101:20)
    at SerialPort.emit (events.js:188:7)
    at whenBored (/usr/local/lib/node_modules/particle-cli/oldlib/SerialBoredParser.js:32:12)
    at Timeout._onTimeout (/usr/local/lib/node_modules/particle-cli/oldlib/SerialBoredParser.js:39:5)
    at ontimeout (timers.js:386:14)
    at tryOnTimeout (timers.js:250:5)
    at Timer.listOnTimeout (timers.js:214:5)

If I say no, then it never asks for security or password but goes into an endless wait

? Should I try to auto-detect the wireless security type? No
Attempting to configure Wi-Fi on /dev/cu.usbmodem1411

also fails if I say no to searching for networks to do a manual try

BTW in my code I had used SYSTEM_THREAD(ENABLED) as you had suggested, but I thought there would be an easy way to supply new credentials in the field

For such a newbie as myself, is there a good way to try SoftAP? Given the long list of mods/comments listed on the BB, I wasn’t sure where a final working version is, or even how to use. What runs on the browser?

Lastly I show my code fragment at the end. Thanks!

WiFiAccessPoint apCredentials[5];
WiFiAccessPoint apFound[20];
int nCredentials = 0;
int nSSIDs = 0;
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);

void setup() {
    Serial.begin(115200);
    WiFi.clearCredentials();  
    niceConnect();
    Serial.println("Done connect");
}

void loop() 
{
}

boolean niceConnect(){
    WiFi.on();
    waitFor(WiFi.ready,10000);
    if (WiFi.ready()) {
        Serial.println("Wifi ready");
    } else {
        Serial.println("Wifi failed to connect, going into listening mode");
        WiFi.setListenTimeout(5*60);
        WiFi.listen();
    }
    	//  need to fix 
      // this needs a waitFor then turn listening off and maybe wifi off
      // could also consider loading in a default set of credentials 
      //
    if (WiFi.listening()) { 
        Serial.println("Failed to get new credentials");
        return false;
    } else waitFor(WiFi.ready,10000);
    if (WiFi.ready()) {
        Serial.println("Wifi connected");
        return true;
    }
    Serial.println("Wifi failed to connect");

    return false;
}

There seems to be a problem with CLI 1.23.0
Try reverting to 1.22.0

Your code has some weak spots :wink:

  1. I’d not clear and reset credentials on each startup
  2. after WiFi.on() you will never get a WiFi.ready() result as this can only happen after a WiFi.connect()
  3. after WiFi.clearCredentials() not even a WiFi.connect() would ever reach WiFi.ready() state since there are no credentials to connect

The code in the docs here runs as is, but for your purpose you probably want to add SYSTEM_THREAD(ENABLED)
https://docs.particle.io/reference/firmware/photon/#complete-example

To test SoftAP

  • put the device in Listening Mode
  • connect your WiFi enabled device to the AP with the SSID like Photon-XXXX
  • on your device browse to 192.168.0.1 (or 192.168.0.1/index)
  • follow the self explanatory web pages

Many Many thanks. Reverting the CLI worked like a charm and I quite like the SoftAP - definitely nicer than using CLI. David

ScruffR
Following on from your previous help, I am trying to build a nice WiFi connect procedure for remotely located photons. The idea is to be in semi_automatic mode with system thread enabled and then

  1. read Photon WiFi credentials
  2. if not already present, add a likely remote credential
  3. try to connect
  4. if fails to connect, then set to listening mode and start SoftAP
  5. connect with new credentials
  6. do Particle.connect & Particle.process —[it seems this is necessary, is that correct?
    I prefer semi_automatic as I can better control that all the network stuff is complete before executing the rest of my code.
    My code (below) works in Automatic mode, but when I try Semi_Automatic mode
    it crashes (flashing red light and repeated rebooting) when it tries to scan for existing SSIDs:
    nSSIDs = WiFi.scan(apFound, 20);

My apologies for imposing, but I would appreciate any suggestions

// This #include statement was automatically added by the Particle IDE.
#include "sh1106.h"

#include "Particle.h"
#include "softap_http.h"
#define ADD_SSID "DAALCMGuest1"
#define ADD_PASSWD "daalcm123"

struct Page
{
    const char* url;
    const char* mime_type;
    const char* data;
};

const char index_html[] = "<!DOCTYPE html><html><head><meta name='viewport' content='width=device-width, initial-scale=1'><title>Setup your device</title><link rel='stylesheet' type='text/css' href='style.css'></head><body><h1>Connect me to your WiFi!</h1><h3>My device ID:</h3><input type=text id='device-id' size='25' value='' disabled/><button type='button' class='input-helper' id='copy-button'>Copy</button><div id='scan-div'><h3>Scan for visible WiFi networks</h3><button id='scan-button' type='button'>Scan</button></div><div id='networks-div'></div><div id='connect-div' style='display: none'><p>Don't see your network? Move me closer to your router, then re-scan.</p><form id='connect-form'><input type='password' id='password' size='25' placeholder='password'/><button type='button' class='input-helper' id='show-button'>Show</button><button type='submit' id='connect-button'>Connect</button></form></div><script src='rsa-utils/jsbn_1.js'></script><script src='rsa-utils/jsbn_2.js'></script><script src='rsa-utils/prng4.js'></script><script src='rsa-utils/rng.js'></script><script src='rsa-utils/rsa.js'></script><script src='script.js'></script></body></html>";
const char rsa_js[] = "function parseBigInt(a,b){return new BigInteger(a,b);}function linebrk(a,b){var c='';var d=0;while(d+b<a.length){c+=a.substring(d,d+b)+'\\n';d+=b;}return c+a.substring(d,a.length);}function byte2Hex(a){if(a<0x10)return '0'+a.toString(16);else return a.toString(16);}function pkcs1pad2(a,b){if(b<a.length+11){alert('Message too long for RSA');return null;}var c=new Array();var d=a.length-1;while(d>=0&&b>0){var e=a.charCodeAt(d--);if(e<128)c[--b]=e;else if((e>127)&&(e<2048)){c[--b]=(e&63)|128;c[--b]=(e>>6)|192;}else{c[--b]=(e&63)|128;c[--b]=((e>>6)&63)|128;c[--b]=(e>>12)|224;}}c[--b]=0;var f=new SecureRandom();var g=new Array();while(b>2){g[0]=0;while(g[0]==0)f.nextBytes(g);c[--b]=g[0];}c[--b]=2;c[--b]=0;return new BigInteger(c);}function RSAKey(){this.n=null;this.e=0;this.d=null;this.p=null;this.q=null;this.dmp1=null;this.dmq1=null;this.coeff=null;}function RSASetPublic(a,b){if(a!=null&&b!=null&&a.length>0&&b.length>0){this.n=parseBigInt(a,16);this.e=parseInt(b,16);}else alert('Invalid RSA public key');}function RSADoPublic(a){return a.modPowInt(this.e,this.n);}function RSAEncrypt(a){var b=pkcs1pad2(a,(this.n.bitLength()+7)>>3);if(b==null)return null;var c=this.doPublic(b);if(c==null)return null;var d=c.toString(16);if((d.length&1)==0)return d;else return '0'+d;}RSAKey.prototype.doPublic=RSADoPublic;RSAKey.prototype.setPublic=RSASetPublic;RSAKey.prototype.encrypt=RSAEncrypt;";
const char style_css[] = "html{height:100%;margin:auto;background-color:white}body{box-sizing:border-box;min-height:100%;padding:20px;background-color:#1aabe0;font-family:'Lucida Sans Unicode','Lucida Grande',sans-serif;font-weight:normal;color:white;margin-top:0;margin-left:auto;margin-right:auto;margin-bottom:0;max-width:400px;text-align:center;border:1px solid #6e6e70;border-radius:4px}div{margin-top:25px;margin-bottom:25px}h1{margin-top:25px;margin-bottom:25px}button{border-color:#1c75be;background-color:#1c75be;color:white;border-radius:5px;height:30px;font-size:15px;font-weight:bold}button.input-helper{background-color:#bebebe;border-color:#bebebe;color:#6e6e70;margin-left:3px}button:disabled{background-color:#bebebe;border-color:#bebebe;color:white}input[type='text'],input[type='password']{background-color:white;color:#6e6e70;border-color:white;border-radius:5px;height:25px;text-align:center;font-size:15px}input:disabled{background-color:#bebebe;border-color:#bebebe}input[type='radio']{position:relative;bottom:-0.33em;margin:0;border:0;height:1.5em;width:15%}label{padding-top:7px;padding-bottom:7px;padding-left:5%;display:inline-block;width:80%;text-align:left}input[type='radio']:checked+label{font-weight:bold;color:#1c75be}.scanning-error{font-weight:bold;text-align:center}.radio-div{box-sizing:border-box;margin:2px;margin-left:auto;margin-right:auto;background-color:white;color:#6e6e70;border:1px solid #6e6e70;border-radius:3px;width:100%;padding:5px}#networks-div{margin-left:auto;margin-right:auto;text-align:left}#device-id{text-align:center}#scan-button{min-width:100px}#connect-button{display:block;min-width:100px;margin-top:10px;margin-left:auto;margin-right:auto;margin-bottom:20px}#password{margin-top:20px;margin-bottom:10px}";
const char rng_js[] = "var rng_state;var rng_pool;var rng_pptr;function rng_seed_int(a){rng_pool[rng_pptr++]^=a&255;rng_pool[rng_pptr++]^=(a>>8)&255;rng_pool[rng_pptr++]^=(a>>16)&255;rng_pool[rng_pptr++]^=(a>>24)&255;if(rng_pptr>=rng_psize)rng_pptr-=rng_psize;}function rng_seed_time(){rng_seed_int(new Date().getTime());}if(rng_pool==null){rng_pool=new Array();rng_pptr=0;var t;if(window.crypto&&window.crypto.getRandomValues){var ua=new Uint8Array(32);window.crypto.getRandomValues(ua);for(t=0;t<32;++t)rng_pool[rng_pptr++]=ua[t];}if(navigator.appName=='Netscape'&&navigator.appVersion<'5'&&window.crypto){var z=window.crypto.random(32);for(t=0;t<z.length;++t)rng_pool[rng_pptr++]=z.charCodeAt(t)&255;}while(rng_pptr<rng_psize){t=Math.floor(65536*Math.random());rng_pool[rng_pptr++]=t>>>8;rng_pool[rng_pptr++]=t&255;}rng_pptr=0;rng_seed_time();}function rng_get_byte(){if(rng_state==null){rng_seed_time();rng_state=prng_newstate();rng_state.init(rng_pool);for(rng_pptr=0;rng_pptr<rng_pool.length;++rng_pptr)rng_pool[rng_pptr]=0;rng_pptr=0;}return rng_state.next();}function rng_get_bytes(a){var b;for(b=0;b<a.length;++b)a[b]=rng_get_byte();}function SecureRandom(){}SecureRandom.prototype.nextBytes=rng_get_bytes;";
const char jsbn_2_js[] = "function bnpRShiftTo(a,b){b.s=this.s;var c=Math.floor(a/this.DB);if(c>=this.t){b.t=0;return;}var d=a%this.DB;var e=this.DB-d;var f=(1<<d)-1;b[0]=this[c]>>d;for(var g=c+1;g<this.t;++g){b[g-c-1]|=(this[g]&f)<<e;b[g-c]=this[g]>>d;}if(d>0)b[this.t-c-1]|=(this.s&f)<<e;b.t=this.t-c;b.clamp();}function bnpSubTo(a,b){var c=0,d=0,e=Math.min(a.t,this.t);while(c<e){d+=this[c]-a[c];b[c++]=d&this.DM;d>>=this.DB;}if(a.t<this.t){d-=a.s;while(c<this.t){d+=this[c];b[c++]=d&this.DM;d>>=this.DB;}d+=this.s;}else{d+=this.s;while(c<a.t){d-=a[c];b[c++]=d&this.DM;d>>=this.DB;}d-=a.s;}b.s=(d<0)?-1:0;if(d<-1)b[c++]=this.DV+d;else if(d>0)b[c++]=d;b.t=c;b.clamp();}function bnpMultiplyTo(a,b){var c=this.abs(),d=a.abs();var e=c.t;b.t=e+d.t;while(--e>=0)b[e]=0;for(e=0;e<d.t;++e)b[e+c.t]=c.am(0,d[e],b,e,0,c.t);b.s=0;b.clamp();if(this.s!=a.s)BigInteger.ZERO.subTo(b,b);}function bnpSquareTo(a){var b=this.abs();var c=a.t=2*b.t;while(--c>=0)a[c]=0;for(c=0;c<b.t-1;++c){var d=b.am(c,b[c],a,2*c,0,1);if((a[c+b.t]+=b.am(c+1,2*b[c],a,2*c+1,d,b.t-c-1))>=b.DV){a[c+b.t]-=b.DV;a[c+b.t+1]=1;}}if(a.t>0)a[a.t-1]+=b.am(c,b[c],a,2*c,0,1);a.s=0;a.clamp();}function bnpDivRemTo(a,b,c){var d=a.abs();if(d.t<=0)return;var e=this.abs();if(e.t<d.t){if(b!=null)b.fromInt(0);if(c!=null)this.copyTo(c);return;}if(c==null)c=nbi();var f=nbi(),g=this.s,h=a.s;var i=this.DB-nbits(d[d.t-1]);if(i>0){d.lShiftTo(i,f);e.lShiftTo(i,c);}else{d.copyTo(f);e.copyTo(c);}var j=f.t;var k=f[j-1];if(k==0)return;var l=k*(1<<this.F1)+((j>1)?f[j-2]>>this.F2:0);var m=this.FV/l,n=(1<<this.F1)/l,o=1<<this.F2;var p=c.t,q=p-j,r=(b==null)?nbi():b;f.dlShiftTo(q,r);if(c.compareTo(r)>=0){c[c.t++]=1;c.subTo(r,c);}BigInteger.ONE.dlShiftTo(j,r);r.subTo(f,f);while(f.t<j)f[f.t++]=0;while(--q>=0){var s=(c[--p]==k)?this.DM:Math.floor(c[p]*m+(c[p-1]+o)*n);if((c[p]+=f.am(0,s,c,q,0,j))<s){f.dlShiftTo(q,r);c.subTo(r,c);while(c[p]<--s)c.subTo(r,c);}}if(b!=null){c.drShiftTo(j,b);if(g!=h)BigInteger.ZERO.subTo(b,b);}c.t=j;c.clamp();if(i>0)c.rShiftTo(i,c);if(g<0)BigInteger.ZERO.subTo(c,c);}function bnMod(a){var b=nbi();this.abs().divRemTo(a,null,b);if(this.s<0&&b.compareTo(BigInteger.ZERO)>0)a.subTo(b,b);return b;}function Classic(a){this.m=a;}function cConvert(a){if(a.s<0||a.compareTo(this.m)>=0)return a.mod(this.m);else return a;}function cRevert(a){return a;}function cReduce(a){a.divRemTo(this.m,null,a);}function cMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c);}function cSqrTo(a,b){a.squareTo(b);this.reduce(b);}Classic.prototype.convert=cConvert;Classic.prototype.revert=cRevert;Classic.prototype.reduce=cReduce;Classic.prototype.mulTo=cMulTo;Classic.prototype.sqrTo=cSqrTo;function bnpInvDigit(){if(this.t<1)return 0;var a=this[0];if((a&1)==0)return 0;var b=a&3;b=(b*(2-(a&0xf)*b))&0xf;b=(b*(2-(a&0xff)*b))&0xff;b=(b*(2-(((a&0xffff)*b)&0xffff)))&0xffff;b=(b*(2-a*b%this.DV))%this.DV;return(b>0)?this.DV-b:-b;}function Montgomery(a){this.m=a;this.mp=a.invDigit();this.mpl=this.mp&0x7fff;this.mph=this.mp>>15;this.um=(1<<(a.DB-15))-1;this.mt2=2*a.t;}function montConvert(a){var b=nbi();a.abs().dlShiftTo(this.m.t,b);b.divRemTo(this.m,null,b);if(a.s<0&&b.compareTo(BigInteger.ZERO)>0)this.m.subTo(b,b);return b;}function montRevert(a){var b=nbi();a.copyTo(b);this.reduce(b);return b;}function montReduce(a){while(a.t<=this.mt2)a[a.t++]=0;for(var b=0;b<this.m.t;++b){var c=a[b]&0x7fff;var d=(c*this.mpl+(((c*this.mph+(a[b]>>15)*this.mpl)&this.um)<<15))&a.DM;c=b+this.m.t;a[c]+=this.m.am(0,d,a,b,0,this.m.t);while(a[c]>=a.DV){a[c]-=a.DV;a[++c]++;}}a.clamp();a.drShiftTo(this.m.t,a);if(a.compareTo(this.m)>=0)a.subTo(this.m,a);}function montSqrTo(a,b){a.squareTo(b);this.reduce(b);}function montMulTo(a,b,c){a.multiplyTo(b,c);this.reduce(c);}Montgomery.prototype.convert=montConvert;Montgomery.prototype.revert=montRevert;Montgomery.prototype.reduce=montReduce;Montgomery.prototype.mulTo=montMulTo;Montgomery.prototype.sqrTo=montSqrTo;function bnpIsEven(){return((this.t>0)?(this[0]&1):this.s)==0;}function bnpExp(a,b){if(a>0xffffffff||a<1)return BigInteger.ONE;var c=nbi(),d=nbi(),e=b.convert(this),f=nbits(a)-1;e.copyTo(c);while(--f>=0){b.sqrTo(c,d);if((a&(1<<f))>0)b.mulTo(d,e,c);else{var g=c;c=d;d=g;}}return b.revert(c);}function bnModPowInt(a,b){var c;if(a<256||b.isEven())c=new Classic(b);else c=new Montgomery(b);return this.exp(a,c);}BigInteger.prototype.copyTo=bnpCopyTo;BigInteger.prototype.fromInt=bnpFromInt;BigInteger.prototype.fromString=bnpFromString;BigInteger.prototype.clamp=bnpClamp;BigInteger.prototype.dlShiftTo=bnpDLShiftTo;BigInteger.prototype.drShiftTo=bnpDRShiftTo;BigInteger.prototype.lShiftTo=bnpLShiftTo;BigInteger.prototype.rShiftTo=bnpRShiftTo;BigInteger.prototype.subTo=bnpSubTo;BigInteger.prototype.multiplyTo=bnpMultiplyTo;BigInteger.prototype.squareTo=bnpSquareTo;BigInteger.prototype.divRemTo=bnpDivRemTo;BigInteger.prototype.invDigit=bnpInvDigit;BigInteger.prototype.isEven=bnpIsEven;BigInteger.prototype.exp=bnpExp;BigInteger.prototype.toString=bnToString;BigInteger.prototype.negate=bnNegate;BigInteger.prototype.abs=bnAbs;BigInteger.prototype.compareTo=bnCompareTo;BigInteger.prototype.bitLength=bnBitLength;BigInteger.prototype.mod=bnMod;BigInteger.prototype.modPowInt=bnModPowInt;BigInteger.ZERO=nbv(0);BigInteger.ONE=nbv(1);";
const char jsbn_1_js[] = "var dbits;var canary=0xdeadbeefcafe;var j_lm=((canary&0xffffff)==0xefcafe);function BigInteger(a,b,c){if(a!=null)if('number'==typeof a)this.fromNumber(a,b,c);else if(b==null&&'string'!=typeof a)this.fromString(a,256);else this.fromString(a,b);}function nbi(){return new BigInteger(null);}function am1(a,b,c,d,e,f){while(--f>=0){var g=b*this[a++]+c[d]+e;e=Math.floor(g/0x4000000);c[d++]=g&0x3ffffff;}return e;}function am2(a,b,c,d,e,f){var g=b&0x7fff,h=b>>15;while(--f>=0){var i=this[a]&0x7fff;var j=this[a++]>>15;var k=h*i+j*g;i=g*i+((k&0x7fff)<<15)+c[d]+(e&0x3fffffff);e=(i>>>30)+(k>>>15)+h*j+(e>>>30);c[d++]=i&0x3fffffff;}return e;}function am3(a,b,c,d,e,f){var g=b&0x3fff,h=b>>14;while(--f>=0){var i=this[a]&0x3fff;var j=this[a++]>>14;var k=h*i+j*g;i=g*i+((k&0x3fff)<<14)+c[d]+e;e=(i>>28)+(k>>14)+h*j;c[d++]=i&0xfffffff;}return e;}if(j_lm&&(navigator.appName=='Microsoft Internet Explorer')){BigInteger.prototype.am=am2;dbits=30;}else if(j_lm&&(navigator.appName!='Netscape')){BigInteger.prototype.am=am1;dbits=26;}else{BigInteger.prototype.am=am3;dbits=28;}BigInteger.prototype.DB=dbits;BigInteger.prototype.DM=((1<<dbits)-1);BigInteger.prototype.DV=(1<<dbits);var BI_FP=52;BigInteger.prototype.FV=Math.pow(2,BI_FP);BigInteger.prototype.F1=BI_FP-dbits;BigInteger.prototype.F2=2*dbits-BI_FP;var BI_RM='0123456789abcdefghijklmnopqrstuvwxyz';var BI_RC=new Array();var rr,vv;rr='0'.charCodeAt(0);for(vv=0;vv<=9;++vv)BI_RC[rr++]=vv;rr='a'.charCodeAt(0);for(vv=10;vv<36;++vv)BI_RC[rr++]=vv;rr='A'.charCodeAt(0);for(vv=10;vv<36;++vv)BI_RC[rr++]=vv;function int2char(a){return BI_RM.charAt(a);}function intAt(a,b){var c=BI_RC[a.charCodeAt(b)];return(c==null)?-1:c;}function bnpCopyTo(a){for(var b=this.t-1;b>=0;--b)a[b]=this[b];a.t=this.t;a.s=this.s;}function bnpFromInt(a){this.t=1;this.s=(a<0)?-1:0;if(a>0)this[0]=a;else if(a<-1)this[0]=a+this.DV;else this.t=0;}function nbv(a){var b=nbi();b.fromInt(a);return b;}function bnpFromString(a,b){var c;if(b==16)c=4;else if(b==8)c=3;else if(b==256)c=8;else if(b==2)c=1;else if(b==32)c=5;else if(b==4)c=2;else{this.fromRadix(a,b);return;}this.t=0;this.s=0;var d=a.length,e=false,f=0;while(--d>=0){var g=(c==8)?a[d]&0xff:intAt(a,d);if(g<0){if(a.charAt(d)=='-')e=true;continue;}e=false;if(f==0)this[this.t++]=g;else if(f+c>this.DB){this[this.t-1]|=(g&((1<<(this.DB-f))-1))<<f;this[this.t++]=(g>>(this.DB-f));}else this[this.t-1]|=g<<f;f+=c;if(f>=this.DB)f-=this.DB;}if(c==8&&(a[0]&0x80)!=0){this.s=-1;if(f>0)this[this.t-1]|=((1<<(this.DB-f))-1)<<f;}this.clamp();if(e)BigInteger.ZERO.subTo(this,this);}function bnpClamp(){var a=this.s&this.DM;while(this.t>0&&this[this.t-1]==a)--this.t;}function bnToString(a){if(this.s<0)return '-'+this.negate().toString(a);var b;if(a==16)b=4;else if(a==8)b=3;else if(a==2)b=1;else if(a==32)b=5;else if(a==4)b=2;else return this.toRadix(a);var c=(1<<b)-1,d,e=false,f='',g=this.t;var h=this.DB-(g*this.DB)%b;if(g-->0){if(h<this.DB&&(d=this[g]>>h)>0){e=true;f=int2char(d);}while(g>=0){if(h<b){d=(this[g]&((1<<h)-1))<<(b-h);d|=this[--g]>>(h+=this.DB-b);}else{d=(this[g]>>(h-=b))&c;if(h<=0){h+=this.DB;--g;}}if(d>0)e=true;if(e)f+=int2char(d);}}return e?f:'0';}function bnNegate(){var a=nbi();BigInteger.ZERO.subTo(this,a);return a;}function bnAbs(){return(this.s<0)?this.negate():this;}function bnCompareTo(a){var b=this.s-a.s;if(b!=0)return b;var c=this.t;b=c-a.t;if(b!=0)return(this.s<0)?-b:b;while(--c>=0)if((b=this[c]-a[c])!=0)return b;return 0;}function nbits(a){var b=1,c;if((c=a>>>16)!=0){a=c;b+=16;}if((c=a>>8)!=0){a=c;b+=8;}if((c=a>>4)!=0){a=c;b+=4;}if((c=a>>2)!=0){a=c;b+=2;}if((c=a>>1)!=0){a=c;b+=1;}return b;}function bnBitLength(){if(this.t<=0)return 0;return this.DB*(this.t-1)+nbits(this[this.t-1]^(this.s&this.DM));}function bnpDLShiftTo(a,b){var c;for(c=this.t-1;c>=0;--c)b[c+a]=this[c];for(c=a-1;c>=0;--c)b[c]=0;b.t=this.t+a;b.s=this.s;}function bnpDRShiftTo(a,b){for(var c=a;c<this.t;++c)b[c-a]=this[c];b.t=Math.max(this.t-a,0);b.s=this.s;}function bnpLShiftTo(a,b){var c=a%this.DB;var d=this.DB-c;var e=(1<<d)-1;var f=Math.floor(a/this.DB),g=(this.s<<c)&this.DM,h;for(h=this.t-1;h>=0;--h){b[h+f+1]=(this[h]>>d)|g;g=(this[h]&e)<<c;}for(h=f-1;h>=0;--h)b[h]=0;b[f]=g;b.t=this.t+f+1;b.s=this.s;b.clamp();}";
const char script_js[] = "var base_url='http://192.168.0.1/';var network_list;var public_key;var rsa=new RSAKey();var scanButton=document.getElementById('scan-button');var connectButton=document.getElementById('connect-button');var copyButton=document.getElementById('copy-button');var showButton=document.getElementById('show-button');var deviceID=document.getElementById('device-id');var connectForm=document.getElementById('connect-form');var public_key_callback={success:function(a){console.log('Public key: '+a.b);public_key=a.b;rsa.setPublic(public_key.substring(58,58+256),public_key.substring(318,318+6));},error:function(a,b){console.log(a);window.alert('There was a problem fetching important information from your device. Please verify your connection, then reload this page.');}};var device_id_callback={success:function(a){var b=a.id;deviceID.value=b;},error:function(a,b){console.log(a);var c='COMMUNICATION_ERROR';deviceID.value=c;}};var scan=function(){console.log('Scanning...!');disableButtons();scanButton.innerHTML='Scanning...';connectButton.innerHTML='Connect';document.getElementById('connect-div').style.display='none';document.getElementById('networks-div').style.display='none';getRequest(base_url+'scan-ap',scan_callback);};var scan_callback={success:function(a){network_list=a.scans;console.log('I found:');var b=document.getElementById('networks-div');b.innerHTML='';if(network_list.length>0)for(var c=0;c<network_list.length;c++){ssid=network_list[c].ssid;console.log(network_list[c]);add_wifi_option(b,ssid);document.getElementById('connect-div').style.display='block';}else b.innerHTML='<p class=\\'scanning-error\\'>No networks found.</p>';},error:function(a){console.log('Scanning error:'+a);document.getElementById('networks-div').innerHTML='<p class=\\'scanning-error\\'>Scanning error.</p>';},regardless:function(){scanButton.innerHTML='Re-Scan';enableButtons();document.getElementById('networks-div').style.display='block';}};var configure=function(a){a.preventDefault();var b=get_selected_network();var c=document.getElementById('password').value;if(!b){window.alert('Please select a network!');return false;}var d={idx:0,ssid:b.ssid,pwd:rsa.encrypt(c),sec:b.sec,ch:b.ch};connectButton.innerHTML='Sending credentials...';disableButtons();console.log('Sending credentials: '+JSON.stringify(d));postRequest(base_url+'configure-ap',d,configure_callback);};var configure_callback={success:function(a){console.log('Credentials received.');connectButton.innerHTML='Credentials received...';postRequest(base_url+'connect-ap',{idx:0},connect_callback);},error:function(a,b){console.log('Configure error: '+a);window.alert('The configuration command failed, check that you are still well connected to the device\\'s WiFi hotspot and retry.');connectButton.innerHTML='Retry';enableButtons();}};var connect_callback={success:function(a){console.log('Attempting to connect to the cloud.');connectButton.innerHTML='Attempting to connect...';window.alert('Your device should now start flashing green and attempt to connect to the cloud. This usually takes about 20 seconds, after which it will begin slowly blinking cyan. \\n\\n\\nIf this process fails because you entered the wrong password, the device will flash green indefinitely. In this case, hold the setup button for 6 seconds until the device starts blinking blue again. Then reconnect to the WiFi hotspot it generates and reload this page to try again.');},error:function(a,b){console.log('Connect error: '+a);window.alert('The connect command failed, check that you are still well connected to the device\\'s WiFi hotspot and retry.');connectButton.innerHTML='Retry';enableButtons();}};var disableButtons=function(){connectButton.disabled=true;scanButton.disabled=true;};var enableButtons=function(){connectButton.disabled=false;scanButton.disabled=false;};var add_wifi_option=function(a,b){var c=document.createElement('INPUT');c.type='radio';c.value=b;c.id=b;c.name='ssid';c.className='radio';var d=document.createElement('DIV');d.className='radio-div';d.appendChild(c);var e=document.createElement('label');e.htmlFor=b;e.innerHTML=b;d.appendChild(e);a.appendChild(d);};var get_selected_network=function(){for(var a=0;a<network_list.length;a++){ssid=network_list[a].ssid;if(document.getElementById(ssid).checked)return network_list[a];}};var copy=function(){window.prompt('Copy to clipboard: Ctrl + C, Enter',deviceID.value);};var toggleShow=function(){var a=document.getElementById('password');inputType=a.type;if(inputType==='password'){showButton.innerHTML='Hide';a.type='text';}else{showButton.innerHTML='Show';a.type='password';}};var getRequest=function(a,b){var c=new XMLHttpRequest();c.open('GET',a,true);c.timeout=8000;c.send();c.onreadystatechange=function(){if(c.readyState==4)if(b){if(c.status==200){if(b.success)b.success(JSON.parse(c.responseText));}else if(b.error)b.error(c.status,c.responseText);if(b.regardless)b.regardless();}};};var postRequest=function(a,b,c){var d=JSON.stringify(b);var e=new XMLHttpRequest();e.open('POST',a,true);e.timeout=4000;e.setRequestHeader('Content-Type','multipart/form-data');e.send(d);e.onreadystatechange=function(){if(e.readyState==4)if(c){if(e.status==200){if(c.success)c.success(JSON.parse(e.responseText));}else if(c.error)c.error(e.status,e.responseText);if(c.regardless)c.regardless();}};};if(scanButton.addEventListener){copyButton.addEventListener('click',copy);showButton.addEventListener('click',toggleShow);scanButton.addEventListener('click',scan);connectForm.addEventListener('submit',configure);}else if(scanButton.attachEvent){copyButton.attachEvent('onclick',copy);showButton.attachEvent('onclick',toggleShow);scanButton.attachEvent('onclick',scan);connectForm.attachEvent('onsubmit',configure);}getRequest(base_url+'device-id',device_id_callback);getRequest(base_url+'public-key',public_key_callback);";
const char prng4_js[] = "function Arcfour(){this.i=0;this.j=0;this.S=new Array();}function ARC4init(a){var b,c,d;for(b=0;b<256;++b)this.S[b]=b;c=0;for(b=0;b<256;++b){c=(c+this.S[b]+a[b%a.length])&255;d=this.S[b];this.S[b]=this.S[c];this.S[c]=d;}this.i=0;this.j=0;}function ARC4next(){var a;this.i=(this.i+1)&255;this.j=(this.j+this.S[this.i])&255;a=this.S[this.i];this.S[this.i]=this.S[this.j];this.S[this.j]=a;return this.S[(a+this.S[this.i])&255];}Arcfour.prototype.init=ARC4init;Arcfour.prototype.next=ARC4next;function prng_newstate(){return new Arcfour();}var rng_psize=256;";

Page myPages[] = {
     { "/index.html", "text/html", index_html },
     { "/rsa-utils/rsa.js", "application/javascript", rsa_js },
     { "/style.css", "text/css", style_css },
     { "/rsa-utils/rng.js", "application/javascript", rng_js },
     { "/rsa-utils/jsbn_2.js", "application/javascript", jsbn_2_js },
     { "/rsa-utils/jsbn_1.js", "application/javascript", jsbn_1_js },
     { "/script.js", "application/javascript", script_js },
     { "/rsa-utils/prng4.js", "application/javascript", prng4_js },
     { nullptr }
};

void myPage(const char* url, ResponseCallback* cb, void* cbArg, Reader* body, Writer* result, void* reserved)
{
    Serial.printlnf("handling page %s", url);

    if (strcmp(url,"/index")==0) {
        Serial.println("sending redirect");
        Header h("Location: /index.html\r\n");
        cb(cbArg, 0, 301, "text/plain", &h);
        return;
    }

    int8_t idx = 0;
    for (;;idx++) {
        Page& p = myPages[idx];
        if (!p.url) {
            idx = -1;
            break;
        }
        else if (strcmp(url, p.url)==0) {
            break;
        }
    }

    if (idx==-1) {
        cb(cbArg, 0, 404, nullptr, nullptr);
    }
    else {
        cb(cbArg, 0, 200, myPages[idx].mime_type, nullptr);
        result->write(myPages[idx].data);
    }
}


// Press SETUP for 3 seconds to make the Photon enter Listening mode
// Navigate to http://192.168.0.1 to setup Wi-Fi

// Include the rest of your application below,
// including your setup and loop functions

SYSTEM_MODE(AUTOMATIC);
SYSTEM_THREAD(ENABLED);
STARTUP(softap_set_application_page_handler(myPage, nullptr));

sh1106_lcd *oled = NULL;

WiFiAccessPoint apCredentials[5];
WiFiAccessPoint apFound[20];
int nCredentials = 0;
int nSSIDs = 0;

void setup() {
    Serial.begin(115200);

    oled = sh1106_lcd::getInstance();
    oled->ClearScreen();
    oled->PrintLine("starting...");
    oled->PrintLine("trying to connect");
    oled->Show();
    if (niceWiFiConnect(true,true)) {
        Serial.println("after nice connect");
        Particle.connect();
        Serial.println("after Particle connect");
    }
    Particle.process();
    Serial.println("after Particle process");
    Serial.println("Done connect");
}

void loop() {
    Serial.println("looping");
    delay(2000);
}


 boolean niceWiFiConnect(boolean addSSID, boolean display){
   
    delay(1500);
    Serial.println("before WiFi on");
    WiFi.on();
    Serial.println("after WiFi on");
    if (canConnect(addSSID, display)) {
        Serial.println("before connect");
        WiFi.connect();
        Serial.println("after WiFi connect");
        waitFor(WiFi.ready,10000);
        Serial.println("after 1st WiFi ready wait");
        if (WiFi.ready()) {
            Serial.println("Wifi ready");
            if (display) {
                oled->PrintLine("Wifi connected");
                oled->PrintLine("now using SSID:");
                oled->PrintLine((char *)WiFi.SSID());
                oled->Show();
            }
            return true;
        }
    }
    Serial.println("Wifi failed to connect, going into listening mode");
    Serial.println("Set network to Photon-XX and browser to 192.168.0.1");
    WiFi.listen();
    if (display) {
        oled->ClearScreen();
        oled->PrintLine("Connection failed");
        oled->PrintLine("Set network to Photon-XX");
        oled->PrintLine("Set Browser to");
        oled->PrintLine("192.168.0.1");
        oled->PrintLine("enter credentials");
        oled->Show();
    }
    WiFi.connect();
    delay(1000);
    waitFor(WiFi.ready,300*1000);
    if (WiFi.ready()) {
        Serial.println("Wifi connected");
        Particle.connect();
        Particle.process();
        if (display) {
            oled->ClearScreen();
            oled->PrintLine("Wifi connected");
            oled->PrintLine("now using SSID:");
            oled->PrintLine((char *)WiFi.SSID());
            oled->Show();
        }
        return true;
    }
    WiFi.listen(false);
    Serial.println("Wifi failed to connect");
    if (display) {
        oled->PrintLine("Connection failed");
        oled->Show();
    }
    Particle.process();
    return false;
}



boolean canConnect(boolean addssid, boolean display) {
    WiFiAccessPoint apCredentials[5];
    WiFiAccessPoint apFound[20];
    int nCredentials,nSSIDs;
    boolean add;
    
    Serial.print("checking Credentials on device ");
    Serial.println(addssid);
    nCredentials = WiFi.getCredentials(apCredentials, 5);
    if (strlen(ADD_SSID) >1) add = addssid;
    else add = false;
    for (int i = 0; i < nCredentials; i++) {
        Serial.print("Credentials on device, ssid: ");
        Serial.println(apCredentials[i].ssid);
        if (!addssid | (strcmp(ADD_SSID,apCredentials[i].ssid)==0)) add = false;
    }
    if (add) {
        Serial.print("adding ssid= "); Serial.println(ADD_SSID);
        WiFi.setCredentials(ADD_SSID, ADD_PASSWD);
        if (display){
            oled->PrintLine("adding SSID:");
            oled->PrintLine(ADD_SSID);
            oled->Show();
            delay(1000);
        }
    }
    Serial.println("before scanning ssids");
    nSSIDs = WiFi.scan(apFound, 20);
    Serial.print("scanning ssids= "); Serial.println(nSSIDs);
    for (int i = 0; i < nSSIDs; i++) {
        Serial.print("ssid: ");
        Serial.println(apFound[i].ssid);
    }
    nCredentials = WiFi.getCredentials(apCredentials, 5);
    for (int j = 0; j < nCredentials; j++) {
        for (int i = 0; i < nSSIDs; i++) {
            if (strcmp(apFound[i].ssid, apCredentials[j].ssid)==0) {
                Serial.print("ssid match ");Serial.println(apCredentials[j].ssid);
                return true;
            } 
        }
    }
    Serial.println(" No ssid match found");
    if (display){
        oled->PrintLine("No ssid match found:");
        oled->Show();
        delay(1500);
    }
    return false;
}

In order to use most of the WiFi.xxxx() commands you need to call WiFi.on() first in non-AUTOMATIC modes.

I do that first thing, followed by getCredentials, setCredential,then scan where it dies in semi_automatic but not automatic modes

boolean canConnect(boolean addssid, boolean display)
  WiFiAccessPoint apCredentials[5];
   WiFiAccessPoint apFound[20];
   int nCredentials,nSSIDs;
   boolean add;
        
nCredentials = WiFi.getCredentials(apCredentials, 5);
    if (strlen(ADD_SSID) >1) add = addssid;
    else add = false;
    for (int i = 0; i < nCredentials; i++) {
        Serial.print("Credentials on device, ssid: ");
        Serial.println(apCredentials[i].ssid);
        if (!addssid | (strcmp(ADD_SSID,apCredentials[i].ssid)==0)) add = false;
    }
    if (add) {
        WiFi.setCredentials(ADD_SSID, ADD_PASSWD);
    }
    nSSIDs = WiFi.scan(apFound, 20);

If you wrap your code in a set of these it’ll be much more readable (I’ve altered your posts accordingly already)

 ```cpp
 // your code goes here

What system version are you targeting?

Also timing might play a role here. The way you are constructing the `sh1106_lcd` instance seems a bit unconventional (although I haven't looked into the libray).
So the slight delay introduced on startup in AUTOMATIC mode might also play a role.

Also
[quote="agard, post:33, topic:14089"]
```cpp
if (!addssid | (strcmp(ADD_SSID,apCredentials[i].ssid)==0)) add = false;

[/quote]
do you really want a binary OR (|) here or not rather a boolean OR (||)?

Just for safe measure, I’d add this after the WiFi.setCredentials() call

  Particle.process();
  WiFi.off();
  delay(500);
  WiFi.on();
  Particle.process();
  delay(1000);

This may not be required but putting it there removes did help another member to get creds stored while without it the creds just didn’t stick.

You also have got a set of local variables which will hide the global versions of the same. Is this intended?

WiFiAccessPoint apCredentials[5];
WiFiAccessPoint apFound[20];
int nCredentials = 0;
int nSSIDs = 0;

BTW, do you deduce that the position of the crash from the serial print output?
The problem with that is that these are asynchronous tasks and hence the actual crash might happen a bit further down in your code but the USB task just wasn’t able to send the data before the actual crash.
Adding some delay statements after each print might help against that.


Update:
@agard, I’ve just flashed your code (with all the oled stuff commented out and the suggested corrections) and I can’t see a crash in SEMI_AUTOMATIC mode.
What exact circumstances did you have when you saw the SOS?

To revisit the timing a bit, in order to get a successful WiFi.scan() I also had to add a delay(500); before the scan instruction.

ScruffR - the delays did the trick. My System is 0.6.2 , so that shouldn’t have been an issue. Re the odd sh1106 instance, I was just following the examples with the library code. I would much rather have a version of the nice Adafruit_SSD1306 library that would work with the 1106 controller. Many thanks.

1 Like

@peekay123 The documentation does say SYS_MANUAL Mode is sure way to give lots of room to hang yourself, because it provides great power. So I use SEMI_AUTOMATIC Mode and
void loop() {
// Wait a few seconds between measurements.
delay(2000);
And it does take a long time to display the readings, Should I remove the delay or put it in MANUAL Mode first.

@Mapoint,

Can you provide a little more context to your question?

What display? What readings?

Here is the Circuit Diagram

How do I power the photon 3.7V and Nextion 5V so it works with Battery only
I need to demo this for a public display and there is no power outlet there so I need to power up
Is it possible to run external source of power that comes as 5V through the micro USB without the laptop By the way the photon is in SEMI-AUTOMATIC mode because default Mode will first try and look for Internet connection and if no Internet connection the Photon will not call the rest of the function for processing.

Thanks