
Specter — пассивный сканер/скиммер NFC 13.56 МГц для поиска жучков на Flipper Zero. Измеритель ЭМП для контрнаблюдения с использованием встроенного чипа NFC. Без дополнительного оборудования, никогда не передаёт сигнал.
<!-- banner: paper by default, true black when the reader's GitHub is dark -->
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/banner-dark.png">
<img src="https://assets.kitploit.com/production/public/readmes/42070/74bc06f0a6a1b0731b4768b4256f662b34a582f5b9701a9bce44cb1b10bedd0d.png" alt="Specter — NFC reader and skimmer bug-sweep for Flipper Zero" width="100%">
</picture>
</p>
<p align="center">
<sub>
Осциллограмма — не графика. Это 128 столбцов несущей 13,56 МГц, которые
Specter записал с реального опрашивающего ридера по 8 мс на столбец, считанные
обратно со скриншота рядом. Её коэффициент заполнения измеряется как 28%, тогда как
собственный счётчик устройства напечатал 27%, и оба числа есть на баннере.
</sub>
</p>
<p align="center"><i>Прочешите пространство в поисках ридеров, которых не видите.</i></p>
<p align="center">
<a href="https://at0m-b0mb.github.io/Specter-FlipperZero/"><b>Сайт проекта</b></a>
·
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/releases/latest">Скачать</a>
·
<a href="https://github.com/at0m-b0mb/specter-flipperzero/blob/main/docs/changelog.md">Список изменений</a>
</p>
<!-- live badges: these track the repo, so the README never goes stale -->
<p align="center">
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/releases/latest"><img src="https://img.shields.io/github/v/release/at0m-b0mb/Specter-FlipperZero?style=flat-square&color=8A6714&labelColor=000000" alt="Latest release"></a>
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/releases"><img src="https://img.shields.io/github/downloads/at0m-b0mb/Specter-FlipperZero/total?style=flat-square&color=8A6714&labelColor=000000&label=downloads" alt="Downloads"></a>
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/stargazers"><img src="https://img.shields.io/github/stars/at0m-b0mb/Specter-FlipperZero?style=flat-square&color=8A6714&labelColor=000000" alt="Stars"></a>
<a href="https://github.com/at0m-b0mb/Specter-FlipperZero/actions/workflows/build.yml"><img src="https://img.shields.io/github/actions/workflow/status/at0m-b0mb/Specter-FlipperZero/build.yml?branch=main&style=flat-square&labelColor=000000&color=8A6714&label=build" alt="Build"></a>
</p>
<p align="center">
<img src="https://img.shields.io/badge/platform-Flipper%20Zero-8A6714?style=flat-square&logo=flipper&logoColor=FE8A2C&labelColor=000000" alt="Flipper Zero">
<img src="https://img.shields.io/badge/radio-13.56%20MHz%20NFC%20(onboard)-8A6714?style=flat-square&labelColor=000000" alt="Onboard NFC">
<img src="https://img.shields.io/badge/extra%20hardware-none-8A6714?style=flat-square&labelColor=000000" alt="No extra hardware">
<img src="https://img.shields.io/badge/transmits-never-8A6714?style=flat-square&labelColor=000000" alt="Listen-only">
<img src="https://img.shields.io/badge/license-MIT-8A6714?style=flat-square&labelColor=000000" alt="MIT">
</p>
<p align="center">
<b>Specter</b> превращает ваш Flipper Zero в карманный <b>поисковик подслушивающих устройств</b> для
<b>активных NFC-ридеров на 13,56 МГц</b>. Он <i>пассивно слушает</i> радиочастотное поле, которое постоянно
излучает включённый ридер — скрытый скиммер, подсунутый в платёжный терминал, тайный
ридер за дверной панелью, нелегальный логгер, приклеенный под столом — а затем сообщает вам <b>где он</b>,
<b>что это за штука</b>, <b>чисто ли в помещении</b> и — если оставить его на страже — <b>в
тот момент, когда он появится, пока вас нет</b>. Он никогда не передаёт сигнал.
</p>
<p align="center"><sub>Ридеры невидимы. Specter делает их видимыми.</sub></p>
<p align="center">
<sub>
<b>Публикации о проекте</b><br>
<a href="https://www.helpnetsecurity.com/2026/07/29/specter-flipper-zero-skimmer-detector/">Help Net Security</a>
·
<a href="https://cybersecuritynews.com/specter-with-flipper-zero/">Cyber Security News</a>
</sub>
</p>
---
## На Flipper
<p align="center">
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/images/demo.gif" width="70%" alt="Specter in use: sweeping a quiet room, closing in on a reader, locking on at PEGGED, fingerprinting its polling cadence, then a site-survey verdict">
</p>
<p align="center">
<sub>Прочёсывание от начала до конца: тихая комната → приближение → захват → <b>что</b> это → вердикт по помещению.</sub>
</p>
### Экраны, которыми не владеет ни один режим
<p align="center">
<picture>
<source media="(prefers-color-scheme: dark)" srcset="images/screens-dark.png">
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/images/screens.png" width="100%" alt="The boot intro, main menu, Sweep listening, Fingerprint with nothing there, a Site Survey walking, Watch with a contact logged, the Logbook and Settings — all captured from a real Flipper Zero">
</picture>
</p>
<p align="center">
<sub>
Тихие состояния и интро — у четырёх режимов ниже есть собственные
снимки, так что здесь те, которых у них нет. <b>Каждое изображение
в этом README — снимок с реального устройства</b>, сделанный через
собственную RPC-сессию Flipper с помощью <code>tools_screenshot.py</code>. В этом проекте
больше нет рендерера макетов — рисунок интерфейса это вторая
его реализация, и он расходился с прошивкой, при этом выглядя
совершенно убедительно (см. <a href="https://github.com/at0m-b0mb/specter-flipperzero/blob/main/docs/changelog.md">3.0.1</a>).
</sub>
</p>
---
## Пять режимов
Specter — это пять инструментов вокруг одного датчика. Каждый отвечает на свой вопрос, так что
правильный выбор зависит от того, что вы на самом деле пытаетесь выяснить:
| Режим | На какой вопрос отвечает | Когда использовать |
|---|---|---|
| **Sweep** | *Где он?* | Вы подозреваете устройство и хотите найти его, перемещаясь вокруг |
| **Fingerprint** | *Что это за штука?* | Вы нашли излучатель и хотите узнать, как он себя ведёт |
| **Site Survey** | *Чисто ли в этом помещении?* | Вы хотите один вердикт для всего пространства, без рук |
| **Watch Mode** | *Появился ли кто-то, пока меня не было?* | Вы оставляете Flipper где-то нести вахту |
| **Logbook** | *Что я нашёл и когда?* | Вы пишете отчёт или сравниваете сегодня с прошлой неделей — фильтр по типу находки |
---
### Sweep — *где он?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/sweep_reader.png" width="42%" align="right" alt="Sweep — reader found, meter pegged">
**Что делает.** Живой измеритель в стиле ЭМП. Держите Flipper плашмя и медленно перемещайте
над тем, что проверяете — платёжный терминал, дверной ридер, нижняя сторона
ATM-щели, посылка, стол. Стрелка следует за напряжённостью поля в реальном времени.
**Что вы видите.**
- **Шкала** — стрелка = текущее показание, маленькая точка = *удержание пика* (самое сильное
место, которого вы касались, чтобы можно было сравнивать позиции).
- **`FIELD %`** — то же показание числом, со **стрелкой тренда ▲ / ▼**,
сообщающей, стало ли за последние полсекунды *теплее или холоднее*. Когда
вы охотитесь, эта стрелка важнее числа.
- **`PEAK %`** — самое сильное показание с момента взведения, число за
точкой удержания пика.
- **Отметка на шкале** — где `READER` *начинается* при вашей текущей
чувствительности. Всё за ней — то, что Specter назовёт попаданием, так что шкала
отвечает на вопрос «что считается?», а не оставляет вас гадать.
- **Нижняя полоса** — пока вы не нашли первый ридер, она несёт две клавиши,
которые нельзя обнаружить, нажимая что попало (`LEFT=cal`, `hold OK=log`); после этого —
ваша чувствительность и живая осциллограмма. Она переключается на чёрную
аварийную полосу **`● ACTIVE READER`** со словом близости в момент обнаружения
несущей.
- **Близость** — `FAINT → NEAR → CLOSE → STRONG → PEGGED`. `PEGGED` означает, что измеритель
*зашкалил*: вы настолько близко, насколько это измерение может различить.
**Звук.** Опциональные гейгеровские щелчки, которые **учащаются по мере приближения**, так что можно
прочёсывать с Flipper в кармане и охотиться на слух.
| Клавиша | Действие |
|---|---|
| `OK` | Сбросить удержание пика и счётчик контактов |
| `hold OK` | Сохранить это показание в журнал |
| `LEFT` | Калибровка по уровню шума помещения (3 с) |
| `UP` / `DOWN` | Пошагово менять чувствительность, не выходя из охоты |
<br clear="right">
### Fingerprint — *что это за штука?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/fingerprint_reader.png" width="42%" align="right" alt="Fingerprint — polling reader identified">
**Что делает.** Когда Sweep что-то нашёл, держите Flipper **неподвижно**
у этого объекта. Fingerprint перестаёт измерять близость и начинает **замерять
фронты включения/выключения несущей**, что и отличает один тип
излучателя от другого.
**Что вы видите.** Вердикт, полосу уверенности и доказательства за обоими:
| Класс | Что это значит на практике |
|---|---|
| **CONTINUOUS** | Несущая удерживается постоянно |
| **POLLING** | Фиксированный, кварц-стабилизированный цикл опроса — период показан в мс |
| **INTERMITTENT** | Пачками, но нерегулярно — часто телефон или используемый ридер |
Ниже — `PER` (период опроса), `BST` (ширина пачки), `JIT` (джиттер) и
`UP` (истинный коэффициент заполнения), плюс **импульсная последовательность логического
анализатора** сырой несущей — так что вердикт никогда не приходится принимать
на веру. Настоящий опрашивающий ридер проявляется как безошибочно узнаваемая прямоугольная волна.
> `~` перед значением тайминга означает, что оно близко к порогу дискретизации 2 мс, и
> уверенность снижается соответственно.
| Клавиша | Действие |
|---|---|
| `OK` | Сохранить находку в журнал |
| `hold OK` | Перезапустить измерение |
<br clear="right">
### Site Survey — *чисто ли в этом помещении?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/survey_active.png" width="42%" align="right" alt="Site Survey — verdict">
**Что делает.** **Ограниченное по времени** прочёсывание всего пространства. Запустите, обойдите помещение
обычным шагом и получите единый вердикт в конце — без слежения за стрелкой.
**Что вы видите.** Обратный отсчёт и полосу прогресса во время работы, затем карточку вердикта:
- **`CLEAN`** — ничего не пересекло уровень шума
- **`TRACE`** — краткие или слабые попадания; стоит пройти медленнее второй раз
- **`ACTIVE READER`** — что-то действительно было включено и излучало
- **`TOO SHORT`** — вы остановили это раньше, чем оно заслужило право сказать `CLEAN`
…с `PEAK` / `AVG` поля, числом контактов и `UP %` — **какую долю обследования
несущая была реально включена**, что часто самое показательное число из четырёх.
Работает 30 с, 60 с или 2 мин и автоматически записывает результат в журнал. Карточка называет
длительность, за которую выставлялась оценка, потому что 30-секундный `CLEAN` — не та же
находка, что двухминутный — а прогон, прерванный раньше 10 с, сообщает
`TOO SHORT` вместо `CLEAN`. Асимметрия намеренная: **присутствие —
доказательство, отсутствие — нет.** Найти что-то за одну секунду — реальная находка, так что
`TRACE` и `ACTIVE READER` никогда не утаиваются за быстроту; не найти ничего
за одну секунду — это утверждение обо всём помещении, которое одна секунда подтвердить не может.
| Клавиша | Действие |
|---|---|
| `OK` *(во время работы)* | Остановить сейчас и оценить то, что есть |
| `OK` *(на карточке)* | Запустить ещё одно обследование |
<br clear="right">
### Watch Mode — *появился ли кто-то, пока меня не было?*
<img src="https://raw.githubusercontent.com/at0m-b0mb/specter-flipperzero/main/screenshots/watch_reader.png" width="42%" align="right" alt="Watch Mode — reader present">
**Что делает.** Несёт вахту **бесконечно**. Взведите его, положите Flipper,
уходите. Если Sweep — это вы на охоте, а Survey — тест фиксированной длины, то Watch
просто ждёт — минуты или часы.
**Что вы видите.** Большие часы прошедшего времени и `NO READER`, пока ничего нет.
В тот момент, когда появляется ридер, полоса инвертируется в **`ACTIVE READER`**, полоса
силы показывает, насколько он близко, **экран просыпается**, и звучит сигнал. Полоса
ровная, а не мигающая — на 1-битном экране сплошной инвертированный блок и так
самое громкое, что на нём есть, а показания, которые действительно меняются, несут
живость. После того как он пройдёт, экран сохраняет доказательства: `HITS` (сколько
отдельных контактов), `PEAK`, `LAST` (как давно был последний) и
`UP` (общее время, когда несущая была реально включена). Часы переходят на `hh:mm`
после 99:59, а не замирают, так что ночная вахта всё равно суммируется.
Watch **намеренно игнорирует режим скрытности** — тёмный, безмолвный страж, который никогда
не скажет, что что-то увидел, был бы хуже, чем бесполезен.
| Клавиша | Действие |
|---|---|
| `OK` | Перевзвести — очищает часы и счётчики |
<br clear="right">
### Logbook — *что я нашёл и когда?*
Находки сохраняются **дважды, одним действием**, так что они читаемы на устройстве
*и* уже являются таблицей — не нужно помнить об экспорте:
<table>
<tr><th><code>logbook.txt</code> — сгруппированный, для просмотрщика на устройстве</th><th><code>logbook.csv</code> — одна плоская строка на запись</th></tr>
<tr><td>
```
2026-07-18 14:35:11
SURVEY 60s ACTIVE max74…
2026-07-18 14:32:07
READER POLLING 204ms…
```
</td><td>
```
timestamp,type,detail
2026-07-18 14:35:11,SURVEY,60s ACTIVE …
2026-07-18 14:32:07,READER,POLLING 204ms …
```
</td></tr>
</table>
Записи помечаются тегами `SWEEP`, `READER`, `SURVEY` или `WATCH` и снабжаются
меткой времени с часов Flipper. Оба файла находятся в `apps_data/specter/` на SD-карте.
Ничего никогда не покидает устройство. Очистить их можно через **Settings → Clear logbook**
(сначала запросит подтверждение — это единственное необратимое действие в приложении).
---
## Вспомогательные элементы
### Автокалибровка
Нажмите **LEFT** на экране Sweep, и Specter будет слушать **фоновый уровень шума
в течение 3 секунд**, затем установит порог обнаружения чуть выше измеренного
значения — это сохранится как ваша **Custom** чувствительность. В каждой комнате
свой уровень RF-шума; эта функция настраивается на тот, в котором вы находитесь,
а не на число, зашитое при сборке. Запускайте её в тихом месте.
### Режим скрытности
Держит **экран и светодиод выключенными** на протяжении всего сканирования, чтобы
Flipper не светился, пока вы сами ведёте наблюдение. Звук и вибрация продолжают
работать — смысл не в том, чтобы отключить обратную связь, по которой вы
сканируете. Выход из скрытого экрана снова включает дисплей, так что `BACK` всегда
приводит вас на освещённое меню.
### Чтение индикатора
**Почему считыватель, к которому вы прикасаетесь, излучает не 100% времени.**
Детектор измеряет одну физическую величину: какую долю времени активна несущая
13.56 МГц. Но считыватели **опрашивают** — короткий импульс, пауза, ещё импульс.
Типичный терминал излучает только **20–35% времени**, поэтому необработанный
коэффициент заполнения *насыщается* около 30%, как бы близко вы ни подошли.
Отображение этого необработанного числа на шкале заставляло идеальное обнаружение
выглядеть как треть от него. Поэтому индикатор масштабирован относительно этого
реального диапазона опроса:
| Необработанный коэффициент заполнения несущей | Индикатор | Близость |
|---|---|---|
| 3% (шум помещения) | 10% | `FAINT` |
| 12% | 40% | `NEAR` |
| 20% | 67% | `CLOSE` |
| 28% | 93% | `STRONG` |
| ≥30% *(лежит на считывателе)* | **100%** | `PEGGED` |
**Необработанный коэффициент заполнения никогда не теряется**: уровень шума,
автокалибровка и `UP` на экране Fingerprint по-прежнему работают в истинном
коэффициенте заполнения, потому что они описывают *сигнал*, а не ваше расстояние
до него. Хотите буквальное число? **Settings → Meter scale → Duty %**.
### Всё сохраняется
Чувствительность, длительность survey, звук, вибрация, LED, скрытность, логирование
и режим индикатора **сохраняются на SD-карту** в момент изменения. Набор для
сканирования, который забывает свои настройки, хуже, чем полное отсутствие
сохранения.
---
## Как это работает
Каждый включённый NFC-считыватель непрерывно **зондирует эфир несущей 13.56 МГц**,
ожидая, когда карта проснётся. Вы этого не видите, но NFC-чип Flipper может:
**ST25R3916** имеет аппаратный **детектор внешнего поля** (та же схема, которая
позволяет Flipper эмулировать карту и знать, когда считыватель обращается к нему).
Specter переводит чип в режим только обнаружения и **сэмплирует этот бит
«поле присутствует?» ~500 раз в секунду** — никогда не включая собственную несущую.
Этот единственный бит, сэмплируемый достаточно быстро, несёт два независимых сигнала:
```mermaid
flowchart LR
R["Hidden 13.56 MHz reader / skimmer<br/>(constantly polling its field)"] -- "RF carrier" --> A
subgraph FLIP["Flipper Zero — Specter.fap"]
A["ST25R3916 NFC chip<br/>external-field detector"] --> S["Sampler thread<br/>~500 samples/s"]
S --> M["<b>How much?</b><br/>carrier duty-cycle<br/>peak · average · contacts"]
S --> C["<b>What rhythm?</b><br/>burst / gap edge timing<br/>period · jitter"]
M --> G["EMF gauge · waveform<br/>geiger clicks · LED · vibe"]
M --> V["Survey verdict<br/>CLEAN / TRACE / ACTIVE"]
C --> K["Emitter class<br/>CONTINUOUS / POLLING /<br/>INTERMITTENT + pulse train"]
V --> L["SD logbook"]
K --> L
end
```
**Сколько → сила.** На коротком окне Specter измеряет, *какую долю времени* присутствует несущая, и сглаживает это в **FIELD %**. Считыватель, находящийся прямо над Flipper, загоняет стрелку в максимум; более слабый или удалённый лишь слегка её сдвигает. Это управляет стрелкой, словом близости и частотой щелчков.
**Какой ритм → идентичность.** Отдельно замеряется каждое переключение этого бита. Считыватель, который просыпается на 20 мс каждые 200 мс, создаёт паттерн всплеск/пауза почти без джиттера, потому что его цикл опроса управляется тактовым конечным автоматом. Движение руки и радиочастотный шум не настолько стабильны. Именно это различие отделяет **POLLING** от **INTERMITTENT**.
### Слои принятия решений тестируются на реальном компьютере
Места, где Specter превращает числа в *утверждение* — «это опрашивающий считыватель», «эта комната чиста», «вот что должна показывать стрелка» — это чистый C без аппаратных зависимостей, и они закреплены хостовыми тестами, а не обнаружены на устройстве:
```bash
make -C test # 527 checks: classifier, verdict, meter scaling, presence,
# cadence, log filtering, log wrapping
```
---
## Установка
Никакой devboard, никакой прошивки для прошивки — это один `.fap`.
**Вариант A — готовый `.fap` (самый простой)**
1. Возьмите нужный `.fap` со страницы [**Releases**](https://github.com/at0m-b0mb/Specter-FlipperZero/releases):
| Ваша прошивка | Файл |
|---|---|
| **Официальная / stock** | `specter.fap` |
| **Unleashed, RogueMaster, Momentum** или что-то новее | `specter-fw-dev.fap` |
> Если Flipper показывает **`APP:87 < FW:88 — This app might not work`**, у вас
> неподходящий файл — возьмите другой. Версия API приложения фиксируется при компиляции,
> поэтому одна сборка не может подойти для обеих линеек прошивок. Обычно оно всё равно
> запускается, если нажать Continue, но подходящая сборка не будет спрашивать.
2. Откройте **qFlipper**, перетащите файл в `SD Card / apps / NFC /`.
3. На Flipper: **Apps → NFC → Specter**.
**Вариант B — соберите сами с помощью [`ufbt`](https://github.com/flipperdevices/flipperzero-ufbt)**
```bash
# one-time
python3 -m pip install --upgrade ufbt
# from the repo root, with your Flipper plugged in over USB:
ufbt # build specter.fap into ./dist
ufbt launch # build, upload to the Flipper and open it
make -C test # run the host tests for the pure logic
```
`.fap` попадает в `dist/specter.fap`; `ufbt launch` копирует его в `apps/NFC/` и запускает за вас.
> Иконки в `icons/` и баннер в `images/` сгенерированы — перегенерируйте с помощью
> `python3 tools_gen_icons.py` и `python3 tools_gen_banner.py` (требуется `pillow`).
> Всё в `screenshots/` снято с реального устройства с помощью
> `python3 tools_screenshot.py` (требуется `pyserial` и Flipper, подключённый по USB).
---
## Использование
**Свип, от начала до конца:**
1. Запустите **Specter**. На экране **Sweep** нажмите **LEFT** и держите неподвижно 3 секунды, чтобы
откалиброваться по уровню шума в помещении.
2. Держите Flipper плашмя и **медленно** перемещайте его по тому, что проверяете — платёжный терминал,
дверной считыватель, нижняя часть банкомата, посылка, стол.
- **Тихая / плоская осциллограмма** → активного считывателя в зоне действия нет.
- **Стрелка ползёт вверх, щелчки ускоряются** → вы приближаетесь к излучателю. Продолжайте двигаться к пику.
- **`● ACTIVE READER` + рамка тревоги** → активный считыватель прямо здесь.
3. Что-то нашли? Выйдите назад и откройте **Fingerprint**. Держите Flipper неподвижно рядом с ним и дайте
ритму устояться — обычно достаточно нескольких секунд. Нажмите **OK**, чтобы сохранить находку.
4. Вместо этого проверяете целое помещение? Откройте **Site Survey**, обойдите пространство, пока шкала не заполнится, и прочитайте
вердикт.
5. Уходите из зоны? Оставьте Flipper в **Watch Mode**, и он будет стоять на страже, просыпаясь и подавая сигнал,
если считыватель появится, пока вас нет.
6. Проверьте **Logbook** — там всё, что вы сохранили, с отметками времени — или выгрузите `logbook.csv` с карты
в электронную таблицу.
> Сначала просканируйте заведомо исправный считыватель (свой телефон с NFC или бесконтактный терминал, которому доверяете), чтобы
> увидеть, как выглядит сильное, легитимное поле на индикаторе — и как читается его отпечаток. Затем
> отправляйтесь на охоту.
### Управление
| Экран | Клавиша | Действие |
|---|---|---|
| **Sweep** | `OK` | Сбросить пик / контакты |
| | `hold OK` | Записать текущее показание |
| | `LEFT` | Калибровать уровень шума (3 с) |
| **Fingerprint** | `OK` | Сохранить находку в журнал |
| | `hold OK` | Перезапустить измерение |
| **Site Survey** | `OK` | Запустить обследование заново |
| **Watch Mode** | `OK` | Перевести в боеготовность (сбросить счётчик и таймер) |
| *где угодно* | `BACK` | На уровень выше |
---
## Честные ограничения
- **Только 13,56 МГц (HF).** Specter чувствует **диапазон NFC** — тот, что используется бесконтактными платёжными скиммерами,
большинством современных считывателей доступа, транспортными и гостиничными считывателями. Он **не может** видеть считыватели **125 кГц (LF)**
(старые дверные панели HID Prox / EM4100); у LF-тракта Flipper нет эквивалентного бита обнаружения поля.
- **Он чувствует *несущую* считывателя, а не то, что тот считывает.** Specter сообщает вам, *что активный считыватель здесь,
примерно насколько близко и в каком ритме он опрашивает* — он не декодирует, не идентифицирует и не захватывает ничего,
что делает считыватель.
- **Сила относительна, а не откалибрована.** `FIELD %` и слова близости — это сравнительный ориентир «теплее /
холоднее» для сканирования, а не измеренное расстояние в см. Это *масштабированное* показание рабочего цикла несущей
относительно типичной полосы опроса (см. [Чтение индикатора](#-reading-the-meter--fixed-in-23));
считыватель, который опрашивает необычно редко, будет показывать меньше на том же расстоянии, а работающий в режиме непрерывной волны
будет зашкаливать с большего расстояния.
- **Индикатор упирается в потолок.** `PEGGED` означает, что несущая поднята настолько, насколько этот считыватель её вообще держит — за
этой точкой приближение действительно не может дать большего числа. Flipper один раз жужжит, когда это происходит,
так что вы знаете, что нужно остановиться, даже когда не видите экран. Используйте удержание `PEAK`, чтобы сравнивать
позиции.
- **Ритм разрешается примерно до ~2 мс.** Это период дискретизации. Значения времени вблизи него показываются с
**`~`**, и уверенность соответственно снижается — Specter предпочтёт обозначить свой собственный предел разрешения, чем назвать
точное на вид число, за которое не может поручиться.
- **`CLEAN` означает чисто при выбранной вами чувствительности.** Дремлющий скиммер, который просыпается только при реальном прикладывании,
или экранированный, остаётся невидимым при *любом* пороге. Отсутствие показания не является гарантией
отсутствия.
- **Одно радио за раз.** Specter захватывает чип NFC во время сканирования, поэтому сначала закройте любое другое приложение NFC
(оно скажет *NFC unavailable*, если радио занято чем-то другим).
---
## Законность и этика
Specter — это **защитный инструмент, работающий только на приём** — он **никогда не передаёт**, никогда не создаёт поле, никогда не касается
считывателя. Используйте его для сканирования **своей собственной** зоны POS, двери, стола или вещей, либо оборудования, которое вы **явно
уполномочены** проверять. Вы несёте ответственность за то, как вы его используете. Знайте свои местные законы.
---
## План развития
- [x] Сохранять чувствительность / звук / вибрацию / LED между перезагрузками
- [x] «Журнал» обнаружений с отметками времени на SD-карту
- [x] Фоновое сканирование с выключенным экраном — *stealth mode*
- [x] Настраиваемый порог для более тонкого контроля диапазона — *авто-калибровка + Custom sensitivity*
- [x] Отпечаток излучателя по его ритму опроса
- [x] Обследование участка по таймеру с вердиктом по помещению
- [x] Экспорт журнала в CSV для отчётности — *записывается в реальном времени рядом с .txt*
- [x] Длительный автономный режим наблюдения с сигналом пробуждения при обнаружении
- [x] Заставить индикатор использовать весь свой диапазон против реальных опрашивающих считывателей
- [x] Стрелка тренда «теплее/холоднее» во время охоты
- [ ] Исследовать чувствительность к считывателям LF (125 кГц) на основе катушки как отдельный режим
- [ ] Фильтрация журнала на устройстве по типу
---
## Структура проекта
```
Specter-FlipperZero/
├── application.fam # Flipper app manifest (category: NFC)
├── specter.c / specter_i.h # app entry, wiring, alert feedback, stealth
├── helpers/
│ ├── field_detector.{c,h} # worker thread: samples the field-present bit,
│ │ # duty-cycle + edge timing + calibration
│ ├── emitter_classify.{c,h} # pure: cadence -> CONTINUOUS/POLLING/INTERMITTENT
│ ├── survey_verdict.{c,h} # pure: survey stats -> CLEAN/TRACE/ACTIVE
│ ├── field_scale.{c,h} # pure: raw carrier duty -> full-scale meter
│ ├── present_hold.h # pure: debounce presence across poll gaps
│ ├── cadence.h # pure: burst/gap/period timing with liveness
│ ├── ema.h # pure: the strength smoother
│ ├── log_filter.h # pure: keep only one kind of finding
│ ├── log_wrap.h # pure: wrap an entry at words, not mid-word
│ ├── specter_settings.{c,h} # persisted settings (saved_struct)
│ └── specter_log.{c,h} # SD logbook, RTC-stamped .txt + live .csv
├── views/
│ ├── view_chrome.h # the header, presence dot and error screen
│ │ # every measurement view shares
│ ├── sweep_view.{c,h} # the EMF gauge / waveform / alarm screen
│ ├── fingerprint_view.{c,h} # classification card + pulse-train trace
│ ├── survey_view.{c,h} # progress + verdict card
│ └── watch_view.{c,h} # unattended guard: clock + count + alarm
├── scenes/ # scene-manager navigation
├── test/ # host tests for the pure decision layers
├── icons/ # 1-bit Flipper icons (generated)
├── images/ # banner, mark and social card (generated), plus
│ # device_*.png: real captures off a Flipper
├── screenshots/ # the six captures the Apps Catalog listing uses
├── docs/catalog/ # the prepared catalog manifest + how to submit
├── tools_check_layout.py # static overlap + clearance checker (CI-gated)
└── tools_gen_*.py # regenerate icons / mockups / banner / GIF
```
---
## Брендинг
**BACKLIGHT.** В основе — **Didot** для идентичности, **Futura** для голоса, **Andale Mono**
для каждого измеренного значения. Оранжевый — это `#FE8A2C`, *взятый* из захвата qFlipper
работающего приложения, так что это собственная подсветка Flipper, а не
цвет, выбранный в подражание ей. Каждый захват в `screenshots/` состоит ровно из двух
цветов, `(254,138,44)` и `(0,0,0)`; именно это излучает аппаратура.
Он никогда не используется как фирменный цвет. **Единственный уголёк на любом ассете — это свет, который
устройство действительно излучало** — пиксели внутри реального захвата, вставленные нетронутыми в
целочисленном масштабе. Графика нарисована в том же оттенке на пятнадцать градусов холоднее. Одна шкала,
читаемая с противоположных концов: **больше чернил — больше сигнала на бумаге, больше света —
больше сигнала на чёрном** — потому что бумага — это то, где заканчивается развёртка (Specter пишет
журнал с отметками времени, который можно передать кому-то), а чёрный — это то, где это происходит
(режим скрытности принудительно отключает подсветку).
Акцент рисует одну вещь и ничего больше: **измеренную несущую.** Он никогда не является
линейкой, рамкой, полем или словом — во всей системе нигде нет золотых слов,
и рендерер выдаёт ошибку, если такое будет предпринято. Так что баннер несёт
ровно один объект акцентного цвета, и этот объект — измерение.
Осциллограмма — это 128 столбцов несущей, которую Specter записал с реального опрашивающего считывателя
при 8 мс на столбец, выскобленных обратно из скриншота рядом с ней. Плоский участок
слева — это буфер трассировки до его заполнения, а не тишина, которую кто-либо измерял. Его
коэффициент заполнения читается как **28%**, тогда как собственный счётчик устройства напечатал **27%**, и оба
числа есть на баннере. Метка — это один цикл опроса при **30%** заполнения —
12 мс из 40 мс того считывателя, а также `SPECTER_FULL_SCALE_DUTY`, коэффициент заполнения, при котором
измеритель показывает 100%. Одно число, две независимые причины быть им.
`#FE8A2C` — единственный цвет здесь, который мы не выбирали.
| Ассет | Размер | Куда идёт |
|---|---|---|
| `images/banner.png` | 2560×800 | Заголовок README, бумажный фон (2×, чтобы оставался чётким на retina) |
| `images/banner-dark.png` | 2560×800 | Тот же рисунок на истинно чёрном, подаётся через `<picture>` читателям тёмного режима |
| `images/social-preview.png` | **1280×640** | **Settings → Social preview** — рекомендуемый GitHub размер (мин. 640×320, лимит 1 МБ) |
| `images/mark.png` | 512×512 | Квадратный логотип: посты в соцсетях, слайды, favicon. **Не аватар репозитория** — GitHub показывает *владельца* профиля рядом с репозиторием, так что аватара для каждого репозитория нет |
| `images/mark-180/64/32/16.png` | как названо | 180 и 64 уменьшены; **32 и 16 нарисованы нативно**, чтобы ничего не сглаживалось в кашу |
| `images/demo.gif` | 384×192 | Скриптованный проход по каждому режиму, **записанный с устройства** — `tools_screenshot.py --tour-gif` |
| `images/splash.gif` | 384×192 | Загрузочное интро, записанное с момента запроса запуска — `--splash` |
| `images/screens.png` | — | Контактный лист захватов, светлый фон, пересобирается через `--sheet` |
| `images/screens-dark.png` | — | Тот же лист на истинно чёрном, подаётся через `<picture>` читателям тёмного режима |
| `screenshots/*.png` | 512×256 | **Каждый экран, захваченный с реального устройства** по RPC. Ровно два цвета: собственные цвета панели. |
```bash
python3 tools_brand_data.py # the facts the branding is allowed to state
python3 tools_gen_banner.py # banner (light + dark), social preview, logo mark
python3 tools_gen_icons.py # 1-bit Flipper icons
# these need a Flipper on USB (pip install pyserial)
python3 tools_screenshot.py --all # walk every screen and capture it
python3 tools_screenshot.py --reader # the shots that need a live reader
python3 tools_screenshot.py --splash # the boot intro
python3 tools_screenshot.py --tour-gif # one GIF of the app being used
```
> Социальное превью — это единственный ресурс, который GitHub не может подхватить из репозитория — его
> нужно один раз загрузить вручную в **Settings → Social preview**. Именно оно отображается,
> когда репозиторий публикуется в Slack, X, LinkedIn или Discord.
---
## Благодарности
- Создано **[at0m-b0mb](https://github.com/at0m-b0mb)**.
- Часть семейства инструментов безопасности для Flipper: **[Argus](https://github.com/at0m-b0mb/Argus-FlipperZero)** (сторож Wi-Fi деаутентификации / evil-twin), **[GhostTag](https://github.com/at0m-b0mb/GhostTag-FlipperZero)** (охотник за BLE-слежкой) и **[Cerberus](https://github.com/at0m-b0mb/flipper-cerberus)** (сторож Sub-GHz RF).
- Работает на [прошивке Flipper Zero](https://github.com/flipperdevices/flipperzero-firmware) NFC HAL (обнаружение поля `furi_hal_nfc`) + [ufbt](https://github.com/flipperdevices/flipperzero-ufbt).
## Лицензия
[MIT](https://github.com/at0m-b0mb/specter-flipperzero/blob/main/LICENSE) © 2026 at0m-b0mb