Commit Graph
23456 Commits
Author SHA1 Message Date
Zhaoqi XuandGrok Bot 10fa02ca24 client: free graph and demod buffer save states after restore
save_graphbuffer() and save_buffer8() calloc a copy, and the restore_*()
helpers only copy out of it, so every owner leaked its save state.
check_chiptype() lost 4 * g_GraphTraceLen bytes plus the packed demod
buffer on each online `lf search` (~2 MB after the COTAG probe), and the
Visa2000 demod leaked on every `lf search`, online or offline.

Add free_buffer_savestate() and call it after the last restore in
check_chiptype(), demodTI(), demodVisa2k(), demodzx(),
try_detect_modulation() and t55xx_psk_untrim_head(), including the
early-return and success paths that never restored.

Reported with the check_chiptype() diagnosis and fix by @BeheSQ.

Fixes #3675

Co-Authored-By: Grok Bot <noreply@users.noreply.github.com>
2026-10-11 06:51:44 +08:00
Innocent Bystander a48ce253f7 adding an entry to CHANGELOG.md per git PR note 2026-10-06 14:57:09 +02:00
Innocent Bystander dfe5f23ae3 Change references to sim014.bin to sim024.bin to reflect the current sim firmware. 2026-10-06 14:57:09 +02:00
Tom HarknessandClaude Sonnet 5 dd8bf6c184 Add CHANGELOG entry for CMD_CEP_STATUS
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 14:42:29 +02:00
Tom HarknessandClaude Sonnet 5 ac9c402e13 PM5: add CMD_CEP_STATUS, a compact hardware-status snapshot
For the Flipper Zero FAP's status page: hw status/CMD_STATUS replies
entirely via free-text Dbprintf packets the FAP's frame layer discards,
with nothing structured on the terminating ack. CMD_CEP_STATUS follows
the CMD_CAPABILITIES precedent instead - one binary cep_status_t reply
covering CEP handshake state, BWM battery telemetry, and a firmware
version string.

Always replies PM3_SUCCESS; fields for a feature this build lacks
(no WITH_CEP, no WITH_BWM_STATUS) read as zero rather than failing the
command, so any client can call it unconditionally. cep_attached isn't
included yet - its getter (cep_is_attached()) only exists on the
not-yet-merged WFI-skip responsiveness branch (#3687); can be appended
later following capabilities_t's own append-only convention.

Stacked on the pm5-bwm-battery-telemetry branch (CMD_PM5_BWM_GET_BATTERY),
which this command's battery field reuses via bwm_read_battery_info().

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 14:42:29 +02:00
Tom HarknessandClaude Sonnet 5 7bd39effbd PM5 CEP: widen reply-header timeout, resync SPI on desync evidence
The Flipper master only clocks the bus in bursts roughly every ~120ms
(its own idle poll cadence). CEP_SPI_BYTE_TIMEOUT (~3-10ms real time)
is ~20x shorter than that gap, so cep_spi_write_sync()'s first reply
byte - the one racing against the master's next poll - was giving up
long before the master actually showed up. Measured CEP reply success
at ~22% before this fix, ~100% after, across repeated LA-captured test
runs.

Fix: a separate, ~20x longer timeout (CEP_SPI_HEADER_BYTE_TIMEOUT) used
only for the first two length-header bytes of a reply - the ones that
can land in the gap. Every byte after that stays on the short timeout,
since once the master starts clocking it stays mid-CS and keeps going.

Also makes cep_spi_data_available() call cep_spi_resync() on its two
existing desync-evidence paths (sync-preamble discard overflow, an
implausible length header) instead of just returning false - those are
exactly the AT32 errata ES0003 corruption cep_spi_resync() already
exists to recover from; silently returning false left SPI1 in that bad
state until a later CRC/length check happened to catch it.

Stacked on fix-cep-spi-reliability (#3686, open) - this builds directly
on cep_spi_resync()/cep_spi_wait_tdbe()'s bounded-wait rewrite from that
PR and can't be tested standalone without it.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 14:39:54 +02:00
Tom HarknessandClaude Sonnet 5 f2df3eba9d Move bwm_battery_info_t into pm3_cmd.h (shared wire-protocol header)
bwm_charger.h is an armsrc-only header included after pm3_cmd.h in
appmain.c, so a struct embedding bwm_battery_info_t by value (e.g. a
future PM5 status command) couldn't see the type at the point it's
needed. bwm_ble_status_t and bwm_autooff_status_t already live in
pm3_cmd.h for the same reason - this just brings
bwm_battery_info_t in line with them. bwm_charger.c/.h keep their
function declarations unchanged; only the struct's home moves.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 14:38:33 +02:00
Tom HarknessandClaude Sonnet 5 5661f21d60 Add CMD_PM5_BWM_GET_BATTERY: compact structured battery telemetry
hw status's battery/BWM section (bwm_print_battery_status()) only ever
formats its numbers into free-text Dbprintf lines, unusable by any
embedded/non-terminal client. Add a new command that returns the same
SoC/voltage/current/capacity/temp/health/fault numbers as one small
packed struct via reply_ng().

Refactor the shared reads into bwm_read_battery_info() so hw status
and the new command read the same values and can't drift apart -
hw status keeps all its existing charger-detail lines (MainCtl, input
limit, VIN_DPM, charge voltage/enable) unchanged, sourced from its own
reads as before; only the fuel-gauge numbers and the two small
charger-status reads the new struct needs are now shared.

Motivated by wanting a Flipper Zero FAP battery-status test screen
(separate app repo), but this command is generic and usable by any
client, not CEP-specific.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 14:38:33 +02:00
Tom HarknessandClaude Sonnet 5 ba4016d7e2 PM5 CEP: data_available() doesn't poll CEP, so CMD_BREAK_LOOP over a
Flipper link can't interrupt a running reader loop

Problem: triggering the Flipper FAP's built-in ReadHitag2 demo command
turns the RF field on and it stays on even after backing out of the
app - only an unrelated USB command clears it. data_available()
(armsrc/util.c) is the standard idiom LF/HF reader loops use to notice
an incoming CMD_BREAK_LOOP, but it only polled USB/BWM/FPC. CEP's own
transport PR never wired it in, so a Flipper-sent break is invisible
to any loop that was already running.

Fix: add cep_spi_rx_pending() to armsrc/pm5_cep.c/.h - a genuinely
non-blocking single SPI1 RDBF flag check, not a reuse of the existing
(bounded-wait) cep_spi_data_available(), since this gets called every
iteration of every reader loop in the codebase and needs to cost
nothing when idle. Wire it into data_available()/data_available_fast()
alongside the existing BWM/FPC branches, additive since CEP and BWM
can coexist.

Hardware-confirmed: field now drops promptly every cycle instead of
sticking on, across repeated ReadHitag2 attach/attempt/cancel cycles.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 14:36:03 +02:00
Tom HarknessandClaude Sonnet 5 dd93406aa0 PM5: skip WFI idle sleep while a Flipper is CEP-attached
Problem

pm5_power_idle()'s WFI sleep adds ~40-50ms of latency before the main
loop notices a fresh byte on CEP's SPI1/USART1. At idle this is the
whole point of WFI - but it's also what made the original attach
handshake miss bytes (GH #3667): cep_attach_poll() runs every 50ms, and
WFI's own latency on top of that was enough to lose bytes of the
Flipper's one-shot handshake string often enough to require a physical
cable replug to recover.

Change

armsrc/appmain.c: skip pm5_power_idle() entirely once cep_is_attached()
is true, so the main loop polls CEP as fast as the hardware allows for
as long as a Flipper is physically present.

armsrc/pm5_cep.c/.h: new cep_is_attached(), true as soon as the CC
controller reports a physical attach - independent of whether the
handshake/SPI transport is actually up yet, so the loop stops sleeping
before the handshake even starts, not after. Paired with a
CEP_ACTIVITY_TIMEOUT_MS (3000ms) idle-detach: the FAP quitting doesn't
touch the CC controller's attach bit (the cable stays connected), so
losing its normal 20-120ms SPI poll cadence for this long is what
actually means it's gone - at that point cep_is_attached() still
reflects the physical attach correctly, but cep_is_active() (and
therefore this PR's WFI-skip, which gates on attachment rather than a
completed handshake) only backs off once the FAP itself has clearly quit
rather than just being between polls.

Built on top of the AT32 SPI1 errata fix (bounded waits + resync) from
the companion CEP-reliability PR - running the main loop flat-out
against an unbounded wait would have just hit that hang faster, which is
why this shipped after rather than alongside it.

Behaviour change

PM5 no longer downclocks to WFI between main-loop iterations while a
Flipper is attached over CEP (idle power draw increases for the duration
of the attach; unaffected when no Flipper is present, and unaffected on
RDV4/non-CEP builds).

Testing

- make clean && make PLATFORM=PM5 PLATFORM_EXTRAS=BWM and
  make clean && make PLATFORM=PM3RDV4 - both zero warnings.
- Hardware: this flag was active throughout all of the companion CEP-
  reliability PR's hardware testing (60s/110s/240s captures, 18x
  `hw status`, 5x attach/detach + `hf search` cycles against a real
  card) - all passed with WFI-skip engaged the whole time. Additionally
  reflashed this exact branch standalone and reconfirmed a live
  attach/detach cycle completes cleanly.
- Not tested: clang toolchain (firmware-only diff); idle power draw was
  not independently measured (no direct ammeter in this session's
  bench setup, only observed BWM telemetry prior to this change).

Checklist

- [x] Proper style of own code, no unrelated reformatting included
- [x] Builds warning-free with gcc (PM5 and RDV4)
- [ ] clang not tried (firmware-only diff)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 14:29:39 +02:00
Tom HarknessandClaude Sonnet 5 8faafcebb0 PM5 CEP: recover from AT32 SPI1 errata, bound all SPI waits
Problem

With PR #3672/#3674 landed, CEP handshakes reliably but the SPI transport
hangs PM5 hard (needs a physical reset, no watchdog on this platform)
after some number of attach/reconnect cycles - sometimes the first
cycle, sometimes dozens of pings in. Logic-analyzer capture of a hang in
progress showed a ~1.2s burst of rapid CS-gated retries, and in one
capture, three periodic transactions after that burst got progressively
slower (2.1ms -> 3.9ms -> 3.2ms -> 5.2ms) instead of recovering.

Root causes (two, compounding)

1. cep_spi_write_sync() had seven bare `while (flag == RESET);` spins
   waiting on SPI1's TDBE/BF flags, with no timeout. PM5 is the SPI
   slave - TDBE only clears when the Flipper is actively clocking, on
   its own ~20-120ms schedule, completely async to when PM5's reply is
   ready. Queue a reply between two master polls and the wait never
   returns - full main-loop death.
2. Artery's own errata sheet for the AT32F435/437 (ES0003) documents
   that in SPI slave hardware-CS mode, the internal clock sync is not
   guaranteed to reset on every CS falling edge - a bad sync can persist
   across transactions with no self-correction. This is PM5's exact CEP
   configuration (SPI_CS_HARDWARE_MODE, slave mode).

Change

armsrc/pm5_cep.c:
- cep_spi_write_sync() rewritten around a bounded cep_spi_wait_tdbe()
  helper (same spin-iteration-count pattern already used for reads) -
  returns PM3_EIO on timeout instead of hanging.
- New cep_spi_resync(): hardware-resets SPI1 via crm_periph_reset()
  (a register re-init alone doesn't clear the errata's bad internal
  state) and brings it back up. This is Artery's own documented recovery
  for ES0003 ("reset SPI and restart communication"), triggered by our
  existing NG-frame CRC/length checks instead of the SPI peripheral's
  hardware CRC feature.
- cep_do_handshake()'s SPI-stall recovery path was itself unbounded
  (retried forever as long as the CC controller reported "still
  attached") and missing the StartTicks()/I2C_init(true) guard every
  other I2C call site in this file has (same bug class as #3672) - now
  bounded (CEP_HANDSHAKE_REPLY_MAX_RETRIES) with the guard added.
- cep_spi_data_available() now discards the Flipper's 0x55 idle-poll
  filler bytes before reading the real length header, instead of ever
  interpreting one as (part of) a frame length.

armsrc/cmd.c: wire cep_spi_resync() into every CEP-reachable error exit
in receive_ng_internal() (short/overflow/CRC-mismatch reads) and both
reply_old()/reply_ng_internal()'s write-failure paths, so any detected
corruption resets SPI1 before the next transaction rather than letting a
bad sync persist. Add a WITH_CEP-gated inline stub for cep_spi_resync()
so receive_ng_internal()'s call sites don't need per-site #ifdef guards
on non-CEP platforms.

Behaviour change

None for USB/FPC/BWM. CEP: a corrupted or stalled SPI1 transaction now
self-heals (one dropped reply, then normal operation) instead of
wedging the device.

Testing

- make clean && make PLATFORM=PM5 PLATFORM_EXTRAS=BWM and
  make clean && make PLATFORM=PM3RDV4 - both zero warnings.
- Hardware: flashed to a real PM5 paired with a real Flipper Zero FAP.
  - 60s continuous-connection capture with zero USB activity (isolates
    CEP from any client-side interaction): clean, no anomalies.
  - 110s capture with 18 real `hw status` calls interspersed: all 18
    succeeded, CEP stayed attached throughout.
  - 5 full attach/detach cycles with `hf search` against a real NTAG213
    card before/during/after each attach (240s, logic-analyzer capture
    throughout): all 15 reads returned the identical UID regardless of
    CEP state, zero hangs, final liveness check succeeded with no reset
    needed.
- Not tested: clang toolchain (firmware-only diff).

Checklist

- [x] Proper style of own code, no unrelated reformatting included
- [x] Builds warning-free with gcc (PM5 and RDV4)
- [ ] clang not tried (firmware-only diff)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 14:25:00 +02:00
Tom HarknessandClaude Sonnet 5 2e2720a1da PM5: bound usb_write()'s tx-complete wait
The two g_tx_completed spin loops in usb_write() had no timeout at all -
if the host ever stopped pulling an IN packet while the device still read
as enumerated, PM5 would spin forever with no recovery path. Add a
spin-iteration bound (not tick-based: g_tx_completed is set from the USB
core's own ISR, and the shared ticks timer is routinely stopped by LF/HF
code at arbitrary points), matching the existing bounded-wait pattern
used elsewhere in this codebase.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-10-04 14:24:10 +02:00
Philippe Teuwen 63063bd030 tune docs for default port 18888 2026-10-02 15:05:19 +02:00
Niel Nielsen f9bd1c3010 Update default port from 7777 to 18888
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-10-02 14:17:01 +02:00
Niel Nielsen 7d339ac4dc Update default port from 7777 to 18888
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-10-02 14:17:01 +02:00
Niel Nielsen 133b981512 Change default TCP server port from 7777 to 18888
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-10-02 14:17:01 +02:00
Niel Nielsen 256f30f0fa Add files via upload
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-10-01 23:04:47 +02:00
Tom HarknessandClaude Sonnet 5 e6d7cd1f9d Fix CEP reply corruption: move payload memset after time-critical reads
receive_ng_internal() zeroed rx->data (up to PM3_CMD_DATA_SIZE bytes)
between the preamble read and the payload read. CEP's SPI1 receiver has
no FIFO (single-byte holding register), so that stall lost bytes on every
frame - confirmed via logic analyzer (wire content byte-perfect) plus
Dbprintf instrumentation showing the payload read consistently short by
half. Reordered so preamble/payload/postamble/CRC are all read back-to-back
before any memset/memcpy happens. USB/FPC/BWM are unaffected: same total
work, just resequenced.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 21:27:50 +02:00
Nemanja Nedeljkovic 23d57af8bd lf em 4x05 chk: add EM4305 anti-clone card password to default dictionary
Adds A1856618 to t55xx_default_pwds. This is the verified working login
password (as used with -p); the bit-reversed on-air form 1866A185 does not
authenticate.
2026-09-28 21:26:56 +02:00
Tom HarknessandClaude Sonnet 5 d26d8d5ef1 Trim comments, add CHANGELOG entry per review feedback
Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 09:32:59 +02:00
Tom HarknessandClaude Sonnet 5 f1cb495208 Fix CEP main-loop hang: start the ticks timer before I2C use, re-enable WITH_CEP
cep_attach_poll() (and cep_init()) call I2C_BufferReadRaw()/I2C_init(true),
which spin on the shared ticks timer via WaitUS()/WaitMS(). That timer is
routinely stopped with StopTicks() by LF/HF protocol code once it's done
with precision timing (em4x50.c, legicrf.c, hitag_common.c, lfsampling.c,
etc). cep_attach_poll() runs unconditionally every 50ms in the main loop
with no idea whether the timer is currently running - if its poll lands in
a window where something just called StopTicks(), WaitTicks()'s
`while (GetTicks() < target)` spins forever on a frozen counter, hanging
the entire main loop irrecoverably. `hf search` cycles through many such
probes, giving this poll many chances to land in that window, matching the
reported "hf search hangs the PM5, needs a battery pull" regression - and
since the poll runs regardless of Flipper attachment, merely compiling in
WITH_CEP was enough to trigger it.

bwm_lowbatt_poll() (armsrc/bwm_charger.c) is the same shape of periodic
main-loop I2C poller and already guards against exactly this by calling
StartTicks(); I2C_init(true); itself immediately before every I2C access.
Mirror that convention in cep_attach_poll() and cep_init().

Re-enables WITH_CEP as the PM5 default (reverts f88880fc6's temporary
disable) now that the underlying bug is fixed. SKIP_CEP=1 remains available
for anyone who wants to opt out at build time regardless.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-28 09:32:59 +02:00
Philippe Teuwen f88880fc61 Temporarely disable CEP due to bug affecting normal usage 2026-09-27 22:26:11 +02:00
iceman1001 2e2598bca9 style 2026-09-27 21:15:18 +02:00
Iceman 2630310336 Merge pull request #3650 from wiillk3/wireless-device-flashing
BWM: add ability to flash over wifi
2026-09-27 19:35:05 +07:00
Iceman 2dea00f183 Merge pull request #3670 from 0x6r1an0y/20260926-felica-raw
Add FeliCa system-specific polling
2026-09-27 19:32:25 +07:00
Philippe Teuwen 12296f4afb CEP: change to SKIP_CEP=1 and clean a bit the doc 2026-09-26 21:16:19 +02:00
Tom HarknessandClaude Sonnet 5 16024ed4a1 Make WITH_CEP default-on for PM5, add SKIP_CEP to opt out
doegox asked on PR #3668 whether CEP really needs to be guarded by a
compile flag, suggesting it ship on by default with a SKIP_CEP opt-out
instead - mirrors the existing NO_LOWBATT_SHUTDOWN/NO_LOWBATT_BEEP
opt-out pattern in common_arm/Makefile.hal. WITH_CEP is now baked into
PM5's base PLATFORM_DEFS; PLATFORM_EXTRAS=SKIP_CEP filters it back out.

No change to armsrc/Makefile (already keys off the WITH_CEP define, not
the old extras token) or to the CEP transport logic itself. Updated the
PM5-FLIPPER-USAGE doc, the CHANGELOG entry, and one stale comment in
include/pm3_cmd.h that referenced the old opt-in token.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 19:55:50 +02:00
歐歪andCodex a991b20a51 hf felica: handle system polling in ARM select
Pass the requested system code to firmware selection so default and system-specific reader modes share one polling path. Keep the RF field active across retries for the system-specific mode.

Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
2026-09-27 01:27:38 +08:00
歐歪andCodex 24e11e3d72 hf felica: support polling a selected system
Add --sys to the FeliCa reader so repeated polling can activate an Express Transit card while keeping the RF field on.

Co-Authored-By: Codex (GPT-6 Sol) <noreply@openai.com>

Co-Authored-By: Codex (GPT-6) <noreply@openai.com>
2026-09-27 01:04:40 +08:00
Will d364c8fb5d BWM: keep app_com de-framer in armsrc 2026-09-26 12:23:47 -04:00
WillandCursor 8150c2af77 BWM: add ability to flash over wifi
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-09-26 12:23:47 -04:00
Iceman 381e1ae2fc Merge pull request #3626 from Msprg/pm5-idle-lowpower
PM5: idle power save, BWM power/WiFi/BLE settings, idle auto power-off, byte-window flow control
2026-09-26 21:51:43 +07:00
Iceman cbadbf4bfe Merge branch 'master' into pm5-idle-lowpower
Signed-off-by: Iceman <iceman@iuse.se>
2026-09-26 21:51:29 +07:00
Iceman 9ba8b13da6 Merge pull request #3653 from 0x6r1an0y/20260917-15magic-fix
Improve `hf 15 csetuid` command
2026-09-26 21:48:47 +07:00
Iceman b30defefa0 Merge pull request #3668 from TomHarkness/pm5-cep-flipper-transport
PM5: add WITH_CEP transport for Flipper Zero (closes #3667)
2026-09-26 21:47:38 +07:00
Tom HarknessandClaude Sonnet 5 2b5e3e51eb Add PM5 CEP transport for Flipper Zero (WITH_CEP)
Wire up the previously-dead Flipper Zero handshake/SPI code from
at32_unit_test.c into a real WITH_CEP platform extra, independent of
BWM (new g_reply_via_cep flag, not reusing BWM's g_reply_via_fpc, so
both transports can run concurrently).

Fixes a real reconnect bug found during hardware bring-up: the
handshake listener was gated on a CC-controller attach edge that
didn't reliably fire on every FAP relaunch, requiring a physical
cable replug to reconnect. Now watches for a fresh incoming byte any
time the link reports attached, with a bounded receive timeout so a
stray byte can never hang the main loop.

RF/FPGA-dependent functionality (hf/lf reads) is out of scope here -
tracked separately. Closes #3667.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
2026-09-26 09:45:51 -04:00
Philippe Teuwen fc024c8038 Fix bug when calling directly recovery Makefile 2026-09-25 22:43:58 +02:00
iceman1001andClaude Opus 4.8 027b225f41 Fix lf t55xx dump password reuse and p1detect for non-Atmel clones
lf t55xx dump: when lf t55xx detect has already recovered and confirmed
the password, T55xxReadBlockEx now trusts that session config instead of
re-probing the config block unauthenticated. A password-gated tag does
not answer that probe, so 'dump -p' failed its safety check on every
block and demanded --override; it now reads directly.

lf t55xx p1detect / lf search: the page-1 trace fingerprint hardcoded
Atmel's manufacturer byte (0x15/0x39) into a 16-bit preamble, so a valid
T5577 clone with another manufacturer (e.g. Tendyron 0x65) was rejected.
The duplicated atmel/silicon checks are collapsed into
t55xx_trace_preamble_match(), which also matches the bare 8-bit ACL 0xE0
- the same anchor lf t55xx trace decodes against.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-09-25 18:24:44 +02:00
Iceman 3d0fa59ab9 Merge pull request #3666 from dunderhay/add-client-cancellation-to-MIFARE-nonce-acquisition
Allow client cancellation during MIFARE nonce acquisition  - Fixes #3665
2026-09-24 21:08:31 +07:00
Daniel Underhay 0350dcf044 Allow client cancellation during MIFARE encrypted nonce acquisition - Fixes #3665
Signed-off-by: Daniel Underhay <27876907+dunderhay@users.noreply.github.com>
2026-09-24 22:36:38 +10:00
MsprgandClaude Fable 5.1 39bbf20cce pm5: hw bwm ble, the module's BLE settings incl. on/off and pairing
The module ships with BLE open: anyone in range can connect and run
commands, and there was no way to turn the radio off or to require
pairing from the PM5, although the module already implements bonding
with a static passkey (LE Secure Connections + MITM, SPP characteristic
encrypted) behind commands nothing exposed.

New CMD_PM5_BWM_BLE (0x0183): one action byte, then a full status
snapshot back (bwm_ble_status_t: switch, state, bonding, passkey, TX
power, address, name, bonded devices). Client menu `hw bwm ble`:
  status              everything above, with a warning while open
  on | off            persisted radio switch (needs the companion
                      Proxmark5_BWM_esp32 PR; off =
                      nothing can connect, USB/WiFi only)
  pairing on|off [-k] require the 6-digit passkey, set the passkey; a
                      change of on/off restarts the stack (drops a
                      connected client), the factory key 123456 is
                      flagged
  forget -i N | --all remove bonded devices
  txpower -a/-c dBm   advertising / connection power, -24..18 and 20

The firmware replies before restarting the stack so the reply survives
when the command itself arrived over BLE. Fields a module without the
switch cannot answer read 0xFF and print as unknown. A module that does
not answer at all ends the status snapshot after its first query instead
of running into the client's timeout.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:47 +02:00
MsprgandClaude Fable 5.1 465cb47b5a pm5: BWM link flow control counts bytes in flight, sized to the ESP ring
A forward frame on the BWM link is anywhere from ~30 bytes to ~2.1 KB (an
NG reply at PM3_FPC_MAX_DATA), so a 16-frame ack window allowed ~33 KB
in flight while the module held un-acked frames in a 4 KB UART ring:
any radio stall of ~45 ms at 921600 baud overflowed it and the module
dropped PM3 bytes. A frame cap tight enough for big frames stalled
uploads, where the module defers small acks behind large incoming
chunks (the old comment on BWM_FC_WINDOW).

Count bytes instead: a FIFO of un-acked frame lengths (the module acks
in order) with budget BWM_FC_BYTES = BWM_ESP_UART_RX_BUF (12288, the
module's ring in the companion Proxmark5_BWM_esp32 PR) minus 1 KB slack,
and a 64-frame FIFO cap for tiny frames. Change the ring size in both
places; the armsrc -pedantic C99 build rejects a static assert tying
them.

The ack timeout no longer forgets the whole window. That made the gate
open loop for the rest of the command: the acks of forgotten frames
released younger ones early, so after the first eaten ack (bwm_cmd()
drains the ring, #3648) the budget protected nothing. Now it forgets
just enough of the oldest frames to send this one: a lost ack costs one
wait and heals, and a stalled radio gets one frame per timeout on top of
a full ring instead of a window. The wait is 200 ms instead of 50: a
full window that stays full is the module stuck on the radio (a phone
scanning stalls BLE for a few hundred ms), and every timeout leaks a
frame into its ring. A CMD_ERROR broadcast naming SEND_FORWARD_DATA
releases the slot like the ack (older module firmware answers that way
when a WiFi forwarding type is configured and no channel takes the
frame). hw status at debug level prints the window counters.

Measured over BLE with the companion module firmware, which acks a
frame as it leaves its ring: 86 KB/s honest against 88 KB/s open loop,
and a 20 s run with the phone scanning meanwhile finishes clean.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:47 +02:00
MsprgandClaude Fable 5.1 0cd1fada0e pm5: auto power-off: idle timeout, unplug switch, stored on the BWM
The USB-unplug power-off left two ways to drain the battery: a PM5
woken by the button and forgotten, and one left idle after the phone
disconnected.

Idle trigger (opt-in, PM5_AUTOOFF_IDLE_MS 0 by default so out of the box
only the unplug trigger exists, as before): on battery the board powers
off after the idle timeout with no interaction and no wireless client.
Interaction is a received command over any transport, any button press,
or a client connecting or leaving; a standalone mode or a long-running
command holds the main loop, so neither can be cut short. The BWM
reports its clients with the LINK_STATE broadcast (8092, companion
Proxmark5_BWM_esp32 PR) which bwm_forward.c tracks; at boot the BLE
half is seeded with a status query since the module only reports
changes. When the timer is due but a client is still tracked, the module
is asked for its BLE state (at most every 10 s), so a lost "client left"
broadcast cannot pin the board on. And right before going off the module
is asked once more: older module firmware never sends the broadcast and
a "connected" one can get lost, and neither may power the board off
under a silent BLE client. No answer means off.

--unplug off: the unplug trigger always won, so "survive the unplug, go
off after N idle seconds" was not reachable. It is its own setting
(default on = unchanged). With it off an unplug only restarts the idle
clock - from the unplug, not from the last command, or a board that sat
idle on USB for an hour would still die with the cable. Unplug detection
is edge-based (VUSB present at the previous poll, absent at this one)
instead of "absent and seen since boot", which would fire on every poll
after a tolerated unplug. VUSB is followed while the feature is switched
off too, so switching it on again on battery is not taken for an unplug.

Stored on the BWM: runtime-only settings reset at every boot, which is
exactly when the idle trigger matters. The PM5 has no settings store, so
the switch, the idle timeout and the unplug switch live in the module's
host value slots (APP_CMD_SET/GET_SYS_HOST_VALUE 1021/1022, companion
PR; slot 0 switch, 1 idle seconds, 2 unplug), loaded at the first
auto-off poll and saved by CMD_PM5_BWM_AUTOOFF with 1 s per write, so a
silent or older module still leaves the reply inside the client's 5 s
wait. Without a module, or with an older one, the defaults apply and the
status says "not stored".

hw bwm autooff [on|off] [--idle <sec>] [--unplug <on|off>]; no argument
shows the state, one fact per line: switch, idle timeout, unplug
power-off, stored on module, USB power, USB seen since boot, unplug
trigger armed, tracked client, the module's BLE state, idle time.
Payload [action][enabled][idle_s u32][unplug, optional] with keep
sentinels; both actions are non-zero so an old firmware, which reads
byte 0 as the enable flag, can only be left enabled. hw status prints
the module's live BLE state and the auto power-off setting; at debug
level also the tracked client, which should agree with the live state.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:46 +02:00
MsprgandClaude Fable 5.1 5e1b04def1 pm5: hw bwm wifipower, WiFi off or the modem power-save type
New `hw bwm wifipower [off|none|min|max]`. `off` turns the BWM's WiFi
fully off (BLE-only, persisted; the same path as `hw bwm wifi stop`,
and the module's default state). The other three set the ESP's persisted
WiFi modem power-save type (CMD_PM5_BWM_WIFI_PS 0x0182, module commands
2052/2053, esp_wifi_set_ps()) used while WiFi is up: min (the ESP-IDF
default, the modem sleeps between DTIM beacons), max (sleeps for the
whole listen interval, lowest current, slower to react), none (modem
always on, lowest latency). With no argument it reports whether WiFi is
up at all and the stored type. Independent of `hw bwm powersave`.

The link helper's "cmd failed" diagnostic is now printed only at debug
level: a status query while WiFi is off fails by design and was
cluttering every call.

Verified on hardware: default reads min with WiFi off; none, max and
min each set and read back; off tears WiFi down; an unknown value is
rejected; max survives a module reboot. Not measured with WiFi
associated.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:46 +02:00
MsprgandClaude Fable 5.1 552276c75f pm5: hw bwm powersave
New `hw bwm powersave [on|off]` (CMD_PM5_BWM_POWERSAVE 0x0181) shows or
sets the ESP's persisted power-save switch (companion Proxmark5_BWM_esp32
PR: DFS, light sleep and slow advertising after a 30 s fast phase, or
the stock always-on behaviour), so the two can be A/B measured and the
saving turned off when chasing a link problem. `hw status` prints the
state next to the BWM firmware version.

Both module queries in `hw status` are time-bounded (version 2 x 800 ms,
power save 300 ms): a module that is silent, or a firmware without the
command, cannot push the report past the client's timeout.

Verified on hardware: state read/set/persist across an ESP reboot,
`hw status` after 4, 15 and 40 s idle in power-save mode and after 6 s
idle with it off.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:46 +02:00
4f4acedbdd pm5: reduce idle power (core clock scaling + WFI idle)
Port the "reduce idle power" feature from the Fantasi firmware
(soeinova/Fantasi @ 9671309, plus its later ACC-SOF fix) to the PM5
(AT32F435) target.

Problem
-------
The PM5 firmware ran the ARM core at 288 MHz with a 1.3 V LDO and a
main loop that busy-polled at full speed even when the device was idle.
The 288 MHz PLL and the 1.3 V LDO dominate idle draw, and the busy loop
adds to it. Nothing scaled back when there was no work.

Change
------
New armsrc/pm5_power.c drives a refcounted clock/voltage governor:
- Idle (nothing pending, boost count 0, boot grace elapsed, FPGA off):
  take SCLK off the PLL onto HICK-48, power the PLL down, drop the LDO
  to 1.1 V, park the FPGA 24 MHz clock (PA8 low), and halt the core in
  WFI until the next IRQ or the 1 ms SysTick wake.
- Any timing-critical work holds a 288 MHz boost: AppMain wraps
  PacketReceived (which on the PM5 also covers a standalone mode, since
  it is only entered through a command there); the BWM low-batt poll
  and the RGB indicator wrap their I2C. Boost is refcounted and
  IRQ-guarded.
- USB (OS image) moves to the crystal-less HICK-48 clock, ACC-trimmed
  against the SOF, so powering the PLL down never touches USB. The
  bootrom keeps the HEXT/PLL USB path (AS_BOOTROM), so DFU flashing is
  unchanged.
- bwm_uart_clock_update() re-inits UART4 through the SDK usart_init()
  on each clock switch (the divider set at 288 MHz is 6x off at 48 MHz).
- FpgaIsOff() (fpga_core.c, PM5 only) tracks the last conf word so the
  governor never downclocks while a reader field / emulation is running.
- Wake preamble on the BWM link: the companion module firmware
  (Proxmark5_BWM_esp32) light-sleeps 2 s after the last byte on the link
  and its UART wakes on RX edges but loses the bytes that carried them.
  bwm_uart_write() therefore leads with four 0x55 bytes and a 10 ms
  settle on its first write and after 1 s without a write of its own
  (RX is not tracked: the ring can be drained long after the bytes
  came in). The module only starts light-sleeping once it has seen a
  preamble, so the one sent while it was still booting is repeated
  after the boot-time link negotiation. A module firmware without light
  sleep drops the preamble as noise.

New `hw powersave on|off` toggles the governor at runtime (default on);
`hw status` reports the idle-clock and WFI-asleep fractions.

Behaviour change
----------------
- PM5 idles at 48 MHz with the PLL off and the CPU halted; it boosts to
  288 MHz for commands and BWM housekeeping. Command timing and
  throughput are unchanged (intra-command work is always boosted).
- PM5 USB now runs off HICK-48 (ACC-trimmed) instead of the HEXT PLL.
- New command id CMD_PM5_POWERSAVE (0x0180).

Testing
-------
Built warning-free (gcc) for PLATFORM=PM5 (with and without
PLATFORM_EXTRAS=BWM), PM3RDV4 and PM3GENERIC, armsrc and bootrom. Client
built via the default Makefile. Not built with clang or on
Windows/macOS; nothing outside PM5/AT32 guards changes.

Flashed to a PM5 and exercised against a MIFARE Classic 4K card:
hw version/status/ping (4000 & 512 B), hw tune, hf search, hf 14a
info/reader (x12 across idle gaps), hf mf info/chk/rdbl, lf search/read,
hw powersave off/on, held-field (raw -sk) correctly blocks the downclock,
hw reset + re-enumerate. All pass; USB stays up across downclocks. Idle
reaches ~80-95% at 48 MHz and ~90%+ WFI-halted after a few seconds idle.
Measured at the USB port: idle draw 283 mA -> 207 mA with the stock
BWM firmware.

Co-authored-by: Soei Nova <soeinova@proton.me>
Co-authored-by: noproto <noproto@zeroday.engineering>
Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:46 +02:00
MsprgandClaude Fable 5.1 622547351b doc: regenerate commands (hf ntag424 gettt / setconfig were missing)
`make commands` on current master: the two hf ntag424 commands had not
made it into the generated docs, and `hw bwm help` is gated by IfBwm
since 339db44cf but the docs still list it as offline-capable. No other
change. Kept separate so the commits that follow only carry their own
doc changes.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>
2026-09-24 10:33:46 +02:00
Philippe Teuwen 339db44cf1 Mask all bwm commands if device is not compiled for it, avoids copy/paste of bwm commands 2026-09-23 13:47:31 +02:00
Niel Nielsen 3401d59513 Add multiple platform definitions for BWM
Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-23 13:44:57 +02:00
Niel Nielsen b7026e42af Simplify FPGA settings in Makefile.hal
Removed unnecessary platform definitions and comments related to low-battery safety.

Signed-off-by: Niel Nielsen <nieldk@gmail.com>
2026-09-23 13:44:57 +02:00