I am using the SEMI_AUTOMATIC mode and would like to be able to show various connection states via an external LED. For the most part, this is doable by observing WiFi.ready(), WiFi.connecting(), and Spark.connected(). However, there seems to be no way to anticipate the core going into the smart config mode if no credentials are stored in the CC3000.
Specifically, I would expect to be able to call WiFi.hasCredentials() before doing WiFi.connect() to figure out if the smart config mode will get triggered. But the hasCredentials method seems to rely on reading a value from the NVMEM_Spark_File_Data variable, which does not get populated with the profile data stored in the CC3000 non-volatile memory until WiFi.connect() is called:
And of course, once WiFi.connect() is called and it goes into the smart config mode, the user code is blocked indefinitely, so WiFi.listening() is kind of useless.
Is there a different way to achieve what I am trying to do, or does this require a firmware change? And if so, is there a plan to address this in the future?
Perhaps it would be possible to add a WiFi.tryConnect() method that would return different values depending on the configuration of the CC3000. Something like:
if (WiFi.tryConnect() == WIFI_NO_CREDENTIALS) {
// user code to toggle external LED to indicate credentials are needed
WiFi.listen();
}
I am pretty new to C++, so apologies in advance for “suboptimal” code suggestion
Maybe @zachary or @kennethlimcp can comment here. I thought that design was such that you are meant to call WiFi.setCredentials() with SSID/password strings if you always want to associate with a particular AP or APs.
Note that the strings could be hard-coded or they could come from non-volatile storage on the Spark. I can imagine having a way to update or add to the non-volative SSID/password storage over the air, preferably via Spark.function() since any other way you would have to send them in the clear or have your own encryption scheme.
If you cannot test if you hasCredentials() before WiFi.connect()ing then in certain forseeable situations you are in a deadly embrace.
E.g. What if you only want to setCredentials() if you have not already done so? E.g. the user code contains a failsafe config only to be used if and only if no other credentials have been set.
The current situation is best seen as a bug. And one which possibly could be more easily fixed in code than it might be explained away or excused in text
Looking at my own code, I call WiFi.setCredentials() before calling WiFi.connect() and it works fine. The only surprise for me was that I had to increase my timeout value of the loop after connect looking for WiFi.ready() to be true to be 20 seconds. 10 seconds was not always enough.
Maybe I’m not fully understanding the problem, but to avoid listening mode, have you tried first calling Wifi.hasCredentials() and if it returns false don’t bother calling Wifi.connect()? AFAIK, will achieve the same result as your tryConnect() suggestion.
My understanding of the original question was for the opposite of this - to not enter listening mode but rather simply not connect so that the “not connected and no credentials” status can be displayed.
@mdma This is exactly what I’ve tried, but my observation is that hasCredentials() will ALWAYS RETURN FALSE regardless of whether you have credentials stored or not UNTIL WiFi.connect() has been called.
To put it differently, this code will ALWAYS trigger listen mode:
SYSTEM_MODE(SEMI_AUTOMATIC);
void setup(){
if(!WiFi.hasCredentials()){
//light up led
WiFi.listen();
}
}
but this code will work correctly:
SYSTEM_MODE(SEMI_AUTOMATIC);
void setup(){
WiFi.connect();
WiFi.disconnect();
if(!WiFi.hasCredentials()){
//light up led
WiFi.listen();
}
}
Except of course it would not, if you didn’t have credentials stored, because .connect() will put it in listening mode, so it will block until credentials are supplied via USB or Smart Config App.
The reason it @mdma 's code does not work is because hasCredentials() internally checks the value of a data structure that’s not been properly initialized:
I suspect that the reason for this is that you need to start the wlan chip first to be able to query it for the stored credentials, and it would not make sense to start it until we want to use it (for power consumption reasons, etc.)
Thanks for that explanation @akiesels, now I get it! I think we can fix this one pretty quickly - the solution is to try to populate the NVMEM variable in SPARK_WLAN_Setup() which is called to initialize the cc3000. Then they will be available for use for testing if there are any credentials.
Thanks @mdma. Just got around to trying your fix. Compiles ok, but when I flash the core it comes up and immediately throws a flashing red “SOS” message. Removed the line - everything peachy (except that hasCredentials is false before connect, of course). Here is my code to try it out, btw:
SYSTEM_MODE(SEMI_AUTOMATIC);
void setup()
{
pinMode(D0, OUTPUT); // RED LED attached here
pinMode(D1, OUTPUT); // BLUE LED attached here
if (WiFi.hasCredentials()) {
digitalWrite(D0, LOW);
digitalWrite(D1, HIGH);
}
else {
digitalWrite(D0, HIGH);
digitalWrite(D1, LOW);
}
WiFi.connect();
if (WiFi.hasCredentials()) {
digitalWrite(D0, LOW);
digitalWrite(D1, HIGH);
}
else {
digitalWrite(D0, HIGH);
digitalWrite(D1, LOW);
}
}
I’ve got a red LED connected to D0 and a blue LED to D1 (each through a 220 ohm resistor). When the core boots, the RED LED comes on first and once the WiFi is connected, it goes off and the BLUE one lights up. Since I have the credentials stored, I should only ever see the BLUE one light up.
So, what’s causing it to crash running that nvmem_read code on line 378? Maybe CC3000 is not quite ready after the 100ms delay? I doubt it, though, as I tried upping it to 1000ms and it still causes the same red SOS (except now I can catch a glimpse of solid white on the core LED when it first boots).
P.S. Tried adding wlan_start(0) right after delay(100) and before calling nvmem_read. Works correctly now - no crash and only the BLUE LED lights up.
But do we want to call wlan_start(0) at this point? What if I want to keep the wlan off for a while for power consumption reasons? I suppose we could add logic to hasCredentials to check to see if wlan has been started and if not, start it a run nvmem_read then? Otherwise, use the current flag-based implementation.
Ah, my mistake - I thought wlan_start(0) was already called. That was my intent.
(I’m working with a different version of the code, so confusing new/old code.)
You can try putting the nvmem read inside WiFi::on(), after the wlan_start(0) that’s there, and call Wifi::on() in your setup() function. That should fix it!
Thanks Matthew! Are you guys working on refactoring this code already? I found some more issues with the way connection states are evaluated. E.g. calling WiFi.on() will toggle WiFi.connecting() state, even though you’re not actually connecting, or similarly, calling WiFi.connect() and then later WiFi.disconnect() will also make WiFi.connecting() evaluate to true - again incorrect. There are others, like the inability to programmatically detect wrong wifi credentials (the infinite flashing green LED problem). I am happy to help with this, if I can, since I will have to make it work for my specific application, but if these are all known issues and fixes are in the works, then I will focus my energy elsewhere