wifi scan characteristic

more or less how the flow would be
This commit is contained in:
samuelbles07
2025-10-28 11:46:07 +07:00
parent 19df574130
commit 449817a384
2 changed files with 177 additions and 28 deletions

View File

@@ -8,7 +8,11 @@
#define BLE_SERVICE_UUID "acbcfea8-e541-4c40-9bfd-17820f16c95c"
#define BLE_CHARACTERISTIC_UUID "703fa252-3d2a-4da9-a05c-83b0d9cacb8e"
#define BLE_CRED_CHAR_UUID "703fa252-3d2a-4da9-a05c-83b0d9cacb8e"
#define BLE_SCAN_CHAR_UUID "467a080f-e50f-42c9-b9b2-a2ab14d82725"
#define BLE_CRED_BIT (1 << 0)
#define BLE_SCAN_BIT (1 << 1)
#define WIFI() ((WiFiManager *)(this->wifi))
@@ -125,7 +129,7 @@ bool WifiConnector::connect(void) {
uint32_t ledPeriod = millis();
bool clientConnectChanged = false;
setupBLE(ssid);
setupBLE();
AgStateMachineState stateOld = sm.getDisplayState();
while (WIFI()->getConfigPortalActive()) {
@@ -183,21 +187,50 @@ bool WifiConnector::connect(void) {
if (provisionMethod == ProvisionMethod::BLE) {
disp.setText("Provision by", "BLE", "");
while (isBleClientConnected() && !wifiConnecting) {
Serial.println("Wait for WiFi credentials through BLE");
delay(1000);
}
int count = 0;
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
count++;
if (count >= 15) {
// give up
WiFi.disconnect();
break;
// Loop until the BLE client disconnected or WiFi connected
while (isBleClientConnected() && !WiFi.isConnected()) {
Serial.println("Wait for BLE provision command");
EventBits_t bits = xEventGroupWaitBits(
bleEventGroup,
BLE_SCAN_BIT | BLE_CRED_BIT,
pdTRUE,
pdFALSE,
portMAX_DELAY
);
if (bits & BLE_CRED_BIT) {
count = 0;
wifiConnecting = true;
Serial.printf("Connecting to %s...\n", ssid.c_str());
while (WiFi.status() != WL_CONNECTED) {
delay(1000);
Serial.print(".");
count++;
if (count >= 15) {
WiFi.disconnect();
wifiConnecting = false;
bleNotifyStatus(10);
break;
}
}
}
else if (bits & BLE_SCAN_BIT) {
String result = scanFilteredWiFiJSON();
NimBLEService* pSvc = pServer->getServiceByUUID(BLE_SERVICE_UUID);
if (pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic(BLE_SCAN_CHAR_UUID);
if (pChr) {
pChr->setValue(result);
pChr->notify();
Serial.println("List of scanned networks sent through BLE notify");
}
}
}
}
Serial.println("Exit provision by BLE");
}
/** Show display wifi connect result failed */
@@ -206,7 +239,6 @@ bool WifiConnector::connect(void) {
if (ag->isOne() || ag->isPro4_2() || ag->isPro3_3() || ag->isBasic()) {
sm.displayHandle(AgStateMachineWiFiManagerConnectFailed);
}
bleNotifyStatus(10);
delay(6000);
} else {
hasConfig = true;
@@ -456,10 +488,14 @@ bool WifiConnector::isConfigurePorttalTimeout(void) { return connectorTimeout; }
void WifiConnector::bleNotifyStatus(int status) {
if (!bleServerRunning) {
return;
}
if (pServer->getConnectedCount()) {
NimBLEService* pSvc = pServer->getServiceByUUID(BLE_SERVICE_UUID);
if (pSvc) {
NimBLECharacteristic* pChr = pSvc->getCharacteristic(BLE_CHARACTERISTIC_UUID);
NimBLECharacteristic* pChr = pSvc->getCharacteristic(BLE_CRED_CHAR_UUID);
if (pChr) {
char tosend[50];
memset(tosend, 0, 50);
@@ -480,9 +516,89 @@ void WifiConnector::setDefault(void) {
WiFi.begin("airgradient", "cleanair");
}
String WifiConnector::scanFilteredWiFiJSON() {
Serial.println("Scanning for Wi-Fi networks...");
int n = WiFi.scanNetworks(false, true); // async=false, show_hidden=true
Serial.printf("Found %d networks\n", n);
void WifiConnector::setupBLE(String bleName) {
NimBLEDevice::init(bleName.c_str());
const int MAX_NETWORKS = 50;
const int MAX_RESULTS = 15;
if (n <= 0) {
Serial.println("No networks found");
return "[]";
}
WiFiNetwork allNetworks[MAX_NETWORKS];
int allCount = 0;
// Collect valid networks (filter weak or empty SSID)
for (int i = 0; i < n && allCount < MAX_NETWORKS; ++i) {
String ssid = WiFi.SSID(i);
int32_t rssi = WiFi.RSSI(i);
bool open = (WiFi.encryptionType(i) == WIFI_AUTH_OPEN);
if (ssid.length() == 0 || rssi < -70) continue;
allNetworks[allCount++] = {ssid, rssi, open};
}
// Remove duplicates (keep the strongest)
WiFiNetwork uniqueNetworks[MAX_NETWORKS];
int uniqueCount = 0;
for (int i = 0; i < allCount; i++) {
bool exists = false;
for (int j = 0; j < uniqueCount; j++) {
if (uniqueNetworks[j].ssid == allNetworks[i].ssid) {
exists = true;
if (allNetworks[i].rssi > uniqueNetworks[j].rssi)
uniqueNetworks[j] = allNetworks[i]; // keep stronger one
break;
}
}
if (!exists && uniqueCount < MAX_NETWORKS) {
uniqueNetworks[uniqueCount++] = allNetworks[i];
}
}
// Sort by RSSI descending (simple bubble sort for small lists)
for (int i = 0; i < uniqueCount - 1; i++) {
for (int j = i + 1; j < uniqueCount; j++) {
if (uniqueNetworks[j].rssi > uniqueNetworks[i].rssi) {
WiFiNetwork temp = uniqueNetworks[i];
uniqueNetworks[i] = uniqueNetworks[j];
uniqueNetworks[j] = temp;
}
}
}
// Limit to top X
if (uniqueCount > MAX_RESULTS)
uniqueCount = MAX_RESULTS;
// Build JSON array
JSONVar jsonArray;
for (int i = 0; i < uniqueCount; i++) {
JSONVar obj;
obj["ssid"] = uniqueNetworks[i].ssid;
obj["rssi"] = uniqueNetworks[i].rssi;
obj["open"] = uniqueNetworks[i].open;
jsonArray[i] = obj;
}
String jsonString = JSON.stringify(jsonArray);
Serial.println("Filtered Wi-Fi Networks (JSON):");
Serial.println(jsonString);
return jsonString;
}
void WifiConnector::setupBLE() {
Serial.printf("Setup BLE with device name %s\n", ssid.c_str());
NimBLEDevice::init(ssid.c_str());
NimBLEDevice::setPower(3); /** +3db */
/** bonding, MITM, don't need BLE secure connections as we are using passkey pairing */
@@ -492,12 +608,20 @@ void WifiConnector::setupBLE(String bleName) {
pServer = NimBLEDevice::createServer();
pServer->setCallbacks(new ServerCallbacks(this));
auto characteristicCallback = new CharacteristicCallbacks(this);
NimBLEService *pService = pServer->createService(BLE_SERVICE_UUID);
NimBLECharacteristic *pSecureCharacteristic =
pService->createCharacteristic(BLE_CHARACTERISTIC_UUID,
NimBLECharacteristic *pCredentialCharacteristic =
pService->createCharacteristic(BLE_CRED_CHAR_UUID,
NIMBLE_PROPERTY::READ | NIMBLE_PROPERTY::READ_ENC |
NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC | NIMBLE_PROPERTY::NOTIFY);
pSecureCharacteristic->setCallbacks(new CharacteristicCallbacks(this));
pCredentialCharacteristic->setCallbacks(characteristicCallback);
NimBLECharacteristic *pScanCharacteristic =
pService->createCharacteristic(BLE_SCAN_CHAR_UUID, NIMBLE_PROPERTY::WRITE | NIMBLE_PROPERTY::WRITE_ENC | NIMBLE_PROPERTY::NOTIFY);
pScanCharacteristic->setCallbacks(characteristicCallback);
pService->start();
@@ -505,6 +629,14 @@ void WifiConnector::setupBLE(String bleName) {
pAdvertising->addServiceUUID(pService->getUUID());
pAdvertising->start();
bleServerRunning = true;
// Create event group
bleEventGroup = xEventGroupCreate();
if (bleEventGroup == NULL) {
Serial.println("Failed to create BLE event group!");
// This case is very unlikely
}
Serial.println("Provision by BLE ready");
}
@@ -556,13 +688,20 @@ void WifiConnector::CharacteristicCallbacks::onWrite(NimBLECharacteristic *pChar
Serial.printf("%s : onWrite(), value: %s\n", pCharacteristic->getUUID().toString().c_str(),
pCharacteristic->getValue().c_str());
JSONVar root = JSON.parse(pCharacteristic->getValue().c_str());
auto bleCred = NimBLEUUID(BLE_CRED_CHAR_UUID);
if (pCharacteristic->getUUID().equals(bleCred)) {
if (!parent->wifiConnecting) {
JSONVar root = JSON.parse(pCharacteristic->getValue().c_str());
String ssid = root["ssid"];
String pass = root["password"];
String ssid = root["ssid"];
String pass = root["password"];
WiFi.begin(ssid.c_str(), pass.c_str());
xEventGroupSetBits(parent->bleEventGroup, BLE_CRED_BIT);
}
} else {
xEventGroupSetBits(parent->bleEventGroup, BLE_SCAN_BIT);
}
Serial.printf("Connecting to %s...\n", ssid.c_str());
WiFi.begin(ssid.c_str(), pass.c_str());
parent->wifiConnecting = true;
}

View File

@@ -8,6 +8,7 @@
#include "Main/PrintLog.h"
#include "NimBLECharacteristic.h"
#include "NimBLEService.h"
#include "esp32-hal.h"
#include <Arduino.h>
#include <NimBLEDevice.h>
@@ -20,6 +21,12 @@ public:
BLE
};
struct WiFiNetwork {
String ssid;
int32_t rssi;
bool open;
};
private:
AirGradient *ag;
OledDisplay &disp;
@@ -27,6 +34,8 @@ private:
Configuration &config;
NimBLEServer *pServer;
EventGroupHandle_t bleEventGroup;
String ssid;
void *wifi = NULL;
bool hasConfig;
@@ -40,6 +49,7 @@ private:
bool wifiClientConnected(void);
bool isBleClientConnected();
String scanFilteredWiFiJSON();
// BLE server handler
class ServerCallbacks : public NimBLEServerCallbacks {
@@ -70,7 +80,7 @@ public:
WifiConnector(OledDisplay &disp, Stream &log, StateMachine &sm, Configuration &config);
~WifiConnector();
void setupBLE(String bleName);
void setupBLE();
void stopBLE();
bool connect(void);
void disconnect(void);