Skip to content

Releases: JimGat/CYM

CYM-NM28C5 v2.14.01

Choose a tag to compare

@JimGat JimGat released this 17 Sep 11:08

Patch release: Classic CYD (CYD-2432S028) regression fix

v2.14.00 shipped with two real regressions on Classic CYD only — NM-CYD-C5 and WS-C5-28 are unaffected, confirmed by identical binary output before/after this fix. Found after Jim compared his known-good v2.13.70 test build against v2.14.00 and the color/SD behavior didn't match.

Fixed: SD card mounted on the wrong SPI bus

v2.13.82 made wifi_wardrive.c's SD init code board-agnostic by switching 3 hardcoded SPI2_HOST references to a shared BOARD_SPI_HOST macro. That's correct for NM-CYD-C5 and WS-C5-28, where the display and SD card already share one SPI bus — but Classic CYD wires its SD card to a separate, dedicated SPI bus from the display, and BOARD_SPI_HOST on that board points at the display bus. SD access was silently trying to talk to the wrong bus. Fixed by using the already-correctly-defined BOARD_SD_SPI_HOST macro instead, which was sitting unused in every board header.

Fixed: color inversion that was never actually validated

BOARD_LCD_INVERT_COLOR has been a Kconfig option with no prompt string since it was introduced (v2.13.66) — a bug that means a value set in sdkconfig.defaults can never actually apply, no matter what. That bug was fixed in v2.13.99, and it made Classic CYD's CONFIG_BOARD_LCD_INVERT_COLOR=y setting take effect for the very first time — which is when the screen started looking inverted. Every build before that, including the known-good v2.13.70, was actually running with no inversion the entire time; the setting had simply never worked. Reverted to n to match the confirmed-good behavior.


Installation

Same as v2.14.00 — flash from the CYM Web Flasher or grab the binaries below. Full v2.14.00 release notes (multi-board support, 80-commit changelog): https://github.com/JimGat/CYM/releases/tag/v2.14.00

CYM-NM28C5 v2.14.00

Choose a tag to compare

@JimGat JimGat released this 15 Sep 02:39

CYM is now a three-board platform

NM-CYD-C5 (optionally with the NM-RF-HAT) remains the primary/preferred board — it's where CYM started, it's where new features land first, and it's the only board with the full plug-and-play RF-HAT (CC1101, nRF24, PN532, IR, RF433). It is joined this release by two boards graduating from beta to fully supported:

Board Status Notes
NM-CYD-C5 + optional NM-RF-HAT ⭐ Primary / preferred Full feature set — WiFi 6, BLE 5, 802.15.4, ESP-NOW, GPS wardriving, plug-and-play RF-HAT
Classic CYD (ESP32-2432S028R) ✅ Supported WiFi 2.4 GHz, BLE, ESP-NOW, GPS wardriving. Dual-core ESP32. NM-RF-HAT reachable via SD Card Shim.
Waveshare ESP32-C5-Touch-LCD-2.8 (WS-C5-28) ✅ Supported — new On par with NM-CYD-C5's core feature set (WiFi 6, BLE 5, 802.15.4, ESP-NOW, GPS wardriving), plus onboard IMU, temp/humidity, RTC, and audio codec that NM-CYD-C5 doesn't have — many new features planned to build on that extra hardware.

All three boards flash from the same web flasher (board selector at the top) and build from one shared firmware source tree. See the wiki's Supported Hardware section, Classic CYD Support, and Waveshare WS-C5-28 Support for board-specific details.

This is a minor version bump to v2.14.00 — 80 commits since the last stable release (v2.13.43, 2026-09-07). Full commit list at the bottom.


What's New

Classic CYD (ESP32-2432S028R) bring-up

The original dual-USB "Cheap Yellow Display" board — the one that started the whole CYD ecosystem — now runs CYM. Getting it working meant working through a real hardware/driver bring-up: DRAM/IRAM overflow fixes to fit the image, ILI9341 display rotation/color/gamma tuning (including a BGR-vs-RGB false start that got reverted), XPT2046 touch moved to a manual GPIO chip-select path after SPI-driver CS conflicts, and a PSRAM-free boot path since this board has none. NM-RF-HAT is reachable here too, via an SD Card Shim adapter and manual wiring — see Classic CYD Support on the wiki.

Waveshare WS-C5-28 first hardware boot

The first physical WS-C5-28 unit surfaced four real, sequential hardware bring-up bugs that only showed up once real hardware was on the bench: the CST3530 capacitive touch driver was missing its raw I2C bus handle and crashed on first touch; the touch controller's reset line (routed through the board's onboard CH32V003 microcontroller) was never actively pulsed; the LCD's reset line had the same problem, leaving the panel black even with backlight on; and a Kconfig authoring bug (BOARD_LCD_INVERT_COLOR had no prompt string, which makes an option silently un-settable from sdkconfig.defaults even though it looks like it's set) meant color inversion never actually applied — on WS-C5-28 (screen looked negative) and, previously unnoticed, on Classic CYD too, where it had been silently inert since v2.13.66.

OT/IoT Air Survey hardening

Passive multi-radio survey (WiFi + BLE + 802.15.4 + ESP-NOW) went through nine rounds of real-world hardening this cycle: fixed a scheduler deadlock and interrupt exhaustion in 802.15.4 mode, a priority-inversion bug that froze the Stop button, wired actual WiFi/BLE passive scanning into the dwell slots (previously scaffolded but never active), fixed obs_count double-counting from re-sighted devices, added BLE address-type classification, GPS-stamped survey start/end in metadata.json, grew the global observation store from 512 to 8192 records to stop saturation evicting stable devices mid-survey, rejected all-zero MAC records at the store boundary, and — the last fix of the cycle — actually wired ESP-NOW detection into the WIFI+ESPNOW dwell slot, which had been silently counting zero ESP-NOW frames the whole time.

ESP-NOW detection fix (Scout + Air Survey)

The shared ESP-NOW frame detector only recognized the unicast 802.11 Action subtype (0xD0) and silently dropped the broadcast Action-No-Ack subtype (0xE0) — meaning broadcast ESP-NOW traffic was invisible to both ESP-NOW Scout and OT Air Survey the entire time, while unicast worked fine. Root-caused by comparing the working vs. non-working code paths rather than chasing 2.4/5 GHz radio theories that turned out to be wrong.

Board HAL abstraction + multi-board build framework

All hardcoded GPIO assignments were migrated to BOARD_* Kconfig defines behind a new board_hal component, so board-specific pin maps, default brightness, and capability flags live in one place instead of scattered #ifdef blocks. A new project rule now requires that any change to the shared main.c source tree be rebuilt and verified on every board that compiles it in the same session — codified after a shared-code regression silently broke the Classic CYD build for six versions before anyone rebuilt that board to notice.


Contributors

Thanks to @birolt29 for the touch/radio-settle race fix during OT Air Survey teardown and the obs_store zero-MAC rejection guard, both field-tested on his external-antenna NM-CYD-C5 build.


Shoutout

Big thanks to BSides Houston and the great Houston hacker community — see you there September 17th, 2026!


Installation

Flash from the CYM Web Flasher — pick your board (NM-CYD-C5, Classic CYD, or Waveshare WS-C5-28) from the selector at the top, connect via USB, and flash. No toolchain required.

For manual flashing with esptool.py, each board's bootloader.bin, partition-table.bin, and app .bin are attached below, along with a merged *-full.bin per board that flashes as a single file at address 0x0000. Bootloader offset differs by chip: ESP32-C5 boards (NM-CYD-C5, WS-C5-28) use 0x2000; Classic CYD (plain ESP32) uses 0x1000. See Where To Get It on the wiki for exact commands per board.


Full changelog since v2.13.43

80 commits
b9b59f7 docs: add LAB5 Discord invite link across README and web flasher
9e413ce v2.13.43: feat(build): Phase 1 multi-board framework
96aacea v2.13.44: feat(board_hal): board HAL abstraction layer + nm-cyd-c5 verified build
49168fd v2.13.45: fix(wardrive): GPS no-data msgbox stuck on screen (@birolt29, closes #12)
69dddc1 v2.13.47: feat(esp32): first successful CYD2USB (ESP32-2432S028R) build
1247ec5 v2.13.48: fix(esp32): CYD2USB DRAM/IRAM overflows resolved — board now fits and builds clean
90c4502 v2.13.49: feat(esp32): Classic CYD boot disclaimer + wiki documentation
4bb3bd9 v2.13.49: fix(rf_hat): SD Card Shim support for Classic CYD — RF-HAT wiring framework
c870ecb v2.13.50: feat(esp32): CYD2USB GPIO22/GPIO27 confirmed + CYD2USB-RFHAT build target
8927186 v2.13.51: feat(esp32c5): WS-C5-28 port - CST3530 touch, CH32V003 backlight, SD CS swap
4c93d6b v2.13.53: fix(esp32c5): board-specific binary dirs, multi-board web flasher
aac3a08 v2.13.54: feat(esp32): CYD-2432S028 board, multi-board web flasher, all-boards CI target
679879d v2.13.55: fix(esp32): skip WS2812 init on boards with no LED (CYD-2432S028 boot fix)
29d8064 v2.13.56: fix(esp32): PSRAM-free boot path for CYD-2432S028
2512225 v2.13.57: fix(esp32): screenshot task stack IRAM assert on CYD-2432S028
67f44ee v2.13.58: fix(esp32): ILI9341 display rotation and color inversion on CYD-2432S028
0da6bad v2.13.59: fix(esp32): remove mirror_x from ILI9341 rotation — caused left-right flip
829c6c0 v2.13.60: diag(touch): XPT2046 z1/z2/pressure logging during calibration
adf5540 v2.13.61: fix(touch): switch XPT2046 to polling_transmit — GPIO33 CS never asserted in ISR mode
800d817 v2.13.62: fix(touch): manual GPIO CS for XPT2046 — bypass SPI driver CS path for GPIO33
26e047d v2.13.63: fix(display): rotate CYD-2432S028 portrait 180 deg — USB at bottom
7583545 v2.13.64: fix(touch): route CYD2USB SPI MISO to GPIO39 for XPT2046
d98572f v2.13.65: fix(touch): CYD-2432S028 XPT2046 uses separate SPI (SW bit-bang)
99f1d83 v2.13.66: fix(display): enable LCD color inversion for CYD-2432S028 ILI9341
07089a7 v2.13.67: fix(cyd2usb): ILI9341 BGR pixel order — fix blue cast on all warm colors
94de6ad v2.13.68: revert(cyd2usb): restore RGB element order — BGR caused hard R/B channel swap
4001a9b v2.13.69: fix(cyd2usb): extended ILI9341 init — power, VCOM, frame rate, gamma tables
ed957a0 v2.13.54/55/70: feat(board_hal): per-board default brightness via Kconfig
1b144cc v2.13.56: feat(ot_survey): OT Air Survey Phase 2 — obs_store v2 + ot_survey component (NM-CYD-C5)
b4fb2e3 v2.13.57: chore(build): WS-C5-28 Phase 2 binary — obs_store v2 + ot_survey component
e599c2c v2.13.71: chore(build): CYD2USB Phase 2 binary — obs_store v2 + ot_survey component
ee28cba v2.13.58: feat(ot_survey): OT Air Survey Phase 3 — radio scheduler + survey lock (NM-CYD-C5)
093c977 v2.13.59: chore(build): WS-C5-28 Phase 3 binary — radio scheduler + survey lock
3876c2e v2.13.72: chore(build): CYD2USB Phase 3 binary — radio scheduler + survey lock
56d4ebd v2.13.73: feat(ot_survey): Phase 4 WiFi/BLE/ESP-NOW obs integration + version sync (NM-CYD-C5)
bdaa969 v2.13.73: chore(build): WS-C5-28 Phase 4 binary — obs integration + version sync
03b126d v2.13.73: chore(build): CYD2USB Phase 4 binary — obs integration + version sync
7ee9076 v2.13.74: feat(ot_survey): Phase 5 — 802.15.4 passive capture + PCAPNG
720c2c4 v2.13.75: feat(ot_survey): Phase 6 — WirelessHART heuristic detector + per-board memory budgets
b030e1a v2.13.76: feat(ui): IOT/OT umbrella menu + OT Air Survey screen (Phase 7)
bc03221 v2.13.77: feat(ot_survey): gap fixes — obs_by_type counte...
Read more

CYM-NM28C5 v2.13.43

Choose a tag to compare

@JimGat JimGat released this 07 Sep 00:33

What's New

BLE Tracker Detection (AirTag Scanner — v2.13.38-43)

The AirTag Scanner now detects three tracker families — Apple AirTag, Samsung SmartTag, and Tile — with a four-category summary screen and per-tracker Found Tags details.

Ring capability by tracker (GATT-confirmed):

Tracker Ring Protocol
Apple AirTag Yes - non-owner trigger Apple FMN service 7DFC9000, char 7DFC9001, write {0x01}. No auth required - Apple designed this for anti-stalking.
Samsung SmartTag No Samsung FMM (0xFEF3) requires account auth. Does not implement Apple FMN. Device name reads "Searching..." when unowned.
Tile No Service 0xFEED, WNR char 9D410018. Commands require owner-signed token from Tile's cloud. Static MAC - useful for List Wizard cross-session correlation.

Found Tags list shows type badge (AirTag/SmartTag/Tile/AirTag?), RSSI, Track, Ring (AirTag only), and GATT Walk buttons per device. Summary counts always reflect unique devices, not advertisement packets.

Passive Observation System (OBS) - Bluetooth menu -> Passive Log

Background passive classifier that runs continuously and categorizes every BLE advertisement seen. 8 classifiers: AirTag, SmartTag, Tile, ESP-NOW device, Drone Remote ID, Matter device, BLE HID device, Fast Pair device. Passive Log UI shows a scrollable live feed with recurrence counts (how many times each device has been seen before). Device Detail screen shows full per-device history; Locate and Ring (AirTag) available directly from the log. JSONL export to /sdcard/lab/obs/.

Go Dark - Full-Screen Touch-to-Wake (v2.13.36)

Hold a finger anywhere on the screen for 5 seconds to wake from Go Dark mode. Previous version required tapping the top-right corner only. The 5-second threshold prevents the LCD DMA bus activity (150-250 ms intermittent bursts during screen-off LVGL flushes) from triggering false wakes on short accidental touches. Boot button always works instantly.

WiFi Frame Capture + BLE Targeted PCAP (v2.12.3)

Raw 802.11 promiscuous frame capture to PCAP files at /sdcard/lab/pcap/. BLE targeted PCAP in Kismet PCAPNG format with BLE 5.0 extended advertising support - importable directly into Wireshark.

Wardrive Improvements (v2.11.47, v2.12.1, v2.12.5 - @birolt29)

Seven field-validated wardrive fixes from 2,500+ km of real-world testing. Radio mode (2.4 GHz / 5 GHz / Both) now persists across reboots via NVS. GPS serial debug logging toggle added to Settings -> GPS Info.

nRF24 Jammer Improvements (v2.13.0-23)

CONT_WAVE mode empirically confirmed to require CE-toggle per channel hop (chip does not retune PLL with CE HIGH). AT2401C PA engagement confirmed at >= 370 us carrier. 10 jammer modes with real-time JAM/STOP color feedback.

Platform note (raised by @Eiswolf, confirmed by Jim field testing both platforms): The jammer works and produces real RF interference (+20 dBm). Effectiveness is not consistent across Bluetooth implementations -- some devices/stacks are disrupted, others are not, depending on the target's AFH aggressiveness and proximity. BLE advertising disruption is consistently effective (no AFH on advertising channels). BT Classic disruption varies: weaker or less capable BT implementations are disrupted; robust implementations with aggressive AFH may not be. A dual-core ESP32 (D0WDQ6/D0WD, used in standard CYD, Bruce, and Halehound) achieves more consistent disruption across implementations because the jammer task can be pinned to a dedicated core -- the ESP32-C5's single-core RISC-V architecture means the jammer competes with all other firmware tasks.

BLE Blaster (v2.13.2)

4-instance BLE advertising flood + optional nRF24 CONT_WAVE RF layer on BLE advertising channels.

GATT Walker fix (v2.13.30)

Fingerprint-first filename for devices with repeated advertising device IDs - closes #16.


Contributors

Special thanks to @birolt29 for wardrive field testing and patches (7 validated fixes over 2,500+ km of real-world testing), and to @Eiswolf for raising the nRF24 jammer platform comparison which led to field-confirmed testing across ESP32-C5 and dual-core ESP32 hardware.


Installation

Flash CYM-NM28C5-full.bin at address 0x0000 using the Web Flasher, or flash the three individual files at their standard offsets.

Web Flasher Dev tab will show this build within ~1 minute of release.

CYM-NM28C5 v2.12.2

Choose a tag to compare

@JimGat JimGat released this 12 Aug 04:38

What's New

v2.12.1 — Birol's wardrive follow-up (@birolt29, field-validated 2026-08-07)

Change Detail
GPS regulatory domain → D-UCB arms wdp_gps_domain() feeds both esp_wifi_set_country() and the D-UCB arm list. ETSI box now caps channels at 140 (was 177) — eliminating rejected-channel dwell bias that was crediting dead arms and stealing time from the 100–144 DFS block. Measured: 41 → 33 arms, zero rejected-channel warnings, upper DFS still captured.
BLE device cap 2000 → 10000 A 54-minute city drive was hitting the 2000 cap and silently dropping devices. Cap raised to 10,000 (PSRAM). Dashboard shows <n> FULL on saturation — both label paths covered.
SATS box on both dashboards Satellite count moved off the crowded GPS line into its own amber box: WiFi mode: D-UCB | WiFi | SATS; BLE mode: BLE counter shifts left, SATS takes the right slot.
Mode transition sidecar CSV Each real adaptive-mode switch appends to WD_<mode>_<stamp>_mode.csv (UTC, From, To, Speed, Unit, Lat, Lon). Drain runs outside sd_spi_mutex — can never nest the way the old rotation path did.
wd_is_uploadable_csv() filter Fixes a pre-existing bug: upload_log.csv and sidecar files were POSTed to WiGLE/WDG on every Upload All. Filter applied at all three enumeration sites (count loop, upload loop, Manage populate). Verified on real upload path.
Highway tier (> 90 km/h) New profile above Car. Enter at 90 km/h, drop to Car below 75 km/h (15 km/h hysteresis). Dwell {110, 100, 100, 90} ms; DFS rate-limited to 1-in-30; non-DFS 5 GHz keeps full weight; GPS 5 Hz fires in Highway. Manual dropdown gains "Highway (110 ms, DFS 1/30)".
Walk ↔ Stationary churn fix GPS reports 3–6 km/h phantom wander at a standstill, causing Walk ↔ Stationary flips every ~5 s and log spam. 15 s minimum dwell applied only to that transition pair. Car/Highway transitions keep the responsive 5 s floor. Field result: 8 → 0 spurious transitions in WiFi run, 7 → 0 in BLE run.
English enum comments Three non-English enum comments corrected (from v2.11.47 upstream). Comment-only.
Radio Mode NVS persistence Wardrive radio mode (WiFi-only / BLE-only) now survives reboots. The save path was already complete; the NVS load was a single commented-out line. Every other wardrive option persists — radio mode now does too.

Field validation (2026-08-07, two runs): Highway tier: clean Driving → Highway at 90.4 km/h, Highway → Driving at 74.5 km/h, no flapping. DFS rate limit in Highway: zero DFS observations inside the Highway window, non-DFS 5 GHz still captured. CSV: 886 WiFi rows / 494 unique APs and 284 BLE devices, 0 malformed rows, 0 duplicate BLE MACs. GPS: 0 zero-coordinate rows, accuracy 2.8–8.8 m on 883/886 rows. Memory: internal BSS 110.2 KB (unchanged).


v2.12.2 — First-run UX improvements (Valleytech review feedback)

Change Detail
Touch calibration hint Persistent amber label added to the calibration screen: "Resistive touch - press firmly / Red dot shows detected point". Stays visible for the entire 4-point sequence.
Wardrive removed from WiFi Attacks Wardrive is a passive collection tool, not an attack. The tile has been removed from the Global WiFi Attacks screen. Wardrive is accessible from the main home screen and its own dedicated menu.
Channel Analyzer label WiFi menu tile renamed from "Chan-alizer" to "Channel Analyzer" for clarity. Routing key unchanged — no navigation regressions.
RF-HAT status popup The "NM-RF-HAT is currently under active development — not all modules functional yet" warning has been replaced with an accurate module status summary: CC1101, nRF24, PN532, IR (Capture/Replay), and RF433 are all active. DIP switch one-at-a-time reminder included.

New wiki page: Getting Started
Covers resistive touch + red dot, SD card layout and provision, RF-HAT enablement, GPS wiring, WiFi tool naming, and wardrive categorization — all the things a first-time user needs before touching the menu.


Contributors

  • @birolt29 — v2.12.1 field-validated wardrive follow-up: Highway tier, GPS regulatory domain, BLE cap, SATS box, sidecar CSV, upload filter, churn fix. Road-tested on a full drive before submission.
  • Kal (Valleytech Custom Solutions) — v2.12.0 video review whose feedback drove the v2.12.2 first-run UX pass.

Flashing

Method Link
Web Flasher (easiest) jimgat.github.io/CYM-NM28C5
Manual (esptool) See table below
File Flash address
bootloader.bin 0x0000
partition-table.bin 0x8000
CYM-NM28C5.bin 0x10000
CYM-NM28C5-full.bin 0x0000 (merged image — flash this alone)

CYM-NM28C5 v2.12.0

Choose a tag to compare

@JimGat JimGat released this 02 Aug 22:08

What's New in v2.12.0

Area Change
Chameleon Ultra Full 6-phase BLE integration: Connect/Pair, Read LF (EM410X/HID), Read HF (ISO 14443-A), Dump Card (full MIFARE sector + NTAG page dump), Slot Manager, Clone to Slot, Load from SD
Wardrive — BLE-only fixed Root-caused and fixed: was collecting 0 devices for months. esp_wifi_stop() leaves the WiFi DMA pool allocated — NimBLE then fails with 0x207 BLE_ERR_MEM_CAPACITY. Fix: call esp_wifi_deinit() before NimBLE starts. Verified 33–48 devices collected, CSV valid, uploaded to WiGLE + WDG.
Wardrive — SD deadlock fixed CSV rotation triggered a nested sd_spi_mutex take (non-recursive mutex) inside a section that already held the lock, blocking forever with portMAX_DELAY. The device froze silently mid-session — no crash, no log. The display and SD share the same bus lock so the whole UI locked with it. Fixed by removing the redundant inner take.
Wardrive — DMA headroom Five large arrays moved to PSRAM BSS (g_drones, wana_aps, BT Lookout entries, BlueDuck name/path tables). Internal BSS 129 KB → 113 KB (−16.6 KB). Wardrive DMA floor: 20,807 → 31,615 bytes (+52%), flat across a run, measured on-device.
Adaptive wardrive Speed-based profiles (Stationary / Walking / Driving) auto-adjust dwell time and DFS channel weighting from live GPS speed. D-UCB bandit keeps all non-DFS 5 GHz at full weight in all modes. Field-verified over two ~17 min city drives: 5 GHz maintained at 35–38% of observations (650–700 unique 5 GHz APs), DFS down to 12–19% of sweeps while driving, zero zero-fix rows.
GPS 5 Hz opt-in Optional 115200 baud / 5 Hz in Wardrive Options -> GPS Baud. Measured 5.2 fix/s under full wardrive load. At 65 km/h: 18 m -> 3.6 m between fixes. Verify-and-revert safety: if the module does not acknowledge the baud switch, the UART is put back and GPS continues normally.
GPS boot auto-detect Module holds baud in battery-backed RAM across power cycles. Firmware now probes the real baud at boot instead of assuming 9600. Multi-vendor commands: CASIC ($PCAS01/$PCAS02), MTK ($PMTK251/$PMTK220), u-blox (UBX-CFG-PRT/UBX-CFG-RATE). CASIC path device-verified in both directions.
GPS status icon Top-bar satellite icon on every screen: green = fix, grey/slash = no fix.
BLE active scan + name backfill Wardrive BLE scan switched from passive to active (solicits SCAN_RSP, where most devices advertise their name). Name backfill on the dedup path catches names that arrive in SCAN_RSP after the initial ADV.
BLE-only dashboard B / MAC / Name / RSSI table with "BLE / SCAN" box, distinct from the WiFi layout that was misrepresenting BLE data.
CSV fsync fsync() after header and each periodic flush. Power-cut verified at 10 s, 20 s, 30 s - all rows survive, no truncation.
CSV rotation Auto-rotates into journey-grouped parts: WD_Wifi_<YYYYMMDD_HHMMSS>-NNN.csv / WD_BLE_<stamp>-NNN.csv. Each part uploads independently. Fixes the 8 MiB single-buffer upload ceiling that long drives hit.
AltitudeMeters + AccuracyMeters Both filled from the real GPS fix at discovery time for WiFi and BLE rows. Was hardcoded 0.00.
FirstSeen fix Was returning a hardcoded 2025-09-26 plus a dwell counter. Now uses the GPS-synced clock.
BLE device cap Raised 200 -> 2000 (PSRAM). A single city session was hitting the old cap and stopping collection.
PN532 Key Test Now loads /sdcard/lab/rfid/keys/mf_keys.dic (up to 512 extra keys) after the 16 built-in keys - same dictionary file as Chameleon Dump Card.
SD Provision All paths used by firmware now created on first provision: rfid/lf, rfid/hf, rfid/keys, rfid/import, rfid/export, rfid/logs, tpms/, espnow/, bluetooth/spooflist.csv, rfid/keys/mf_keys.dic (seeded).

Chameleon Ultra - Operation Detail

CYM connects over BLE and provides a full touchscreen NFC/RFID workflow - no phone or laptop needed.

  • Connect & pair - BLE scan with RSSI signal bars; auto-injects passkey 123456 for devices with BLE pairing enabled; connection persists across screens
  • Read LF (125 kHz) - EM4100/EM410X and HID H10301; saved as Flipper .rfid to /sdcard/lab/rfid/lf/
  • Read HF (13.56 MHz) - ISO 14443-A scan with UID/ATQA/SAK detection; saved as Flipper .nfc to /sdcard/lab/rfid/hf/
  • Dump Card - appears after any HF card detect; for MIFARE Classic 1K: key-attack (8 built-in keys + mf_keys.dic) then reads all 64 blocks; for NTAG/Ultralight: reads all pages via raw ISO 14443-A READ commands; output is Flipper-format .nfc with Block N: / Page N: data
  • Slot Manager - browse all 8 Chameleon slots; tap to set active
  • Clone to Slot - write a captured UID to any slot (EM410X and HID H10301 supported)
  • Load from SD - long-press any .rfid or .nfc file in the SD browser to load it into the active Chameleon slot; Slot Manager auto-refreshes after write

Note: The MIFARE key dictionary feature (mf_keys.dic) is untested on real hardware at time of release. The built-in 16-key list (PN532) and 8-key list (Chameleon Dump Card) have been verified; SD-loaded additional keys compile and load correctly but have not been validated against a physical card requiring a non-default key. Extended dictionary use is at your own risk until confirmed in the field.


Wardrive - Adaptive Capture Profiles

Profile Trigger Dwell DFS policy
Stationary < 5 km/h 500 ms Full weight
Walking 5-20 km/h 300 ms Full weight
Driving > 20 km/h 150 ms 1 in 10 sweeps

Speed Adaptive Mode can be disabled and a fixed profile selected via the Manual Mode dropdown in Options. Profile names show their parameters ("Driving (150ms, DFS 1/10)") so behaviour is visible without reading the source.


GPS - 5 Hz Detail

Switching to 115200 / 5 Hz is opt-in and gated behind a consent popup. The verify-and-revert safety net: after sending the baud command to all supported chipset families, CYM listens 1.3 s at the target baud. If the module does not respond, the UART is restored and the setting is not saved. This handles modules that do not speak CASIC/MTK/u-blox - they ignore the command and GPS carries on.

The CASIC path (ATGM336H) is device-verified in both directions. The MTK and u-blox paths are written from interface specs and are safe to send blind, but have not been validated on physical hardware.


DMA Headroom - Internal BSS Before / After

Array Size moved to PSRAM
g_drones ~5.0 KB
wana_aps ~4.8 KB
bt_lookout entries table ~3.6 KB
ble_blueduck script name/path tables ~3.5 KB
Total freed from internal BSS ~16.9 KB

Internal BSS: 129 KB -> 113 KB. Wardrive DMA floor: 20,807 -> 31,615 bytes (+52%).


Contributors

  • @birolt29 - wardrive overhaul, DMA headroom, GPS hardening, BLE-only fix, SD deadlock fix, field testing across all changes

Flashing

Flash CYM-NM28C5-full.bin at address 0x0000 (single merged image - bootloader + partition table + firmware).

Web flasher: cym-nm28c5.netlify.app - use the Dev tab for this build, or download and flash manually from the assets below.

File Offset
bootloader.bin 0x2000
partition-table.bin 0x8000
CYM-NM28C5.bin 0x10000

CYM-NM28C5 v2.11.7

Choose a tag to compare

@JimGat JimGat released this 26 Jul 14:23

v2.11.7 — Bug Fixes

GATT Walker double-init reset — fixed (@birolt29)

Root cause (serial-confirmed): The GATT Walker's bottom Back button called wifi_cli_init() directly, leaving BLE still active and the DMA pool cramped. On the next GATT Walker entry, ensure_ble_mode() skipped the WiFi→BLE switch; the following Back press then called esp_wifi_init() on an already-initialized stack (error 0x103) → the firmware's own "recover RAM" path fired esp_restart(). Reproducible on the 2nd Back→Enter cycle.

Fix: Removed the bottom Back button and gw_back_btn_cb entirely. The top-bar ‹ Back is now the single exit path, consistent with all other BT/RF screens. A new gw_screen_stop() hook cancels any in-progress walk (skipped on GW_STATE_COMPLETE so forward navigation to the Interact / HID / Probe screens keeps the connection alive), NULLs all UI pointers, and does not touch radio mode — BLE stays up and is reclaimed lazily by ensure_wifi_mode() on the next WiFi feature. g_screen_back_fn = show_bt_attack_tiles_screen routes the top bar back to the "BT: <target>" tiles correctly. Validated by @birolt29: 10+ enter/exit cycles, no reset, DMA holds ~33 KB after returning to BT Scan & Select.

CC1101 TPMS Monitor crash on Back/Home — fixed

Missing g_screen_stop_fn on the TPMS Monitor screen left the 500 ms UI timer and scan task running against freed LVGL objects after navigation → Load access fault. cc1101_tpms_screen_stop() now sets cancel=true, deletes the timer, NULLs all 26 lv_obj_t* pointers (including all 20 sensor_lbl[] slots), and waits up to 3 s for the scan task to exit cleanly.

All 4 Zigbee screens (Scout, Pan Detail, Locator, Assoc Flood) were confirmed to already have correct stop hooks — not the crash source.

GATT Clone button block character — fixed

The Unicode right arrow (U+2192) in the "Clone Device → SD" button label is not present in lv_font_montserrat_12, rendering as a solid block on device. Replaced with LV_SYMBOL_RIGHT from LVGL's built-in symbol font.


🎬 Featured Video

A big thank you to Anubis for creating the first community video of CYM in the wild! Check it out: https://youtu.be/IDm4uz38IDE


Flashing

Flash CYM-NM28C5-full.bin at address 0x0000 using the web flasher or any compatible tool.

CYM-NM28C5 v2.11.4

Choose a tag to compare

@JimGat JimGat released this 17 Jul 16:40

v2.11.4 — Major Render Performance + Stability

A huge thank you to @birolt29

This release is almost entirely the work of Birol (@birolt29), who analyzed the CYM-NM28C5 render pipeline, identified three separate stability issues, measured everything on real hardware, and provided meticulously documented patches with full data and rationale. His contributions this cycle represent the single biggest performance improvement in the project's history. Thank you, Birol — this one's yours.


What's new

Render performance: 148 ms → ~77 ms (-48%) — @birolt29

Two changes that must always move as a pair:

  • LCD SPI 40 → 80 MHz (init_display() pclk_hz): halves the per-flush SPI push time
  • LVGL draw buffers → internal DMA SRAM (MALLOC_CAP_DMA | MALLOC_CAP_INTERNAL): PSRAM cannot feed the SPI FIFO fast enough at 80 MHz — using PSRAM at 80 MHz causes display tearing. Internal SRAM feeds it clean.

⚠️ If you ever need to revert the LCD clock, you must also revert the buffer allocation back to MALLOC_CAP_SPIRAM — they are a hard constraint pair.

Combined with the Tier-1 sdkconfig tuning below: full-frame LVGL render drops from 148 ms to ~77 ms.

sdkconfig render tuning (Tier-1) — @birolt29

Four settings measured on real hardware (BT Observer, BLE-active):

Setting Change Measured effect
CONFIG_COMPILER_OPTIMIZATION_PERF=y -Og → -O2 -8% render; also shrinks .iram0.text → +4.6 KB DMA heap
CONFIG_LV_MEMCPY_MEMSET_STD=y LVGL byte loops → libc memcpy faster buffer fills
CONFIG_LV_DISP_DEF_REFR_PERIOD=15 30 → 15 ms refresh period higher UI refresh ceiling
CONFIG_SPIRAM_SPEED_80M=y PSRAM 40 → 80 MHz -8% per-flush via buffer-access path

Note: CONFIG_LV_ATTRIBUTE_FAST_MEM_USE_IRAM was tested and rejected — it grew .iram0.text +13.8 KB, collapsing DMA-free heap from 18 → 4 KB. Gain wasn't worth it.

SD remount mutex fix — @birolt29

s_sd_remount_task() was the only SD access path in the firmware missing sd_spi_mutex. Since the LCD and SD share SPI2_HOST, the remount task could race an in-flight LCD DMA transfer, tripping assert(spi_ll_get_running_cmd(hw)==0) in spi_hal_setup_trans → immediate hard reset. Both the unmount call and each of the three re-mount frequency attempts are now properly mutex-guarded.

9 IR/RF433 name arrays → PSRAM BSS — @birolt29

Nine large static char arrays (s_ir_edit_remote_names, s_ir_edit_sig_names, s_ir_cap_remotes, s_ir_remote_names, s_ir_signal_names, s_ur_search_remotes, s_rf433_cap_remotes, s_rf433_remote_names, s_rf433_signal_names) tagged with EXT_RAM_BSS_ATTR, moving 17.4 KB from internal .dram0.bss to PSRAM .ext_ram.bss. Raises the DMA-free heap floor.

IDF v6.0.2 (was v6.0.0-era tip)

Updated to the v6.0.2 stable maintenance tag (supported through Sep 2028). Key fix: IDF-15338 (ESP32-C5 eco2 RF PLL hang on PHY init) is now resolved inside the PHY blob via phy_wait_freq_hw_hop_done(). No manual patch to phy_init.c is needed or appropriate on v6.0.2.

Drone Detector stop hook

drone_detector_stop() registered as g_screen_stop_fn — fixes crash when navigating Home or Back from the Drone Detector screen while a scan is active. Follows the mandatory stop-hook pattern established in v2.10.23.


Memory after this release

Segment Size
Internal BSS (.dram0.bss) 113.7 KB
PSRAM BSS (.ext_ram.bss) 145.5 KB
Binary size 2902.4 KB

Flashing

Flash CYM-NM28C5-full.bin at address 0x0000 using the web flasher or any compatible tool. The full image includes bootloader + partition table + firmware in a single file.

CYM-NM28C5 v2.11.2

Choose a tag to compare

@github-actions github-actions released this 15 Jul 17:59

Bug Fixes

Navigation crash fix — Home button from RF/BLE/Zigbee screens

Pressing the Home button from any of the following screens caused a Load access fault crash (MTVAL = 0xaaaa…). The root cause was missing `g_screen_stop_fn` stop hooks: background tasks and LVGL timers kept running against freed UI objects after screen teardown.

Screens fixed:

Screen What was left running
CC1101 Fox Hunt 50 ms RSSI timer
MITM (scan phase) 200 ms scan-check timer
CCCD Probe / GATT Walker 200 ms poll timer
BLE Scan BLE scan task
CC1101 Weather Station OOK receiver task
CC1101 Alarm Sensors OOK receiver task
RF433 OOK Scan RF433 capture task
Zigbee Scout 500 ms UI timer + scan task
Zigbee PAN Detail 300 ms poll timer
ZB Locator 500 ms RSSI timer + locate task
Zigbee Assoc Flood 300 ms UI timer + flood task

Each screen now registers a stop hook that deletes its timers, NULLs all LVGL object pointers, and signals task stop flags before navigation completes.


Flashing

Web flasher (easiest): https://jimgat.github.io/CYM-NM28C5/

Full image (single binary, flash at `0x0000`): `CYM-NM28C5-full.bin`

Manual esptool:
```
esptool.py --chip esp32c5 write_flash
0x2000 bootloader.bin
0x8000 partition-table.bin
0x10000 CYM-NM28C5.bin
```

CYM-NM28C5 v2.11.1

Choose a tag to compare

@JimGat JimGat released this 14 Jul 02:47

What's New

Full Flash Image — CYM-NM28C5-full.bin

A new merged binary is now generated on every build (local dev and CI). It combines bootloader + partition table + firmware into a single flat image. Flash it at address 0x0000 with any flasher that accepts a full binary — no need to specify three separate files and addresses.

esptool:

esptool.py --chip esp32c5 --baud 460800 write_flash 0x0000 CYM-NM28C5-full.bin

The three-file flash method still works as before.

Fixes

  • CI workflow --flash_size corrected from 8MB16MB to match the NM-CYD-C5 hardware
  • No firmware changes — binary content identical to v2.11.0

Flash

Easiest: ⚡ Web Flasher

Single binary (new):

esptool.py --chip esp32c5 --baud 460800 write_flash 0x0000 CYM-NM28C5-full.bin

Three-file:

esptool.py --chip esp32c5 --baud 460800 write_flash \
  0x2000  bootloader.bin \
  0x8000  partition-table.bin \
  0x10000 CYM-NM28C5.bin

CYM-NM28C5 v2.11.0

Choose a tag to compare

@JimGat JimGat released this 10 Jul 04:24

What's New

IR Edit Files — Delete Support

  • Delete remote files: tap the red trash button on any remote row to delete the entire .ir file, with a confirmation popup ("This cannot be undone.")
  • Delete individual signals: each signal row in the signals view now has a trash button alongside Rename — deletes only that signal and rewrites the file
  • Delete from signals view: the signals header has a new trash button that deletes the currently-open remote without going back to the remote list
  • Confirmation popup: dark modal overlay, red border card, item name, warning text, Cancel + Delete buttons — no destructive action fires until Delete is tapped

IR Edit Files — Keyboard Fixes

  • Cursor now appears immediately in the rename textarea (blinking left-bar cursor, teal colour)
  • Popup style matches the project-wide keyboard standard (teal border card, Montserrat 14 label)

LED Remote — 28-Button Layout

  • Grid expanded from 24 to 28 buttons (7 rows × 4 columns)
  • All codes verified by physical capture from a real remote — no more guessed codes
  • Library selector toggle in the top-right corner switches between:
    • Custom — 28 confirmed codes from a Submersable-style LED strip remote
    • 44-Key — 28-button subset of the standard 44-key NEC RGB LED remote
  • Row 7 adds timer shortcuts: 2H · 4H · 6H · Timer Off

Fixes

  • IR Edit Files rename keyboard: cursor was invisible without explicit LV_STATE_FOCUSED — fixed
  • LED Remote Brt+/Brt- codes corrected after physical-remote verification (capture file labels were swapped vs. physical button positions)
  • ir_hat_claim() now called on LED Remote screen entry; ir_hat_deinit() on exit — prevents GPIO resource leak if user navigates away while remote is open

Binary

Flash via the Web Flasher or use esptool manually with the files attached below.

esptool.py --chip esp32c5 write-flash \
  0x2000  bootloader.bin \
  0x8000  partition-table.bin \
  0x10000 CYM-NM28C5.bin