# JanOS Ecosystem Knowledge Base

> Deep behavioral reference for the JanOS WiFi/BT/GPS/Zigbee/nRF24 platform,
> the RF overlay (SubGHz + NFC), and seven companion UIs. Every entry is a
> concrete, non-obvious fact backed by a `path:line` citation. Behaviors that
> contradict comments, READMEs, or skill docs are flagged **DOC-MISMATCH**.
>
> Audit date: 2026-10-16. Line numbers are a snapshot; if code has moved, search
> for the cited symbol or UART marker rather than trusting the exact line.

## 0. Ecosystem map

JanOS is a **dumb-terminal architecture**. The ESP32-C5 radio brain owns every
attack, capture, and file write. Companion UIs send newline-terminated text
commands (`scan_networks`, `start_deauth`, `nfc_read`, `subghz_rx`, …) and parse
streamed text (`[ZIG] … END`, `[NFC] END`, `"Scan results printed"`,
`[SUBGHZ_RX] …`). UIs hold presentation, local caches, and a few **device-local**
features that never touch the C5 (CoreS3 IR, screenshots, battery, Mate softAP).

### 0.1 Two firmware trees (the most important RAG fact)

The **workspace public firmware does not contain NFC or SubGHz.** Companion UIs
still send those commands because they target an RF overlay build.

| Tree | Path | What it is |
|------|------|------------|
| **Public JanOS 1.7.5** | `janos_public/official/projectZero/ESP32C5` | WiFi/BLE/Zigbee/nRF24/wardrive/CGW/crack_worker. `JANOS_VERSION "1.7.5"`. `main/main.c` ~29 447 lines. **No** `nfc_*` / `subghz_*` symbols. |
| **RF overlay** | `/Users/jan/kod/thin/janos_subghz_lib` | `components/janos_subghz` (CC1101) + `components/janos_nfc` (ST25R3916 I2C/SPI, PN532). Compiled into thin firmware, **not** into the public ESP32C5 tree. |

Public OTA is GitHub `C5Lab/projectZero`. A separate RF image channel exists
(`OTA_RF_*` → `elpadrino26/janosrf-web-flasher`, `main.c:147-151`). Tab5
`JANOS_VERSION_REQUIRED` is **1.7.5**, matching public firmware; NFC/SubGHz
tiles only appear after a live probe (`subghz_status` / `init_nfc`) succeeds.

### 0.2 Repos in this workspace

| Component | Path | Role |
|-----------|------|------|
| JanOS firmware | `janos_public/official/projectZero/ESP32C5` | ESP32-C5 radio CLI. Monolithic `main/main.c` plus extracted modules (`attack_handshake.c`, `wifi_analyzer*.c`, `crack_worker*.c`, `oled_display.c`, `artifact_inventory_core.c`). |
| Tab5 UI | `M5MonsterC5-Tab5` | 720×1280 (native portrait) LVGL touch. Multi-tab, multi-UART (Grove/USB/MBus/INTERNAL). `JANOS_TAB_VERSION "1.5.5"`. |
| CoreS3 UI | `coreS3` | 320×240 LVGL touch. Single firmware UART + local IR + optional CardKB + stacked GPS. |
| CardputerADV UI | `M5MonsterC5-CardputerADV` | 240×135 keyboard text UI (no LVGL). ADV / K132. `JANOS_ADV_VERSION "1.8.7"`. |
| Mate | `Mate` | Multi-board ESP softAP + Preact phone SPA. `JANOS_WC_VERSION "1.0.0"`. |
| Commander | `MonsterC5-Commander` | M5 StopWatch 466×466 LVGL. `commander_release_version "0.4.8-dev"`. |
| FlipperLight | `janos_public/official/projectZero/FlipperLight` | Flipper Zero FAP `c5lab_dev` 0.0.1. WiFi/BT/wardrive only. |
| DoubleBlast | `DoubleBlast` | ESP32-WROOM 320×240 LVGL, 5 buttons. Broadest small-screen feature set. |
| This KB | `janos_public/4AI/KNOWLEDGE_BASE.md` | RAG source. |

Older citations to `janos_public/projectZero/ESP32C5` are **stale** — the live
public tree is `janos_public/official/projectZero/ESP32C5`.

### 0.3 Model

```
 UI device (7 hosts)  --UART 115200 8N1 text-->  ESP32-C5 JanOS
   screens + parsers                             WiFi / BLE / 802.15.4 / nRF24
                                                 (+ overlay: CC1101, NFC)
                                                 SD /sdcard/lab/...
```

The C5 REPL is `esp_console` on **UART0** at 115200 8N1 (`JANOS_UART_NUM`,
`JANOS_UART_DEFAULT_BAUD`, `main.c:19519-19520`). Companion devices talk to
that console through Grove/MBus/USB-CDC/Flipper USART — they do not run JanOS
themselves.

**Version gating (Tab5):** mismatch vs `JANOS_VERSION_REQUIRED "1.7.5"` sets
`janos_version_mismatch` and surfaces a non-blocking **"V!"** badge. The Tab
app version is independent (`JANOS_TAB_VERSION "1.5.5"`).
`M5MonsterC5-Tab5/main/main.c:110-111`, `:52782`, `:52944`

---

## 1. Transport and wiring

Link parameters are universal: **115200 baud, 8N1, no flow control**, RX buffer
typically 4096. The **line terminator differs by host**. Firmware linenoise
accepts `\n` or `\r\n`; hosts must still match their own convention so
command-complete detection stays consistent.

| Host | UART | TX/RX | Send EOL | Notes |
|------|------|-------|----------|-------|
| Tab5 Grove | UART1 | GPIO **53 / 54** (fixed) | `\r\n` | Shares UART1 hardware with the USB tab. `main.c:52128-52132`, `:3993` |
| Tab5 MBus ("Kraken") | UART2 | GPIO **37 / 38** (fixed) | `\r\n` | Skill name "Kraken" does not appear in Tab5 source. `:52138-52141` |
| Tab5 USB | UART1 via CH34x/CP210x CDC | same UART1 | `\r\n` | Separate `tab_context_t`; CDC write path. |
| Tab5 INTERNAL | none | — | ignored | JanOS commands silently dropped. |
| CoreS3 | UART1 | MBus **43/44** or Port C **17/18** | `\n` | Port change needs reboot. `uart_handler.h:9-21`, `uart_handler.c:253` |
| CardputerADV | UART1 | default TX=**2** RX=**1**, NVS | `\r\n` | Display SPI pins 33–37 blocked. `settings.h:14-15`, `uart_handler.c:329` |
| Mate | UART1 | Kconfig per board | `\r\n` | RX accepts `\r\n`/`\n`/`\r`. `uart_bridge.c:51-55`, `:148-149` |
| Commander | UART1 | StopWatch TX=**10** RX=**11** | `\r\n` | `Kconfig.projbuild:16-28`, `janos_transport_uart.c:128` |
| FlipperLight | Flipper USART | header pins | `\n` | Strips trailing `\r` on RX. `uart_comm.c:255-258`, `:312-313` |
| DoubleBlast | UART2 | TX=**17** RX=**16** | `\n` | Avoid WROVER (those pins are PSRAM). `pins.h:8-10`, `:45-48`, `uart_janos.c:272` |

**Firmware-side UART0** can be retimed with `uart_baud` (115200…4000000). Change
requires `uart_baud_confirm` within **10 s** (`UART_BAUD_CONFIRM_US`); idle
reverts after **60 s**. Markers: `[UARTB] … END`. `main.c:19519-19523`, `:25886-25904`

`send_file` uses binary FTB blocks after an `[FT] begin …` header; ACK timeout
is published as `ack_ms=` in that header. `main.c:19549-19553`, `:20100-20103`

### 1.1 Tab5 four isolated worlds

`TAB_GROVE / TAB_USB / TAB_MBUS / TAB_INTERNAL` each own a `tab_context_t`
(scan lists, observer, SubGHz, NFC). Commands route via
`uart_send_command_for_tab()`. Grove and USB share UART1 silicon but keep
separate containers; switching hides/shows without destroying state.
`M5MonsterC5-Tab5/main/main.c:1683-1686`, `:2308-2311`

**DOC-MISMATCH:** comments / Settings copy historically claimed NVS-configurable
UART pins. Pins are hard-coded in `get_uart1_pins` / `get_uart2_pins`
(`:52128-52141`).

Board auto-detect uses `ping`/`pong` on UART1 and UART2 plus USB VID. Only
detected endpoints get a tab.

### 1.2 CoreS3 Port C vs stacked GPS

Port C (G17/G18) is the **same pair** as stacked GPS UART2. Settings blocks the
GPS toggle and warns if the C5 link is on Port C. `coreS3/main/gps_module.h:14-17`

Collected UART lines are capped (PSRAM); excess is dropped.

---

## 2. Firmware 1.7.5 — WiFi recon and attacks

Citations in §§2–6 are `official/projectZero/ESP32C5/main/main.c` unless noted.
**118** console commands are registered (`\.command =` count). Indices for
WiFi scan/select/inspect/handshake are **1-based** (firmware does `idx--`).

**DOC-MISMATCH:** help text still says `select_networks 0 2 5`
(`:25140`) but `cmd_select_networks` subtracts 1 (`:5848`). Out-of-range
indices are skipped, not fatal (`:5849-5851`). Selection **replaces** the whole
set (`g_selected_count = 0` first, `:5845`). `unselect_networks` calls global
`stop` then clears selection; scan results stay.

Registration lives in `register_commands()` (`:24974-26022`) plus a static
`start_ap_locator` descriptor at `:397`. Overlay NFC/SubGHz commands are
**not** in this table — they are registered from `janos_nfc` /
`janos_subghz` when those components are linked.

### 2.0 Public command catalog (118 names)

Scan / inspect: `wifi_analyzer`, `scan_networks`, `show_scan_results`,
`inspect_network`, `select_networks`, `unselect_networks`, `select_stations`,
`unselect_stations`.

Sniffer / RF view: `start_sniffer`, `start_sniffer_noscan`, `packet_monitor`,
`start_ap_locator`, `channel_view`, `show_sniffer_results`,
`show_sniffer_results_vendor`, `clear_sniffer_results`, `show_probes`,
`show_probes_vendor`, `list_probes`, `list_probes_vendor`, `sniffer_debug`,
`start_sniffer_dog`, `deauth_detector`.

Attacks: `start_evil_twin`, `start_deauth`, `start_handshake`,
`save_handshake`, `sae_overflow`, `start_blackout`, `start_beacon_spam`,
`start_beacon_spam_ssids`.

Portal / rogue: `start_portal`, `start_admin_portal`, `start_darksword`,
`start_rogueap`, `start_rogue_gitm`, `start_karma`, `select_html`, `set_html`.

Cloud / wardrive files: `wpasec_key`, `wpasec_upload`, `wigle_key`,
`wigle_upload`, `wdgwars_key`, `wdgwars_upload`, `upload_state`,
`wardrive_files`, `wardrive_cleanup`, `wardrive_fix`, `wardrive_fix_kml`.

GPS / wardrive / antisurv: `start_gps_raw`, `gps_set`, `set_gps_position`,
`set_gps_position_cap`, `start_wardrive`, `get_wardrive_config`,
`set_wardrive_bands`, `set_wardrive_channels`, `set_wardrive_rssi_delta`,
`set_wardrive_memcap`, `set_wardrive_cooldown`, `wardrive_blacklist`,
`start_antisurveillance`, `set_antisurv_sensitivity`,
`start_wardrive_promisc`, `start_wardrive_promisc_trace`.

PCAP / STA / CGW: `start_pcap`, `capture_gateway`, `wifi_connect`,
`wifi_disconnect`.

Zigbee: `start_zig_recon`, `zig_recon_status`, `zig_recon_list`,
`zig_recon_nodes`, `zig_recon_clear`.

nRF24: `init_nrf24`, `start_jammer24`.

Network tools: `list_hosts`, `list_hosts_vendor`, `start_nmap`, `arp_ban`.

BLE: `scan_bt`, `scan_airtag`.

SD / UART / crack: `list_sd`, `sd_status`, `sd_benchmark`, `list_dir`,
`send_file`, `file_delete`, `uart_baud`, `uart_baud_confirm`,
`uart_baud_status`, `crack_worker`, `artifact_inventory`.

Config / OTA / misc: `vendor`, `display`, `led`, `channel_time`,
`boot_button`, `list_ssid`, `list_ssids`, `add_ssid`, `remove_ssid`,
`home_list`, `home_add`, `home_remove`, `show_pass`, `save_pass`,
`ota_check`, `ota_list`, `ota_channel`, `ota_info`, `ota_boot`, `download`,
`reboot`, `ping`, `version`, `stop`.

### 2.1 Radio mutex

Three exclusive radio modes (`:855-858`):

- `RADIO_MODE_WIFI`
- `RADIO_MODE_BLE`
- `RADIO_MODE_IEEE802154`

`ensure_wifi_mode` / `ensure_ble_mode` tear down the other stack before
switching. Zigbee recon additionally consults `zig_recon_radio_busy()`
(`:13604-13631`) — blocks if scan, sniffer, pcap, CGW, deauth, blackout, SAE,
sniffer-dog, deauth-detector, handshake, beacon spam, portal/darksword,
wardrive, antisurv, BLE scan, or nRF24 is live.

A second gate, `analyzer_host_busy_reason()`, also covers `wifi_connect`,
portals/karma/rogue/gitm, arp_ban, OTA, gps_raw, artifact inventory, and UART
file transfer. While `wifi_analyzer` runs, non-analyzer commands can be
rejected with `[WFACTL1] busy`.

### 2.2 scan / inspect / select

- **`scan_networks` is blocked while wardrive runs** (`:5565-5568`).
- Background active scan (`channel=0`, hidden SSIDs on); dwell from NVS
  `channel_time` (`scancfg` / `min_time`/`max_time`).
- Results capped at **64 APs** (`MAX_AP_CNT`, `:224`, `:1840`).
- CSV: 1-based index, SSID, vendor (empty unless `vendor set on`), BSSID,
  channel, auth, RSSI, band. Band is a **primary-channel heuristic**
  (`primary <= 14` → `2.4GHz`).
- Scan RAM (`g_scan_results[]`) is overwritten by the next scan; scanning
  never writes SD.
- **`inspect_network <index>`** needs a completed scan; refuses if sniffer /
  wardrive / beacon spam is active. Locks to the AP primary (no hop). Marker
  `[INSPECT] …` includes MFP and TSF uptime; open/WPA-only APs print
  `mfp_capable=- mfp_required=- note=no_rsn_ie` (`:5800-5816`).
- `wifi_analyzer` is a **separate** isolated PSRAM survey (limit 1..128), not
  `g_scan_results`. See §4.3.

### 2.3 Deauth

- **`start_deauth` is not its own attack.** It sets `onlyDeauth = 1` and calls
  `cmd_start_evil_twin`, skipping portal setup (`:8522-8529`).
- **Bug (still live):** `onlyDeauth` is never reset to 0. After one
  `start_deauth`, a later `start_evil_twin` can stay deauth-only until reboot.
  (`:5955`, `:8529`, `:12535`, `:12558`)
- Default frame: broadcast deauth (subtype 0xC0, reason 0x0001). With
  `select_stations` + `applicationState == DEAUTH`, firmware sends **targeted
  deauth to the station MAC**.
- WPA3 / mixed targets also get **Channel Switch Announcement** frames
  (help at `:25185-25186`).
- Channel hop: 50 ms settle per target **unless** evil-twin/GITM has SoftAP
  clients (see §2.4). `:26419-26425`
- Periodic re-scan (~5 min) refreshes target channels by BSSID match.
- **Whitelist does not protect regular deauth** — only `start_blackout` (and
  Sniffer Dog skip). `:26406-26408`, `:28267-28269`

### 2.4 Evil Twin

- Clone target = first selected network (`g_selected_indices[0]`); also the
  password-verification target. `:12560`
- Fake-AP SSID = real SSID + UTF-8 **U+200B** (bytes `E2 80 8B`) so iOS does
  not group it with the real network. `:12613-12623`
- Open AP on `172.0.0.1/24`, DHCP + DNS hijack + captive HTTP :80.
- **AP channel** is `target_bssids[0].channel` **or 1** if count is 0
  (`:12606`). That read can be **stale from a previous attack**.
- Deauth continues against all selected networks while the portal runs.
- **Channel narrowing:** when `applicationState == DEAUTH_EVIL_TWIN` and
  `softap_clients > 0`, only targets on the **first selected network's
  channel** are attacked; hopping is suppressed. When the last client leaves,
  multi-channel hopping resumes. `:26411-26425`
- Password POST → `verify_password()` → stop deauth → real STA association to
  the **original** SSID (not the ZWS AP). Wrong password: up to 3 retries,
  then deauth resumes with `last_password_wrong`. During
  `EVIL_TWIN_PASS_CHECK`, deauth frames are suppressed (`:26393-26396`).
- On success: `save_evil_twin_password()` appends `/sdcard/lab/eviltwin.txt`
  (verified association, not mere form submit). `:2626`, `:29143`
- Custom HTML from `select_html` / `set_html`, files in `/sdcard/lab/htmls/`
  (large templates loaded into RAM).
- UART strings UIs parse: `"Password received:"`, `"Wi-Fi: connected to SSID="`,
  `"Password verified!"`, `"Evil Twin portal shut down successfully!"`.

### 2.5 Handshake capture

Two modes (`:8602-8622`, help `:25194`):

1. **Selected** (or inline `start_handshake 1 3`): per-target burst + capture
   on that AP's channel. Existing PCAPs are **skipped** (`check_handshake_file_exists`
   marks `handshake_captured[i]`, then `continue`) — this is intentional, not
   the old "skip every network" bug. `:8226-8233`
2. **No selection:** promiscuous **D-UCB sniffer** across dual-band channels
   with targeted deauth on discovered clients until `stop`. Help still says
   "scans every 5min and attacks all" (`:25194`).

Complete = EAPOL M1–M4; HCCAPX needs ANonce, SNonce, MIC, MACs, EAPOL, valid
SSID. Saved to `/sdcard/lab/handshakes/{SSID}_{MAC3}_{timestamp}.pcap` **and**
`.hccapx` (beacon included in PCAP). `:7867-7905`

UART: `"HANDSHAKE IS COMPLETE AND VALID"`, `"PCAP saved:"`, `"HCCAPX saved:"`,
`"Skipping" … "PCAP already exists"`. `attack_handshake.c:597`, `main.c:7907-7908`

**Old-KB bug status:** the selected-mode loop no longer has an unconditional
`continue` after the existing-file check. It attacks remaining networks.

### 2.6 SAE overflow, blackout, beacon spam

- **`sae_overflow`:** exactly one selected network; lock channel; sniff SAE
  commit anti-clogging token; flood forged SAE Commit with random
  locally-administered source MACs (~50 ms/frame).
- **`start_blackout`:** rescan ~3 min, channel-sort, attack all **except
  whitelist**.
- **`start_beacon_spam` / `start_beacon_spam_ssids`:** max 32 SSIDs from
  `/sdcard/lab/ssids.txt` (`add_ssid`/`remove_ssid`/`list_ssids`). Legacy
  `/sdcard/lab/ssid.txt` is `list_ssid` presets — **not** the spam list.
- **Whitelist:** `/sdcard/lab/white.txt`, one BSSID per line (`:` or `-`),
  max **150** (`MAX_WHITELISTED_BSSIDS`). **Loaded once at boot**
  (`load_whitelist_from_sd`, `:26315-26319`, `:29358-29375`). Runtime file
  edits are ignored until reboot.

### 2.7 Other WiFi tools

- **`start_pcap radio|net|gateway`** (`:17202-17237`):
  - *radio* (default): linktype 105, current channel only.
  - *net*: linktype 1 Ethernet; requires `wifi_connect`; can ARP-spoof MITM.
  - *gateway*: only while Capture Gateway is active; CGW forbids the other
    two modes.
  - Files `/sdcard/lab/pcaps/`; stop emits `[PCAP_FINAL]`.
- **`start_nmap`:** quick=20 / medium=50 / heavy=100 ports; subnet ARP-flood
  then ICMP; single-host skips discovery; 500 ms TCP timeout; `stop` between
  ports.
- **`arp_ban`:** bidirectional ARP poison, fake MAC `DE:AD:BE:EF:00:00`;
  needs `wifi_connect` + gateway MAC (`list_hosts` first).
- **`wifi_connect`:** no password → OPEN; with password → `WIFI_AUTH_WPA2_PSK`
  (not WPA3/auto). 15 s wait. **Side effect:** successful password connect
  **also** `save_evil_twin_password()` → `eviltwin.txt` (`:14663-14664`).
  `--saved` searches `eviltwin.txt` → `portals.txt` → `home.txt`.
- **`start_ap_locator`:** exactly one selection; lock scanned channel; 1 Hz
  print; `beacons:` is last-second delta, not cumulative.
- **`wpasec_upload`:** STA + API key (NVS `wpasec`/`api_key` or
  `/sdcard/lab/wpa-sec.txt`) + SD; `handshakes/*.pcap` only; skip `.hccapx`;
  reject >512 KB; TLS verify disabled.
- **`show_pass`:** `portals.txt` by default; `show_pass evil` → `eviltwin.txt`.
- **`deauth_detector [indices…]`:** streaming
  `[DEAUTH] CH: … | AP: … (BSSID) | RSSI: …`.
- **`channel_view` / `packet_monitor`:** utilization / PPS; refuse if other
  RF work is active.

---

## 3. Firmware — portal, karma, rogue, DarkSword, admin, GITM

### 3.1 Captive portal and Karma

- **`start_portal <SSID>`:** open AP, typically channel 1, `172.0.0.1/24`,
  max 4 clients, **no deauth**. Form POST → `/sdcard/lab/portals.txt` as
  `"SSID","key=value",...` (URL-decoded).
- **`start_karma <probe_index>`:** requires prior sniffer probe capture.
  Index maps to the Nth unique SSID in the deduped probe list. Sets
  `karma_mode_active` and calls `start_portal` with that SSID — **no
  probe-response spoofing**, just an open AP with the probed name. Creds go
  to `portals.txt`.
- Probes: max **200** (`MAX_PROBE_REQUESTS`, `:231`). `list_probes` dedupes
  by SSID (first MAC wins); `show_probes` lists every entry.

### 3.2 Rogue AP / Rogue GITM / Capture Gateway

- **`start_rogueap <SSID> <password>`:** needs `select_html`; deauths selected
  networks if any.
- **`start_rogue_gitm`:** mirror SoftAP + capture gateway; needs
  `wifi_connect`; optional `--pcap-name`; **no captive portal**.
- **`capture_gateway start <ssid> [pass] [--pcap-name] | status | stop`:**
  APSTA + NAPT, DNS forward, **no** portal HTML. Markers `[CGW]`,
  `[CGW_CLIENT]`, `[CGW] END`. Implementation:
  `components/capture_gateway/capture_gateway.c`.

### 3.3 DarkSword and Admin portal

- **`start_darksword <SSID>`:** serves `/sdcard/lab/htmls/darksword.html[.gz]`,
  exfil HTTP **:8080**, loot under **`/sdcard/loot/portal/<uuid>/<category>/`**
  (outside `lab/`). `:21791-21817`, `:22053-22183`, help `:25509`
- **`start_admin_portal <password>`:** WPA2 SoftAP SSID **`JanOS-Admin`**,
  web file manager for `/sdcard/lab` (tree, upload/download, rename/delete,
  text edit). `:23003-23039`, `:25499-25500`

---

## 4. Firmware — handshake cloud, crack worker, artifacts, WFA/1

### 4.1 Handshake cloud / inventory

- `save_handshake` — manual save if 4-way complete.
- `wpasec_key` / `wpasec_upload`
- `wigle_key` / `wigle_upload` — NVS or `/sdcard/lab/wigle.txt` as
  `api_name:api_token`
- `wdgwars_key` / `wdgwars_upload` — NVS or `/sdcard/lab/wdgwars.txt`;
  60 MiB cap; rate-limit backoff
- `upload_state [clear]` → `[UPLOAD_STATE] … END`
- `wardrive_files` → `[WARD_FILE] BEGIN…END`
- `wardrive_cleanup` → `[WARD_CLEANUP]`
- `wardrive_fix` / `wardrive_fix_kml` → copies under `wardrives/fixed/`
  **excluded from upload scans**

### 4.2 `crack_worker` and `artifact_inventory`

Distributed WPA cracking and read-only SD inventory over UART (extracted
modules, not the UI). Markers:

| Command | Markers |
|---------|---------|
| `crack_worker capabilities\|probe\|receive\|reset\|start\|status\|cancel\|diag` | `[CRACK/1] …` |
| `artifact_inventory capabilities\|list\|inspect\|cancel` | `[ARTIFACT/1] BEGIN/ITEM/RESULT/END` |

`artifact_inventory` scopes `handshakes,pcaps`; page_max=32, entries_max=256,
inspect_max=16 MiB (`artifact_inventory_core.c:400`). Tab5 INTERNAL
**WPA PSK Auditor** is the GUI front-end for this stack.

### 4.3 `wifi_analyzer` (WFA/1)

Isolated AP survey, **not** `g_scan_results`. Subcommands: `caps | scan
[--band 2.4|5|both] [--channels …] [--profile quick|detailed|passive]
[--limit 1..128] | status | stop | clear`. JSON lines `[WFA1]` (begin/ap/end)
and control `[WFACTL1]`. `wifi_analyzer.c`, `wifi_analyzer_core.c:468-577`.
`stop` joins the analyzer first; failure → `[WFACTL1] … not_quiescent`.

---

## 5. Firmware — GPS, wardrive 2.0, anti-surveillance

GPS UART is **UART_NUM_1**, TX=13 / RX=14 (Marauder-compatible). `:202-208`

**`gps_set`** persisted in NVS `gpscfg`/`module` (no reboot):

| Token | Hardware / path | Baud |
|-------|-----------------|------|
| `m5` / `m5stack` | M5Stack GPS v1.1 | 115200 |
| `atgm` / `atgm336h` | ATGM336H (default) | 9600 |
| `external` / `usb` / `tab` / `tab5` | host injects `set_gps_position` | — |
| `cap` / `external_cap` / `lora_cap` / `adv_cap` | host injects `set_gps_position_cap` | — |

`start_gps_raw [baud]` streams NMEA.

### 5.1 Wardrive modes

- **`start_wardrive` (classic):** waits up to **120 s** for a fix (aborts if
  none); WiGLE CSV `/sdcard/lab/wardrives/<base>.log`. Filename uses GPS UTC
  `YYYYMMDD_HHMMSS` when available, else legacy `wN`.
- **`start_wardrive_promisc`:** D-UCB promiscuous WiFi (+ optional BLE);
  GPS wait is **infinite** (`wait_for_gps_fix(0)`).
- **`start_wardrive_promisc_trace`:** plus KML
  `/sdcard/lab/wardrives/<base>_track.kml` (point every ≥3 m). In-progress
  file uses `_track.inprogress.kml`.
- Stop ack: `[WARDRIVE] STOPPED running=0|1` (`:13600`).
- Fix-loss: classic external-feed pauses; promisc pauses WiFi/BLE after
  **3 invalid cycles**.
- NVS `wdcfg`: `set_wardrive_bands/channels/rssi_delta/memcap/cooldown`;
  `wardrive_blacklist` blob `wdbl`/`macs`. Dump: `[WDCFG] … END`.

Most Tab5/CoreS3/Cardputer/DoubleBlast UIs expose **promisc/trace only**.
FlipperLight still sends classic `start_wardrive`.

### 5.2 Anti-surveillance

`start_antisurveillance` + `set_antisurv_sensitivity low|med|high`. BLE
follower + GPS; mutually exclusive with wardrive on UIs that share the radio.
NVS key `assens` lives in `wdcfg`.

---

## 6. Firmware — BLE, nRF24, Zigbee, OLED, OTA, files

### 6.1 Bluetooth

- **`scan_bt`:** 10 s one-shot BLE; lists devices + AirTag/SmartTag counts.
- **`scan_airtag`:** continuous; count every 30 s until `stop`.
- Requires `ensure_ble_mode()` (may restart WiFi). Wardrive-promisc BLE
  logging is a separate path.

### 6.2 nRF24 jammer

External nRF24L01+ on **SPI2** (SCK=6, MOSI=7, MISO=2) with **CS=GPIO3,
CE=GPIO4** — the **former CC1101 CS/GDO0 pins**. Not concurrent with SubGHz
or SPI NFC. `components/nrf24_jammer/nrf24_jammer.c:20-21`

- `init_nrf24` → `[NRF24] detected` / `not detected` (RF_CH write/readback).
- `start_jammer24 [ble|bt|wifi|drone|all]` (default `all`): constant-carrier
  sweep. `stop` ends it.

### 6.3 Zigbee / IEEE 802.15.4 recon (`zig_recon`)

Passive hop channels 11–26. Commands: `start_zig_recon [all|ch,ch…] [dwell_ms]`,
`zig_recon_status`, `zig_recon_list`, `zig_recon_nodes`, `zig_recon_clear`.

Every dump ends with `[ZIG] END`. Lines: `[ZIG] status …`, `[ZIG] pan …`,
`[ZIG] node …`, `[ZIG] cleared`. `main.c:13718-14009`,
`components/zig_recon/zig_recon.c`. Needs `RADIO_MODE_IEEE802154` and a free
radio (`zig_recon_radio_busy`).

Tab5 Mesh Recon / Mate Mesh Recon / Cardputer Mesh Recon are the UIs.
Commander, FlipperLight, DoubleBlast **do not** surface zig_recon.

### 6.4 OLED / boot button / LED / OTA

No compile-time board variants. Runtime NVS:

| Namespace | Keys | Purpose |
|-----------|------|---------|
| `display` | `mode` | `auto`, `ssd1306`, `sh1107`, `sh1106`, `unit_lcd` |
| `ledcfg` | `enabled`, `level` | NeoPixel GPIO27 |
| `bootcfg` | `short_cmd`, `long_cmd`, `short_en`, `long_en` | GPIO28; default short=`start_sniffer_dog`, long=`start_blackout`; allowlisted commands only |
| `ota` | `channel` | `main` / `dev` |
| `vendorcfg` | `enabled` | OUI lookup (`oui_wifi.bin`) |

OTA: `ota_check [rf] [latest|tag]`, `ota_list`, `ota_channel`, `ota_info`,
`ota_boot`. `download` enters ROM download mode. `ping` → `pong`. `version`
prints `JANOS_VERSION`. `reboot`. Universal `stop`.

### 6.5 SD file transfer

- `list_sd` — HTML templates under `htmls/`
- `sd_status` — `SD_OK` / `SD_NONE`
- `sd_benchmark` — `[SD_BENCH]`, scratch `.janos_sd_benchmark.tmp`
- `list_dir [path] [-s]` — paths not under `/sdcard` are rebased
  (`build_sd_path`, `:19488-19516`)
- `send_file <path> [offset] [bsize]` — `[FT] begin|done|error|cancelled|END`
- `file_delete <path…>` — one `[FILE_DELETE] path=… result=…` per argv

Partitions: dual OTA `ota_0`/`ota_1` ~3.94 MiB each (`partitions.csv`).
IDF `>=6.0.2,<6.1`.

---

## 7. Overlay — SubGHz (CC1101, not in public ESP32C5)

Source of truth: `/Users/jan/kod/thin/janos_subghz_lib/components/janos_subghz`.
Console table: `src/subghz_console.c:853-909`. **Public `main.c` has zero
`subghz_*` commands.** UIs probe with `subghz_status` → `[SUBGHZ_STATUS]`.

### 7.1 Commands

`subghz_status`, `subghz_rx [raw|binraw] [rssi=…]`, `subghz_tx <idx> <mem|sd> [raw]`
or `subghz_tx tesla`, `subghz_jam`, `subghz_freq_analyzer` (incl. `hunt`),
`subghz_scanner`, `subghz_weather`, `subghz_tpms`, `subghz_freq`,
`subghz_set_freq_correction`, `subghz_get_freq_correction`, `subghz_list mem|sd`,
`subghz_info`, `subghz_save <mem_idx>|all`, `subghz_delete`, `subghz_rename`,
`subghz_clear` (mem only), `subghz_stop`, `subghz_protocols`, `subghz_keys`,
`subghz_selftest`, `subghz_replay_test`, `subghz_debug`, `get_radio`, `set_radio`.

`set_radio capa` is the Cap board variant (NFC CS vs CC1101 CS swap; see §8).

### 7.2 Two-tier storage

| Tier | Backed by | Filled by | Index | Survives reboot? |
|------|-----------|-----------|-------|------------------|
| `mem` | Dynamic PSRAM | `subghz_rx`, analyzer `hunt` | `capture_seq` monotonic 1-based | **No** (`subghz_clear` / reboot) |
| `sd` | `/sdcard/lab/subghz/*.sub` | **only** `subghz_save` | Positional 1..N sorted dir | **Yes** |

RX/hunt never auto-write SD. `subghz_clear` does not touch the SD library.
After reboot, `capture_seq` bootstraps from the max `NNNNN_` prefix on SD so
new mem names do not collide — mem itself is **not** reloaded.
`subghz_cc1101.c:63-65`, `:127-132`

SD index changes after every save/delete/rename — UIs must re-`subghz_list sd`.

**DOC-MISMATCH:** overlay README historically said "per-signal NVS"; runtime
is PSRAM mem + SD `.sub`.

### 7.3 Rolling-code counter persistence on TX

Headline behavior: **each TX sends an incremented rolling code and persists it.**

- Encode uses `decoded.counter = key->rc_counter + 1` (`:3695`).
- After successful rolling re-encode, RAM keystore updates (`rc_counter`,
  OTA `key_id`, `raw_timings`).
- **mem-source TX** rewrites the SD file **only if it already exists**.
- **SD-source TX always rewrites** the source `.sub` (`RC_Counter`/`Key`/`RAW_Data`).
- **`raw` bypass:** `subghz_tx <idx> mem raw` / `sd raw` replays captured
  edges, does **not** bump the counter, does **not** rewrite via the encoder.
- Per-protocol TX repetition (Flipper-like): KeeLoq/StarLine/Faac/… = 3;
  Nice FloR-S / CAME Atomo = 1.
- Unified encoder covers KeeLoq, StarLine, Faac SLH, Nice FloR-S, CAME Atomo,
  Alutech AT-4N, Somfy, Hörmann, Security+ v1/v2, KingGates, Hay21,
  Scher-Khan, KIA Seed, X10, plus car families (`:3669-3687`).
- TX is license-gated (`license_runtime_ok()`, `:3504-3506`).

### 7.4 RX noise gate, analyzer, scanner, weather

- Default RX gate **−80 dBm**; override `subghz_rx rssi=-N` (−120..−40);
  **`0` resets to −80**, it does not disable the gate. Drop:
  `[SUBGHZ_RX_DROP] reason=rssi …`. `[SUBGHZ_RSSI]` ~1 Hz.
- Decode mode drops UNKNOWN / all-0 / all-1 / noise; only `raw` keeps edges.
  Minimum 8 edges.
- **Analyzer:** default trigger −85 dBm; coarse 650 kHz RxBW then fine 58 kHz
  ±300 kHz / 20 kHz. Hunt auto-saves to **mem** (not NVS/SD). Hunt is
  UART-quiet unless `verbose`. Blacklist: >920 MHz; 311.9–312.2; 462.75 /
  464.00 / 467.75 MHz.
- **Scanner:** dwell 80 ms (20–500), edges=4, rssi=−75; cluster collapse
  ±0.5 MHz; cross-pass sample-hold. `[SUBGHZ_SCAN_HIT]`, `[SUBGHZ_SCAN_PASS]`.
- **Weather:** 433.92 MHz OOK; ~24 WS decoders; 8-slot 30 s dedup.
- **`subghz_tpms`:** TPMS receiver path (newer than the previous KB).

### 7.5 Hardware

SPI2 shared with SD: MOSI=7, MISO=2, SCK=6, **CS=GPIO3**, **GDO0=GPIO4**
(ESP32-C5 JTAG MTCK — `gpio_reset_pin` before use). Same CS/GDO0 as nRF24
and SPI NFC — **one module at a time**. Frequency correction lives in NVS
(`subghz`/`freq_corr_cc`, ±5.00 MHz) and **survives reboot**. Capture
buffers: 8192 edges × 2 in PSRAM. Jammer is 2-FSK continuous carrier, not OOK.

`.sub` `Filetype: JanOS SubGHz`; Flipper `Filetype: Flipper SubGhz` dropped
into the same directory is imported (KeeLoq decrypt if MF key is in the
keystore). `subghz_rename` patches the in-file `Name:` then unlinks the old
path. Name charset `[A-Za-z0-9_.-]`, max 63.

---

## 8. Overlay — NFC (not in public ESP32C5)

Source: `/Users/jan/kod/thin/janos_subghz_lib/components/janos_nfc`,
console `src/nfc_console.c:446-537`. **Every NFC command ends with `[NFC] END`.**
UIs must wait for that marker. Cards: `/sdcard/lab/nfc/*.nfc` (Flipper NFC v4).
MIFARE Classic dictionary is **built-in** (~thousands of keys), not on SD
(`nfc_dict_status` → `[NFC] dict: built-in`).

**Indexing: `nfc_list` is 0-based** (unlike WiFi 1-based). `nfc_load` /
`nfc_rename` / `nfc_delete` / `start_nfc_emulate` accept `index|name`.

### 8.1 Commands

| Command | Role |
|---------|------|
| `get_nfc_bus` | Print `i2c` / `spi` / `pn532` + pin summary |
| `set_nfc_bus i2c\|spi\|pn532` | Persist NVS `nfc`/`bus`; aliases `st25`→i2c, `st25spi`→spi |
| `init_nfc` | Probe selected backend; gate the NFC tile on success |
| `nfc_read [timeout_ms]` | Default 5000 ms; Classic dict attack (no 12 s cap); UL page dump; DESFire ATS/GetVersion/AIDs |
| `nfc_save <name>` | Last card → SD `.nfc` |
| `nfc_list` | 0-based listing |
| `nfc_load` / `nfc_rename` / `nfc_delete` | RAM slot / SD |
| `start_nfc_emulate [index\|name]` | UID-level NFC-A; UL pages / DESFire ATS listen on ST25 SPI and PN532 |
| `nfc_dict_status` | Built-in dict key count |

Global `stop` calls `nfc_stop_all()` (halts emulation). Lazy `nfc_read` tries
init; failure: `[NFC] not initialized - run init_nfc first`.

### 8.2 Buses (NVS default **i2c**)

| Bus | Chip | Pins | Notes |
|-----|------|------|-------|
| `i2c` | ST25R3916 (M5 NFC Unit) | SDA=GPIO8, SCL=GPIO9, addr `0x50`, 400 kHz | 5 V; IRQ polled over I2C |
| `spi` | ST25R3916 Elechouse | SPI2 CS=**GPIO3**, IRQ=**GPIO4** | Same CS as CC1101/nRF24; IRQ required for UL/DESFire emu |
| `pn532` | PN532 SPI | SPI2 SS=**GPIO3** | DIP=SPI; IRQ/RSTO unused |
| `set_radio capa` | ST25 on Cap | NFC CS=GPIO3, CC1101 CS=GPIO1 held high, IRQ=GPIO9 | `set_nfc_bus i2c\|pn532` **rejected** |

`nfc_config.c:11-15` — namespace `nfc`, key `bus`. Firmware releases SubGHz
when switching onto SPI NFC.

CoreS3 Settings and Cardputer `nfc_settings_screen` / DoubleBlast Settings
expose the bus picker. Tab5 currently has no bus UI (probe-only). Mate SPA
includes NFC pages.

### 8.3 Read / emulate behavior (what UIs must parse)

`nfc_read` polls NFC-A / B / F / V until a card or timeout. On NFC-A it
classifies Classic 1K/4K/Mini, Ultralight/NTAG, ISO14443-4A / DESFire, or
generic 3A. Classic runs AUTH-nonce detect then a dictionary attack until
**both keys and every block of each sector** are read, or the built-in dict
is exhausted — there is **no 12 s cap**. Ultralight dumps pages. DESFire
does RATS/ATS then native GetVersion / GetFreeMemory / GetApplicationIDs
(no keys, no file dump). Extra UART lines: `ats:`, `version:`, `free:`,
`apps:`. The result sits in the **last card** RAM slot for `nfc_save`.

`start_nfc_emulate` is UID-level NFC-A on all backends. Ultralight **page
emulation** and DESFire **ATS/GetVersion listen** require ST25 SPI or PN532
(IRQ on ST25 SPI). I2C ST25 is UID-only. Emulation is a background task;
`[NFC] END` still arrives after start, then the UI must send `stop` (or
leave the screen) to halt.

Typical UI sequence: `init_nfc` (gate tile) → `nfc_read` → parse card
fields until `[NFC] END` → optional `nfc_save <name>` → `nfc_list` →
`nfc_load` / `start_nfc_emulate`. Never pipeline a second NFC command
before `END`.

---

## 9. SD / NVS layout

### 9.1 Boot-created dirs (`create_sd_directories`, `:28764-28774`)

```
/sdcard/lab
/sdcard/lab/htmls
/sdcard/lab/handshakes
/sdcard/lab/wardrives
/sdcard/lab/wardrives/uploaded
/sdcard/lab/pcaps
```

Overlay / other writers also use `/sdcard/lab/subghz/`, `/sdcard/lab/nfc/`,
`/sdcard/loot/portal/`.

### 9.2 Files

| Path | Writer | Notes |
|------|--------|-------|
| `lab/white.txt` | hand-edited | BSSID whitelist, boot-load only; blackout + sniffer-dog |
| `lab/ssids.txt` | `add_ssid` | Beacon-spam list |
| `lab/ssid.txt` | legacy | `list_ssid` presets — not spam |
| `lab/home.txt` | `home_add` | `wifi_connect --saved` last resort |
| `lab/htmls/*.html` | hand-supplied | Portal templates; `darksword.html[.gz]` |
| `lab/portals.txt` | portal/karma POST | `"SSID","key=value",...` |
| `lab/eviltwin.txt` | verified ET **and** `wifi_connect` w/ password | `"SSID","password"` |
| `lab/handshakes/*.pcap` + `.hccapx` | handshake | wpa-sec uploads `*.pcap` only |
| `lab/pcaps/` | `start_pcap` | radio/net/gateway |
| `lab/wardrives/<base>.log` | wardrive | WiGLE CSV |
| `lab/wardrives/<base>_track.kml` | promisc-trace | ≥3 m |
| `lab/wardrives/fixed/` | `wardrive_fix` | excluded from uploads |
| `lab/wardrives/upload_state.csv` | upload journal | |
| `lab/wigle.txt` / `wdgwars.txt` / `wpa-sec.txt` | key fallbacks | copied into NVS |
| `lab/oui_wifi.bin` | vendor DB | |
| `lab/observer/observer_<ts>.csv` | Tab5 observer export | UI-side; blocked while observer runs |
| `lab/subghz/*.sub` | overlay `subghz_save` | rewritten on rolling TX |
| `lab/nfc/*.nfc` | overlay `nfc_save` | Flipper v4 |
| `loot/portal/<uuid>/…` | DarkSword | **outside** `lab/` |

### 9.3 NVS (survives reboot, not user-visible SD)

`ota/channel`, `wpasec/api_key`, `wigle/api_name+api_token`, `wdgwars/api_key`,
`display/mode`, `scancfg/min_time+max_time`, `wdcfg/*`, `wdbl/macs`,
`ledcfg/*`, `vendorcfg/enabled`, `gpscfg/module`, `bootcfg/*`, overlay
`subghz/freq_corr_cc`, `nfc/bus`. Per-UI: Tab5/CoreS3/Cardputer/DoubleBlast
`red_team`, brightness, UART port, NFC bus cache, SubGHz RF settings.

### 9.4 Volatile RAM

Scan results, selection, `target_bssids[]`, probe list, SubGHz **mem**, NFC
last/loaded card, `onlyDeauth` flag.

C5 hardware pins (public): NeoPixel GPIO27, Boot GPIO28, GPS UART1 13/14,
SD SPI2 MISO2/MOSI7/CLK6/**CS10** (`:196-220`). nRF24/CC1101/NFC-SPI steal
GPIO3/4.

---

## 10. Tab5 (720×1280 LVGL touch)

Citations: `M5MonsterC5-Tab5/main/...`. UI is still mostly `main.c` (~71k
lines) plus `screens/nfc_*`, `screens/subghz_*`, `hs_*` auditor, `cgw_parser`.

### 10.1 Versions, display, power

- `JANOS_TAB_VERSION "1.5.5"`, `JANOS_VERSION_REQUIRED "1.7.5"` (`:110-111`).
- Native panel **720×1280 portrait**; rotation setting 0 is that native mode
  (`screen_rotation_setting`). Older KB saying only "1280×720" is incomplete.
- **INA226 I2C `0x41`** (not 0x40) for voltage/current (`:162`).
- Screenshot: tap LAB5 logo → BMP `/sdcard/SCREENS/`.
- Optional **A164 keyboard** I2C **`0x6d`**, Ext.Port1 SDA=0 SCL=1, 100 kHz
  (`tab5_keyboard.c:48`, `:135`). SYS I2C stays on GPIO31/32.

### 10.2 Home tiles (transport tabs)

WiFi Scan & Attack/Test, Global WiFi Attacks/Tests, Compromised Data, Deauth
Detector, Bluetooth, Network Observer, Karma, Wardrive, **Anti-Surv**, Mesh
Recon (user-data `"IoT"`), Sub-GHz if `has_subghz`, NFC if `has_nfc`
(`:18228-18252`). Red Team NVS `red_team` default **off** (`:1703`): labels
say "Test"; offensive tiles shrink (ARP+Nmap in the scan bar).

**INTERNAL tiles** (`:18577-18581`): **WPA PSK Auditor**, Settings, Ad Hoc
Portal & Karma. Auditor is Tab5-local (uses `crack_worker` /
`artifact_inventory` over the C5 UART plus ESP-Hosted where needed).

Settings: Scan Setup, Red Team, timeout/brightness/rotation, Theme, Time,
Screen Lock, Monster OTA, Monster SD Admin, Transfer Speed.

### 10.3 Distinctive behaviors

- **Network Observer:** up to 100 nets × 20 clients in PSRAM. Sequence:
  `scan_networks` → `inspect_network` (must finish **before** sniffer) →
  `start_sniffer_noscan` → poll `show_sniffer_results` every 20 s. Tapping a
  **client MAC** opens "Deauth Station" → `select_networks` +
  `select_stations <MAC>` + `start_deauth` (Red Team required). Network-row
  focus popup polls clients every 10 s; launching an attack
  `pause_observer_for_attack()` (`stop`).
- **Mesh Recon:** `zig_recon_*`, `[ZIG]` parser, 32 PANs / 128 nodes.
  Tab5-only among the original three; Mate also has it.
- **Ad Hoc Portal (INTERNAL):** merges probes from UART1 **and** MBus
  (`adhoc_fetch_probes_from_all_uarts`). Observer Karma uses only the current
  tab UART.
- **Wardrive 2.0:** UI sends `start_wardrive_promisc` / `_trace` only.
  Mutually exclusive with Anti-Surv.
- **GITM:** `cgw_parser.c` understands `[CGW]` / `[PCAP_FINAL]`.
- **ESP-Hosted C6:** still used for Ad Hoc / Monster file transfer. The
  `"Internal C6 Test"` **tile is still absent** (orphaned handler). Native
  `esp_wifi_scan` on the Tab5 C6 is not a home feature.
- **SubGHz hub:** Quick Scan, Hunter (cap 2048 PSRAM), Listen (**800×70**
  RGB565 waterfall), SD Signals, Weather, Jammer, Tesla, Settings.
- **NFC hub:** INIT/READ/SAVE/LIST/LOAD/RENAME/DELETE/EMULATE
  (`screens/nfc_host.h:19-28`). State is **per-tab** (`nfc_tab_state.tab_id`)
  so Grove and MBus can each have a different last card. Parsers:
  `nfc_parser.c` (`[NFC]` / `[NFC] END`), `subghz_parser.c` (`[SUBGHZ_*]`),
  `cgw_parser.c`. No bus picker — the overlay default (usually I2C) is used.
- **USB VCP / transfer speed:** `usb_vcp_config.*`, `transfer_speed_config.*`
  persist baud/CDC options used with `uart_baud` on the C5.

Per-tab PSRAM at boot: networks×50, observer×100, deauth×200, BT×50,
karma×64, evil twin×32, beacon SSIDs×128, ARP×64, nmap×64.

---

## 11. CoreS3 (320×240 LVGL touch)

Citations: `coreS3/main/...`. No `JANOS_*_VERSION` macro; splash title
`"LAB5"`. Capabilities probed at boot (`device_info_has_subghz` /
`device_info_has_nfc`).

### 11.1 Home tiles

WiFi Scan & Attack, Global WiFi Attacks, Network Observer & Karma,
Compromised Data, Bluetooth, Deauth Detector, Sub-GHz (if probe), NFC (if
probe), **IR TV Power** (always), Wardrive, File Admin AP, Settings
(`screens/home_screen.c:150-165`).

Red Team off → attack picker **ARP + Nmap** only; ARP screen also blocked.
WiFi scan parses up to **512** networks via `parse_worker`.

### 11.2 CardKB (required for typed input)

CardKB Mini I2C **`0x5F`**, SDA=GPIO2, SCL=GPIO1, 100 kHz, Grove Port A
(`cardkb.h:16-20`). Always inited; if absent, `cardkb_read_key()` returns 0
and **typing silently does nothing**. Touch OK/Cancel still work.

Need CardKB: SubGHz rename (max 63, `[A-Za-z0-9_.-]`), portal SSID, WiFi
passwords (MITM/ARP/Nmap/Rogue/wardrive upload). Do **not** need it for
frequency correction / repeat-TX (touch rollers). Esc/Backspace = back on
many screens; Jammer Space toggles; Tesla Space/Enter fires.

### 11.3 IR (CoreS3-local — not a C5 feature)

Home tile always shown. M5GO Bottom3 IR LED **GPIO7**, RMT 1 µs
(`ir_tx.h:9-11`). Database `assets/tv_power.irdb` — **TV power codes only**.
UI sweeps every DB entry every 15 ms (blocks LVGL); Repeat switch loops.
**No IR receive, no protocol picker.** Public C5 firmware has **zero** IR
commands.

### 11.4 GPS, LEDs, memory

- Stacked GPS UART2 RX=18 TX=17; auto-baud 9600→115200→38400 until `$`;
  stale after 5 s; NVS `ext_gps`. Clash with Port C (§1.2). CoreS3 pushes
  `set_gps_position` on movement or a 2 s heartbeat; answers `tab_gps_read`.
- 10× WS2812 **GPIO5**: green pulse SubGHz RX, red blink TX.
- BT nRF24 jammer start: backlight **10%** (`ui_screen_low_power`) to avoid
  brownout. SubGHz jammer does not dim.
- `parse_worker`: PSRAM-stack CPU0, queue 12, drops if full. `psram_dynarr`
  caps (WiFi 512, BT 1024, SubGHz 2048, deauth 2048). LVGL heap in PSRAM.
- NFC bus dropdown in Settings (`set_nfc_bus` / `get_nfc_bus`).
- SubGHz Listen waterfall **320 px** wide. SD Signals: Export/Import/Clear-all
  removed; UI-side duplicate hiding; max 2048. Hub title = eFuse `board_name`
  else `"SubGhz-NoName"`.
- **AP Radar** (`start_ap_locator`) exists here (absent on Cardputer).
- Rogue GITM + PCAPs under Compromised Data. File Admin AP =
  `start_admin_portal`.
- Dead: `show_keyboard_screen()` never called.

---

## 12. CardputerADV (240×135 keyboard text UI)

Citations: `M5MonsterC5-CardputerADV/main/...`. `JANOS_ADV_VERSION "1.8.7"`
(`version.h:4`), home title `LAB5 v` + version.

### 12.1 Screen system and grid

Custom `screen_t` (`on_key`/`on_draw`/`on_tick`/`on_destroy`/`on_resume`);
stack max depth **8** (`MAX_SCREEN_STACK`, `screen_manager.h:14`) — overflow
fails silently; cannot pop the root. Tick ~500 ms (50 × 10 ms loop). UART
callback only sets dirty flags; `on_tick` repaints dirty rectangles. Ctrl+S
screenshot is intercepted globally (never reaches the screen) → **local** SD
`/screens/` (not Monster SD). If JanOS `SD_NONE`, a warning blocks until a
key; `is_board_sd_missing()` then limits Wardrive/WPA-SEC.

Fixed **30×8** cells (8×16 font). Backlight: PWM below ~88% is effectively
off. ST7789 `OFFSET_X=40, OFFSET_Y=53` with swap/mirror.

### 12.2 ADV vs K132

`-DBOARD=adv` / `-DBOARD=k132`. ADV: TCA8418 I2C `0x34` SDA8/SCL9, ES8311
buzzer, Caps key. K132: 74HC138 matrix, NS4168 I2S, **no Caps**. Same logical
key API.

**Dual-mode arrows:** menu mode `, . ; /` = LEFT/RIGHT/UP/DOWN without Fn;
text-input mode those keys type punctuation (Fn required for arrows/Esc).
Text input: Enter submits, Esc pops, max 32 chars, cursor is trailing `_`.

### 12.3 Home menu (2 pages, hardware-gated)

Page 1: WiFi Scan, Global WiFi, Network Tools, Wardrive, WiFi Sniff&Karma,
Deauth Detector. Page 2: **Mesh Recon**, Compromised data, Bluetooth, NFC
(`nfc_only`), Sub-GHz (`subghz_only`), Settings (`home_screen.c:42-56`).
Boot: `uart_check_subghz_available(800)`, `uart_check_nfc_available(3000)`
(`main.c:260-261`).

Network Tools: Connect, ARP, WPA-SEC, MITM, Nmap, **GITM**, **Rogue GITM**.
Wardrive menu: Start, **Anti-Surv**, Setup, Blacklist, Wdgwars/Wigle upload,
Trace toggle (list shows 4 files). BT: AirTag, scan, locator, **Jammer**.
NFC menu: Read/List + settings bus SPI/I2C/PN532. SubGHz 8-entry menu +
dedicated **freq picker** screen (no waterfall, no Header/Raw/Decoded tabs).

### 12.4 Attack quirks

- Cannot ESC mid-scan — only after completion.
- Rogue AP needs **exactly 1** selected network (SSID from selection).
- Evil Twin extra step: `evil_twin_name_screen` re-orders `select_networks`
  so the chosen ID is **first**, then HTML picker, then `start_evil_twin`.
- Station Deauth exists; **AP Locator / WiFi radar does not**.
- List scrolling: page-jump (networks, Listen/Hunter/Scanner, home) vs
  line-scroll (evil-twin name, data detail). Network list row 6 is `Next>`;
  **N** jumps, **RIGHT** launches, **I** inspects.
- GPS module screen: ATGM / M5 GPS 1.1 / **CAP GPS** + GPS Read; `gps_set`.
- SD Signals Clear-all sends repeated `subghz_delete 1` (firmware
  `subghz_clear` is mem-only). Action menu includes Multi Transmit.
- Fixed RAM arrays (128 Listen/Hunter/SD, 16 scanner freqs, 8 weather) with
  ring eviction.

---

## 13. Mate (phone browser + ESP softAP)

Path: `/Users/jan/kod/Mate`. **Not LVGL.** Phone → WiFi AP → ESP32 WebClient
→ UART → C5.

- Version `JANOS_WC_VERSION "1.0.0"` (`main/main.c:18`).
- SoftAP prefix `JanOS` + MAC suffix; default password **`janos1337`**,
  channel 6 (`Kconfig.projbuild:128-140`).
- UART1, baud/pins from Kconfig (examples: XIAO TX21/RX20; CoreS3 TX2/RX1;
  StickC+2 TX32/RX33). Send `\r\n`; RX accepts all three EOLs.
- Boards via Kconfig: XIAO-C6, NanoC6, CoreS3 SE, AtomS3/R/Lite/U, StickC
  PLUS2, StickS3, Atom Lite/Matrix, CoreInk, CardKB2. Optional status LCD
  (`CONFIG_JANOS_HAS_DISPLAY`) — SSID/TX/RX only, not an attack UI.
  StickS3 enables Grove 5 V via M5PM1 **before** LCD init.
- **No Red Team gating.** No local IR/screenshots.

### 13.1 SPA routes (`web/src/app.jsx:391-425`)

`/`, `/scan`, `/deauth`, `/eviltwin`, `/rogueap`, `/sae`, `/handshaker`,
`/sniffer`, `/observer`, `/global`, `/sddata`, `/bluetooth`, `/wardrive`,
`/mesh`, `/wardrive/upload`, `/gps/diag`, `/karma`, `/portal`, `/snifferdog`,
`/beaconspam`, `/detector`, `/gitm`, `/roguegitm`, `/subghz` (+ listen/tx/
manage/jammer/tesla), `/nfc` (+ read/library/card/emulate).

Home modules (`Home.jsx:6-18`): Scan, Attacks, GITM, Detector, SubGHZ, NFC,
Observer, Wardrive, **Mesh Recon**, Bluetooth, Data.

### 13.2 Boot and architecture

`app_main` (`main/main.c:20-82`): NVS → suppress noisy `wifi` driver logs
(softAP deauth warnings used to starve other tasks and crash the LCD) →
**StickS3 M5PM1 5 V boost before `display_init`** (inrush on VBUS_L0 blanks
an already-inited ST7789) → UART bridge → `cmd_handler_init` → `wifi_ap_start`
→ HTTP/WebSocket/SPIFFS → `uart_bridge_send("ping")`.

Pieces: `uart_bridge.c` (line CB), `command_handler.c` (caches + domain
parsers for SubGHz/`[NFC]`/`[ZIG]`/wardrive/nmap/ARP/PCAP),
`response_parser.c` (handshake/PCAP/password strings), `web_server.c`,
`wifi_ap.c`, SPA under `web/src/pages/`. UART lines become REST + WebSocket
JSON events so the phone UI does not talk UART itself.

SD listings go through the C5: `list_sd`, `list_dir /sdcard/lab/handshakes`,
`…/wardrives`. UART pins are **menuconfig**, not runtime NVS.

**DOC-MISMATCH:** README board table is a subset of Kconfig (missing StickS3,
CoreInk, NanoC6, CardKB2, several Atom variants). REST table lags
`web/src/api.js`. Bundled `SKILL/` describes Tab5 LVGL, not Mate's web
architecture.

---

## 14. MonsterC5-Commander (StopWatch 466×466)

Path: `/Users/jan/kod/MonsterC5-Commander`. LVGL 9.x round AMOLED (CO5300
QSPI 466×466), CST820B touch, Button A GPIO2 / B GPIO1. Version
`commander_release_version = "0.4.8-dev"` (`main/app_main.c:20`).

UART1 TX=**10** RX=**11** @ 115200, send `\r\n`. Detect: `ping` until line
equals `pong` (infinite retry; Tab5-style fast ping then 1 s). Line ownership:
`monster_detector` until first pong, then `janos_client`.

**Blue B stops JanOS from any page** (`stop`). Grove 5 V powers the Monster
board (M5PM1 `0x6E`). Local NVS `round_ui`: brightness, haptics, clock.
**No Red Team. No IR. No zig_recon UI.**

### 14.1 Nine-column deck

`ui_round_internal.h:14-24`:

1. SCAN — scan + actions (Deauth, Evil Twin, SAE, Handshake, Sniffer, RogueAP,
   MITM, Nmap, Radar, Karma)
2. OBSERVE — observer + deauth detector
3. GLOBAL — Blackout / Handshaker / Portal / Sniffer Dog
4. DATA — passwords, portal, handshakes, PCAPs
5. WARDRIVE — run + config + GPS (3 subpages)
6. BT — AirTag, locator, nRF jammer
7. NFC — read / library / emulate / rename / delete (cap 128)
8. SUBGHZ — Scan, Hunter, Listen, Signals, Weather, Jammer, Tesla, Settings
9. SETUP — RTC/brightness/haptics, file admin AP

Horizontal swipe = columns; vertical swipe = stacked subpages (max 8).

Scan complete marker `"Scan results printed"`; start `"Background scan started"`
(`janos_scan.c`). CSV quoted network lines. NFC ops in `janos_client.h`:
INIT/READ/LIST/LOAD/SAVE/RENAME/DELETE/EMULATE, list cap 128, name max 48.
NFC/SubGHz sessions are mutually exclusive with the action/html parsers —
starting one tears down the other.

Local-only: brightness/haptics/clock/battery in NVS `round_ui`; RX8130CE
RTC; haptics. Captures stay on the C5 SD and are listed via JanOS commands.
PaperColor is an experimental alternate BSP (`commander_board.c` has two
pin tables sharing `CONFIG_COMMANDER_UART_TX_PIN`).

**DOC-MISMATCH:** README mermaid still shows only SCAN / WARDRIVE / SETUP.
Code has nine columns including NFC/SubGHz/BT. The abbreviated log in
`ui_round.c` still understates the deck.

---

## 15. FlipperLight (Flipper Zero FAP)

Path: `janos_public/official/projectZero/FlipperLight`. `application.fam`:
appid `c5lab_dev`, name "C5Lab Dev", `fap_version="0.0.1"`, entry
`wifi_attacks_app`. Canvas **128×64**. Enables **OTG 5 V** for the C5.

USART 115200, send **`\n` only**, strip `\r` on RX. Detect `ping` +
`strstr(..., "pong")`; SD via `list_sd`. Blocking per-screen UART threads
(`uart_read_line` timeouts) — not an event-driven LVGL parser.

### 15.1 Menu and Red Team

Main menu (`screen_main_menu.c:48-56`): WiFi Scan & Attack/Test, Global WiFi,
Sniff&Karma, Deauth Detector, Bluetooth, Compromised Data, Settings.

Red Team default off; Flipper FS `APP_DATA_PATH("redteam.conf")`
(`app.c:79-87`). Filters Blackout/Handshaker/Sniffer Dog and per-AP offensive
items. Sniffer / Portal / Wardrive remain without Red Team.

Per-AP: Deauth, Evil Twin, SAE, Handshaker, Sniffer, Rogue AP, ARP.
Global: Blackout, Handshaker, Portal, Sniffer Dog, Wardrive (**classic
`start_wardrive`**, not promisc). BT: AirTag, scan, locator — **no jammer**.
Compromised: ET passwords, portal, `list_dir /sdcard/lab/handshakes`.

**Absent:** SubGHz, NFC, Mesh/Zigbee, GITM/MITM/Nmap/Radar, File Admin AP,
nRF jammer UI. No README in tree.

Handshake marker `"Complete 4-way handshake saved for SSID:"`
(`screen_global_handshaker.c`). Sniffer `"Sniffer packet count:"`. Scan end
`"Scan results printed"` (`uart_comm.c:160`). Quoted CSV network parse is
local to the FAP (`uart_comm.c`).

Pattern for almost every attack screen: clear the UART buffer →
`uart_send_command` → poll `uart_read_line` on a FuriThread until a success
string or timeout → `stop` on Back. This is slower and more serial than
Tab5/CoreS3 event-driven parsers; two screens must not share the USART.

No ESP NVS: the only persisted host setting is Flipper `redteam.conf`. SD
warning on the main menu if `list_sd` fails (`screen_main_menu.c:45-46`).

---

## 16. DoubleBlast (ESP32-WROOM 320×240, 5 buttons)

Path: `/Users/jan/kod/DoubleBlast`. **Not S3, not WROVER.** ST7789V 320×240
SPI (CS GPIO5 strapping — pull-up). Buttons UP/DOWN/BACK/OK/ACTION =
GPIO 32/33/25/26/27. UART2 115200 TX17→C5 RX12, RX16←C5 TX11. Send `\n`.
Waits for `BOARD READY`, then `version` / `JanOS version:`.

Live UI is a single state machine in `main/app.c` (~2800 lines): `VIEW_MENU`
/ list / run / detail / text-input frames (`M_HOME`, `M_ATTACK`, `M_NFC`,
…). `main/screens/{home,wifi_scan,network_detail}_screen.c` exist but are
**not in CMakeLists.txt** (dead prototype); `main.c` calls `app_show_home()`.
No app semver; log `"DoubleBlast JanOS UI"`. `uart_janos.c` owns an RX task,
a line queue, collect-until-marker, and a sync wait for `BOARD READY`.

### 16.1 Home and Red Team

Home (`app.c:147-151`): WiFi Scan & Attack, Global WiFi, Network Observer &
Karma, Compromised Data, Bluetooth, Deauth Detector, Sub-GHz, NFC, Wardrive,
File Admin AP, Settings.

NVS namespace `doubleblast`, key `red_team` default false (`:2783`). Without
Red Team the attack submenu is **ARP + Nmap** (`:156`). Home titles still say
"Attack". Local NVS also: lock, timeout, screen_off, admin_pass, rf_settings.

Attacks include Rogue GITM, MITM/PCAP, **AP Radar**. BT jammer:
`init_nrf24` + `start_jammer24`. NFC: Read, Saved cards; bus SPI/I2C/PN532.
Wardrive live = `start_wardrive_promisc` / `_trace`; GPS picker "M5 (no local
sensor)" / ATGM / CAP. Markers: `[NFC] END`, `[SUBGHZ_LIST_END]`, `[WDCFG] END`,
`Scan results printed`, `=== END ===`. Leaving run screens sends `stop` or
`subghz_stop`. Caps 96 rows / 64 nets (no PSRAM-friendly host). No touch,
sounds, or IR.

**DOC-MISMATCH:** `ESP32S3-mapping.md` is a past snapshot; active map is
`pins.h` (WROOM).

---

## 17. Cross-device feature matrix

"Firmware" = a JanOS C5 (or overlay) command. Columns = whether the UI
surfaces it.

| Feature | Tab5 | CoreS3 | Cardputer | Mate | Commander | FlipperLight | DoubleBlast |
|---------|------|--------|-----------|------|-----------|--------------|-------------|
| UI | LVGL touch 720×1280 | LVGL 320×240 | text 240×135 | Preact SPA | LVGL 466² | Flipper 128×64 | LVGL 320×240 5-btn |
| Firmware UART links | 4 (Grove/USB/MBus/INT) | 1 | 1 | 1 | 1 | 1 | 1 |
| Send EOL | `\r\n` | `\n` | `\r\n` | `\r\n` | `\r\n` | `\n` | `\n` |
| Text input | on-screen + A164 `0x6D` | **CardKB `0x5F`** | physical kbd | phone | touch | Flipper keys | 5 buttons |
| Red Team | NVS | NVS | NVS | **no** | **no** | Flipper FS | NVS `doubleblast` |
| Observer + client deauth | **yes** | observer | Station Deauth | observer page | OBSERVE col | deauth client scr | observer menu |
| Evil Twin extra SSID pick | no | no | **yes** | no | no | no | no |
| AP Locator / radar | yes | **yes** | **no** | no | Radar action | **no** | **yes** |
| Mesh / zig_recon | **yes** | no | **yes** | **yes** | no | no | no |
| GITM / Rogue GITM | yes | Rogue GITM | Network Tools | **yes** | MITM-ish | no | **yes** |
| Anti-Surv | home tile | via wardrive | wardrive menu | no | no | no | ward setup |
| Wardrive command | promisc/trace | promisc/trace | promisc/trace | SPA | run+config | **classic** | promisc/trace |
| SubGHz UI | waterfall 800 | waterfall 320 | list + freq picker | SPA | 8 pages | **no** | 8 items |
| NFC UI | hub, no bus picker | hub + bus | menu + bus | SPA | read/lib | **no** | read/lib + bus |
| BT nRF jammer UI | yes | yes (dim 10%) | yes | limited | yes | **no** | yes |
| IR transmit | no | **local RMT** | no | no | no | no | no |
| File Admin AP | Monster SD Admin | yes | SD Admin | SDData | SETUP | no | yes |
| Screenshot | LAB5 logo | — | Ctrl+S local | no | no | no | no |
| HW variants | 1 | 1 | **ADV/K132** | many Kconfig | StopWatch (+PaperColor exp.) | Flipper | WROOM DevKit |

---

## 18. Gotchas and DOC-MISMATCH index

### Persistence

- KeeLoq/rolling counter is persisted on every (non-raw) TX: mem always;
  SD `.sub` if it exists (mem-source) or always (SD-source). §7.3
- Successful `wifi_connect` with a password appends `eviltwin.txt`. §2.7
- Evil-twin passwords save only on **verified** association. §2.4
- Whitelist `white.txt` is read **once at boot**; protects blackout + sniffer-dog
  only. §2.6
- SubGHz mem wiped on reboot; `subghz_clear` never touches SD. §7.2
- SubGHz freq correction and NFC bus live in NVS. §7.5, §8.2
- DarkSword loot is under `/sdcard/loot/`, not `lab/`. §3.3

### Channel / radio

- Evil twin / GITM **locks to the first selected network's channel** while
  SoftAP clients are present. §2.4
- `start_pcap radio` is current channel only; `gateway` mode requires CGW. §2.7
- Evil-twin AP channel can be stale (`target_bssids[0]` before save). §2.4
- WiFi / BLE / 802.15.4 are exclusive radio modes. §2.1
- CC1101, nRF24, and SPI NFC **share GPIO3/4 + SPI2**. §6.2, §7.5, §8.2

### State that leaks

- `onlyDeauth` set by `start_deauth` and **never cleared**. §2.3
- `target_bssids[]` is not always cleared when a deauth task ends.
- NFC `nfc_list` is **0-based**; WiFi `select_networks` is **1-based**. §2, §8

### Bugs / dead code / mismatches

- Handshake selected-mode "unconditional continue skips every AP" from the
  previous KB is **fixed** — existing PCAPs are skipped, others are attacked.
  §2.5
- `select_networks` help still shows 0-based examples. `:25140`
- Tab5 UART pins are fixed, not NVS (comment/Settings historically wrong). §1.1
- Tab5 `"Internal C6 Test"` handler has no tile; ESP-Hosted still used. §10.3
- CoreS3 `show_keyboard_screen()` never called. §11.4
- Commander README still draws 3 columns; code has 9. §14.1
- Mate README REST/board tables lag code. §13
- FlipperLight has no README; feature set is WiFi/BT-era. §15
- DoubleBlast `screens/` are unused; `ESP32S3-mapping.md` is stale. §16
- Public ESP32C5 tree has **no NFC/SubGHz** — UIs that send those commands
  require the RF overlay image. §0.1
- `DRAGON_DRAIN` exists in `ApplicationState` but is not a registered command.

### EOL cheat-sheet (easy to get wrong)

`\r\n`: Tab5, Cardputer, Mate, Commander.
`\n`: CoreS3, FlipperLight, DoubleBlast.

---

## 19. RAG chunk keys

Stable headings for embeddings / retrieval:

- `janos.version` → public `1.7.5`; Tab5 UI `1.5.5` req `1.7.5`; Cardputer `1.8.7`; Mate `1.0.0`; Commander `0.4.8-dev`; Flipper FAP `0.0.1`
- `janos.path.firmware` → `official/projectZero/ESP32C5`
- `janos.path.overlay` → `thin/janos_subghz_lib` (`janos_subghz`, `janos_nfc`)
- `janos.absent.nfc` / `janos.absent.subghz` → public tree
- `janos.uart.eol.<device>`
- `janos.uart.commands` → 118 public names (§2 register block `:24974-26022`)
- `janos.marker.ZIG` / `NFC` / `SUBGHZ_*` / `CGW` / `WFA1` / `CRACK/1` / `ARTIFACT/1` / `FT` / `DEAUTH` / `WARD_*` / `UARTB` / `NRF24` / `WARDRIVE`
- `janos.index.wifi_1based` / `janos.index.nfc_0based`
- `janos.nvs.*` / `janos.sd.lab.*` / `janos.sd.loot`
- `janos.bug.onlyDeauth`
- `janos.persist.keeloq_tx`
- `janos.device.tab5` … `janos.device.doubleblast`

---

*Generated from a source audit of public ESP32C5 1.7.5, the thin SubGHz/NFC
overlay, and seven workspace GUIs. Highest-value claims (onlyDeauth leak,
evil-twin ZWS + SoftAP channel lock, whitelist boot-load, wifi_connect
eviltwin.txt side effect, NFC `[NFC] END` + 0-based list, SubGHz mem vs SD,
CC1101/nRF24/NFC pin sharing, Tab5 version gate 1.7.5) were re-verified in
source on 2026-10-16.*
