Comments
Figures ▾
Tables ▾

SquachWatch · Volume 7

SquachWatch Volume 7 — What It Detects and How

Two radios on one antenna, seventeen detection types, ninety-seven graded prefixes, and an honest list of what a match does and does not prove

Figure 1 — A DRONE alert card: the DroneID vendor plate, the signal bar, and the HUNT, SNOOZE and MORE INFO controls. Remote ID is one of the few detections on this device that is an exact-format…
Figure 1 — A DRONE alert card: the DroneID vendor plate, the signal bar, and the HUNT, SNOOZE and MORE INFO controls. Remote ID is one of the few detections on this device that is an exact-format decode rather than a vendor inference. Image: SquachWatch-CYD firmware screen capture (skizzophrenic, GPL-3.0).

7.1 The detection model at a glance

SquachWatch is a passive signature matcher. It does not classify radio behaviour statistically, it does not fingerprint traffic patterns, and it does not decrypt anything. It listens to 2.4 GHz WiFi management and data frames and to Bluetooth Low Energy advertisements, and it compares a small number of fields in each against fixed tables: an address prefix (OUI), a network name (SSID), a 16-bit or 128-bit service UUID, a Bluetooth SIG company identifier, an advertised device name, or a fixed-format payload. Three detections are behavioural rather than table lookups: a deauthentication burst, an evil-twin access point, and a spam flood of short-lived tracker addresses. Everything else is “this field equals that value.”

That simplicity is the point and the limitation. Every alert can be traced back to a single row in src/signatures.cpp and a single entry in docs/DETECTIONS.md, which records where the row came from and how much it should be trusted. The project’s own summary of the trade: “It’s not magic and it’s not X-ray vision. It’s a small board doing pattern-matching on radio noise, wearing a mascot costume.”

7.1.1 Two radios, one antenna

The ESP32-WROOM-32 on the 2.8” CYD has one 2.4 GHz RF path shared between the WiFi MAC and the Bluetooth controller. SquachWatch runs both at once — the WiFi side as a promiscuous sniffer, the BLE side as an indefinite NimBLE scan — and the coexistence arbiter divides the air time. The knob that sets the split is the BLE scan window within the scan interval: interval 100, window 75. The source records the bench measurement behind that choice, four minutes at each setting in the same spot on 2026-09-15:

Table 1 — The ESP32-WROOM-32 on the 2.8" CYD has one 2.4 GHz RF path shared between the WiFi MAC and the Bluetooth controller. SquachWatch runs both at once — the WiFi side as a promiscuous sniffer, the BLE side as an indefinite NimBLE scan — and the coexistence arbiter divides the air time. The knob that sets the split is the BLE scan window within the scan interval: interval 100, window 75. The source records the bench measurement behind that choice, four minutes at each setting in the same spot on 2026-09-15

BLE scan window (of 100)WiFi frames capturedBLE adverts capturedVerdict in the source
99442641Sniffer “deaf” — about one frame a second next to a router beaconing ten times a second
75 (shipped)8662458Twenty times the WiFi of 99 for a dip in adverts “inside run-to-run noise”
5018511485Costs half the adverts

Note — The practical consequence is that neither radio is listening continuously. A device that transmits rarely — one beacon every few seconds, a single probe request — can fall into the other radio’s share of time. This is one reason the same device can appear, vanish and reappear in the LOG.

7.1.2 The pipeline

  ┌───────────────────────────────── ESP32 RF (2.4 GHz, shared) ─────────────────────────────────┐
  │                                                                                               │
  │  WiFi promiscuous RX (mgmt + data)                       NimBLE scan (forever, dup filter off) │
  │  hop ch 1..13 (+5 GHz on C5 boards)                      passive at boot; active when quiet    │
  └───────────────┬───────────────────────────────────────────────────────┬───────────────────────┘
                  │ IRAM callback                                         │ host-task callback
                  ▼                                                       ▼
   ┌──────────────────────────────┐                        ┌──────────────────────────────────┐
   │ frame type/subtype switch     │                        │ heap seatbelt (drop if heap low) │
   │  probe req  → addr2           │                        │ SquachMesh peer? → handle, RETURN│
   │  data       → addr1 + addr2   │                        │ byte-order flip (printed order)  │
   │  beacon     → Remote ID? /    │                        │ watch / hunt slot check          │
   │               pwnagotchi? /   │                        │ mfg ID → AirTag check → iBeacon  │
   │               BSSID+SSID+priv │                        │ raw AirTag fallback              │
   │  deauth     → burst queue     │                        │ Remote ID service data (0xFFFA)  │
   └──────────────┬───────────────┘                        │ 16-bit / 128-bit service UUIDs   │
                  │ mailbox (8 deep)                       │ name fallback                    │
                  ▼                                        └────────────────┬─────────────────┘
   ┌──────────────────────────────┐                                         │
   │ loop(): processWiFiQ()        │                                         │
   │  pwnagotchi > drone >         │                                         │
   │  EVIL TWIN > OUI > SSID       │                                         │
   └──────────────┬───────────────┘                                         │
                  └──────────────────────┬───────────────────────────────────┘
                                         ▼
                  ┌────────────────────────────────────────────────┐
                  │ TYPE FILTER gate → dedupe/merge into LOG (64)  │
                  │ → black box (flash) → SD CSV (if card)          │
                  │ → spam watch → alert gate (ALERT FILTER,        │
                  │   IGNORE/SNOOZE, AUTO SNOOZE, watch list)       │
                  │ → ALERT card / LOCKED ON / quiet log            │
                  └────────────────────────────────────────────────┘

The split between “decided in the radio callback” and “decided in loop()” matters. The WiFi callback runs in IRAM and does only byte-offset work: it copies an address, an RSSI, a channel and (for beacons) up to 32 bytes of SSID into an eight-entry mailbox. Every table lookup happens later on the loop task. A full mailbox drops frames silently, which is acceptable for a detector because the same device will transmit again.

7.1.3 The seventeen types

The FAQ counts seventeen detection types. The table below gives each one’s internal name, what it claims, which radio carries the evidence, and its base grade as set by confidenceFor() in src/signatures.cpp. “Per row” means the OUI rows carry their own grade, which overrides the base (§4.1).

Table 2 — 1.3 The seventeen types

TypeMain-screen columnClaimRadioMatched onBase grade
FLOCKFLOCKFlock Safety ALPR camera or accessoryWiFi + BLE49 OUIs, SSIDs, BLE names, company ID, 128-bit UUIDHigh (OUI rows per row: 1 High, 48 Low)
AXONAXONAxon body camera / TASER / LE gearWiFi + BLE3 OUIs, 4 SSID prefixes, BLE nameHigh (OUI rows per row)
METAGLASSCamera glasses (Ray-Ban Meta, Snap)BLE0xFD5F UUID, 4 company IDsMedium
SKIMMERSKIMBluetooth card skimmerBLE + WiFiModule names, SPP UUID, 3 OUIsHigh (OUI rows Low)
RAVENRAVRaven gunshot detectorBLEFive service UUIDsMedium
AIRTAGTRACKERApple AirTag / Find MyBLECompany ID 0x004C + payload checkMedium
SAMSUNG_TAG(in TRACKER)Galaxy SmartTagBLE0xFD5A UUIDHigh
GOOGLE_TAG(in TRACKER)Find My Device Network trackerBLE0xFEAA UUIDMedium
TILE(in TRACKER)Tile trackerBLE0xFEED / 0xFEEC UUIDsHigh
DRONEDRONEASTM F3411 Remote ID aircraftBLE + WiFi0xFFFA service data, beacon vendor elementMedium
ALPRALPRMotorola Solutions / Genetec plate readerWiFi7 OUIsMedium (rows High)
CAMERACAMIP / covert cameraWiFi17 OUIsHigh (rows per row)
RING(in CAM)Ring doorbell / cameraWiFi15 OUIsHigh (rows per row)
IBEACONBCONRetail proximity beaconBLEExact Apple iBeacon headerHigh — off by default
DEAUTHDEAUTHDeauthentication floodWiFi6 frames in 3 sMedium
EVILTWIN(in HACK)Rogue / spoofed APWiFiSSID from two vendors disagreeing on encryptionHigh
HACKERHACKFlipper Zero, Pwnagotchi, Pineapple, deauthersBLE + WiFiUUIDs, company ID, OUIs, beacon JSON, SSIDsMedium base; exact signatures set High

The main screen packs these into eleven fixed counter columns by folding three groups: GOOGLE_TAG, TILE and SAMSUNG_TAG count under TRACKER with AIRTAG; RING counts under CAM; EVILTWIN counts under HACK. Every other screen — the LOG, the alert card, the colours — keeps the types apart.

Figure 2 — The LOG, one row per device: DRONE, HACKER, IBEACON, TILE, META and RING entries from the firmware's own renderer. Each row is one signature match, not one transmission. Image: SquachW…
Figure 2 — The LOG, one row per device: DRONE, HACKER, IBEACON, TILE, META and RING entries from the firmware's own renderer. Each row is one signature match, not one transmission. Image: SquachWatch-CYD firmware screen capture (skizzophrenic, GPL-3.0).

7.2 The WiFi side

7.2.1 Promiscuous capture and which address is read

The sniffer filter admits management and data frames only. The callback then reads the 802.11 header by fixed offset — address 1 at byte 4, address 2 at byte 10, address 3 (BSSID) at byte 16 — and chooses which one to post according to frame type:

Table 3 — The sniffer filter admits management and data frames only. The callback then reads the 802.11 header by fixed offset — address 1 at byte 4, address 2 at byte 10, address 3 (BSSID) at byte 16 — and chooses which one to post according to frame type

FrameType / subtypeAddress(es) postedExtra fields extractedFeeds
Probe request0 / 4addr2 (transmitter)—OUI lookup
Data2 / anyaddr1 and addr2—OUI lookup
Beacon0 / 8addr3 (BSSID), or addr2 for Remote ID / PwnagotchiSSID (first IE), Privacy bit (capability byte 34, bit 4)Remote ID, Pwnagotchi, evil twin, OUI, SSID
Deauthentication0 / 12addr2—Burst counter only
Anything else—not posted—Channel-busyness counter only

The addr1 trick. Posting the receiver address of data frames is deliberate. docs/DETECTIONS.md attributes it to the Flock work: a camera that is not itself transmitting still appears as the destination of frames addressed to it, so a “sleeper” device’s MAC leaks through traffic sent to it. The same mechanism applies to every OUI type, not only Flock.

Every frame that reaches the loop task — matched or not — also feeds the per-channel activity level that drives the spectrum-style background and the hop scheduler, and is checked against the watch-list and hunt slots, so a device the user chose to WATCH is followed even if it matches no signature (Vol 8 §4).

7.2.2 Channel hopping

A promiscuous ESP32 hears one channel at a time, so the firmware cycles channels 1 to 13. The dwell is not uniform. Each channel’s frame rate is measured on every visit, smoothed (new = (3 × old + this visit) / 4), and the next sweep gives shares accordingly:

Table 4 — A promiscuous ESP32 hears one channel at a time, so the firmware cycles channels 1 to 13. The dwell is not uniform. Each channel's frame rate is measured on every visit, smoothed (new = (3 × old + this visit) / 4), and the next sweep gives shares accordingly

Channel stateDwell shareRationale in the source
At least a quarter as busy as the busiest channel4”Most of a building’s WiFi sits on two or three channels”
Any traffic at all2
Silent1A quick look; still longer than a ~100 ms beacon interval
Whole sweep≈ 3.9 sKeeps the watch’s five-second window covering every channel

On the boards built with SQW_WIFI_5G — the NM-CYD-C5 and Marauder V8, both ESP32-C5 — the sweep continues into 5 GHz after channel 13: every 5 GHz channel that has been busy recently plus two silent ones in rotation, from the list the radio accepts for the configured country. WIFI BANDS under Settings → BEHAVIOR appears only on those builds. Every other board is 2.4 GHz only.

7.2.3 OUI lookup and precedence

lookupOui() walks kOuiTable top to bottom and returns the first match, so row order is precedence. The design contract (docs/DESIGN.md §6.2) specifies the order Axon → Flock → ALPR → specific camera vendors → ESP32 family → skimmer, so that a prefix appearing in two contexts resolves to the more specific claim.

Caution — docs/DESIGN.md is the v1.0 contract and has not been kept fully current. Its step 5, “ESP32 family OUIs → CAMERA,” was never built: docs/DETECTIONS.md states that a generic “flag any Espressif OUI as a possible camera” fallback “is not built.” In the shipping table the Espressif prefixes sit under FLOCK at Low, and nowhere else.

The table currently holds 97 rows. Counting them by type and grade in src/signatures.cpp reproduces the project’s published totals of 33 High, 4 Medium and 60 Low:

Table 5 — The table currently holds 97 rows. Counting them by type and grade in src/signatures.cpp reproduces the project's published totals of 33 High, 4 Medium and 60 Low

TypeHighMediumLowTotalNote
FLOCK104849Only B4:1E:52 is registered to Flock Safety
AXON102300:25:DF (Taser International) High
ALPR70075 Motorola Solutions, 2 Genetec
CAMERA102517Amazon’s two blocks Medium
RING132015Ring LLC’s 13 High; two inherited from CAMERA Medium
SKIMMER0033Module-vendor blocks
HACKER10230C:FA:22 Flipper High; two Hak5 locally-administered Low
Total3346097Matches docs/DETECTIONS.md

An OUI match carries the row’s own grade. An SSID match, which has no per-row grade, falls back to the type’s base grade.

7.2.4 SSID matching

SSIDs are read only from beacons and matched case-insensitively. There is one exact match and eight prefixes:

Table 6 — SSIDs are read only from beacons and matched case-insensitively. There is one exact match and eight prefixes

SSIDMatchTypeVendor label
FlockexactFLOCKFlock
flock-, FLOCK-prefixFLOCKFlock-Setup
AB2-, AB3-, AB4-prefixAXONAxon-Body2/3/4
AXON-prefixAXONAxon-Field
Pineapple_prefixHACKERPineapple
pwnedprefixHACKERDeauther

The bare Flock is exact precisely so that “Flockhart Family WiFi” does not match. The SSID path runs only when the OUI lookup found nothing, so an Axon camera in pairing mode on a WiFi module whose OUI is unknown is still caught by its AB3- network name.


7.3 The Bluetooth side

7.3.1 Scanner configuration

The NimBLE scanner is configured once at boot and then runs forever. Each setting in the source carries the incident that produced it:

Table 7 — The NimBLE scanner is configured once at boot and then runs forever. Each setting in the source carries the incident that produced it

SettingValueWhy
Scan mode at bootPassive”The first second of an active scan in a crowded building is what killed a user’s board.”
Switch to activeWhen the advert rate is low enough and heap allowsActive scanning asks for scan responses, which is where most device names live
Back to passiveAbove 300 adverts/s, or three heap-pressure flushes in a minute (then held passive 120 s)An active scan holds a record per unanswered device
Minimum time between mode changes30 sHysteresis
Scan interval / window100 / 75WiFi coexistence (§1.1)
Duplicate filterOffEvery sighting updates RSSI and last-seen
Scan-response timeout200 msLibrary default of 10.24 s let two thousand pending records pile up — “the college-floor crash”
Max retained results0NimBLE otherwise keeps every address ever seen; the leak crashed boards on commutes
Heap seatbeltDrop the advert when heap is lowA string allocation on a fragmented heap throws on the host task

Note — Active scanning is the one way the detector transmits while detecting: a scan request is a radio frame from the board. The FAQ lists it first in its “Does it transmit anything?” table and states there is no menu switch for it. Vol 9 §10 has the complete transmit inventory.

7.3.2 Match order inside the advert callback

The BLE callback stops at the first branch that assigns a type. The order is fixed in the code and explains several behaviours users report:

 advert in
   │
   ├─ heap low? ───────────────────────────────────────────► drop (counted)
   │
   ├─ manufacturer data, company 0xFFFF + 'SQM1' magic? ───► SquachMesh peer: draw visitor,
   │                                                         feed watch/hunt, RETURN
   │                                                         (never becomes a Detection)
   ├─ flip address to printed order (issue #22)
   ├─ watch / hunt slot check (any device, matched or not)
   │
   ├─ manufacturer data present?
   │     company ID in table ──► 0x004C Apple? ──► AirTag payload check
   │                                               ├─ pass → AIRTAG
   │                                               └─ fail → iBeacon header? → IBEACON
   │                                                                    else → nothing
   │     0x09C8 → FLOCK   0x01AB/0x058E/0x0D53/0x03C2 → META   0x0E29 → HACKER
   │
   ├─ still nothing: raw-advert AirTag pattern anywhere? ──► AIRTAG
   ├─ still nothing: Remote ID in service data 0xFFFA? ────► DRONE (decode, §5)
   ├─ still nothing: 16-bit service UUID in list? ─────────► SKIMMER / TILE / META /
   │                                                         RAVEN / DRONE / SAMSUNG_TAG /
   │                                                         GOOGLE_TAG / HACKER
   │                 128-bit Flock accessory UUID? ────────► FLOCK
   └─ still nothing: advertised name lookup ───────────────► SKIMMER / FLOCK / AXON / HACKER

Grades on the BLE side are per type, not per row, with two exceptions coded at the match site: a HACKER match that arrived only by name is Medium while every exact HACKER signature is High, and a FLOCK match on a bare ten-digit name is Low.

Two details of the name path are worth knowing. The module names used for skimmers (HC-03, HC-05, HC-06, RN42, BT04-A) and two Flock accessory names (Flock_Setup, FS Ext Battery) are exact case-insensitive matches. The Flock, Penguin, Pigvision, Axon and Flipper checks are substring matches (strcasestr), so any BLE device whose name merely contains one of those words is classified. That is a wider net than the exact SSID rule above, and the source does not remark on the asymmetry.

7.3.3 What the Bluetooth side does not do

  • No Bluetooth Classic inquiry. It “conflicts with NimBLE on a single radio” and has not been switched on, so a skimmer is caught only if its module also advertises over BLE.
  • No connections. Nothing is read from a GATT server; every field comes from the advert or scan response.
  • No Bluetooth 5 extended advertising or Coded PHY on the ESP32-WROOM (BLE 4.2). No build listens for it on the newer chips either (§5).

7.4 Signatures and confidence, type by type

This chapter follows docs/DETECTIONS.md section by section. Where the document’s per-type headline grade, the README and the code disagree, the disagreement is stated rather than smoothed over.

7.4.1 The grading rule

The confidence scheme was originally a property of the type; after an audit of every prefix against the IEEE MA-L registry it became a property of the matched signature. The document defines three grades two ways. The original definitions:

Table 8 — The confidence scheme was originally a property of the type; after an audit of every prefix against the IEEE MA-L registry it became a property of the matched signature. The document defines three grades two ways. The original definitions

GradeOriginal definition (source agreement)
HighMultiple independent sources, verified against the canonical research (Flock You, Marauder, Eye Spy)
MediumOne or two sources, plausible but not cross-verified
LowSingle source, anecdotal, may have false positives

and the registry-based definitions that now apply to OUI rows:

Table 9 — and the registry-based definitions that now apply to OUI rows

GradeRegistry definition (OUI rows)Example
HighThe block is registered to the company that makes the productB4:1E:52 Flock Safety
MediumRegistered to a parent whose range is far wider than the productAmazon blocks under CAMERA / RING
LowA module or ODM vendor whose parts are in everything, or a block not in the IEEE registryEspressif, Liteon, Silicon Labs

A rule stated under the META section governs mixed types: “a type carrying signatures of mixed quality reports the lower one.” On screen the grade appears as HIGH CONF, MED CONF or LOW CONF, with an internal percentage of 90, 60 or 30.

Note — The grade is what makes ALERT FILTER useful. It is a minimum-confidence gate on the full-screen alert: set to HIGH ONLY, a passing Espressif device is still logged and counted but does not take over the screen. Before the per-row grading, confidence was constant per type and the filter “had nothing to filter on.”

7.4.2 FLOCK

Headline in DETECTIONS.md: one High prefix, the rest Low.

Evidence. Flock Safety cameras carry on-board WiFi (typically ESP32) that emits probe requests. Of the original 29 prefixes, exactly one, B4:1E:52, is registered to Flock Safety; sixteen are Espressif blocks, the rest Liteon, one Shenzhen Intellirocks, one SPECTRA - TEK (long mislabelled “Flock-Sierra”), one not in the registry and one locally administered. v1.24.0 added twenty more from Flock-You’s field list and a Flock camera firmware dump — thirteen Liteon, two USI, three Silicon Labs, one Espressif and Atheros’s factory-default block — for 49 prefixes with one High. 48:27:EA was deliberately left out: it is Samsung Electronics’ and would call every Samsung TV a Flock camera.

Non-OUI signatures: the exact SSID Flock; setup SSIDs flock-/FLOCK-; Flock’s 128-bit accessory GATT service e8ccbb38-9532-46a8-9fe5-1814df172e6f; BLE names Flock, Penguin, Pigvision, Flock_Setup, FS Ext Battery; BLE company ID 0x09C8 (XUNTONG, Flock’s radio supplier); and a battery pack that names itself by a bare ten-digit serial, logged Low.

Why the Low rows stay. “Flock really does build on ESP32, so an Espressif prefix really is evidence — it is just evidence shared with every dev board, smart plug and hobby project on earth, including another SquachWatch.”

Caveats. Flock has reportedly turned off Bluetooth on newer hardware, so BLE-name detection is opportunistic. The non-OUI Flock matches carry the FLOCK base grade, which confidenceFor() sets to High; DETECTIONS.md does not grade them individually.

Source: colonelpanichacks/flock-you (MIT), OUI research by @NitekryDPaul and the DeFlockJoplin community, every prefix’s registrant checked against the IEEE MA-L registry.

7.4.3 AXON

Headline: High.

Evidence. Axon body cameras broadcast AB2-, AB3-, AB4- SSIDs in pairing mode (an AXON- prefix is also matched), and use 00:25:DF (legacy Taser International) and E4:05:40. A BLE name containing Axon also matches. Grades since the audit: 00:25:DF High; E4:05:40 and 28:24:FF Low. The SSID match was graded High in v1.0 (“we catch the camera in pairing mode”), the OUI Medium in v1.0 because it depends on the camera being powered and transmitting.

Source: Axon Evidence admin documentation on body-worn-camera WiFi networks (public), the ESPHome community OUI registry, and a Gemini-compiled signature set.

7.4.4 META, shown as GLASS

Headline: Medium — and it was High before company IDs were added.

Evidence. Two independent signatures. The specific one is BLE service UUID 0xFD5F, advertised by Ray-Ban Meta glasses (“High on its own”). The broad one is four Bluetooth SIG company IDs that dedicated glasses-spotting apps key on: 0x01AB Meta Platforms, 0x058E Meta Platforms Technologies, 0x0D53 Luxottica, and 0x03C2 Snap (Spectacles).

Caveat. Meta uses the same IDs on other products, Quest headsets included, so a company-ID hit “means a Meta radio is nearby rather than a camera necessarily pointed at you.” The type was graded down deliberately under the mixed-quality rule. The vendor label distinguishes the two paths: RayBanMeta for the UUID, Meta/Meta-Tech/Luxottica/Snap for the company IDs.

Source: the Eye Spy project, the Hacker News “glasses to detect smart-glasses” project, banrays, and the company-ID list reported for the Nearby Glasses / AntiZuck apps.

7.4.5 SKIMMER

Headline: High.

Evidence. Cheap Bluetooth skimmers are built from HC-05/HC-06/HC-03-class modules (or RN42, BT04-A) that broadcast the module’s default name, and expose the Serial Port Profile UUID 0x1101. Three module OUIs (20:13:00, 98:D3:00, 00:1A:7D) also match at Low.

Grades. High for the BLE name match; Medium in v1.0 for the SPP UUID, “some skimmers use different SPP implementations.” Limit: BT Classic inquiry is off, so a skimmer whose module only speaks Classic is invisible.

Source: SparkFun Skimmer Scanner, the ESP32 Marauder “Detect Card Skimmers” wiki page, Eye Spy.

7.4.6 RAVEN

Headline: Medium.

Evidence. Raven devices advertise five proprietary service UUIDs outside the SIG-assigned range, 0x3100, 0x3200, 0x3300, 0x3400 and 0x3500, and only those five match. From v1.24.0 to v1.35.0 the firmware matched every value from 0x3100 to 0x3500, on the strength of 0x3101/0x3102 — UUIDs that belong to Raven’s GPS service rather than its advert. The wide range named an NVIDIA Shield a gunshot detector (issue #41).

Caveat in the project’s own words: “We match the UUIDs but haven’t verified them against a physical Raven device. Likely works.”

Source: the Flock You detection-datasets wiki.

Caution — The README’s “What it detects” table still describes RAVEN as “Any service UUID from 0x3100 to 0x3500.” That row predates the v1.36.0 fix; DETECTIONS.md and the code (five discrete values) are authoritative.

7.4.7 AIRTAG

Headline in DETECTIONS.md: High — but its own “Confidence in v1.0” line says Medium, and the code grades AIRTAG Medium. Medium is what the device shows.

Evidence. Apple’s company ID 0x004C alone is every Apple device, so a second check confirms a tag. The check, ported from nyanBOX by jbohack (MIT), scans the raw advert for either of two byte sequences:

Table 10 — Evidence. Apple's company ID 0x004C alone is every Apple device, so a second check confirms a tag. The check, ported from nyanBOX by jbohack (MIT), scans the raw advert for either of two byte sequences

PatternBytesMeaningWhat else matches
11E FF 4C 00An Apple manufacturer AD structure exactly 30 bytes longApple’s Proximity Pairing advert (4C 00 07 19 …) — AirPods and similar
24C 00 12 19Find My type 0x12, length 0x19 — a tag separated from its owner—

Why the false positive is accepted. A real AirTag test (registered to an Apple ID whose iPhone no longer exists, battery freshly reinserted) showed the tag broadcasting subtype 0x07 (Proximity Pairing), not 0x12, which starts only after a tag has been in “separated” state for a while. Rejecting 0x07 meant the detector could not see a tag “at the exact moment someone is most likely to be testing it.” The source names the proper fix — reading the device model ID inside the Proximity Pairing payload — and declines to guess model IDs from memory.

Other caveats. AirTags rotate their address frequently, so detection flickers. A raw-advert fallback catches the Find My structure even when it sits behind another AD structure or in a scan response.

7.4.8 SAMSUNG_TAG, GOOGLE_TAG and TILE

Table 11 — 4.8 SAMSUNGTAG, GOOGLETAG and TILE

TypeSignatureRegistry statusGradeMain caveat
SAMSUNG_TAGService UUID 0xFD5ASIG-assigned to Samsung for SmartTag discovery; 0xFD59 (onboarding) and 0xFE59 (firmware update) not neededHighNone stated; “not shared with any of Samsung’s other product lines”
GOOGLE_TAGService UUID 0xFEAAGoogle’s long-standing Eddystone UUID, reused by the Find My Device NetworkMediumRetail, asset and museum Eddystone beacons share it — a match means “some Google-beacon-class device”
TILEService UUIDs 0xFEED, 0xFEECBoth SIG-assigned to Tile, Inc.HighWas counted under AIRTAG in v1.0

Sources: the Bluetooth SIG 16-bit UUID registry; arXiv:2210.14702 (a security and privacy analysis of Samsung’s crowd-sourced location system that reverse-engineered the SmartTag protocol); Google’s Find My Device Network specification. The AirTags deep dive covers the four finding networks and their anti-stalking behaviour in depth; this device matches only the advertisement identifiers above.

7.4.9 ALPR

Headline: Medium for the type; the seven rows are High.

The correction that rewrote the section. Until v1.5.20 the only ALPR prefix was 00:0E:58, documented as Vigilant hardware. The IEEE registry assigns it to Sonos, Inc. (Goleta, California, February 2004). For eleven releases every Sonos speaker in range was logged as a licence-plate reader. It was removed, not corrected, “because there was nothing to correct it to.”

Evidence now. Blocks registered to the vendors that sell these systems, read per vendor from the registry: Motorola Solutions (which absorbed Vigilant) 00:04:7D, 00:18:85, 00:1F:92, 4C:CC:34, plus B8:E2:8C (Motorola Solutions Malaysia); Genetec (AutoVu) 00:BF:15, 0C:BF:15. Cross-checked against FlipDeFlock.

Caveat. “The blocks are certain; what is not certain is that a given device on one is a plate reader rather than some other product from a large vendor.” Motorola Solutions sells radios and much else.

7.4.10 CAMERA and RING

CAMERA — graded per row. Seventeen OUIs: Wyze, Arlo, Blink, Hikvision, Amazon, Realtek and Tuya on the consumer side; Verkada (E0:A7:00), Avigilon Alta (70:1A:D5) and Axis (00:40:8C, B8:A4:4F) on the commercial side. After the audit: Wyze, Hikvision, Verkada, Avigilon and Axis blocks High; Amazon’s two Medium; Realtek, Tuya, Arlo, Blink and Wyze’s module block Low. Two rows were relabelled: F0:27:2D (“Hikvision”) is Amazon’s; 28:57:BE (“Reolink”) is Hikvision’s. Sources: the author’s earlier Cardputer-CSI-Human-Detector (MIT), Gemini additions for Tuya/Wyze-module prefixes, and the IEEE registry via maclookup.app.

RING — High. Fifteen prefixes: Ring LLC’s full MA-L registration (13 blocks, registered 2019-03-01, cross-checked on netify.ai and maclookup.app) at High, plus FC:65:DE and 68:37:E9, inherited from CAMERA, at Medium.

Caution — An OUI match says what company registered the radio’s address block. It says nothing about whether the device is a camera, where it points, or whether it records. Consumer camera brands overwhelmingly use the vendor’s real MAC, which is why this works at all; a camera on a randomised or locally-administered address, a non-WiFi camera, or a camera that records to SD with no radio, is invisible here. The CameraDetection deep dive covers the techniques that reach those classes.

7.4.11 IBEACON

Headline: High, off by default — the only type that ships disabled.

Evidence. An exact format: manufacturer data 4C 00 02 15, then a 16-byte proximity UUID, 2-byte major, 2-byte minor and a measured-power byte — 25 bytes. The code checks the length and the four header bytes. Before this type existed, iBeacons were matched by Apple’s company ID, failed the AirTag check and were dropped, “so the most numerous tracking transmitter most people walk past all day was the one thing the detector deliberately ignored.”

What the log shows. Six hex digits of the proximity UUID, then major.minor. The UUID identifies the deployment — “the same first half in two different places is the same operator.” Major and minor are big-endian inside the block, though the company ID before them is little-endian; that is Apple’s format.

Why off. Volume, not importance: “one shop can put more beacons in range than this device would otherwise see all week,” and because the ALERT screen is gated on confidence rather than type, an exact-match type would let every shelf in a supermarket take over the display. What it claims: “High for ‘this is an iBeacon’. That is all it claims.” A beacon does not follow anyone; an app that is already installed decides to care.

7.4.12 HACKER

Headline: graded per signature. One type for Flipper Zero, Pwnagotchi, WiFi Pineapple and the ESP deauther family, because the useful statement is “there is a tool for attacking radios in this room.” It shares the red of DEAUTH and EVILTWIN rather than the cyan of the cameras.

Flipper Zero — four signatures, three exact:

Table 12 — Flipper Zero — four signatures, three exact:

SignatureValueGrade
BLE service UUID0x3081 / 0x3082 / 0x3083, one per case colourHigh
BLE company ID0x0E29, Flipper Devices Inc.High
MAC OUI0C:FA:22, FLIPPER DEVICES INCHigh
Advertised namebegins Flipper Medium — the owner can change it

Two constants the ecosystem gets wrong. ESP32 Marauder’s source comments Flipper’s company ID as 0x0FBA, which is registered to Cosonic Intelligent Technologies (headsets); Flipper Devices is 0x0E29. Wall-of-Flippers checks 80:E1:26 and 80:E1:27, neither of which appears in the IEEE registry at all. Neither ships, and test/hacker_test.cpp pins both so a re-import fails the tests.

Pwnagotchi — High, because it volunteers. A pwnagotchi announces itself to its own kind with a plain-ASCII JSON blob in a vendor information element of its beacons, carrying name, version, uptime, handshake count and whether deauth is on. SquachWatch does not parse JSON in the promiscuous callback; two byte scans suffice — the key pwnd_tot must be present, and the "name" value is extracted. Because the name is attacker-controlled and lands in a rendered field, any byte outside printable ASCII rejects it. The detection posts the transmitter address, not the BSSID, and skips the evil-twin tracker. “Nothing else on this device is a self-declaration.”

Pineapple and deauthers — circumstantial:

Table 13 — Pineapple and deauthers — circumstantial:

SignatureValueGrade
Pineapple management APSSID prefix Pineapple_Medium
Deauther control APSSID prefix pwnedMedium
Hak5 MACs02:C0:CA, 02:13:37Low

Hak5 hold no IEEE registration; their hardware is OpenWrt on ODM boards. The two addresses are locally administered (bit 1 of the first octet set), so “by construction they identify no vendor and anyone can set them.” The test suite enforces that no locally-administered row is graded above Low.

Deliberately not in this bucket: bare Espressif silicon (a nyanBOX, an ESP32 Marauder, an M5Stack and a SquachWatch “are the same chip”); receive-only and wired gear that no firmware can see — HackRF, Ubertooth One, RTL-SDR, Proxmark3, Wi-Fi Coconut, an Alfa card in monitor mode, Bash Bunny, Rubber Ducky, Key Croc, Shark Jack, LAN Turtle, Packet Squirrel, and the O.MG cable except while its AP is up; the WiFi Pineapple Pager (no published signature, “and a guess is exactly how the Sonos entry happened”); and nyanBOX, whose “Device Networking” broadcast is a genuine hook but whose format is unpublished and needs one capture from real hardware.


7.5 Remote ID decoding

DRONE is the one detection that reads the content of what it matched. ASTM F3411 Remote ID adverts carry a 25-byte message; SquachWatch decodes three message types and accumulates them per transmitter address, because a drone sends them as separate adverts in rotation.

Table 14 — 5. Remote ID decoding

TransportHeard?Where the message isDecoded
Bluetooth 4 Legacy advertisingYesAD type 0x16 service data under UUID 0xFFFA, one message per advertFull: serial, airframe type, position, altitude, operator position
WiFi BeaconYes (since v1.24.0)ASD-STAN vendor element (FA:0B:BC, type 0x0D), a pack of messagesNamed by the serial in the log
Bluetooth 5 Long Range (Coded PHY, extended advertising)No—ESP32-WROOM is BLE 4.2; “no build listens for it on the newer chips either”
WiFi NANNoAction frames, not beacons—

Table 15 — 5. Remote ID decoding

MessageFields usedScaling (from opendroneid-core-c)
Basic IDAircraft serial (up to 20 chars), UA type (QUAD, PLANE, HELI…)—
LocationAircraft latitude, longitude, geodetic altitudeDegrees × 10⁷; altitude in half-metres offset by 1000
SystemOperator latitude and longitude — where the pilot is standingDegrees × 10⁷

The bug that defined this section. Until v1.24.0 the check looked for 0xFFFA among the advert’s listed service UUIDs. Because the 25-byte message fills all 31 bytes of a legacy advert, a real drone has no room to list it, and “no real drone was ever reported before then.” It now reads the service data. The source also warns that a field not yet received must not be drawn as zero — “zero is a real coordinate in the Gulf of Guinea.”

Grade: Medium. Compliance is still rolling out, and the Long Range transport is unreachable on this hardware.


7.6 Behavioural detections

7.6.1 DEAUTH, the burst counter

A single deauthentication frame is normal: a phone leaving, an AP restarting. The firmware counts frames in a rolling window and fires on a burst:

Table 16 — A single deauthentication frame is normal: a phone leaving, an AP restarting. The firmware counts frames in a rolling window and fires on a burst

ParameterValue
Threshold6 frames
Window3000 ms, rolling (resets after a gap longer than the window)
Cooldown before re-firing15000 ms
GradeMedium — “a real burst pattern, but the threshold/window are still heuristic”
Address loggedaddr2 of the frame that tripped it (the attacker, or a spoofed AP address)
hits fieldFrames in the triggering window, not repeat sightings

A second flood from the same source lands on the same LOG row rather than creating a new one. The window bookkeeping keeps running while DEAUTH is disabled, so re-enabling it does not fire on a stale count. DEAUTH keeps its own counter column because “a deauth flood is a burst rate, and plenty of things that are not a Pineapple produce one.” Note the counter is not per-source: six deauths from six different transmitters inside three seconds also trip it.

7.6.2 EVILTWIN, the encryption disagreement

The obvious test — same SSID, different BSSID — fires constantly on every mesh network and dual-band AP, and “confirmed on a real mesh here it fires constantly.” Even same-SSID-different-OUI over-fires because vendors ship across several blocks. The shipped test adds the one thing a mesh never does: disagree with itself about security.

 beacon with SSID S from BSSID B, Privacy bit P
   │
   ├─ S not on file ───────────────────────► record (S, B, P) as baseline; no alert
   │                                          (24-entry table, round-robin eviction)
   ├─ S on file with same BSSID ───────────► refresh P (a real security change re-baselines)
   ├─ S on file, BSSID from same vendor ──► mesh node / other band; never flagged
   ├─ S on file, different vendor, same P ► can't tell rogue from mixed-vendor network; quiet
   └─ S on file, different vendor, P differs ► EVILTWIN  (name = the impersonated SSID)

The practical attack this catches is an open twin of an encrypted network — exactly what a captive “evil portal” is. The cost, stated plainly in the source: an attacker who advertises matching encryption walks past, though that attacker also cannot complete a handshake. A second limitation: whichever BSSID is seen first becomes the baseline, so if a rogue is already up when the board arrives, the real AP trips the alert. “The device is telling you two boxes claim one name and disagree about security, which is the finding; it can’t tell you which of them is lying.”

confidenceFor() grades EVILTWIN High as “a specific and hard-to-fake” pattern. The evil-twin check runs ahead of the OUI and SSID lookups and wins over them, because a rogue AP on commodity hardware would otherwise be logged as whatever its OUI matched.


7.7 Spam and flood handling

At a hacker convention on 2026-09-26, someone broadcast fake AirTags and later fake Google tags, each burst from a fresh address: 91 AirTag alerts in 22 minutes, 50 of 69 addresses heard exactly once, and the only remedy was to switch AirTags off. The response is include/spam_watch.h, built on one observation: “A real tag keeps announcing for as long as it is near. A fake is heard for one burst and never again.”

Table 17 — 7. Spam and flood handling

ConstantValueRole
SHORT_MS10 000 msA log row heard for less than this, with one hit, then gone, is evidence of a fake
TRIP16112 (seven fakes × 16)Evidence that trips the flood state
HALF_MS90 000 msEvidence halves every ninety seconds
BURST_TRIP40Or this many new addresses of one type in a minute
QUIET_MS300 000 msFive minutes without evidence ends the flood

Eight fakes in quick succession trip it; a flood of one every fifteen seconds (the convention’s rate) trips it in about ten. Once tripped, the type raises one alert carrying a “SPAM: N FAKE TAGS” banner, and every later sighting of that type is still logged but does not interrupt. The trade is explicit: real tags of that type are held along with the fakes for those five minutes, “and the alert says so.” A device on the watch list always gets through (Vol 8 §3).


7.8 False positives, blind spots and limits

Known false positives, each documented by the project:

Table 18 — Known false positives, each documented by the project

SymptomCauseGrade shownWhat to do
Another SquachWatch, ESP32 Marauder, nyanBOX, smart plug or dev board logged as FLOCK48 of 49 Flock prefixes are module-vendor blocks (17 Espressif)LowALERT FILTER to MED+ or HIGH ONLY
AirPods or other Apple accessories as AIRTAGPattern 1 matches the 30-byte Proximity Pairing advertMediumAccepted by design; IGNORE the accessory
A Quest headset or other Meta product as GLASSCompany-ID pathMediumCheck vendor label: RayBanMeta is the specific path
An Eddystone retail or museum beacon as GOOGLE_TAGShared 0xFEAAMediumContext; IGNORE if fixed
Any Motorola Solutions or Genetec product as ALPRRegistry block certain, product class notMedium typeContext — is there a pole-mounted camera?
An Amazon Echo, Fire TV or Kindle as CAMERA or RINGAmazon blocksMediumCheck the vendor label
A home network named pwned… as HACKERSSID prefixMediumContext
A BLE device whose name contains Flock, Penguin, Axon or FlipperSubstring name matchingType grade (Flock High, Axon High, Flipper Medium)Inspect the name on the card
The real AP flagged as an evil twinThe rogue was there first and became the baselineHighTreat the pair as the finding
Your own AirTag or doorbellTrue positive—IGNORE

What the device cannot see at all:

Table 19 — What the device cannot see at all

Blind spotReason
5 GHz and 6 GHz WiFiESP32-WROOM is 2.4 GHz only; the two ESP32-C5 boards add 5 GHz
Bluetooth Classic-only devices (some skimmers)Inquiry not enabled alongside NimBLE
Bluetooth 5 Long Range Remote IDBLE 4.2 radio; not implemented on newer chips
Cellular / LTE backhaul (Flock’s LTE modem, 4G cameras)No cellular receiver
Sub-GHz, LoRa (except the T-Watch builds, receive-only), analog videoNo radio for those bands
SD-only cameras, wired cameras, anything not transmittingNothing to hear
Devices on randomised or locally-administered MACsNo vendor in the address
Products with no signature yet (Pineapple Pager, nyanBOX)Deliberately not guessed
Receive-only attack tools (HackRF, RTL-SDR, Ubertooth, Proxmark3)They emit nothing identifying

Caution — A detection is evidence that a radio with a particular identifier is within range, at a particular RSSI, at a particular time. It is not evidence of what the device is pointed at, who operates it, whether it is recording, or that it is following anyone. The Low grades exist because the project’s own audit found that copied constants had shipped for releases — 00:0E:58 “Vigilant” was Sonos for eleven releases. The README’s own status line: Flock, Axon, skimmer and camera glasses are the reliable targets; Remote ID and iBeacon are exact-format matches; Raven, generic ALPR and the Google tracker network are best-effort.

Tip — The project asks for false-positive reports with the type, the confidence, the first half of the address, the name if any, and what the thing actually was — with PRIVACY MODE on before photographing the screen.


7.9 Cheatsheet updates from this volume

┌──────────────────────────── SQUACHWATCH — WHAT A MATCH MEANS ─────────────────────────────┐
│ GRADES   HIGH = block/UUID registered to the product's maker, or exact format            │
│          MED  = parent company far wider than product, shared UUID, or heuristic          │
│          LOW  = module/ODM vendor (Espressif, Liteon…), locally-admin, not in registry    │
│ OUI ROWS 97 total: 33 High / 4 Med / 60 Low.  FLOCK: 49 rows, ONE High (B4:1E:52)        │
│ RADIOS   2.4 GHz WiFi (hop 1-13, weighted) + BLE 4.2 adverts. 5 GHz only on ESP32-C5      │
│ WIFI     probe→addr2  data→addr1+addr2  beacon→BSSID+SSID+Privacy  deauth→burst ctr       │
│ BLE      mesh peer > mfg ID (Apple→AirTag check→iBeacon) > raw AirTag > RemoteID >        │
│          16-bit UUID / Flock 128-bit > name (some names are SUBSTRING matches)            │
│ DEAUTH   ≥6 frames / 3 s rolling, 15 s cooldown, MED                                      │
│ EVILTWIN same SSID, different vendor, DISAGREE on encryption, HIGH (first seen = base)   │
│ SPAM     one-burst addresses → trip at ~7 fakes (or 40 new/min); 5 min quiet ends it      │
│ DRONE    BT Legacy 0xFFFA svc data + WiFi beacon. Decodes serial, pos, alt, OPERATOR.    │
│          NOT BT5 Long Range, NOT WiFi NAN                                                 │
│ OFF      IBEACON ships disabled (volume). Turn on in TYPE FILTER                          │
│ BLIND    5/6 GHz (most boards), BT Classic, cellular, SD-only cams, randomised MACs       │
└────────────────────────────────────────────────────────────────────────────────────────────┘

Table 20 — 9. Cheatsheet updates from this volume

RememberValue
Confidence labels on screenHIGH CONF / MED CONF / LOW CONF
Exact SSIDFlock only; everything else in the SSID table is a prefix
Raven UUIDs0x3100, 0x3200, 0x3300, 0x3400, 0x3500 — five values, not a range
Flipper company ID0x0E29 (not 0x0FBA)
Flipper OUI0C:FA:22 only (not 80:E1:26/27)
Removed for cause00:0E:58 (Sonos, was “Vigilant”); 48:27:EA (Samsung) never added

Comments (0)

  1. Loading…

Comments are held for moderation — nothing appears until approved.