I’m encountering an issue with my Photon 2 device where Particle.connect() seems to block the system thread, preventing me from using BLE to reconfigure Wi-Fi credentials if the initial Wi-Fi setup fails to connect to the internet. This effectively leaves the device in an inaccessible state, requiring a reset.
I am using the BLE iOS solution to transfer credentials as this is a product that my customers will be setting up themselves. Is there any way to still allow BLE connections so that they can erase the old creds and add new ones?
I'm not sure that's what's happening. Particle.connect() is partially non-blocking; it can return before the connection is complete. Make sure you are not repeatedly making those two calls; you probably need to set a flag to make sure you don't call them on every loop.
WiFi.hasCredentials() runs on the system thread, so it will block the main thread until the system thread is free to handle it, which may be part of the problem as well.
What you are trying to do is definitely possible; you should see how it was done in the library in this post which is known to work.
Also make sure you're not doing a Particle.publish() when in the disconnected state elsewhere in your code. That will block its calling thread and the system thread.
I also keep track of the network transition but in that example @rickkas7 posted he just keeps track of the wifi being ready or connecting before calling a disconnect before trying to connect again, which is cleaner than what I do. Just make sure you do something to call it once when credentials are added and not repeatedly.
if (WiFi.ready() || WiFi.connecting()) {
WiFi.disconnect();
}
Particle.connect();
Sorry I might not have explained this well enough. I appreciate the responses. I am following the wifi setup tutorial here using the iOS native library here
This all works really well with my code now, I am able to connect to a wifi network and if the internet works it will then it will attempt to connect after the void nw_creds_handler(system_event_t evt, int param) callback is called.
There was one scenario that did not work while I was testing. I accidentally connected to a wifi network that was NOT connected to the internet. In that case when after the Particle.connect() was called, it continued to cycle through the connection attempt over and over again with no success. It was during this time that I could no longer connect via BLE on my iOS device. I assumed that the Connect function was blocking the system thread and that was causing the issue because of this tidbit in the Particle.Connect() docs:
After you call Particle.connect(), your loop will not be called again until the device finishes connecting to the Cloud. Typically, you can expect a delay of approximately one second.
I could be wrong, but I did solve the issue by testing that the Photon was connected to the internet before calling the connect function:
// Include Particle Device OS APIs
#include "Particle.h"
// Run the application and system concurrently in separate threads
SYSTEM_THREAD(ENABLED);
SYSTEM_MODE(SEMI_AUTOMATIC);
SerialLogHandler logHandler(LOG_LEVEL_TRACE);
PRODUCT_VERSION(1)
STARTUP(System.enableFeature(FEATURE_DISABLE_LISTENING_MODE));
bool shouldRetryConnection = false;
unsigned long lastRetryAttempt = 0;
const unsigned long RETRY_INTERVAL = 20000;
void attemptConnection();
void ble_prov_mode_handler(system_event_t evt, int param)
{
if (param == ble_prov_mode_connected)
{
// Log.info("BLE Event detected: ble_prov_mode_connected");
}
if (param == ble_prov_mode_disconnected)
{
// Log.info("BLE Event detected: ble_prov_mode_disconnected");
}
if (param == ble_prov_mode_handshake_failed)
{
// Log.info("BLE Event detected: ble_prov_mode_handshake_failed");
}
if (param == ble_prov_mode_handshake_done)
{
// Log.info("BLE Event detected: ble_prov_mode_handshake_done");
}
}
void nw_creds_handler(system_event_t evt, int param)
{
if (param == network_credentials_added)
{
Log.info("BLE Event detected: network_credentials_added");
attemptConnection();
}
}
// setup() runs once, when the device is first turned on
void setup()
{
Serial.begin(115200);
Serial1.begin(9600);
delay(1000);
// ---------System Events---------
System.on(ble_prov_mode, ble_prov_mode_handler);
System.on(network_credentials, nw_creds_handler);
System.setControlRequestFilter(SystemControlRequestAclAction::ACCEPT);
// ---------Custom mobile secret---------
char arr[HAL_DEVICE_SECRET_SIZE] = {};
memcpy(arr, MOBILE_SECRET, HAL_DEVICE_SECRET_SIZE);
hal_set_device_secret(arr, sizeof(arr), nullptr);
// ---------Provisioning Service and Characteristic UUIDs---------
STARTUP(
BLE.setProvisioningSvcUuid(serviceUuid);
BLE.setProvisioningTxUuid(txUuid);
BLE.setProvisioningRxUuid(rxUuid);
BLE.setProvisioningVerUuid(versionUuid););
BLE.setDeviceName(DEVICE_NAME);
BLE.setProvisioningCompanyId(0x1234);
BLE.provisioningMode(true);
LOG(TRACE, "BLE prov mode status: %d", BLE.getProvisioningStatus());
WiFi.selectAntenna(ANT_AUTO);
attemptConnection();
}
void loop()
{
if (shouldRetryConnection && (millis() - lastRetryAttempt > RETRY_INTERVAL))
{
lastRetryAttempt = millis();
attemptConnection();
}
}
bool hasInternetConnectivity()
{
IPAddress ip = WiFi.resolve("www.google.com");
if (ip)
{
return true;
}
else
{
return false;
}
}
void attemptConnection()
{
if (WiFi.hasCredentials() && !Particle.connected())
{
if (hasInternetConnectivity())
{
shouldRetryConnection = false;
Log.info("Internet available. Connecting to Particle Cloud...");
Particle.connect();
}
else
{
shouldRetryConnection = true;
Log.warn("Wi-Fi connected, but no internet. Will retry in loop.");
}
}
else
{
shouldRetryConnection = false;
}
}
I'm sure there is a better solution to this but I got it back to working for now. I understand this is probably an edge case, but didn't want to leave anything to chance when non technical clients will be setting this up. Thanks again!
Glad you resolved the issue. In the case where you don't have connection, yeah maybe just having a timeout like you are effectively doing does the trick. With my app once I give it credentials I'm done with my BLE connection so that might be why I don't see that issue exactly.
Also, it's a good idea to set shouldRetryConnection to true in that handler instead of calling a function from it, especially since that function does stuff on the system thread. Then the work is done in the loop instead.