/*
╔══════════════════════════════════════════════════════╗
║ PRIVACY GUARDIAN — FULL SYSTEM SIMULATION ║
║ ESP32-C6 | Trinity College IoT Project ║
║ Wokwi Simulation Version ║
╚══════════════════════════════════════════════════════╝
SENSORS SIMULATED:
┌─────────────────────────────────────────────────────┐
│ 1. WiFi/BLE scanning → Mock AP list │
│ 2. RF 433MHz detection → Potentiometer (GPIO2) │
│ 3. EMF / Hall-effect → Potentiometer (GPIO3) │
│ 4. Audio INMP441 ×4 → Potentiometer (GPIO4) │
│ 5. Threat inject button → GPIO9 (simulate event) │
└─────────────────────────────────────────────────────┘
ACTUATORS:
┌─────────────────────────────────────────────────────┐
│ • Pan Servo → GPIO18 (directional targeting) │
│ • Tilt Servo → GPIO19 (elevation pointing) │
│ • Red LED → GPIO4 (THREAT) │
│ • Green LED → GPIO5 (SAFE) │
│ • Yellow LED → GPIO6 (SCANNING) │
│ • Buzzer → GPIO8 (alert tone) │
│ • OLED 128×64 → I2C SDA=21, SCL=22 │
└─────────────────────────────────────────────────────┘
*/
#include <WiFi.h>
#include <ESP32Servo.h>
#include <Wire.h>
#include <Adafruit_GFX.h>
#include <Adafruit_SSD1306.h>
// ==================== PIN DEFINITIONS ====================
#define BUZZER_PIN 8
#define RED_LED 4
#define GREEN_LED 5
#define YELLOW_LED 6
#define PAN_SERVO 18
#define TILT_SERVO 19
#define RF_PIN 2 // Potentiometer → simulates 433MHz RF signal strength
#define EMF_PIN 3 // Potentiometer → simulates EMF / Hall-effect level
#define AUDIO_PIN 10 // Microphone AO → real wokwi-microphone analog output
#define THREAT_BTN 9 // Push button → injects a sudden threat event
// Microphone sampling — mic signal fluctuates rapidly so we
// take peak-to-peak amplitude over a short window for stable loudness
#define MIC_SAMPLE_COUNT 64
#define MIC_SAMPLE_MS 50
// ==================== OLED CONFIG ====================
#define SCREEN_WIDTH 128
#define SCREEN_HEIGHT 64
#define OLED_RESET -1
#define OLED_SDA 21
#define OLED_SCL 22
#define SCREEN_ADDRESS 0x3C
Adafruit_SSD1306 display(SCREEN_WIDTH, SCREEN_HEIGHT, &Wire, OLED_RESET);
// ==================== THREAT THRESHOLDS ====================
#define WIFI_THREAT_THRESHOLD 30 // WiFi score to trigger alert
#define RF_THRESHOLD 600 // Raw ADC value (0–4095) → threat
#define EMF_THRESHOLD 700 // Raw ADC value → threat
#define AUDIO_THRESHOLD 650 // Raw ADC value → covert recording suspect
// ==================== WIFI DEVICE TRACKING ====================
#define MAX_DEVICES 30
#define MAX_WHITELIST 50
#define SCORE_STRONG 10
#define SCORE_MEDIUM 5
#define SCORE_WEAK 2
#define DEVICE_TIMEOUT_MS 8000
#define SCORE_DECAY 15
struct Device {
String mac;
int smoothedRSSI;
int threatScore;
unsigned long lastSeen;
bool isWhitelisted;
bool counted;
};
Device devices[MAX_DEVICES];
int deviceCount = 0;
String whitelist[MAX_WHITELIST];
int whitelistCount = 0;
// ==================== SENSOR READINGS ====================
int rfLevel = 0;
int emfLevel = 0;
int audioLevel = 0;
bool rfAlert = false;
bool emfAlert = false;
bool audioAlert = false;
bool wifiAlert = false;
bool btnInjected = false;
// ==================== SYSTEM STATE ====================
int totalThreats = 0;
String currentThreatMAC = "";
int currentThreatRSSI = 0;
int wifiHighScore = 0;
// Threat source flags for display
bool threatFromWifi = false;
bool threatFromRF = false;
bool threatFromEMF = false;
bool threatFromAudio = false;
bool threatFromBtn = false;
// ==================== SERVO ====================
Servo panServo;
Servo tiltServo;
// ==================== TIMING ====================
unsigned long lastScan = 0;
unsigned long lastDisplay = 0;
unsigned long lastSensor = 0;
unsigned long lastBuzzer = 0;
bool buzzerOn = false;
const long scanInterval = 4000;
const long displayInterval = 500;
const long sensorInterval = 800;
// ==================== DISPLAY PAGE ====================
int displayPage = 0; // 0 = overview, 1 = sensors, 2 = wifi detail
// =====================================================
// SETUP
// =====================================================
void setup() {
Serial.begin(115200);
delay(500);
printBanner("PRIVACY GUARDIAN — FULL SYSTEM");
// GPIO setup
pinMode(BUZZER_PIN, OUTPUT);
pinMode(RED_LED, OUTPUT);
pinMode(GREEN_LED, OUTPUT);
pinMode(YELLOW_LED, OUTPUT);
pinMode(THREAT_BTN, INPUT_PULLUP);
allLedsOff();
// OLED
Wire.begin(OLED_SDA, OLED_SCL);
if (!display.begin(SSD1306_SWITCHCAPVCC, SCREEN_ADDRESS)) {
Serial.println("[ERROR] OLED init failed!");
while (true);
}
Serial.println("[OK] OLED ready");
showBootScreen();
// Servos
panServo.attach(PAN_SERVO);
tiltServo.attach(TILT_SERVO);
panServo.write(90);
tiltServo.write(60);
Serial.println("[OK] Servos centered");
// WiFi
WiFi.mode(WIFI_STA);
WiFi.disconnect(true);
delay(500);
Serial.println("[OK] WiFi ready");
performWhitelistScan();
digitalWrite(GREEN_LED, HIGH);
Serial.println("\n[OK] ALL SYSTEMS READY");
Serial.println("================================================");
Serial.println(" SENSOR MAP:");
Serial.println(" GPIO2 (pot) → RF 433MHz level");
Serial.println(" GPIO3 (pot) → EMF / Hall-effect level");
Serial.println(" GPIO10(pot) → Audio (4-mic array)");
Serial.println(" GPIO9 (btn) → Inject threat event");
Serial.println("================================================\n");
}
// =====================================================
// LOOP
// =====================================================
void loop() {
// Analog sensor readings (RF, EMF, Audio)
if (millis() - lastSensor > sensorInterval) {
lastSensor = millis();
readAnalogSensors();
}
// WiFi scan
if (millis() - lastScan > scanInterval) {
lastScan = millis();
intelligentScan();
}
// Display refresh
if (millis() - lastDisplay > displayInterval) {
lastDisplay = millis();
displayPage = (displayPage + 1) % 3;
updateDisplay();
}
// Buzzer non-blocking pulse
handleBuzzer();
}
// =====================================================
// BOOT SCREEN
// =====================================================
void showBootScreen() {
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
// Title block
display.fillRect(0, 0, 128, 18, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
display.setTextSize(1);
display.setCursor(8, 5);
display.println("PRIVACY GUARDIAN");
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.setCursor(10, 24);
display.println("Trinity College IoT");
display.setCursor(20, 36);
display.println("Initialising...");
display.setCursor(0, 52);
display.println("WiFi RF EMF Audio Servo");
display.display();
delay(2500);
display.clearDisplay();
display.display();
Serial.println("[DISPLAY] Boot screen shown");
}
// =====================================================
// WHITELIST SCAN (simulated — no real WiFi needed)
// =====================================================
void performWhitelistScan() {
Serial.println("\n[WHITELIST] Running 5s simulated baseline scan...");
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.fillRect(0, 0, 128, 12, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
display.setCursor(4, 2);
display.println("BASELINE SCAN");
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 18);
display.println("Learning environment...");
display.setCursor(0, 30);
display.println("Please wait 5s");
display.display();
// Simulate scanning with blinking yellow
unsigned long t = millis();
while (millis() - t < 5000) {
digitalWrite(YELLOW_LED, !digitalRead(YELLOW_LED));
delay(300);
}
digitalWrite(YELLOW_LED, LOW);
// Pre-populate whitelist with "known safe" devices
String safeDevices[] = {
"F4:CF:A2:11:22:33",
"B8:27:EB:44:55:66",
"DC:A6:32:77:88:99",
"48:3B:38:AA:BB:CC",
"3C:22:FB:DD:EE:FF"
};
for (int i = 0; i < 5; i++) {
whitelist[whitelistCount++] = safeDevices[i];
Serial.print("[WHITELIST] Added safe: ");
Serial.println(safeDevices[i]);
}
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
display.setTextSize(1);
display.fillRect(0, 0, 128, 12, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
display.setCursor(4, 2);
display.println("BASELINE COMPLETE");
display.setTextColor(SSD1306_WHITE);
display.setCursor(0, 18);
display.print("Whitelisted: ");
display.println(whitelistCount);
display.setCursor(0, 30);
display.println("Monitoring started.");
display.display();
delay(2000);
Serial.print("[WHITELIST] Total safe devices: ");
Serial.println(whitelistCount);
}
// =====================================================
// ANALOG SENSOR READING
// RF: pot on GPIO2 | EMF: pot on GPIO3 | Audio: pot on GPIO10
// =====================================================
void readAnalogSensors() {
rfLevel = analogRead(RF_PIN);
emfLevel = analogRead(EMF_PIN);
audioLevel = analogRead(AUDIO_PIN); // Pot simulating INMP441 mic array level
// Read button (active LOW)
btnInjected = (digitalRead(THREAT_BTN) == LOW);
rfAlert = (rfLevel > RF_THRESHOLD);
emfAlert = (emfLevel > EMF_THRESHOLD);
audioAlert = (audioLevel > AUDIO_THRESHOLD);
// Serial telemetry
Serial.print("[SENSORS] RF:");
Serial.print(rfLevel);
Serial.print(rfAlert ? "! " : " ");
Serial.print("EMF:");
Serial.print(emfLevel);
Serial.print(emfAlert ? "! " : " ");
Serial.print("MIC(peak-pk):");
Serial.print(audioLevel);
Serial.print(audioAlert ? "! " : " ");
Serial.print("BTN:");
Serial.println(btnInjected ? "PRESSED" : "off");
evaluateAnalogThreats();
}
// =====================================================
// ANALOG THREAT EVALUATION
// =====================================================
void evaluateAnalogThreats() {
bool anyAnalogThreat = rfAlert || emfAlert || audioAlert || btnInjected;
threatFromRF = rfAlert;
threatFromEMF = emfAlert;
threatFromAudio = audioAlert;
threatFromBtn = btnInjected;
if (anyAnalogThreat) {
Serial.println("\n *** ANALOG THREAT DETECTED ***");
if (rfAlert) Serial.println(" [RF] 433MHz signal above threshold!");
if (emfAlert) Serial.println(" [EMF] Hall-effect anomaly detected!");
if (audioAlert) Serial.println(" [AUDIO] Covert recording suspected!");
if (btnInjected) Serial.println(" [BTN] Manual threat injection!");
activateAlerts();
// Point servo toward estimated direction based on signal strength
int angle = map(max({rfLevel, emfLevel, audioLevel}), 0, 4095, 20, 160);
panServo.write(constrain(angle, 20, 160));
// Tilt servo based on audio level (elevation estimate)
int tilt = map(audioLevel, 0, 4095, 40, 120);
tiltServo.write(constrain(tilt, 40, 120));
Serial.print(" [SERVO] Pan=");
Serial.print(constrain(angle, 20, 160));
Serial.print("° Tilt=");
Serial.println(constrain(tilt, 40, 120));
if (!wifiAlert) totalThreats++; // Only count if WiFi didn't already count it
} else if (!wifiAlert) {
safeMode();
}
}
// =====================================================
// WiFi INTELLIGENT SCAN (mocked for Wokwi)
// =====================================================
void intelligentScan() {
Serial.println("\n[WIFI SCAN] ======== New Scan ========");
// Scenario: 4 APs cycle in/out to simulate a real environment
// The first 5 MACs match the whitelist — safe devices
// The rest are unknown suspects
struct FakeAP { String mac; String ssid; int rssi; };
static int scanCycle = 0;
scanCycle++;
// Core always-present whitelist devices
FakeAP safeAPs[] = {
{ "F4:CF:A2:11:22:33", "HomeRouter_5G", -65 },
{ "B8:27:EB:44:55:66", "OfficeNet", -72 },
{ "DC:A6:32:77:88:99", "IoT_Hub", -80 },
};
// Unknown devices — rotate to simulate device appearing / disappearing
FakeAP suspectAPs[] = {
{ "42:76:72:C1:07:CE", "Nothing Phone", -31 }, // The real device from project slides!
{ "AA:BB:CC:DD:EE:01", "Neighbor_WiFi", -55 },
{ "AA:BB:CC:DD:EE:02", "Hidden", -80 },
{ "AA:BB:CC:DD:EE:03", "CoffeeShop_5G", -68 },
{ "DE:AD:BE:EF:00:01", "AndroidAP_7231", -44 }, // Strong hidden hotspot
};
// Vary which suspects appear each cycle to simulate real scanning
bool showSuspect[5];
showSuspect[0] = (scanCycle % 5 >= 1); // Nothing Phone — appears after 1 cycle
showSuspect[1] = (scanCycle % 7 >= 2);
showSuspect[2] = (scanCycle % 4 >= 2);
showSuspect[3] = (scanCycle % 6 >= 3);
showSuspect[4] = (scanCycle % 8 >= 4); // Strong hotspot — appears less often
bool seenThisScan[MAX_DEVICES] = {false};
int unknownCount = 0;
// Process safe APs
for (int i = 0; i < 3; i++) {
processAP(safeAPs[i].mac, safeAPs[i].ssid, safeAPs[i].rssi, seenThisScan, unknownCount);
}
// Process suspect APs based on cycle
for (int i = 0; i < 5; i++) {
if (showSuspect[i]) {
processAP(suspectAPs[i].mac, suspectAPs[i].ssid, suspectAPs[i].rssi, seenThisScan, unknownCount);
}
}
decayMissingDevices(seenThisScan);
Serial.print("[WIFI SCAN] Unknown devices seen: ");
Serial.println(unknownCount);
evaluateWifiThreats();
}
// =====================================================
// PROCESS A SINGLE AP (helper for intelligentScan)
// =====================================================
void processAP(String mac, String ssid, int rssi, bool seenThisScan[], int &unknownCount) {
bool wl = isWhitelisted(mac);
Serial.print(wl ? " [SAFE] " : " [SUSPECT] ");
Serial.print("MAC: "); Serial.print(mac);
Serial.print(" | \""); Serial.print(ssid);
Serial.print("\" | RSSI: "); Serial.println(rssi);
if (!wl) unknownCount++;
int index = findDevice(mac);
if (index == -1 && deviceCount < MAX_DEVICES) {
index = deviceCount++;
devices[index] = { mac, rssi, 0, millis(), wl, false };
}
if (index >= 0) {
devices[index].smoothedRSSI =
(int)(0.7f * devices[index].smoothedRSSI + 0.3f * rssi);
devices[index].lastSeen = millis();
seenThisScan[index] = true;
if (!devices[index].isWhitelisted)
updateThreatScore(index);
}
}
// =====================================================
// DECAY ABSENT DEVICES
// =====================================================
void decayMissingDevices(bool seenThisScan[]) {
for (int i = 0; i < deviceCount; i++) {
if (!devices[i].isWhitelisted && !seenThisScan[i]) {
unsigned long absentFor = millis() - devices[i].lastSeen;
if (absentFor > DEVICE_TIMEOUT_MS) {
int old = devices[i].threatScore;
devices[i].threatScore -= SCORE_DECAY;
if (devices[i].threatScore < 0) devices[i].threatScore = 0;
if (devices[i].threatScore == 0) devices[i].counted = false;
Serial.print(" [DECAY] "); Serial.print(devices[i].mac);
Serial.print(" | "); Serial.print(old); Serial.print("→");
Serial.println(devices[i].threatScore);
}
}
}
}
// =====================================================
// UPDATE THREAT SCORE
// =====================================================
void updateThreatScore(int i) {
int rssi = devices[i].smoothedRSSI;
if (rssi > -60) devices[i].threatScore += SCORE_STRONG;
else if (rssi > -75) devices[i].threatScore += SCORE_MEDIUM;
else devices[i].threatScore += SCORE_WEAK;
if (devices[i].threatScore > 100) devices[i].threatScore = 100;
Serial.print(" Score: ");
Serial.print(devices[i].threatScore);
Serial.print("/100 (need ");
Serial.print(WIFI_THREAT_THRESHOLD);
Serial.println(" to alert)");
}
// =====================================================
// EVALUATE WiFi THREATS
// =====================================================
void evaluateWifiThreats() {
int highestScore = 0;
int threatIndex = -1;
for (int i = 0; i < deviceCount; i++) {
if (!devices[i].isWhitelisted && devices[i].threatScore > highestScore) {
highestScore = devices[i].threatScore;
threatIndex = i;
}
}
wifiHighScore = highestScore;
threatFromWifi = (highestScore >= WIFI_THREAT_THRESHOLD);
if (threatFromWifi) {
currentThreatMAC = devices[threatIndex].mac;
currentThreatRSSI = devices[threatIndex].smoothedRSSI;
if (!devices[threatIndex].counted) {
totalThreats++;
devices[threatIndex].counted = true;
}
Serial.println("\n !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Serial.println(" !!! WiFi THREAT DETECTED !!!");
Serial.println(" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!");
Serial.print (" MAC: "); Serial.println(currentThreatMAC);
Serial.print (" RSSI: "); Serial.println(currentThreatRSSI);
Serial.print (" Score: "); Serial.print(highestScore); Serial.println("/100");
Serial.print (" Total: "); Serial.println(totalThreats);
Serial.println(" !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!\n");
activateAlerts();
int angle = map(currentThreatRSSI, -90, -30, 0, 180);
panServo.write(constrain(angle, 20, 160));
} else {
if (!rfAlert && !emfAlert && !audioAlert && !btnInjected) {
safeMode();
currentThreatMAC = "";
currentThreatRSSI = 0;
}
if (highestScore > 0) {
Serial.print("[WIFI] Monitoring — score: ");
Serial.print(highestScore); Serial.print("/100 (need ");
Serial.print(WIFI_THREAT_THRESHOLD); Serial.println(")");
}
}
}
// =====================================================
// ALERT MODE
// =====================================================
void activateAlerts() {
allLedsOff();
digitalWrite(RED_LED, HIGH);
triggerBuzzer(150);
}
// =====================================================
// SAFE MODE
// =====================================================
void safeMode() {
allLedsOff();
digitalWrite(GREEN_LED, HIGH);
panServo.write(90);
tiltServo.write(60);
}
// =====================================================
// NON-BLOCKING BUZZER
// =====================================================
void triggerBuzzer(int durationMs) {
digitalWrite(BUZZER_PIN, HIGH);
lastBuzzer = millis();
buzzerOn = true;
}
void handleBuzzer() {
if (buzzerOn && millis() - lastBuzzer > 200) {
digitalWrite(BUZZER_PIN, LOW);
buzzerOn = false;
}
}
// =====================================================
// ALL LEDs OFF
// =====================================================
void allLedsOff() {
digitalWrite(RED_LED, LOW);
digitalWrite(GREEN_LED, LOW);
digitalWrite(YELLOW_LED, LOW);
}
// =====================================================
// DISPLAY — 3 rotating pages
// =====================================================
void updateDisplay() {
bool anyThreat = threatFromWifi || threatFromRF || threatFromEMF ||
threatFromAudio || threatFromBtn;
display.clearDisplay();
display.setTextColor(SSD1306_WHITE);
// ---- Header bar ----
if (anyThreat) {
display.fillRect(0, 0, 128, 12, SSD1306_WHITE);
display.setTextColor(SSD1306_BLACK);
}
display.setTextSize(1);
display.setCursor(4, 2);
display.print("PRIVACY GUARDIAN");
display.setTextColor(SSD1306_WHITE);
// ---- Page routing ----
if (displayPage == 0) drawOverviewPage(anyThreat);
else if (displayPage == 1) drawSensorsPage();
else drawWifiPage();
display.display();
}
// ---- Page 0: Overview ----
void drawOverviewPage(bool anyThreat) {
display.setCursor(0, 15);
if (anyThreat) {
display.setTextSize(2);
display.setCursor(8, 18);
display.println("!! THREAT !!");
display.setTextSize(1);
display.setCursor(0, 42);
display.print("Total: "); display.print(totalThreats);
display.setCursor(0, 52);
// Show which sensors triggered
if (threatFromWifi) display.print("WiFi ");
if (threatFromRF) display.print("RF ");
if (threatFromEMF) display.print("EMF ");
if (threatFromAudio) display.print("Audio");
if (threatFromBtn) display.print("Manual");
} else {
display.setTextSize(2);
display.setCursor(14, 20);
display.println("** SAFE **");
display.setTextSize(1);
display.setCursor(0, 44);
display.print("Threats logged: "); display.println(totalThreats);
display.setCursor(0, 54);
display.print("Devices: "); display.println(deviceCount);
}
}
// ---- Page 1: Sensor Levels ----
void drawSensorsPage() {
display.setTextSize(1);
display.setCursor(0, 15);
display.println("-- SENSOR LEVELS --");
// RF bar
display.setCursor(0, 27);
display.print("RF ");
int rfBar = map(rfLevel, 0, 4095, 0, 70);
display.fillRect(24, 27, rfBar, 6, SSD1306_WHITE);
if (rfAlert) { display.setCursor(100, 27); display.print("ALERT"); }
// EMF bar
display.setCursor(0, 38);
display.print("EMF ");
int emfBar = map(emfLevel, 0, 4095, 0, 70);
display.fillRect(24, 38, emfBar, 6, SSD1306_WHITE);
if (emfAlert) { display.setCursor(100, 38); display.print("ALERT"); }
// Audio bar
display.setCursor(0, 49);
display.print("MIC ");
int audBar = map(audioLevel, 0, 4095, 0, 70);
display.fillRect(24, 49, audBar, 6, SSD1306_WHITE);
if (audioAlert) { display.setCursor(100, 49); display.print("ALERT"); }
}
// ---- Page 2: WiFi Detail ----
void drawWifiPage() {
display.setTextSize(1);
display.setCursor(0, 15);
display.println("-- WiFi SCAN --");
if (currentThreatMAC != "") {
display.setCursor(0, 27);
display.print("THR:");
display.println(currentThreatMAC.substring(currentThreatMAC.length() - 8));
display.setCursor(0, 38);
display.print("RSSI: "); display.print(currentThreatRSSI); display.println(" dBm");
display.setCursor(0, 49);
display.print("Score: "); display.print(wifiHighScore); display.println("/100");
} else {
display.setCursor(0, 27);
display.print("Devices: "); display.println(deviceCount);
display.setCursor(0, 38);
display.print("Unknown: ");
int unk = 0;
for (int i = 0; i < deviceCount; i++)
if (!devices[i].isWhitelisted) unk++;
display.println(unk);
display.setCursor(0, 49);
display.print("Best score: "); display.print(wifiHighScore);
}
}
// =====================================================
// MICROPHONE — Peak-to-peak amplitude sampling
// Samples the mic over a short window and returns
// the peak-to-peak range as a 0–4095 loudness value.
// This is how real mic circuits measure sound level.
// =====================================================
int readMicLevel() {
int micMax = 0;
int micMin = 4095;
unsigned long start = millis();
while (millis() - start < MIC_SAMPLE_MS) {
int sample = analogRead(AUDIO_PIN);
if (sample > micMax) micMax = sample;
if (sample < micMin) micMin = sample;
}
int peakToPeak = micMax - micMin;
// Scale: a completely silent mic gives ~0 peak-to-peak
// A loud sound gives a large swing. Map to 0–4095 range.
return constrain(map(peakToPeak, 0, 2048, 0, 4095), 0, 4095);
}
// =====================================================
// HELPERS
// =====================================================
int findDevice(String mac) {
for (int i = 0; i < deviceCount; i++)
if (devices[i].mac == mac) return i;
return -1;
}
bool isWhitelisted(String mac) {
for (int i = 0; i < whitelistCount; i++)
if (whitelist[i] == mac) return true;
return false;
}
void printBanner(const char* title) {
Serial.println("================================================");
Serial.print (" "); Serial.println(title);
Serial.println("================================================");
}
Loading
esp32-c6-devkitc-1
esp32-c6-devkitc-1
RF 433MHz Sensor
EMF / Hall-Effect Sensor
Audio / INMP441 Mic Array