@@ -1,2 +0,0 @@
|
||||
## Build
|
||||
- use ./ctl build target <platformio env>
|
||||
4
ctl.sh
4
ctl.sh
@@ -51,8 +51,4 @@ function cluster {
|
||||
${@:-info}
|
||||
}
|
||||
|
||||
function monitor {
|
||||
pio run -t monitor
|
||||
}
|
||||
|
||||
${@:-info}
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
# WiFi Scanner Example
|
||||
|
||||
This example demonstrates how to use the async WiFi scanning functionality in SPORE with periodic scanning via tasks.
|
||||
|
||||
## Features
|
||||
|
||||
- **Async WiFi Scanning**: Non-blocking WiFi network discovery via NetworkManager
|
||||
- **Task-based Periodic Scanning**: Automatically scans for networks every minute using TaskManager
|
||||
- **Event-driven**: Uses the event system to handle scan events
|
||||
- **REST API**: HTTP endpoints to manually trigger scans and view results
|
||||
- **Detailed Results**: Shows SSID, RSSI, channel, and encryption type for each network
|
||||
|
||||
## Usage
|
||||
|
||||
1. Upload this example to your ESP8266 device
|
||||
2. Open the Serial Monitor at 115200 baud
|
||||
3. The device will automatically start scanning for WiFi networks every minute
|
||||
4. Results will be displayed in the Serial Monitor
|
||||
5. Use the REST API to manually trigger scans or view current results
|
||||
|
||||
## Event System
|
||||
|
||||
The WiFi scanner uses the following events:
|
||||
|
||||
- `wifi/scan/start`: Fired when scan starts (both manual and periodic)
|
||||
- `wifi/scan/complete`: Fired when scan completes successfully
|
||||
- `wifi/scan/error`: Fired when scan fails to start
|
||||
- `wifi/scan/timeout`: Fired when scan times out (10 seconds)
|
||||
|
||||
## Task Management
|
||||
|
||||
The example registers a periodic task:
|
||||
- **Task Name**: `wifi_scan_periodic`
|
||||
- **Interval**: 60 seconds (60000ms)
|
||||
- **Function**: Fires `wifi/scan/start` event and starts WiFi scan
|
||||
|
||||
## REST API
|
||||
|
||||
### Manual Scan Control
|
||||
- **Start Scan**: `POST /api/wifi/scan`
|
||||
- **Get Status**: `GET /api/wifi/status`
|
||||
|
||||
### Example API Usage
|
||||
```bash
|
||||
# Start a manual scan
|
||||
curl -X POST http://192.168.1.50/api/wifi/scan
|
||||
|
||||
# Get current scan status and results
|
||||
curl http://192.168.1.50/api/wifi/status
|
||||
|
||||
# Check task status
|
||||
curl http://192.168.1.50/api/tasks/status
|
||||
|
||||
# Disable periodic scanning
|
||||
curl -X POST http://192.168.1.50/api/tasks/control \
|
||||
-d task=wifi_scan_periodic -d action=disable
|
||||
```
|
||||
|
||||
## Architecture
|
||||
|
||||
- **WiFiScannerService**: Manages periodic scanning and API endpoints
|
||||
- **NetworkManager**: Handles WiFi scanning operations and callbacks
|
||||
- **NodeContext**: Stores the WiFi access points data
|
||||
- **TaskManager**: Schedules periodic scan tasks
|
||||
- **Event System**: Provides communication between components
|
||||
|
||||
## WiFiAccessPoint Structure
|
||||
|
||||
```cpp
|
||||
struct WiFiAccessPoint {
|
||||
String ssid; // Network name
|
||||
int32_t rssi; // Signal strength
|
||||
uint8_t encryption; // Encryption type
|
||||
uint8_t* bssid; // MAC address
|
||||
int32_t channel; // WiFi channel
|
||||
bool isHidden; // Hidden network flag
|
||||
};
|
||||
```
|
||||
|
||||
## Task Configuration
|
||||
|
||||
The periodic scanning task can be controlled via the standard TaskManager API:
|
||||
- Enable/disable the task
|
||||
- Change the scan interval
|
||||
- Monitor task status
|
||||
- Start/stop the task
|
||||
|
||||
This provides flexibility to adjust scanning behavior based on application needs.
|
||||
@@ -1,183 +0,0 @@
|
||||
#include <Arduino.h>
|
||||
#include <functional>
|
||||
#include "Globals.h"
|
||||
#include "NodeContext.h"
|
||||
#include "NetworkManager.h"
|
||||
#include "ClusterManager.h"
|
||||
#include "ApiServer.h"
|
||||
#include "TaskManager.h"
|
||||
|
||||
using namespace std;
|
||||
|
||||
class WiFiScannerService {
|
||||
public:
|
||||
WiFiScannerService(NodeContext& ctx, TaskManager& taskMgr, NetworkManager& networkMgr)
|
||||
: ctx(ctx), taskManager(taskMgr), network(networkMgr) {
|
||||
registerTasks();
|
||||
}
|
||||
|
||||
void registerApi(ApiServer& api) {
|
||||
api.addEndpoint("/api/wifi/scan", HTTP_POST, [this](AsyncWebServerRequest* request) {
|
||||
if (network.isWiFiScanning()) {
|
||||
JsonDocument resp;
|
||||
resp["success"] = false;
|
||||
resp["message"] = "WiFi scan already in progress";
|
||||
String json;
|
||||
serializeJson(resp, json);
|
||||
request->send(409, "application/json", json);
|
||||
return;
|
||||
}
|
||||
|
||||
network.startWiFiScan();
|
||||
|
||||
JsonDocument resp;
|
||||
resp["success"] = true;
|
||||
resp["message"] = "WiFi scan started";
|
||||
String json;
|
||||
serializeJson(resp, json);
|
||||
request->send(200, "application/json", json);
|
||||
});
|
||||
|
||||
api.addEndpoint("/api/wifi/status", HTTP_GET, [this](AsyncWebServerRequest* request) {
|
||||
JsonDocument doc;
|
||||
doc["scanning"] = network.isWiFiScanning();
|
||||
doc["networks_found"] = ctx.wifiAccessPoints->size();
|
||||
|
||||
JsonArray networks = doc["networks"].to<JsonArray>();
|
||||
for (const auto& ap : *ctx.wifiAccessPoints) {
|
||||
JsonObject network = networks.add<JsonObject>();
|
||||
network["ssid"] = ap.ssid;
|
||||
network["rssi"] = ap.rssi;
|
||||
network["channel"] = ap.channel;
|
||||
network["encryption"] = ap.encryption;
|
||||
network["hidden"] = ap.isHidden;
|
||||
}
|
||||
|
||||
String json;
|
||||
serializeJson(doc, json);
|
||||
request->send(200, "application/json", json);
|
||||
});
|
||||
}
|
||||
|
||||
private:
|
||||
void registerTasks() {
|
||||
// Register task to start WiFi scan every minute (60000ms)
|
||||
taskManager.registerTask("wifi_scan_periodic", 60000, [this]() {
|
||||
if (!network.isWiFiScanning()) {
|
||||
Serial.println("[WiFiScannerService] Starting periodic WiFi scan...");
|
||||
ctx.fire("wifi/scan/start", nullptr);
|
||||
network.startWiFiScan();
|
||||
} else {
|
||||
Serial.println("[WiFiScannerService] Skipping scan - already in progress");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
NodeContext& ctx;
|
||||
TaskManager& taskManager;
|
||||
NetworkManager& network;
|
||||
};
|
||||
|
||||
NodeContext ctx({
|
||||
{"app", "wifiscan"},
|
||||
{"role", "demo"}
|
||||
});
|
||||
NetworkManager network(ctx);
|
||||
TaskManager taskManager(ctx);
|
||||
ClusterManager cluster(ctx, taskManager);
|
||||
ApiServer apiServer(ctx, taskManager, ctx.config.api_server_port);
|
||||
WiFiScannerService wifiScanner(ctx, taskManager, network);
|
||||
|
||||
// WiFi scan start callback
|
||||
void onWiFiScanStart(void* data) {
|
||||
Serial.println("[WiFi] Scan started...");
|
||||
}
|
||||
|
||||
// WiFi scan complete callback
|
||||
void onWiFiScanComplete(void* data) {
|
||||
Serial.println("\n=== WiFi Scan Results ===");
|
||||
|
||||
std::vector<WiFiAccessPoint>* accessPoints = static_cast<std::vector<WiFiAccessPoint>*>(data);
|
||||
|
||||
if (accessPoints->empty()) {
|
||||
Serial.println("No networks found");
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.printf("Found %d networks:\n", accessPoints->size());
|
||||
Serial.println("SSID\t\t\tRSSI\tChannel\tEncryption");
|
||||
Serial.println("----------------------------------------");
|
||||
|
||||
for (const auto& ap : *accessPoints) {
|
||||
String encryptionStr = "Unknown";
|
||||
switch (ap.encryption) {
|
||||
case ENC_TYPE_NONE:
|
||||
encryptionStr = "Open";
|
||||
break;
|
||||
case ENC_TYPE_WEP:
|
||||
encryptionStr = "WEP";
|
||||
break;
|
||||
case ENC_TYPE_TKIP:
|
||||
encryptionStr = "WPA";
|
||||
break;
|
||||
case ENC_TYPE_CCMP:
|
||||
encryptionStr = "WPA2";
|
||||
break;
|
||||
case ENC_TYPE_AUTO:
|
||||
encryptionStr = "Auto";
|
||||
break;
|
||||
}
|
||||
|
||||
Serial.printf("%-20s\t%d\t%d\t%s\n",
|
||||
ap.ssid.c_str(), ap.rssi, ap.channel, encryptionStr.c_str());
|
||||
}
|
||||
Serial.println("========================\n");
|
||||
}
|
||||
|
||||
// WiFi scan error callback
|
||||
void onWiFiScanError(void* data) {
|
||||
Serial.println("[WiFi] Scan failed - error occurred");
|
||||
}
|
||||
|
||||
// WiFi scan timeout callback
|
||||
void onWiFiScanTimeout(void* data) {
|
||||
Serial.println("[WiFi] Scan failed - timeout");
|
||||
}
|
||||
|
||||
void setup() {
|
||||
Serial.begin(115200);
|
||||
Serial.println("\n=== WiFi Scanner Example ===");
|
||||
|
||||
// Initialize task manager first
|
||||
taskManager.initialize();
|
||||
ctx.taskManager = &taskManager;
|
||||
|
||||
// Initialize network tasks
|
||||
network.initTasks();
|
||||
|
||||
// Setup WiFi
|
||||
network.setupWiFi();
|
||||
|
||||
// Start the API server
|
||||
apiServer.begin();
|
||||
wifiScanner.registerApi(apiServer);
|
||||
|
||||
// Register WiFi scan event callbacks
|
||||
ctx.on("wifi/scan/start", onWiFiScanStart);
|
||||
ctx.on("wifi/scan/complete", onWiFiScanComplete);
|
||||
ctx.on("wifi/scan/error", onWiFiScanError);
|
||||
ctx.on("wifi/scan/timeout", onWiFiScanTimeout);
|
||||
|
||||
// Start an initial WiFi scan
|
||||
Serial.println("Starting initial WiFi scan...");
|
||||
ctx.fire("wifi/scan/start", nullptr);
|
||||
network.startWiFiScan();
|
||||
|
||||
// Print initial task status
|
||||
taskManager.printTaskStatus();
|
||||
}
|
||||
|
||||
void loop() {
|
||||
taskManager.execute();
|
||||
yield();
|
||||
}
|
||||
@@ -7,18 +7,6 @@ public:
|
||||
NetworkManager(NodeContext& ctx);
|
||||
void setupWiFi();
|
||||
void setHostnameFromMac();
|
||||
void startWiFiScan();
|
||||
void updateWiFiScan();
|
||||
bool isWiFiScanning() const;
|
||||
void processScanResults(int networksFound);
|
||||
void initTasks(); // Initialize tasks after TaskManager is ready // Public method to process scan results
|
||||
|
||||
private:
|
||||
NodeContext& ctx;
|
||||
bool wifiScanInProgress;
|
||||
unsigned long wifiScanStartTime;
|
||||
static const unsigned long WIFI_SCAN_TIMEOUT_MS = 10000; // 10 seconds timeout
|
||||
static const char* const WIFI_SCAN_MONITOR_TASK; // Task name for WiFi scan monitoring
|
||||
|
||||
void onWiFiScanComplete(int networksFound); // Keep private for internal use
|
||||
};
|
||||
|
||||
@@ -2,33 +2,12 @@
|
||||
|
||||
#include <WiFiUdp.h>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include "NodeInfo.h"
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <initializer_list>
|
||||
#include "Config.h"
|
||||
|
||||
// Forward declaration
|
||||
class TaskManager;
|
||||
|
||||
// WiFi access point structure
|
||||
struct WiFiAccessPoint {
|
||||
String ssid;
|
||||
int32_t rssi;
|
||||
uint8_t encryption;
|
||||
uint8_t* bssid;
|
||||
int32_t channel;
|
||||
bool isHidden;
|
||||
|
||||
WiFiAccessPoint() : rssi(0), encryption(0), bssid(nullptr), channel(0), isHidden(false) {}
|
||||
~WiFiAccessPoint() {
|
||||
if (bssid) {
|
||||
delete[] bssid;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
class NodeContext {
|
||||
public:
|
||||
NodeContext();
|
||||
@@ -40,14 +19,10 @@ public:
|
||||
NodeInfo self;
|
||||
std::map<String, NodeInfo>* memberList;
|
||||
Config config;
|
||||
TaskManager* taskManager;
|
||||
|
||||
using EventCallback = std::function<void(void*)>;
|
||||
std::map<std::string, std::vector<EventCallback>> eventRegistry;
|
||||
|
||||
void on(const std::string& event, EventCallback cb);
|
||||
void fire(const std::string& event, void* data);
|
||||
|
||||
// WiFi access points storage
|
||||
std::vector<WiFiAccessPoint>* wifiAccessPoints;
|
||||
};
|
||||
|
||||
@@ -4,11 +4,9 @@
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <map>
|
||||
#include "NodeContext.h"
|
||||
#include <ArduinoJson.h>
|
||||
|
||||
// Forward declaration
|
||||
class NodeContext;
|
||||
|
||||
// Define our own callback type to avoid conflict with TaskScheduler
|
||||
using TaskFunction = std::function<void()>;
|
||||
|
||||
|
||||
@@ -124,32 +124,3 @@ build_src_filter =
|
||||
+<examples/neopattern/main.cpp>
|
||||
+<src/*.c>
|
||||
+<src/*.cpp>
|
||||
|
||||
[env:esp01_1m_wifiscan]
|
||||
platform = platformio/espressif8266@^4.2.1
|
||||
board = esp01_1m
|
||||
framework = arduino
|
||||
upload_speed = 115200
|
||||
monitor_speed = 115200
|
||||
board_build.partitions = partitions_ota_1M.csv
|
||||
board_build.flash_mode = dout
|
||||
board_build.flash_size = 1M
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_src_filter =
|
||||
+<examples/wifiscan/*.cpp>
|
||||
+<src/*.c>
|
||||
+<src/*.cpp>
|
||||
|
||||
[env:d1_mini_wifiscan]
|
||||
platform = platformio/espressif8266@^4.2.1
|
||||
board = d1_mini
|
||||
framework = arduino
|
||||
upload_speed = 115200
|
||||
monitor_speed = 115200
|
||||
board_build.flash_mode = dio
|
||||
board_build.flash_size = 4M
|
||||
lib_deps = ${common.lib_deps}
|
||||
build_src_filter =
|
||||
+<examples/wifiscan/*.cpp>
|
||||
+<src/*.c>
|
||||
+<src/*.cpp>
|
||||
|
||||
@@ -1,29 +1,8 @@
|
||||
#include "NetworkManager.h"
|
||||
#include "TaskManager.h"
|
||||
|
||||
// SSID and password are now configured via Config class
|
||||
|
||||
const char* const NetworkManager::WIFI_SCAN_MONITOR_TASK = "wifi_scan_monitor";
|
||||
|
||||
NetworkManager::NetworkManager(NodeContext& ctx) : ctx(ctx), wifiScanInProgress(false), wifiScanStartTime(0) {
|
||||
// Register scan process callback
|
||||
ctx.on("wifi/scan/process", [this](void* data) {
|
||||
int networksFound = reinterpret_cast<intptr_t>(data);
|
||||
this->processScanResults(networksFound);
|
||||
});
|
||||
}
|
||||
|
||||
void NetworkManager::initTasks() {
|
||||
if (!ctx.taskManager) {
|
||||
Serial.println("[WiFi] Error: TaskManager not initialized");
|
||||
return;
|
||||
}
|
||||
|
||||
// Register task to check scan status every 100ms (initially disabled)
|
||||
ctx.taskManager->registerTask(WIFI_SCAN_MONITOR_TASK, 100, [this]() {
|
||||
this->updateWiFiScan();
|
||||
}, false); // false = disabled by default
|
||||
}
|
||||
NetworkManager::NetworkManager(NodeContext& ctx) : ctx(ctx) {}
|
||||
|
||||
void NetworkManager::setHostnameFromMac() {
|
||||
uint8_t mac[6];
|
||||
@@ -86,133 +65,3 @@ void NetworkManager::setupWiFi() {
|
||||
// Notify listeners that the node is (re)discovered
|
||||
ctx.fire("node_discovered", &ctx.self);
|
||||
}
|
||||
|
||||
void NetworkManager::startWiFiScan() {
|
||||
if (wifiScanInProgress) {
|
||||
Serial.println("[WiFi] Scan already in progress");
|
||||
return;
|
||||
}
|
||||
|
||||
// Check if we're in AP mode only
|
||||
if (WiFi.getMode() == WIFI_AP) {
|
||||
// Enable STA mode while keeping AP mode
|
||||
WiFi.mode(WIFI_AP_STA);
|
||||
delay(100); // Give some time for mode change
|
||||
}
|
||||
|
||||
// Ensure we have STA mode enabled
|
||||
if (WiFi.getMode() != WIFI_STA && WiFi.getMode() != WIFI_AP_STA) {
|
||||
Serial.println("[WiFi] Error: Cannot scan without STA mode enabled");
|
||||
ctx.fire("wifi/scan/error", nullptr);
|
||||
return;
|
||||
}
|
||||
|
||||
Serial.println("[WiFi] Starting WiFi scan...");
|
||||
wifiScanInProgress = true;
|
||||
wifiScanStartTime = millis();
|
||||
|
||||
// Enable the monitoring task if TaskManager is available
|
||||
if (ctx.taskManager) {
|
||||
ctx.taskManager->enableTask(WIFI_SCAN_MONITOR_TASK);
|
||||
}
|
||||
|
||||
// Clear previous results safely
|
||||
if (ctx.wifiAccessPoints) {
|
||||
ctx.wifiAccessPoints->clear();
|
||||
}
|
||||
|
||||
// Disable interrupts briefly during scan start
|
||||
noInterrupts();
|
||||
// Start the scan
|
||||
WiFi.scanNetworksAsync([this](int networksFound) {
|
||||
// Schedule callback in main loop
|
||||
ctx.fire("wifi/scan/process", reinterpret_cast<void*>(networksFound));
|
||||
}, true); // Show hidden networks
|
||||
interrupts();
|
||||
}
|
||||
|
||||
void NetworkManager::updateWiFiScan() {
|
||||
if (!wifiScanInProgress) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Check for timeout
|
||||
if (millis() - wifiScanStartTime > WIFI_SCAN_TIMEOUT_MS) {
|
||||
Serial.println("[WiFi] WiFi scan timeout");
|
||||
wifiScanInProgress = false;
|
||||
if (ctx.taskManager) {
|
||||
ctx.taskManager->disableTask(WIFI_SCAN_MONITOR_TASK);
|
||||
}
|
||||
ctx.fire("wifi/scan/timeout", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkManager::processScanResults(int networksFound) {
|
||||
// This is called from the main loop context via the event system
|
||||
if (!wifiScanInProgress) {
|
||||
return; // Ignore if we're not expecting results
|
||||
}
|
||||
|
||||
wifiScanInProgress = false;
|
||||
// Disable the monitoring task if TaskManager is available
|
||||
if (ctx.taskManager) {
|
||||
ctx.taskManager->disableTask(WIFI_SCAN_MONITOR_TASK);
|
||||
}
|
||||
|
||||
Serial.printf("[WiFi] Processing scan results, found %d networks\n", networksFound);
|
||||
|
||||
// Create a temporary vector to hold new results
|
||||
std::vector<WiFiAccessPoint> newAccessPoints;
|
||||
|
||||
if (networksFound > 0) {
|
||||
newAccessPoints.reserve(networksFound); // Pre-allocate space
|
||||
|
||||
// Process each found network
|
||||
for (int i = 0; i < networksFound; i++) {
|
||||
WiFiAccessPoint ap;
|
||||
ap.ssid = WiFi.SSID(i);
|
||||
ap.rssi = WiFi.RSSI(i);
|
||||
ap.encryption = WiFi.encryptionType(i);
|
||||
ap.channel = WiFi.channel(i);
|
||||
ap.isHidden = WiFi.isHidden(i);
|
||||
|
||||
// Copy BSSID - with null check and bounds protection
|
||||
uint8_t* bssid = WiFi.BSSID(i);
|
||||
ap.bssid = nullptr; // Initialize to null first
|
||||
if (bssid != nullptr) {
|
||||
ap.bssid = new (std::nothrow) uint8_t[6]; // Use nothrow to prevent exceptions
|
||||
if (ap.bssid != nullptr) {
|
||||
memcpy(ap.bssid, bssid, 6);
|
||||
}
|
||||
}
|
||||
|
||||
newAccessPoints.push_back(ap);
|
||||
|
||||
Serial.printf("[WiFi] %d: %s (RSSI: %d, Ch: %d, Enc: %d)\n",
|
||||
i, ap.ssid.c_str(), ap.rssi, ap.channel, ap.encryption);
|
||||
}
|
||||
}
|
||||
|
||||
// Free the scan results from WiFi to prevent memory leaks
|
||||
WiFi.scanDelete();
|
||||
|
||||
// Safely swap the new results with the stored results
|
||||
if (ctx.wifiAccessPoints) {
|
||||
ctx.wifiAccessPoints->swap(newAccessPoints);
|
||||
// Fire the scan complete event with a copy of the pointer
|
||||
auto accessPoints = ctx.wifiAccessPoints;
|
||||
ctx.fire("wifi/scan/complete", accessPoints);
|
||||
} else {
|
||||
Serial.println("[WiFi] Error: wifiAccessPoints is null");
|
||||
ctx.fire("wifi/scan/error", nullptr);
|
||||
}
|
||||
}
|
||||
|
||||
void NetworkManager::onWiFiScanComplete(int networksFound) {
|
||||
// Internal callback that schedules processing in the main loop
|
||||
ctx.fire("wifi/scan/process", reinterpret_cast<void*>(networksFound));
|
||||
}
|
||||
|
||||
bool NetworkManager::isWiFiScanning() const {
|
||||
return wifiScanInProgress;
|
||||
}
|
||||
|
||||
@@ -1,11 +1,8 @@
|
||||
#include "NodeContext.h"
|
||||
#include "TaskManager.h"
|
||||
|
||||
NodeContext::NodeContext() {
|
||||
udp = new WiFiUDP();
|
||||
memberList = new std::map<String, NodeInfo>();
|
||||
wifiAccessPoints = new std::vector<WiFiAccessPoint>();
|
||||
taskManager = nullptr; // Will be set by TaskManager constructor
|
||||
hostname = "";
|
||||
self.hostname = "";
|
||||
self.ip = IPAddress();
|
||||
@@ -22,7 +19,6 @@ NodeContext::NodeContext(std::initializer_list<std::pair<String, String>> initia
|
||||
NodeContext::~NodeContext() {
|
||||
delete udp;
|
||||
delete memberList;
|
||||
delete wifiAccessPoints;
|
||||
}
|
||||
|
||||
void NodeContext::on(const std::string& event, EventCallback cb) {
|
||||
|
||||
Reference in New Issue
Block a user