Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
d255408
Add BLE GATT server, serial host detection, and companion mode
dougborg Feb 8, 2026
a8882b4
Add OUI prefixes for Flock WiFi cameras, Flock Safety direct, and Sho…
dougborg Feb 8, 2026
00afc28
Defer companion mode switch from BLE callbacks to loop()
dougborg Feb 8, 2026
06d7349
Always emit is_raven and raven_fw in detection JSON
dougborg Feb 8, 2026
c9afc34
Split MAC prefixes by vendor/confidence to reduce false positives
dougborg Feb 8, 2026
064b43c
Fix heartbeat timer leak for low-confidence detections, bump method b…
dougborg Feb 8, 2026
03672d8
Merge pull request #29 from dougborg/add-oui-prefixes
colonelpanichacks Feb 15, 2026
cab512c
Merge pull request #30 from dougborg/ble-gatt-companion
colonelpanichacks Feb 15, 2026
7aaaaac
Adding Rayhunter/external AP integration
pezhore Mar 7, 2026
434b8e7
updating log output to reflect new localIP value
pezhore Mar 7, 2026
7cf6838
updating web content to use new localIP value
pezhore Mar 7, 2026
00a0ba3
updating comments
pezhore Mar 7, 2026
63beaf2
Adding build params for rayhunter
pezhore Mar 7, 2026
dcaaa7a
Fixing string quoting
pezhore Mar 7, 2026
32f19ab
Changing replace string to potentially include hostname in the future
pezhore Mar 7, 2026
56749a4
Removing string replace in favor of javascript.
pezhore Mar 7, 2026
0352dd2
Adding rayhunter details to readme
pezhore Mar 7, 2026
c2dde90
Removing string replace in favor of javascript.
pezhore Mar 7, 2026
d1f72be
re-adding brackets
pezhore Mar 7, 2026
41cf4a3
Using consistent placehold values for ssid and pass
pezhore Mar 7, 2026
71f3fe7
Updating comments to reflect hostname based url
pezhore Mar 7, 2026
06ae668
Merge branch 'main' into feature/rayhunter-wifi
pezhore Mar 27, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 14 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,9 @@ All detection is BLE-based:

## Features

- **WiFi AP**: `flockyou` / password `flockyou123`
- **Web dashboard** at `192.168.4.1` — live detection feed, pattern database, export tools
- **Standalone WiFi AP** (ssid `flockyou` / password `flockyou123`) or connects to Rayhunter AP for multi-tool usage
- **Web dashboard** live detection feed, pattern database, export tools. `192.168.4.1` for standalone AP mode, or at
either `http://flockyou` or the DHCP address with Rayhunter
- **GPS wardriving** — phone GPS via browser Geolocation API tags every detection with coordinates
- **Session persistence** — detections auto-save to flash (SPIFFS) every 60 seconds
- **Prior session tab** — previous session survives reboot and is viewable in the PREV tab
Expand Down Expand Up @@ -83,6 +84,17 @@ pio run # build
pio run -t upload # flash
pio device monitor # serial output
```
### Rayhunter/External AP Integration (Optional)

Update `platformio.ini` with the following values under the `build_flags` section:

```ini
-DRAYHUNTER_ENABLED=true ; Set to true to enable Rayhunter Integration
-DRAYHUNTER_SSID='"Rayhunter-SSID"' ; Your Rayhunter SSID
-DRAYHUNTER_PASS='"Rayhunter-PASS"' ; Your Rayhunter WiFi Password
```

Build and run as above.

**Dependencies** (managed by PlatformIO):

Expand Down
3 changes: 3 additions & 0 deletions platformio.ini
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,9 @@ build_flags =
-DBOARD_HAS_PSRAM
-mfix-esp32-psram-cache-issue
-DCONFIG_BT_NIMBLE_ENABLED=1
-DRAYHUNTER_ENABLED=false ; Set to true to enable Rayhunter Integration
-DRAYHUNTER_SSID='"Rayhunter-SSID"' ; Rayhunter SSID (Default Placeholder)
-DRAYHUNTER_PASS='"Rayhunter-PASS"' ; Rayhunter Password (Default Placeholder)

; Libraries
lib_deps =
Expand Down
113 changes: 104 additions & 9 deletions src/main.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@
// 4. Raven gunshot detector service UUID matching
// 5. Raven firmware version estimation from service UUID patterns
//
// WiFi AP "flockyou" / "flockyou123" serves web dashboard at 192.168.4.1
// Optionally connects to a specified Rayhunter AP, serving web dashboard at DHCP IP or http://flockyou
// Standalone WiFi AP "flockyou" / "flockyou123" serves web dashboard at 192.168.4.1
// All detections stored in memory, exportable as JSON or CSV
// Optional WiFi STA connection for future features
// ============================================================================
Expand Down Expand Up @@ -54,6 +55,21 @@ static unsigned long fyBleScanInterval = 3000; // ms between scans
#define FY_AP_SSID "flockyou"
#define FY_AP_PASS "flockyou123"

// Rayhunter Configuration
// Either hardcode Rayhunter config here (bad), or set via build flags and add to platformio.ini
// Default behavior is to disable Rayhunter and run in standalone AP mode
#ifndef RAYHUNTER_ENABLED
#define RAYHUNTER_ENABLED false // Set to true to enable Rayhunter integration, false to run in standalone mode with WiFi AP
#endif

#ifndef RAYHUNTER_SSID
#define RAYHUNTER_SSID "Rayhunter-SSID" // Rayhunter SSID (default placeholder)
#endif

#ifndef RAYHUNTER_PASS
#define RAYHUNTER_PASS "Rayhunter-PASS" // Rayhunter WiFi Password (default placeholder)
#endif

// ============================================================================
// DETECTION PATTERNS
// ============================================================================
Expand Down Expand Up @@ -469,6 +485,50 @@ static void fyOnCompanionChange() {
printf("[FLOCK-YOU] Companion mode: WiFi AP OFF, scan duration %ds\n",
fyBleScanDuration);
} else {
// Standalone mode

// If Rayhunter is enabled, try to connect to their AP, but fall back to standalone AP if connection fails
if ( RAYHUNTER_ENABLED ) {
WiFi.begin(RAYHUNTER_SSID, RAYHUNTER_PASS);
fyBleScanDuration = 2;
printf("[FLOCK-YOU] Attempting to connect to Rayhunter WiFi network");

int attempts = 0;
while(WiFi.status() != WL_CONNECTED) {
delay(500);
printf(".");
attempts++;
if (attempts >= 20) { // ~10 seconds timeout
printf("\n[FLOCK-YOU] Failed to connect to RayHunter WiFi - starting in AP mode\n");
break;
}
}

// If we connected to RayHunter's WiFi, use that; otherwise, fallback to our own AP
if (WiFi.status() == WL_CONNECTED) {
printf("\n[FLOCK-YOU] Connected to RayHunter WiFi\n");
printf("[FLOCK-YOU] IP: %s\n", WiFi.localIP().toString());
printf("[FLOCK-YOU] Companion mode with RayHunter: scan duration %ds\n", fyBleScanDuration);
} else {
printf("[FLOCK-YOU] Connection to Rayhunter AP Failed. Falling back to standalone AP mode.\n");
WiFi.mode(WIFI_AP);
delay(100);
WiFi.softAP(FY_AP_SSID, FY_AP_PASS);
fyBleScanDuration = 2;
printf("[FLOCK-YOU] Standalone mode: WiFi AP ON (%s), scan duration %ds\n",
FY_AP_SSID, fyBleScanDuration);
}


// If Rayhunter is not enabled, re-enable WiFi AP
} else {
WiFi.mode(WIFI_AP);
delay(100);
WiFi.softAP(FY_AP_SSID, FY_AP_PASS);
fyBleScanDuration = 2;
printf("[FLOCK-YOU] Standalone mode: WiFi AP ON (%s), scan duration %ds\n",
FY_AP_SSID, fyBleScanDuration);
}
// Standalone mode — re-enable WiFi AP and web dashboard
WiFi.mode(WIFI_AP);
delay(100);
Expand Down Expand Up @@ -848,7 +908,7 @@ let g=document.getElementById('sG');g.textContent='...';g.style.color='#facc15';
_gW=navigator.geolocation.watchPosition(sendGPS,gpsErr,{enableHighAccuracy:true,maximumAge:5000,timeout:15000});return true;}
function reqGPS(){if(!navigator.geolocation){alert('GPS not available in this browser.');return;}
if(_gOk){return;}
if(!window.isSecureContext){alert('GPS requires a secure context (HTTPS). This HTTP page may not get GPS permission.\\n\\nAndroid Chrome: try chrome://flags and enable "Insecure origins treated as secure", add http://192.168.4.1\\n\\niPhone: GPS will not work over HTTP.');}
if(!window.isSecureContext){alert('GPS requires a secure context (HTTPS). This HTTP page may not get GPS permission.\n\nAndroid Chrome: try chrome://flags and enable "Insecure origins treated as secure", add http://'+window.location.hostname+'\n\niPhone: GPS will not work over HTTP.');}
startGPS();_gTried=true;}
refresh();setInterval(refresh,2500);
</script></body></html>
Expand Down Expand Up @@ -1146,18 +1206,53 @@ void setup() {
// Crow calls play WHILE BLE is already scanning
fyBootBeep();

// Start WiFi AP (no need to connect to anything -- AP only)
WiFi.mode(WIFI_AP);
delay(100);
WiFi.softAP(FY_AP_SSID, FY_AP_PASS);
printf("[FLOCK-YOU] AP: %s / %s\n", FY_AP_SSID, FY_AP_PASS);
printf("[FLOCK-YOU] IP: %s\n", WiFi.softAPIP().toString().c_str());
// Set an empty localIP for now; will be updated after WiFi setup
IPAddress localIP(0,0,0,0);

// If Rayhunter is enabled, try to connect to their AP, but fall back to standalone AP if connection fails
if ( RAYHUNTER_ENABLED ) {
WiFi.setHostname("flockyou");
printf("[FLOCK-YOU] Attempting to connect to RayHunter WiFi (SSID: %s)\n", RAYHUNTER_SSID);
WiFi.begin(RAYHUNTER_SSID, RAYHUNTER_PASS);

int attempts = 0;
while(WiFi.status() != WL_CONNECTED) {
delay(500);
printf(".");
attempts++;
if (attempts >= 20) { // ~10 seconds timeout
printf("\n[FLOCK-YOU] Failed to connect to RayHunter WiFi - starting in AP mode\n");
break;
}
}

// If we connected to RayHunter's WiFi, use that; otherwise, fallback to our own AP
if (WiFi.status() == WL_CONNECTED) {
printf("\n[FLOCK-YOU] Connected to RayHunter WiFi\n");
localIP = WiFi.localIP();
} else {
WiFi.mode(WIFI_AP);
delay(100);
WiFi.softAP(FY_AP_SSID, FY_AP_PASS);
printf("[FLOCK-YOU] AP: %s / %s\n", FY_AP_SSID, FY_AP_PASS);
localIP = WiFi.softAPIP();
}

// If Rayhunter is not enabled, start WiFi AP (no need to connect to anything -- AP only)
} else {
WiFi.mode(WIFI_AP);
delay(100);
WiFi.softAP(FY_AP_SSID, FY_AP_PASS);
printf("[FLOCK-YOU] AP: %s / %s\n", FY_AP_SSID, FY_AP_PASS);
localIP = WiFi.softAPIP();
}

// Start web dashboard
fySetupServer();

printf("[FLOCK-YOU] IP: %s\n", localIP.toString().c_str());
printf("[FLOCK-YOU] Detection methods: MAC prefix, device name, manufacturer ID, Raven UUID\n");
printf("[FLOCK-YOU] Dashboard: http://192.168.4.1\n");
printf("[FLOCK-YOU] Dashboard: http://%s\n", localIP.toString().c_str());
printf("[FLOCK-YOU] Ready - BLE GATT + AP mode\n\n");
}

Expand Down