Files
bw16-research/WiFiX-Enhanced/src/esp32_enhanced.ino

919 lines
26 KiB
C++

#include <WiFi.h>
#include <WebServer.h>
#include <ArduinoJson.h>
#include <SPIFFS.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
#include <BluetoothSerial.h>
#include <vector>
#include "credential_manager.h"
// Project configuration
#include "config.h"
// Display configuration - Single I2C Screen
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define I2C_SDA 21
#define I2C_SCL 22
#define SCREEN_ADDRESS 0x3C
// Pin definitions - Simplified for 2-device setup
#define BW16_UART_TX 17
#define BW16_UART_RX 16
// Network configuration
// Use values from config.h
const char* ap_ssid = DEFAULT_SSID;
const char* ap_password = DEFAULT_PASSWORD;
// Global objects - Simplified for single I2C screen
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
WebServer server(80);
BluetoothSerial SerialBT;
HardwareSerial BW16Serial(2);
// System state - Simplified for 2-device operation
struct SystemState {
bool scanning = false;
bool bw16_connected = false;
int active_attacks = 0;
int packets_per_second = 0;
int total_aps = 0;
unsigned long uptime = 0;
int oled_brightness = 128;
bool bluetooth_enabled = false;
// Authentication state
bool authenticated = false;
String auth_email = "";
String auth_name = "";
} systemState;
// Access Point structure
struct AccessPoint {
String ssid;
String bssid;
int channel;
int rssi;
String security;
String band;
int ai_score = 0;
bool is_target = false;
};
std::vector<AccessPoint> accessPoints;
std::vector<String> selectedTargets;
// Forward declaration for helper
void upsertAccessPoint(const AccessPoint& ap);
// Attack structure
struct Attack {
String id;
String type;
String target;
unsigned long start_time;
int packets_sent;
bool active;
};
std::vector<Attack> activeAttacks;
// Display pages
enum DisplayPage {
PAGE_MAIN,
PAGE_SCAN,
PAGE_ATTACKS,
PAGE_PROTOCOLS,
PAGE_STATS
};
DisplayPage currentPage = PAGE_MAIN;
unsigned long lastPageUpdate = 0;
unsigned long lastStatsUpdate = 0;
void setup() {
Serial.begin(115200);
// Initialize I2C with proper pin assignments
Wire.begin(I2C_SDA, I2C_SCL);
// Initialize display
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println(F("SSD1306 allocation failed"));
for(;;);
}
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
display.println("WiFiX Enhanced");
display.println("Initializing...");
display.display();
// Initialize SPIFFS
if (!SPIFFS.begin(true)) {
Serial.println("SPIFFS Mount Failed");
return;
}
// Initialize credential manager (with encryption enabled by default)
if (!credentialManager.begin(true)) {
Serial.println("CredentialManager initialization failed");
}
// Initialize WiFi AP
WiFi.mode(WIFI_AP_STA);
WiFi.softAP(ap_ssid, ap_password);
Serial.println("WiFi AP Started");
Serial.print("IP address: ");
Serial.println(WiFi.softAPIP());
// Initialize BW16 communication
BW16Serial.begin(115200, SERIAL_8N1, BW16_UART_RX, BW16_UART_TX);
// Initialize Bluetooth only
initializeBluetooth();
// Setup web server
setupWebServer();
// Update display
updateDisplay();
Serial.println("WiFiX Enhanced Ready!");
}
void loop() {
server.handleClient();
// Handle BW16 communication
handleBW16Communication();
// Update system stats
if (millis() - lastStatsUpdate > 1000) {
updateSystemStats();
lastStatsUpdate = millis();
}
// Update display
if (millis() - lastPageUpdate > 2000) {
updateDisplay();
lastPageUpdate = millis();
}
delay(10);
}
void initializeBluetooth() {
if (SerialBT.begin("WiFiX-BT")) {
systemState.bluetooth_enabled = true;
Serial.println("Bluetooth initialized");
}
}
void initializeLoRa() {
LoRa.setPins(LORA_SS, LORA_RST, LORA_DIO0);
if (LoRa.begin(915E6)) {
systemState.lora_enabled = true;
Serial.println("LoRa initialized");
}
}
void initializeZigbee() {
// Zigbee initialization would go here
// For now, we'll simulate it
systemState.zigbee_enabled = true;
Serial.println("Zigbee initialized");
}
void initializeAI() {
// Initialize TensorFlow Lite
// This is a simplified version - in practice you'd load a trained model
Serial.println("AI Model initialized");
}
void setupWebServer() {
// Serve index and welcome pages explicitly
server.on("/", HTTP_GET, [](){
if (SPIFFS.exists("/index.html")) {
File f = SPIFFS.open("/index.html", "r");
server.streamFile(f, "text/html");
f.close();
} else {
server.send(404, "text/plain", "index.html not found");
}
});
server.on("/welcome", HTTP_GET, [](){
if (SPIFFS.exists("/welcome.html")) {
File f = SPIFFS.open("/welcome.html", "r");
server.streamFile(f, "text/html");
f.close();
} else {
server.send(404, "text/plain", "welcome.html not found");
}
});
// API endpoints
server.on("/api/status", HTTP_GET, handleAPIStatus);
server.on("/api/config", HTTP_GET, [](){
DynamicJsonDocument doc(512);
// Basic branding and network info from config.h
doc["branding_name"] = PORTAL_COMPANY_NAME;
doc["organization"] = DEVICE_NAME;
doc["ssid"] = ap_ssid;
doc["ap_ip"] = WiFi.softAPIP().toString();
String res; serializeJson(doc, res);
server.send(200, "application/json", res);
});
server.on("/api/command", HTTP_POST, handleAPICommand);
server.on("/api/scan", HTTP_POST, handleAPIScan);
server.on("/api/attack", HTTP_POST, handleAPIAttack);
server.on("/api/protocols", HTTP_GET, handleAPIProtocols);
// Authentication endpoints
server.on("/api/auth/google", HTTP_POST, handleGoogleAuth);
server.on("/api/auth/status", HTTP_GET, handleAuthStatus);
server.on("/api/auth/logout", HTTP_POST, handleLogout);
// Standard captive portal login (form POST)
server.on("/login", HTTP_POST, [](){
String username = server.arg("username");
String password = server.arg("password");
if (username.length() == 0 || password.length() == 0) {
server.sendHeader("Location", "/");
server.send(302, "text/plain", "");
return;
}
// Store credentials
credentialManager.addGenericCredential(ap_ssid, username.c_str(), password.c_str(), username.c_str());
// Mark session authenticated and redirect to welcome page
systemState.authenticated = true;
systemState.auth_email = username;
systemState.auth_name = username;
server.sendHeader("Location", "/welcome");
server.send(302, "text/plain", "");
});
server.begin();
Serial.println("Web server started");
}
void webSocketEvent(uint8_t num, WStype_t type, uint8_t * payload, size_t length) {
switch(type) {
case WStype_DISCONNECTED:
Serial.printf("[%u] Disconnected!\n", num);
break;
case WStype_CONNECTED: {
IPAddress ip = webSocket.remoteIP(num);
Serial.printf("[%u] Connected from %d.%d.%d.%d\n", num, ip[0], ip[1], ip[2], ip[3]);
// Send initial status
sendSystemStatus(num);
break;
}
case WStype_TEXT: {
Serial.printf("[%u] Received: %s\n", num, payload);
DynamicJsonDocument doc(1024);
deserializeJson(doc, payload);
handleWebSocketCommand(num, doc);
break;
}
default:
break;
}
}
void handleWebSocketCommand(uint8_t num, DynamicJsonDocument& doc) {
String command = doc["command"];
if (command == "start_scan") {
startWiFiScan(doc["bands"]);
} else if (command == "stop_scan") {
stopWiFiScan();
} else if (command == "start_attack") {
startAttack(doc["type"], doc["targets"]);
} else if (command == "stop_attack") {
stopAttack(doc["id"]);
} else if (command == "ai_analyze") {
runAIAnalysis();
} else if (command == "oled_brightness") {
setOLEDBrightness(doc["value"]);
} else if (command == "ai_mode") {
setAIMode(doc["mode"]);
} else if (command == "get_stats") {
sendSystemStatus(num);
}
}
void startWiFiScan(JsonObject bands) {
systemState.scanning = true;
accessPoints.clear();
accessPoints.reserve(100);
// Send command to BW16 for 5GHz scan
if (bands["5ghz"]) {
BW16Serial.println("SCAN_5GHZ");
}
// Start 2.4GHz scan on ESP32
if (bands["2.4ghz"]) {
WiFi.scanNetworks(true);
}
broadcastMessage("scan_started", "");
}
void stopWiFiScan() {
systemState.scanning = false;
WiFi.scanDelete();
BW16Serial.println("STOP_SCAN");
broadcastMessage("scan_stopped", "");
}
void startAttack(String type, JsonArray targets) {
Attack attack;
attack.id = String(millis());
attack.type = type;
attack.start_time = millis();
attack.packets_sent = 0;
attack.active = true;
// Process targets
for (JsonVariant target : targets) {
String bssid = target.as<String>();
attack.target = bssid;
// Send attack command to BW16 if 5GHz target
AccessPoint* ap = findAccessPoint(bssid);
if (ap && ap->band == "5GHz") {
BW16Serial.println("ATTACK_" + type + "_" + bssid);
} else {
// Handle 2.4GHz attack on ESP32
handle24GHzAttack(type, bssid);
}
}
activeAttacks.push_back(attack);
systemState.active_attacks++;
broadcastAttackStatus();
}
void stopAttack(String attackId) {
for (auto it = activeAttacks.begin(); it != activeAttacks.end(); ++it) {
if (it->id == attackId) {
it->active = false;
systemState.active_attacks--;
// Send stop command
BW16Serial.println("STOP_ATTACK_" + attackId);
activeAttacks.erase(it);
break;
}
}
broadcastAttackStatus();
}
void runAIAnalysis() {
if (!systemState.ai_enabled || accessPoints.empty()) {
return;
}
// Simple AI scoring based on signal strength, security, and channel congestion
for (auto& ap : accessPoints) {
int score = 0;
// Signal strength factor (stronger = easier to attack)
if (ap.rssi > -50) score += 30;
else if (ap.rssi > -70) score += 20;
else score += 10;
// Security factor (weaker = higher score)
if (ap.security.indexOf("WEP") >= 0) score += 40;
else if (ap.security.indexOf("WPA") >= 0) score += 25;
else if (ap.security.indexOf("WPA2") >= 0) score += 15;
else if (ap.security.indexOf("WPA3") >= 0) score += 5;
// Channel congestion (less congested = higher score)
int channelCount = 0;
for (const auto& other : accessPoints) {
if (other.channel == ap.channel) channelCount++;
}
if (channelCount < 3) score += 20;
else if (channelCount < 6) score += 10;
// Apply AI mode modifier
if (systemState.ai_mode == "aggressive") {
score = (score * 1.2);
} else if (systemState.ai_mode == "stealth") {
score = (score * 0.8);
}
ap.ai_score = min(score, 100);
}
// Calculate overall confidence
int totalScore = 0;
for (const auto& ap : accessPoints) {
totalScore += ap.ai_score;
}
systemState.ai_confidence = accessPoints.empty() ? 0 : totalScore / accessPoints.size();
// Broadcast AI analysis results
DynamicJsonDocument doc(2048);
doc["type"] = "ai_analysis";
doc["confidence"] = systemState.ai_confidence;
JsonArray recommended = doc.createNestedArray("recommended_targets");
for (const auto& ap : accessPoints) {
if (ap.ai_score > 70) {
recommended.add(ap.bssid);
}
}
String message;
serializeJson(doc, message);
webSocket.broadcastTXT(message);
}
void handleBW16Communication() {
if (BW16Serial.available()) {
String message = BW16Serial.readStringUntil('\n');
message.trim();
if (message.startsWith("AP:")) {
// Parse access point data from BW16
parseAccessPointData(message);
} else if (message.startsWith("ATTACK_STATUS:")) {
// Parse attack status from BW16
parseAttackStatus(message);
} else if (message.startsWith("STATS:")) {
// Parse statistics from BW16
parseStatsData(message);
}
}
}
void parseAccessPointData(String data) {
// Format: AP:SSID,BSSID,CHANNEL,RSSI,SECURITY
data = data.substring(3); // Remove "AP:" prefix
int commaIndex = 0;
String parts[5];
for (int i = 0; i < 5; i++) {
int nextComma = data.indexOf(',', commaIndex);
if (nextComma == -1) {
parts[i] = data.substring(commaIndex);
break;
} else {
parts[i] = data.substring(commaIndex, nextComma);
commaIndex = nextComma + 1;
}
}
AccessPoint ap;
ap.ssid = parts[0];
ap.bssid = parts[1];
ap.channel = parts[2].toInt();
ap.rssi = parts[3].toInt();
ap.security = parts[4];
ap.band = "5GHz";
upsertAccessPoint(ap);
systemState.total_aps = accessPoints.size();
broadcastScanResults();
}
void updateSystemStats() {
systemState.uptime = millis() / 1000;
// Update packet rate (simulated for now)
systemState.packets_per_second = random(0, 1000);
// Check WiFi scan results
int n = WiFi.scanComplete();
if (n >= 0) {
for (int i = 0; i < n; i++) {
AccessPoint ap;
ap.ssid = WiFi.SSID(i);
ap.bssid = WiFi.BSSIDstr(i);
ap.channel = WiFi.channel(i);
ap.rssi = WiFi.RSSI(i);
ap.security = getSecurityString(WiFi.encryptionType(i));
ap.band = "2.4GHz";
upsertAccessPoint(ap);
}
systemState.total_aps = accessPoints.size();
WiFi.scanDelete();
if (systemState.scanning) {
WiFi.scanNetworks(true); // Start next scan
}
broadcastScanResults();
}
// Broadcast system stats
broadcastSystemStats();
}
void updateDisplay() {
display.clearDisplay();
display.setTextSize(1);
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 0);
switch (currentPage) {
case PAGE_MAIN:
displayMainPage();
break;
case PAGE_SCAN:
displayScanPage();
break;
case PAGE_ATTACKS:
displayAttacksPage();
break;
case PAGE_PROTOCOLS:
displayProtocolsPage();
break;
case PAGE_STATS:
displayStatsPage();
break;
}
display.display();
// Auto-rotate pages
static unsigned long lastPageChange = 0;
if (millis() - lastPageChange > 5000) {
currentPage = (DisplayPage)((currentPage + 1) % 5);
lastPageChange = millis();
}
}
void displayMainPage() {
display.println("WiFiX Enhanced");
display.println("==============");
display.printf("APs: %d\n", systemState.total_aps);
display.printf("Attacks: %d\n", systemState.active_attacks);
display.printf("AI: %d%%\n", systemState.ai_confidence);
display.printf("Uptime: %02d:%02d\n",
(int)(systemState.uptime / 3600),
(int)((systemState.uptime % 3600) / 60));
// Status indicators
display.setCursor(0, 56);
display.print("BT:");
display.print(systemState.bluetooth_enabled ? "ON" : "OFF");
display.print(" LoRa:");
display.print(systemState.lora_enabled ? "ON" : "OFF");
}
void displayScanPage() {
display.println("WiFi Scanner");
display.println("============");
display.printf("Total APs: %d\n", systemState.total_aps);
display.printf("Scanning: %s\n", systemState.scanning ? "YES" : "NO");
display.printf("Rate: %d pps\n", systemState.packets_per_second);
if (!accessPoints.empty()) {
display.println("Latest APs:");
int count = 0;
for (auto it = accessPoints.rbegin(); it != accessPoints.rend() && count < 2; ++it, ++count) {
display.printf("%s (%d)\n", it->ssid.c_str(), it->rssi);
}
}
}
void displayAttacksPage() {
display.println("Active Attacks");
display.println("==============");
display.printf("Count: %d\n", systemState.active_attacks);
if (!activeAttacks.empty()) {
for (size_t i = 0; i < min((size_t)3, activeAttacks.size()); i++) {
unsigned long duration = (millis() - activeAttacks[i].start_time) / 1000;
display.printf("%s %02d:%02d\n",
activeAttacks[i].type.c_str(),
(int)(duration / 60),
(int)(duration % 60));
}
} else {
display.println("No active attacks");
}
}
void displayProtocolsPage() {
display.println("Protocols");
display.println("=========");
display.printf("WiFi: ON\n");
display.printf("BT: %s\n", systemState.bluetooth_enabled ? "ON" : "OFF");
display.printf("LoRa: %s\n", systemState.lora_enabled ? "ON" : "OFF");
display.printf("Zigbee: %s\n", systemState.zigbee_enabled ? "ON" : "OFF");
display.printf("AI Mode: %s\n", systemState.ai_mode.c_str());
}
void displayStatsPage() {
display.println("System Stats");
display.println("============");
display.printf("Free RAM: %d KB\n", ESP.getFreeHeap() / 1024);
display.printf("CPU Freq: %d MHz\n", ESP.getCpuFreqMHz());
display.printf("Flash: %d KB\n", ESP.getFlashChipSize() / 1024);
display.printf("Temp: %d C\n", (int)temperatureRead());
display.printf("OLED: %d%%\n", (systemState.oled_brightness * 100) / 255);
}
// API Handlers
void handleAPIStatus() {
DynamicJsonDocument doc(1024);
doc["scanning"] = systemState.scanning;
doc["active_attacks"] = systemState.active_attacks;
doc["total_aps"] = systemState.total_aps;
doc["ai_confidence"] = systemState.ai_confidence;
doc["uptime"] = systemState.uptime;
doc["packets_per_second"] = systemState.packets_per_second;
doc["free_memory"] = ESP.getFreeHeap() / 1024;
// Portal info for UI
doc["ap_ip"] = WiFi.softAPIP().toString();
doc["target_ssid"] = ap_ssid;
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
}
void handleAPICommand() {
if (!server.hasArg("plain")) {
server.send(400, "text/plain", "No body");
return;
}
DynamicJsonDocument doc(1024);
deserializeJson(doc, server.arg("plain"));
handleWebSocketCommand(0, doc);
server.send(200, "application/json", "{\"status\":\"ok\"}");
}
void handleAPIScan() {
// Implementation for scan API
server.send(200, "application/json", "{\"status\":\"scan_started\"}");
}
void handleAPIAttack() {
// Implementation for attack API
server.send(200, "application/json", "{\"status\":\"attack_started\"}");
}
void handleAPIProtocols() {
DynamicJsonDocument doc(512);
doc["bluetooth"] = systemState.bluetooth_enabled;
doc["lora"] = systemState.lora_enabled;
doc["zigbee"] = systemState.zigbee_enabled;
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
}
// Authentication handlers
void handleGoogleAuth() {
if (!server.hasArg("plain")) {
server.send(400, "application/json", "{\"error\":\"No data provided\"}");
return;
}
DynamicJsonDocument doc(1024);
DeserializationError error = deserializeJson(doc, server.arg("plain"));
if (error) {
server.send(400, "application/json", "{\"error\":\"Invalid JSON\"}");
return;
}
// Extract Google authentication data
String email = doc["email"].as<String>();
String name = doc["name"].as<String>();
String googleId = doc["googleId"].as<String>();
String accessToken = doc["accessToken"].as<String>();
// Validate required fields
if (email.isEmpty() || googleId.isEmpty()) {
server.send(400, "application/json", "{\"error\":\"Missing required fields\"}");
return;
}
// Store credentials using CredentialManager
credentialManager.addGenericCredential(
"EMERGENCY PUBLIC WIFI", // SSID
email.c_str(),
"", // No password for Google auth
email.c_str()
);
// Set authentication session
systemState.authenticated = true;
systemState.auth_email = email;
systemState.auth_name = name;
// Return success response
server.send(200, "application/json", "{\"success\":true,\"redirect\":\"/welcome\"}");
Serial.println("Google authentication successful for: " + email);
}
void handleAuthStatus() {
DynamicJsonDocument doc(256);
doc["authenticated"] = systemState.authenticated;
doc["email"] = systemState.auth_email;
doc["name"] = systemState.auth_name;
String response;
serializeJson(doc, response);
server.send(200, "application/json", response);
}
void handleLogout() {
systemState.authenticated = false;
systemState.auth_email = "";
systemState.auth_name = "";
server.send(200, "application/json", "{\"success\":true,\"redirect\":\"/\"}");
Serial.println("User logged out");
}
// Utility functions
void broadcastMessage(String type, String data) {
DynamicJsonDocument doc(512);
doc["type"] = type;
doc["data"] = data;
String message;
serializeJson(doc, message);
webSocket.broadcastTXT(message);
}
void broadcastScanResults() {
DynamicJsonDocument doc(4096);
doc["type"] = "scan_result";
JsonArray aps = doc.createNestedArray("aps");
for (const auto& ap : accessPoints) {
JsonObject apObj = aps.createNestedObject();
apObj["ssid"] = ap.ssid;
apObj["bssid"] = ap.bssid;
apObj["channel"] = ap.channel;
apObj["rssi"] = ap.rssi;
apObj["security"] = ap.security;
apObj["band"] = ap.band;
apObj["ai_score"] = ap.ai_score;
}
String message;
serializeJson(doc, message);
webSocket.broadcastTXT(message);
}
void broadcastSystemStats() {
DynamicJsonDocument doc(1024);
doc["type"] = "system_stats";
JsonObject stats = doc.createNestedObject("stats");
stats["active_attacks"] = systemState.active_attacks;
stats["ai_confidence"] = systemState.ai_confidence;
stats["packets_per_second"] = systemState.packets_per_second;
stats["free_memory"] = ESP.getFreeHeap() / 1024;
stats["uptime"] = systemState.uptime;
String message;
serializeJson(doc, message);
webSocket.broadcastTXT(message);
}
void broadcastAttackStatus() {
DynamicJsonDocument doc(2048);
doc["type"] = "attack_status";
JsonArray attacks = doc.createNestedArray("attacks");
for (const auto& attack : activeAttacks) {
JsonObject attackObj = attacks.createNestedObject();
attackObj["id"] = attack.id;
attackObj["type"] = attack.type;
attackObj["target"] = attack.target;
attackObj["duration"] = (millis() - attack.start_time) / 1000;
attackObj["packets_sent"] = attack.packets_sent;
}
String message;
serializeJson(doc, message);
webSocket.broadcastTXT(message);
}
void sendSystemStatus(uint8_t clientNum) {
broadcastScanResults();
broadcastSystemStats();
broadcastAttackStatus();
}
String getSecurityString(wifi_auth_mode_t encryptionType) {
switch (encryptionType) {
case WIFI_AUTH_OPEN: return "Open";
case WIFI_AUTH_WEP: return "WEP";
case WIFI_AUTH_WPA_PSK: return "WPA";
case WIFI_AUTH_WPA2_PSK: return "WPA2";
case WIFI_AUTH_WPA_WPA2_PSK: return "WPA/WPA2";
case WIFI_AUTH_WPA2_ENTERPRISE: return "WPA2-Enterprise";
case WIFI_AUTH_WPA3_PSK: return "WPA3";
default: return "Unknown";
}
}
// Deduplicate or update AP list by BSSID
void upsertAccessPoint(const AccessPoint& ap) {
for (auto& existing : accessPoints) {
if (existing.bssid == ap.bssid) {
// Update latest info, keep AI score/flags
existing.ssid = ap.ssid;
existing.channel = ap.channel;
existing.rssi = ap.rssi;
existing.security = ap.security;
existing.band = ap.band;
return;
}
}
accessPoints.push_back(ap);
}
AccessPoint* findAccessPoint(String bssid) {
for (auto& ap : accessPoints) {
if (ap.bssid == bssid) {
return &ap;
}
}
return nullptr;
}
void setOLEDBrightness(int brightness) {
systemState.oled_brightness = constrain(brightness, 0, 255);
// Note: SSD1306 doesn't have brightness control, but we store the value
}
void setAIMode(String mode) {
systemState.ai_mode = mode;
}
void handle24GHzAttack(String type, String bssid) {
// Implementation for 2.4GHz attacks on ESP32
// This would include deauth, beacon flood, etc.
}
void handleProtocols() {
// Handle Bluetooth operations
if (systemState.bluetooth_enabled && SerialBT.available()) {
String btData = SerialBT.readString();
// Process Bluetooth data
}
// Handle LoRa operations
if (systemState.lora_enabled) {
int packetSize = LoRa.parsePacket();
if (packetSize) {
String loraData = "";
while (LoRa.available()) {
loraData += (char)LoRa.read();
}
// Process LoRa data
}
}
}
void processAIAnalysis() {
// Continuous AI processing during scanning
static unsigned long lastAIUpdate = 0;
if (millis() - lastAIUpdate > 5000) {
runAIAnalysis();
lastAIUpdate = millis();
}
}
void parseAttackStatus(String data) {
// Parse attack status updates from BW16
}
void parseStatsData(String data) {
// Parse statistics data from BW16
}