Skip to content

About

Helps you find things! Choose a wireless type (wifi, bluetooth, etc) and the flipper will tell you when you get closer to the device.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Repository files navigation

flipper_hot_cold - Hot/Cold Wireless Tracker

A Flipper Zero app that plays "hot and cold" with wireless signals: pick a radio type, optionally pick a specific device/frequency/channel, then hit GO and the screen shows a big live number that climbs as you get closer and falls as you move away - like a metal detector, but for WiFi, Bluetooth, Sub-GHz, Infrared, RFID, and NFC.

 WiFi  Ch 6                    <- header: type + config
------------------------------
          -47                  <- big number: dBm, or a 0-100% score
        [######----]           <- proximity bar
           HOT!                <- hot/cold word

How it works

The Flipper Zero has its own radios for Bluetooth, Sub-GHz, Infrared, 125kHz RFID, and NFC - those run entirely on-device using the Flipper's built-in hardware. It has no WiFi radio, though, so WiFi tracking needs an ESP32 Marauder board wired to the Flipper's GPIO header, running the stock, unmodified ESP32Marauder firmware

  • there's no companion sketch of ours to flash. The Flipper drives it entirely through Marauder's own serial CLI (scanall, list -a, list -c, foxhunt -w, stopscan...) - see src/helpers/esp32_uart.c for the client and esp32_firmware/README.md for exactly which commands are used and why, including a couple of real limitations that fall out of Marauder's own CLI:

  • No BSSID over serial for access points, so no MAC/vendor lookup for them - APs are identified by SSID + Marauder's own list index instead.

  • Stations (clients) never get an RSSI from Marauder at all - list -c only ever prints a MAC, no signal strength, for any station. That's a hard protocol limit, not a gap on our side. So WiFi discovery shows both access points and clients (their real MAC, and which AP they're talking to), but selecting a client for the GO screen tracks its parent access point as a proxy instead - there's nothing else to track it with.

  • Channel locking is a real lock for tracking, not just a hint. The app sends channel -s <n> itself before foxhunt -w, using the AP's actual discovered channel - it doesn't rely on Marauder's own fox-hunt channel lock, since that logic (WiFiScan::runFoxHunt()) only runs inside #ifdef HAS_SCREEN code on the ESP32 side and may be a no-op on headless board builds. It also doesn't trust Marauder's own (selected) marker in list -a to find the tracked AP's row back - some boards never seem to actually set it - so the tracked AP is matched by list index instead, which only depends on Marauder's sniffer still updating that row's RSSI.

Bluetooth does not go through the ESP32 - most Marauder boards are built on an ESP32-S2, which is WiFi-only silicon with no Bluetooth radio at all, so there's no blescan to drive there. Instead, src/helpers/ble_scan.c uses the Flipper's own radio in raw RX test mode (furi_hal_bt_start_rx/furi_hal_bt_get_rssi, the same primitive used for RF certification) to read undecoded BLE channel activity - like the Sub-GHz mode, it tells you "how much signal is on this channel", not which specific device it's from, so Bluetooth has no device picker. This only works at all if the Flipper's BLE radio coprocessor has the Full stack loaded (furi_hal_bt_is_testing_supported()), since the Light stack most Flippers ship with by default doesn't implement the HCI test commands this needs; the app checks for this up front and says so plainly on the GO screen rather than just sitting at a blank reading. Getting a real BLE scan (attributing a reading to one specific device) would need raw HCI access to the radio coprocessor, which stock Flipper firmware doesn't expose to external apps at all - see the comment at the top of ble_scan.c for what was actually checked before landing on this raw-RX approach as the ceiling of what's reachable here.

Type Radio used Extra hardware
WiFi ESP32 Marauder's scanall/list -a/list -c/foxhunt -w ESP32 Marauder board, stock firmware
Bluetooth Flipper's built-in radio, raw RX + RSSI (undecoded channel activity) -
Sub-GHz Flipper's built-in CC1101 -
Infrared Flipper's built-in IR receiver -
RFID (125kHz) Flipper's built-in LF coil -
NFC (13.56MHz) Flipper's built-in NFC front end -

Menu flow

  1. Type - WiFi, Bluetooth, Sub-GHz, Infrared, RFID, or NFC.

  2. Configure (rows shown depend on the type you picked):

    • Device (WiFi only) - opens a live scan list mixing both access points ([AP] SSID, or "(hidden network)") and clients ([STA] MAC -> SSID, i.e. which network that device is talking to). Picking a client tracks its parent AP as a proxy - see the limitation above. Bluetooth has no device picker at all - see above.
    • Channel (WiFi and Bluetooth) - WiFi: a specific channel (1-14) or All to hop, best-effort (Marauder's own scan loop re-hops regardless). Bluetooth: one of the 3 BLE advertising channels (37/38/39) or All to hop - a real, hard restriction here, since the Flipper's own radio can only listen on one at a time.
    • Frequency (Sub-GHz only) - a common ISM frequency, or All to sweep a preset list (300-915MHz) looking for activity.
    • IR Mode (Infrared only) - Data (only count fully decoded remote-control protocol messages) or All (any raw IR pulses at all, decoded or not).
    • NFC Mode (NFC only) - Reader (act as a reader and detect a passive tag/card held up to the Flipper - any protocol, ISO14443-3A/3B, ISO15693, FeliCa, ...) or Card (emulate a card so an active reader notices the Flipper - e.g. a phone sitting in a tap-to-pay UI). These detect genuinely different things and neither one covers the other: two readers pointed at each other don't see one another at all, so a phone will never show up in Reader mode.
    • (RFID has no options here - it's inherently short range and just reports whether a tag is currently in range.)
    • Min Signal - a threshold below which the reading is flagged as "too far" (dBm for WiFi/Bluetooth/Sub-GHz, a 0-100% closeness score for Infrared/RFID/NFC, since those don't have a real signal strength to read).
    • Sound - On/Off. When on, the GO screen beeps once you're really close (not continuously - see below).
    • GO - starts tracking with the above config.
  3. GO screen - continuously scans/sniffs and shows a big number (dBm or %), a proximity bar, a HOT/COLD word, an LED color tier (red/yellow/green), and - if Sound is on - a rising-pitch beep once you're in the top tier ("ON TOP OF IT!"), so a near-hit doesn't turn into a constant drone. WiFi re-requests a reading from the ESP32 once a second (faster floods Marauder's serial link) and displays anything the board pushes on its own the moment it arrives, so the number moves as fast as Marauder's fox-hunt reports it; everything else, including Bluetooth, updates every tick since it's all native radio reads. Press Back to stop and return to the config screen.

    If Marauder's live fox-hunt stream goes quiet for 5 seconds, the app re-issues foxhunt -w by itself - that stream is the only thing that makes an AP's RSSI actually change (see esp32_firmware/README.md), and it can silently fail to start. After 5 seconds with no reading at all from any source the big number drops back to -- rather than freezing on a stale value.

Debugging

The app writes a running debug log to /ext/apps_data/hot_cold/hot_cold.log on the SD card - src/helpers/applog.c opens the file, writes one line, and closes it again on every single call (slower than keeping a handle open, but it means a crash a moment later doesn't lose whatever was already logged - the point of the file is diagnosing crashes and stuck states, so that trade-off is worth it). It's separate from the system-wide FURI_LOG_* stream, which needs a serial terminal to capture; this one you can just pull off the SD card.

It's truncated fresh on every app launch, and currently covers: every command sent to the ESP32, WiFi discovery/tracking decisions (including the fox-hunt retry logic), Bluetooth's raw RSSI reads for the first few ticks after each start, and every single call in and out of the NFC helper (the most crash-prone one) with its return value. If you hit a bug, reproduce it, then pull that file and it should make the cause obvious - or at least narrow it down to one specific call.

Note on WiFi tracking specifically. Individual received lines are not logged any more. Writing to the SD card takes a file open/write/close each time, and doing that per parsed line - on the UART worker thread, while bytes are still streaming in at 115200 - was slow enough to back up the receive buffer and drop the very lines it was trying to capture. The GO screen emits one wifi poll: summary line per second instead, built from counters kept on the UART side:

wifi poll: rssi=-62 has=1 age=180ms live=47(180ms ago) list=6 mismatch=0 unparsed=0 last=
  • live climbing - Marauder's fox-hunt push is working; the number on screen is genuinely live. This is the healthy case.
  • live=0 but list climbing - foxhunt -w never took. list -a is answering, but an AP's RSSI there is write-once, so the displayed value will never change no matter how often it's polled. The app re-issues foxhunt -w every 5s in this state; if it never recovers, suspect the board's Marauder build or the AP index.
  • both 0 - nothing is coming back at all. Check wiring/baud, and that esp32_uart_ping logged OK at the top of the session.
  • unparsed climbing with a last= value - lines are arriving in a shape the parser doesn't recognize; last= is the most recent one.

Note on NFC, for the same reason and then some. The NFC callbacks are hard real-time on the Card side - the listener has to answer a reader inside the ISO14443-3A frame delay time, and an SD-card write there blows straight through that budget, so the reader gives up and the Flipper is never seen. Nothing is logged from those callbacks either; the GO screen emits one nfc poll: line per second instead:

nfc poll: mode=0 detections=12 age=140ms field_off=0 rescans=11 last_event=0 last_proto=0

mode is 0 for Reader and 1 for Card. detections climbing means cards (Reader) or readers (Card) are being seen. rescans is Reader-only and counts scanner restarts, which only happen after a detection - so detections=0 rescans=0 with a card physically on the antenna means the scan is running but nothing is answering it.

Repository layout

application.fam                  Flipper app manifest (ufbt/fbt)
src/                              Flipper app source (C)
  hot_cold.c                      Entry point
  hot_cold_i.h                    Shared app struct/types
  scenes/                         Scene manager: TypeSelect, Configure, DeviceScan, Go
  views/go_view.c                 The big-number/proximity-bar canvas view
  helpers/
    wireless_type.h               The 6 radio types + which fields each one needs
    esp32_uart.c/.h                Client for the ESP32 Marauder board's own serial CLI (WiFi only)
    ble_scan.c/.h                  Native Bluetooth RSSI meter (furi_hal_bt raw RX)
    subghz_scan.c/.h               Native CC1101 RSSI sweep
    infrared_scan.c/.h             Native IR presence detector
    rfid_scan.c/.h                 Native 125kHz RFID presence detector
    nfc_scan.c/.h                  Native NFC presence detector
    signal_source.c/.h             Unifies all 6 into one poll() interface
    applog.c/.h                    Crash-resilient debug log written to the SD card - see "Debugging" below
icons/hot_cold_10px.png           App list icon
esp32_firmware/README.md          Wiring + which Marauder CLI commands are used (no code - stock firmware)
scripts/install_to_sd.py          Copies the built .fap onto the Flipper
Makefile                          Wires the above together

mac-address-vendor-database/, tools/build_vendor_db.py, and vendor_db/ are leftovers from an earlier design that resolved MAC addresses to vendor names for a device picker - WiFi APs never expose a MAC over Marauder's serial protocol at all (see "How it works" above), so nothing in the app calls into them anymore. Left in place in case WiFi vendor lookup gets wired up later (Marauder's info -a <idx> does return a BSSID, just not in the live discovery list this app polls), but safe to delete if you don't need that.

Setup

You'll need:

  • A Flipper Zero.
  • An ESP32 Marauder board if you want WiFi tracking, flashed with the stock ESP32Marauder firmware (not something built from this repo) and wired to the Flipper's GPIO header - see esp32_firmware/README.md for pinout and flashing links. Not needed for Bluetooth/Sub-GHz/IR/RFID/NFC.
  • Python 3.9+.
make setup     # pip install -r requirements.txt, then `ufbt update` to pull the Flipper SDK
make build     # builds the .fap

ufbt build doesn't drop the .fap inside this project - it builds into ~/.ufbt/build/hot_cold.fap, a per-user cache shared across every ufbt app you build. make install/make launch know where to find it; you only need the path yourself if you're scripting around this repo.

Installing

The most reliable way to get the app onto the Flipper is to mount its SD card as a regular drive (pull the card and use a reader, or use qFlipper's file browser mount) and point the installer at it:

make install SD=E:\           # Windows drive letter
make install SD=/media/flipper-sd   # Linux/macOS mount point

That copies ~/.ufbt/build/hot_cold.fap -> <sd>/apps/GPIO/hot_cold.fap.

There's also an experimental USB-serial path (make install PORT=COM5, via the optional pyflipper dependency) for when you don't want to pull the SD card, and make launch to build-and-run directly over USB via ufbt launch. Both need the app to already be built (make build); the serial path is best-effort - if it fails, fall back to SD=.

ESP32 Marauder board

There's nothing to build for it - flash the stock ESP32Marauder firmware using its own releases/web flasher, then wire it up. See esp32_firmware/README.md for links, pinout, and exactly which Marauder CLI commands this app relies on.

Releases

Pushing to a branch named rel-* (e.g. rel-1.0.0) triggers .github/workflows/release.yml, which checks out the repo (submodules included - mac-address-vendor-database/ is a git submodule, just a reference to its own repo, not content copied in here), runs make setup && make build on a clean Ubuntu runner, and publishes/updates a GitHub Release named after the branch with hot_cold.fap attached. Re-pushing to the same rel-* branch (or re-running the workflow manually) replaces that release's asset rather than failing on a duplicate tag.

A note on the Flipper SDK surface

This targets the standard ufbt/firmware SDK API (scene manager, view dispatcher, furi_hal_serial, furi_hal_subghz, furi_hal_bt, infrared_worker, lfrfid_worker, the low-level Nfc transport). It's been built and verified clean (zero warnings, -Werror and all) against SDK/API 87.1 (firmware 1.4.3) via ufbt build. Most of that surface is stable across releases, but the Sub-GHz/Bluetooth/Infrared/RFID/NFC helper files (src/helpers/subghz_scan.c, ble_scan.c, infrared_scan.c, rfid_scan.c, nfc_scan.c) touch lower-level radio APIs that can still shift between firmware versions and forks (official vs. Momentum/Unleashed/etc) - if ufbt build reports an error there on your firmware, check the exact symbol against your pinned SDK (~/.ufbt/current/sdk_headers); the surrounding logic shouldn't need to change, just the specific API names.

License

No license has been chosen yet for this project's own code. The bundled mac-address-vendor-database/ is MIT-licensed (see its own LICENSE file).

About

Helps you find things! Choose a wireless type (wifi, bluetooth, etc) and the flipper will tell you when you get closer to the device.

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages