Keep SCAN(3019)/WRITE(3020) adjacent, matching the SCAN+WRITE
adjacency convention used by every other LF protocol
(EM410X 3000/3001, HIDPROX 3002/3003, VIKING 3004/3005,
IOPROX 3010/3011, PAC 3014/3015). Slot 3017 is now free for
future use.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Resolved conflicts from upstream feature additions (IDTECK PR #407,
LF_T55XX_WRITE PR #413, ISO14443-4 T=CL emulation, HF14A scan-keep,
etc.) by keeping both sides where independent.
Command ID note for reviewer:
- Maintainer's IDTECK shift commit (1e78976) stated "Jablotron retains
the original slots 3016 and 3017", but upstream had already taken
3016 for DATA_CMD_LF_T55XX_WRITE. Only 3017 was actually free.
- Resolution: JABLOTRON_WRITE_TO_T55XX kept at 3017 (honors intent).
JABLOTRON_SCAN moved 3016 -> 3019 (next free slot).
- 5xxx range: JABLOTRON_SET/GET_EMU_ID = 5010/5011 unchanged
(IDTECK shifted to 5012/5013 as planned).
Python files parse cleanly. No duplicate command IDs in data_cmd.h
(3xxx and 5xxx ranges checked).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
LupusE requested in the PR #407 review (2026-05-13) to move IDTECK
command IDs since PR #404 (Jablotron) also uses 3017 / 5010 / 5011.
Both PRs are open with the feature-freeze label; Jablotron retains
the original slots and IDTECK shifts up:
DATA_CMD_IDTECK_WRITE_TO_T55XX: 3017 -> 3018
DATA_CMD_IDTECK_SET_EMU_ID: 5010 -> 5012
DATA_CMD_IDTECK_GET_EMU_ID: 5011 -> 5013
Files changed:
- firmware/application/src/data_cmd.h
- software/script/chameleon_enum.py
app_cmd.c references the constants by name only, no edit needed there.
Adds host-side CLI support for IDTECK:
- lf idteck econfig -s <slot> [--id <hex>] set or read the emulated frame
- lf idteck write --id <hex> clone to a T55xx tag in reader mode
- lf clone -t idteck --id <hex> same via the unified clone command
- hw slot list now renders Frame and Card ID
for IDTECK slots
Input accepts 16 hex characters for the full 64-bit frame, or 8 hex
for the 32-bit payload (the fixed preamble 4944544B is auto-prepended).
A non-blocking informational note is emitted when the payload checksum
does not match the value computed from the card number, since some
readers validate this field and some do not.
Private helpers in chameleon_cli_unit.py (_idteck_compute_checksum,
_idteck_compose_frame, _idteck_frame_info) parse and compose IDTECK
frames and expose card-number-driven composition for a future
`lf idteck compose` command.
Jablotron uses differential biphase (inverted) at RF/64, 64-bit frames:
bits 0-15: 0xFFFF preamble
bits 16-55: 40-bit data (5 bytes), bit 16 must be 0
bits 56-63: 8-bit checksum = (sum of data bytes) XOR 0x3A
Firmware:
- rfid/nfctag/lf/protocols/jablotron.c - encoder/decoder codec
- rfid/nfctag/lf/utils/diphase.c - inverted-biphase state machine
(shared util, reusable by other diphase protocols)
- rfid/reader/lf/lf_jablotron_data.c - GPIO-interval reader path
- app_cmd.c: JABLOTRON_SCAN, JABLOTRON_WRITE_TO_T55XX,
JABLOTRON_SET_EMU_ID, JABLOTRON_GET_EMU_ID
- tag_base_type.h: TAG_TYPE_JABLOTRON enum
- t55xx.h: T5577_JABLOTRON_CONFIG (DIPHASE modulation, RF/64)
- lf_tag_em.c: load callback, factory-default data, save callback
Python CLI (software/script/):
- lf jablotron read - scan a real tag
- lf jablotron write --id - clone onto T55xx
- lf jablotron econfig -s N --id - set emulator ID on a slot
- hw slot list shows Jablotron ID and decimal card number
Python test (software/script/tests/test_jablotron_modulator.py):
Pure-Python round-trip validator that reimplements the modulator and
diphase decoder, expands PWM entries to an edge stream, and confirms
the decoded data matches the input. Regression guard for both the
firmware's double-frame encoding and the single-frame variant.
Notable PWM design choices:
- Constant-level diphase encoding uses the same PAC pattern:
CC=0 for LOW, CC=counter_top+1 for HIGH. counter_top=31 gives
exactly 32 carrier cycles per half-bit at NRF_PWM_CLK_125kHz.
- The 64-bit frame is encoded twice in the 256-entry PWM buffer with
the internal level variable persisting between the two passes.
This is required for clean PWM looping: a single 64-bit diphase
frame with an odd number of zero bits ends at a level opposite the
starting level, leaving no transition at the loop boundary where
the reader expects one. Encoding twice guarantees a continuous
diphase stream regardless of the data's zero-count parity.
Reference: Proxmark3 cmdlfjablotron.c
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Split the single --id argument into --cn (8 ASCII chars) and --raw
(32 hex char T55XX bitstream, directly compatible with PM3 raw output).
Add Python-side PAC bitstream encoder/decoder for raw format support.
Output now shows CN and Raw labels matching PM3's format.
Add NRF_LOG module registration to pac.c for debug logging,
consistent with other protocol implementations.
Reassign PAC command IDs (3014/3015) to avoid collision with ioProx
(3010/3011) after rebase onto upstream/main.
Add pac_t55xx_writer() for encoding PAC card data into T55XX blocks,
along with the T5577_PAC_CONFIG (NRZ/Direct, RF/32, password-protected,
4 data blocks). Wire DATA_CMD_PAC_WRITE_TO_T55XX (3011) through the
command processor, dispatch table, and Python client.
Implements NRZ/Direct modulation decoder for PAC/Stanley 125kHz cards
using SAADC ADC sampling with spike-aware threshold calibration.
The LC antenna produces brief high-amplitude transients at NRZ transitions
which are clipped before the moving-average filter to isolate the actual
data levels.
- refines comments
- use unified code for `lf tag not found` error
- merge lf_em410x_data.h & lf_hidprox_data.h, remove unused macros
- split btn_fn_copy_ic_uid as btn_fn_copy_lf & btn_fn_copy_hf
- fixes offline em410x tag copy not working
- fixes conflict SAADC usage between battery level service & lf fsk demodulator
* feat: add lf HIDProx read, t55xx write, emulate function
code quaility:
- consistance: simulation -> emulation, label -> tag
- machine translated unreadable comments are made native
logic:
- newly added cli command includes: `lf hid prox read`, `lf hid prox write`, `lf hid prox econfig`
- machester demodulator is simplified
- various wiegand formats of HIDProx are supported
- goertzel algorithm is used in HIDProx FSK demod
- lf read is refactored using stream/feed pattern to boost scan speed
- t55xx write is refactored to share same logic between em410x & HIDProx
- lf emulating is refactored to use PWM peripheral, allowing more card type to be added
closes: #212, #210
* chore: remove not implemented wiegand format comments
* fix: build ci
* fix: build ci
* fix: build ci
* fix: build ci
* Implement MF1_ENC_NESTED_ACQUIRE in firmware
* Implement MF1_ENC_NESTED_ACQUIRE in software + fix code formatter
* Remove xz and pthreads from sources, use CMake FetchContent
* Update changelog
* lzma.h
* Update CMakeLists.txt
* Update CMakeLists.txt
* Probably fix workflow taking wrong commit for building
* Fix CMake building tools into bin/Debug on Windows
* Added cmd for fetching all slots nicks (without 16 commands)
* Fix type and use temp directory instead cwd (https://github.com/RfidResearchGroup/ChameleonUltra/pull/261)
* Fix endian for mfu_read_emu_counter_data and mfu_write_emu_counter_data
* Fix --key interpreted as list