214 lines
6.2 KiB
C
214 lines
6.2 KiB
C
#include "wifi_scan.h"
|
|
#include "esp_wifi.h"
|
|
#include "esp_log.h"
|
|
#include "cJSON.h"
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
|
|
#define TAG "wifi_scan"
|
|
#define MAX_NETWORKS 100
|
|
#define MAX_JSON_SIZE 16384
|
|
|
|
// Static buffer for JSON results
|
|
static char scan_results_json[MAX_JSON_SIZE];
|
|
static int network_count = 0;
|
|
|
|
// Helper function to get security type string
|
|
static const char* get_security_type(wifi_auth_mode_t authmode) {
|
|
switch (authmode) {
|
|
case WIFI_AUTH_OPEN:
|
|
return "Open";
|
|
case WIFI_AUTH_WEP:
|
|
return "WEP";
|
|
case WIFI_AUTH_WPA_PSK:
|
|
return "WPA PSK";
|
|
case WIFI_AUTH_WPA2_PSK:
|
|
return "WPA2 PSK";
|
|
case WIFI_AUTH_WPA_WPA2_PSK:
|
|
return "WPA/WPA2 PSK";
|
|
case WIFI_AUTH_WPA3_PSK:
|
|
return "WPA3 PSK";
|
|
case WIFI_AUTH_WPA2_WPA3_PSK:
|
|
return "WPA2/WPA3 PSK";
|
|
default:
|
|
return "Unknown";
|
|
}
|
|
}
|
|
|
|
// Helper function to determine band from channel
|
|
static const char* get_band(uint8_t channel) {
|
|
if (channel >= 1 && channel <= 13) {
|
|
return "2.4GHz";
|
|
}
|
|
if ((channel >= 36 && channel <= 144) || (channel >= 149 && channel <= 165)) {
|
|
return "5GHz";
|
|
}
|
|
return "Unknown";
|
|
}
|
|
|
|
// Helper function to get PHY mode string
|
|
static const char* get_phy_mode(const wifi_ap_record_t *ap) {
|
|
if (ap->phy_11n) {
|
|
return "802.11n";
|
|
}
|
|
if (ap->primary > 14) {
|
|
return "802.11a";
|
|
}
|
|
return "802.11b/g";
|
|
}
|
|
|
|
int wifi_scan_networks(void) {
|
|
ESP_LOGI(TAG, "Starting dual-band WiFi scan...");
|
|
|
|
// Clear previous results
|
|
network_count = 0;
|
|
scan_results_json[0] = '\0';
|
|
|
|
// Configure scan
|
|
wifi_scan_config_t scan_config = {
|
|
.ssid = NULL,
|
|
.bssid = NULL,
|
|
.channel = 0, // Scan all channels
|
|
.show_hidden = true,
|
|
.scan_type = WIFI_SCAN_TYPE_ACTIVE,
|
|
.scan_time.active.min = 50,
|
|
.scan_time.active.max = 100,
|
|
.scan_time.passive = 100
|
|
};
|
|
|
|
// Start scan
|
|
esp_err_t err = esp_wifi_scan_start(&scan_config, true);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "WiFi scan failed: %s", esp_err_to_name(err));
|
|
return -1;
|
|
}
|
|
|
|
// Get number of APs found
|
|
uint16_t ap_count = 0;
|
|
err = esp_wifi_scan_get_ap_num(&ap_count);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to get AP count: %s", esp_err_to_name(err));
|
|
return -1;
|
|
}
|
|
|
|
if (ap_count == 0) {
|
|
ESP_LOGI(TAG, "No networks found");
|
|
strcpy(scan_results_json, "[]");
|
|
network_count = 0;
|
|
return 0;
|
|
}
|
|
|
|
// Limit to MAX_NETWORKS
|
|
if (ap_count > MAX_NETWORKS) {
|
|
ap_count = MAX_NETWORKS;
|
|
}
|
|
|
|
// Allocate memory for AP records
|
|
wifi_ap_record_t *ap_records = malloc(sizeof(wifi_ap_record_t) * ap_count);
|
|
if (!ap_records) {
|
|
ESP_LOGE(TAG, "Failed to allocate memory for scan results");
|
|
return -1;
|
|
}
|
|
|
|
// Get AP records
|
|
err = esp_wifi_scan_get_ap_records(&ap_count, ap_records);
|
|
if (err != ESP_OK) {
|
|
ESP_LOGE(TAG, "Failed to get AP records: %s", esp_err_to_name(err));
|
|
free(ap_records);
|
|
return -1;
|
|
}
|
|
|
|
// Create JSON array
|
|
cJSON *networks_array = cJSON_CreateArray();
|
|
if (!networks_array) {
|
|
ESP_LOGE(TAG, "Failed to create JSON array");
|
|
free(ap_records);
|
|
return -1;
|
|
}
|
|
|
|
// Process each network
|
|
for (int i = 0; i < ap_count; i++) {
|
|
cJSON *network = cJSON_CreateObject();
|
|
if (!network) {
|
|
continue;
|
|
}
|
|
|
|
// SSID (handle hidden networks)
|
|
const char *ssid_str = (ap_records[i].ssid[0] == 0) ? "(hidden)" : (char*)ap_records[i].ssid;
|
|
cJSON_AddStringToObject(network, "ssid", ssid_str);
|
|
|
|
// BSSID
|
|
char bssid_str[18];
|
|
snprintf(bssid_str, sizeof(bssid_str), "%02x:%02x:%02x:%02x:%02x:%02x",
|
|
ap_records[i].bssid[0], ap_records[i].bssid[1], ap_records[i].bssid[2],
|
|
ap_records[i].bssid[3], ap_records[i].bssid[4], ap_records[i].bssid[5]);
|
|
cJSON_AddStringToObject(network, "bssid", bssid_str);
|
|
|
|
// Channel
|
|
cJSON_AddNumberToObject(network, "channel", ap_records[i].primary);
|
|
|
|
// Band
|
|
cJSON_AddStringToObject(network, "band", get_band(ap_records[i].primary));
|
|
|
|
// RSSI
|
|
cJSON_AddNumberToObject(network, "rssi", ap_records[i].rssi);
|
|
|
|
// Security
|
|
cJSON_AddStringToObject(network, "security", get_security_type(ap_records[i].authmode));
|
|
|
|
// PHY mode
|
|
cJSON_AddStringToObject(network, "phy_mode", get_phy_mode(&ap_records[i]));
|
|
|
|
// Second channel (if using 40MHz)
|
|
if (ap_records[i].second) {
|
|
cJSON_AddNumberToObject(network, "second_channel", ap_records[i].second);
|
|
}
|
|
|
|
// Hidden network flag
|
|
cJSON_AddBoolToObject(network, "is_hidden", (ap_records[i].ssid[0] == 0));
|
|
|
|
cJSON_AddItemToArray(networks_array, network);
|
|
}
|
|
|
|
// Generate JSON string
|
|
char *json_string = cJSON_PrintUnformatted(networks_array);
|
|
if (json_string) {
|
|
size_t json_len = strlen(json_string);
|
|
|
|
// Check if JSON is too large and warn
|
|
if (json_len >= MAX_JSON_SIZE) {
|
|
ESP_LOGW(TAG, "JSON too large (%zu >= %d), truncating", json_len, MAX_JSON_SIZE);
|
|
}
|
|
|
|
// Safe copy to static buffer with guaranteed null termination
|
|
size_t copy_len = (json_len < MAX_JSON_SIZE - 1) ? json_len : MAX_JSON_SIZE - 1;
|
|
memcpy(scan_results_json, json_string, copy_len);
|
|
scan_results_json[copy_len] = '\0';
|
|
|
|
free(json_string);
|
|
network_count = ap_count;
|
|
} else {
|
|
ESP_LOGE(TAG, "Failed to generate JSON string");
|
|
cJSON_Delete(networks_array);
|
|
free(ap_records);
|
|
return -1;
|
|
}
|
|
|
|
cJSON_Delete(networks_array);
|
|
free(ap_records);
|
|
|
|
ESP_LOGI(TAG, "Scan completed, found %d networks", network_count);
|
|
return 0;
|
|
}
|
|
|
|
const char* wifi_scan_get_results_json(void) {
|
|
if (network_count == 0 && scan_results_json[0] == '\0') {
|
|
return "[]";
|
|
}
|
|
return scan_results_json;
|
|
}
|
|
|
|
int wifi_scan_get_count(void) {
|
|
return network_count;
|
|
}
|