Fixing My Proxpass II with an Attiny85 Rebrain

My MCU died, and rebraining the board seemed like the cleanest way to understand the design. There is almost no useful public documentation on this device, which is a shame, because the engineering is brilliant: a tiny MCU, Schmitt-trigger logic, and a compact LF antenna network all working together with elegant economy. This is a bench repair and reverse-engineering exercise on my own hardware, not a guide for bypassing or testing anyone else’s access-control system.

Note the back traces are overlayed in red. A key one that you can’t see is processor pin 5 goes to the 74HC14 input at U2 pin 3.

Hardware Disassembly

To inspect an HID ProxPass II, a Dremel is required. The sonic‑welded casing must be carefully cut along all four sides, taking care not to slice through the large rectangular loop antenna pressed against the plastic. Once opened, the device reveals a compact piece of 1990s engineering: an active, battery‑powered LF radiator.

The ProxPass II is not a passive HID card with a coin cell. It does not output ordinary Wiegand DATA0/DATA1 pulses. Because the original ProxPass lacked a replaceable battery, the ProxPass II introduced analog tuning modifications to suppress the 125 kHz carrier and concentrate energy into the lower sideband.

This document provides a complete breakdown of the original hardware, the RF protocol, and working C source code to “rebrain” the tag using an ATtiny85.

Hardware Architecture

The logic layer is HID/Wiegand‑like, but the physical layer is an MCU‑generated LF waveform that transmits and receives on the same antenna. The original board architecture is as follows:

Battery Stack → PIC12CE519 (or similar PIC12C5xx) → 4.000 MHz Crystal → 74HC14 Hex Inverter → Q3 Transistor / LC Tank → Loop Antenna

1. The 4.000 MHz Crystal (The Heartbeat)

Timing is critical. The board uses a parallel‑resonant 4.000 MHz ECS crystal with ≈22 pF load capacitors. On this PIC class, the instruction clock is the oscillator frequency divided by four, yielding a 1 MHz instruction clock (1 µs per instruction tick). This makes generating a 125 kHz square‑wave grid mathematically clean: an 8 µs period with 4 µs half‑periods.

2. The 74HC14 Schmitt‑Trigger Hex Inverter (The Glue)

This chip provides Schmitt cleanup, edge sharpening, and electrical isolation between the noisy RF/analog network and the PIC. Its specific hysteresis squares up the slow, noisy field‑detect signals from the antenna, giving the MCU a clean, jitter‑free digital wake interrupt.

3. The Analog Gear Shift (Pin 6 & Transistor Q3)

Pin 6 acts as the master “gear shift”. When the tag wakes, Pin 6 drops LOW exactly 17.6 ms before the RF burst begins. This state change triggers the hex inverter to flood the primary drive capacitor with power, ensuring the active amplifier has enough stored energy regardless of the coin cell’s internal resistance. Simultaneously, it opens Q3 to disconnect an ≈3.8 nF tuning capacitor network from the antenna. This physically detunes the LC tank’s resonant frequency from the 125 kHz listening band up to the 139.6 kHz transmission band. By holding this configuration throughout the burst, Pin 6 turns the antenna into a mechanical bandpass filter perfectly primed to broadcast high‑speed FSK sidebands.

RF Protocol: Phase Slips & FSK

The tag does not phase‑sync to the reader. The reader field simply wakes the device, and the tag asynchronously transmits its own battery‑powered LF FSK burst.

On the MCU’s RF‑drive pin (Pin 5), the waveform is a continuous 125 kHz square wave with deliberate, timed phase slips (missed toggles). Instead of transitioning every 4 µs, the MCU occasionally holds the state for 8 µs.

These slips occur at two specific rhythms:

  • ≈32 µs slip intervals (cadence 8) → 15.625 kHz
  • ≈40 µs slip intervals (cadence 10) → 12.5 kHz

This generates the standard HID LF FSK sidebands. As confirmed by the FCC filings, the sidebands land precisely at:

Lower sidebands Upper sidebands
109.375 kHz 137.5 kHz
112.5 kHz 140.625 kHz

Measured vs. Theoretical Sideband Frequencies

Lobe avg Measured (kHz) Theory (kHz) Δ (kHz)
109.69 109.375 0.315
111.605 113.52 112.5 1.02
137.74 137.5 0.24
139.665 141.59 140.625 0.965

These measurements (from the FCC test report) show that the strongest LF components cluster in the upper sideband, with the device formally certified at 139.6 kHz.

Field‑off / Rearm Behavior

The tag is a one‑shot device:

  1. Enters the reader field → wakes up.
  2. Delays ≈37 ms.
  3. Fires one ≈376 ms burst (containing 6–10 repeated frame copies).
  4. Goes completely silent.

It will not transmit again until it leaves the magnetic field for approximately half a second to reset.

  • Minimum reliable ProxMark OFF delay: 519 ms
  • Safe value: 520 ms

Pin 4 (wake / enable event) sends a low pulse for at least 1.19 seconds to keep the pin low.

The ATtiny85 Rebrain

If the original MCU dies, an ATtiny85 can be dead bugged onto the board, reusing the original 4 MHz crystal. (Given the standard 8‑pin footprint, other modern MCU equivalents might also drop in as more direct replacements, or someone could rewrite the original PIC assembly using an LLM.) With a 4 MHz crystal, the original MCU’s instruction-cycle cadence lands on an exact divisor of the 125 kHz carrier timing, making bit-level waveform generation unusually clean.

Pin Mapping

ATtiny85 pin Function ProxPass II connection
Pin 1 (PB5) VDD (from battery/regulator) VLOGIC
Pin 2 (PB3) 4.000 MHz crystal (XTAL1) Y1 pin 2
Pin 3 (PB4) 4.000 MHz crystal (XTAL2) Y1 pin 3
Pin 4 (PB2) Wake / field‑detect input Pin 7 (original)
Pin 5 (PB0) RF payload bit‑bang output Pin 5 (original)
Pin 6 (PB1) Analog gate / detune control Pin 6 (original)
Pin 8 (GND) Ground GND

Code

The hardest part was timing: the ATtiny’s “8 MHz internal clock” is not a crystal but an RC oscillator, only roughly ±10% unless calibrated, while the 4.0 MHz ECS quartz crystal is on the order of ±100 ppm, roughly 1,000× tighter, which is why the external crystal finally keeps the 4 µs bitbang grid stable. So we have to put it in 4mhz external crystal mode after we upload the binary. You will have to externally connect the crystal to talk to it again if you change anything.

The code avoids C‑compiler delay loops (which introduce fatal jitter) and instead uses the ATtiny’s internal hardware timer (OCR0A CTC mode) to physically toggle Pin 5. Loops are unrolled, and timer limits are manipulated on the fly to achieve zero‑overhead phase slips that match the original PIC’s timing down to the microsecond.

I used a logic analyzer to pull before/after bits until they exactly matched the original MPU. Here’s the pins on the bitbang side. Again pin 4 is the passive wake pin that charges off the 125khz field. Pin 5 is the bit bang, pin 6 switches to 139.6khz tuning (suppresses lower sideband and carrier). Pin 7 is a “listen” pin, but I didn’t see real use for it other than perhaps verifying it’s 125khz carrier vice anything than pumps pin 4 to wake.

/*
 * ATtiny85 4 MHz External Crystal ProxPass Hardware Emulator
 *
 * ATtiny85 physical pin mapping:
 * - Pin 5 = payload waveform output
 * - Pin 6 = analog gate / enable / original D6-like control
 * - Pin 7 = wake input, active low
 * - Pin 2 = crystal
 * - Pin 3 = crystal
 *
 * Behavior:
 * - Pin 7 wakes on field-detect low using INT0 low-level wake.
 * - Pin 6 performs measured precharge and low-pause timing.
 * - Pin 5 emits recovered real 6-frame RF payload cadence.
 * - Pin 5 RF payload time: 225.792 ms.
 * - Pin 6 goes high 1 us before pin 5 starts transmitting.
 * - Pin 6 remains high through the pin 5 payload.
 * - After burst, wait POST_BURST_MS, then repeat only if pin 7 is still low.
 * 
 * AFTER YOU UPLOAD to attiny you need to use the 4mhz external clock on proxpass
 * avrdude -c usbtiny -p t85 -B 10 -U lfuse:w:0xFD:m <--slow crystal
 * 
 * Then to go back you have to have a 4mhz clock on the burner and do:
 * avrdude -c usbtiny -p t85 -B 250 -v
 * avrdude -c usbtiny -p t85 -B 250 -U lfuse:w:0xE2:m    <-- back to internal 8mhz clock
 */

#define F_CPU 4000000UL

#include <avr/io.h>
#include <avr/interrupt.h>
#include <avr/pgmspace.h>
#include <avr/sleep.h>
#include <util/delay.h>
#include <stdint.h>

/*
 * Register-level names:
 * - PB0 = physical pin 5
 * - PB1 = physical pin 6
 * - PB2 = physical pin 7
 */
#define TX_PIN            PB0
#define TX_EN_PIN         PB1
#define WAKE_PIN          PB2

#define FRAMES_PER_BURST  6
#define POST_BURST_MS     1000

/*
 * Measured pin 6 sequence:
 * - 3.0625 ms after wake before pin 6 precharge high
 * - pin 6 high for 48.29475 ms
 * - pin 6 low for ~47.54025 ms
 * - pin 6 high 1 us before pin 5 payload starts
 */

#define WAKE_TO_PRECHARGE_CYCLES   0UL
#define PRECHARGE_HIGH_CYCLES      193179UL
#define PRE_TX_LOW_PAUSE_CYCLES    145983UL //#deliberately shortened from #define PRE_TX_LOW_PAUSE_CYCLES    190161UL

#define PIN6_LEAD_CYCLES           4UL
#define PIN6_TAIL_CYCLES           0UL

static volatile uint8_t wake_latched = 0;

/*
 * Raw 44-bit Frame: 3F8A2B1C701
 * Transport hex:    1D5AAA9599599A56A56A5556
 */

static const uint8_t FRAME_CADENCE_PROGMEM[96] PROGMEM = {
    8, 8, 8, 10, 10, 10, 8, 10,
    8, 10, 8, 10, 10, 8, 10, 8,
    10, 8, 10, 8, 10, 8, 10, 8,
    10, 8, 8, 10, 8, 10, 8, 10,
    10, 8, 8, 10, 10, 8, 8, 10,
    8, 10, 8, 10, 10, 8, 8, 10,
    10, 8, 10, 8, 8, 10, 8, 10,
    8, 10, 10, 8, 10, 8, 10, 8,
    8, 10, 8, 10, 8, 10, 10, 8,
    10, 8, 10, 8, 8, 10, 8, 10,
    8, 10, 8, 10, 8, 10, 8, 10,
    8, 10, 8, 10, 8, 10, 10, 8
};

/*
 * SRAM cache reduces per-cell overhead during transmit.
 * 96 bytes is cheap on ATtiny85 and avoids pgm_read_byte during pin 5 emission.
 */
static uint8_t frame_cadence[96];

ISR(INT0_vect) {
    wake_latched = 1;

    // Disable wake interrupt immediately so held-low pin 7 does not keep interrupting.
    GIMSK &= ~(1 << INT0);
    GIFR = (1 << INTF0);
}

static void force_clock_div1(void) {
    CLKPR = (1 << CLKPCE);
    CLKPR = 0;
}

static void cache_frame_cadence_to_sram(void) {
    for (uint8_t i = 0; i < 96; i++) {
        frame_cadence[i] = pgm_read_byte(&FRAME_CADENCE_PROGMEM[i]);
    }
}

static void timer0_init_hw_ctc(void) {
    TCCR0A = (1 << WGM01);     // CTC mode, pin 5 disconnected
    OCR0A = 15;                // 4 us compare interval at 4 MHz
    TCCR0B = (1 << CS00);      // No prescaler
}

/* ---------- WAKE / SLEEP LOGIC ---------- */

static void wake_disarm(void) {
    GIMSK &= ~(1 << INT0);
    GIFR = (1 << INTF0);
}

static void wake_arm_low_level_int0(void) {
    wake_latched = 0;

    // INT0 low-level mode: ISC01 = 0, ISC00 = 0
    MCUCR &= ~((1 << ISC01) | (1 << ISC00));

    GIFR = (1 << INTF0);
    GIMSK |= (1 << INT0);
}

static void wait_for_pin7_high(void) {
    while ((PINB & (1 << WAKE_PIN)) == 0) {
        _delay_ms(1);
    }
}

static void sleep_until_pin7_low(void) {
    // Safe muted idle state before sleep.
    TCCR0A &= ~(1 << COM0A0);
    PORTB |= (1 << TX_PIN);        // physical pin 5 idle high
    PORTB &= ~(1 << TX_EN_PIN);    // physical pin 6 low

    wake_disarm();
    wake_latched = 0;

    // Edge-like behavior: do not arm while pin 7 is already low.
    wait_for_pin7_high();

    cli();

    wake_arm_low_level_int0();
    sleep_enable();

    if ((PINB & (1 << WAKE_PIN)) == 0) {
        wake_latched = 1;
    } else {
        sei();
        sleep_cpu();
    }

    sleep_disable();
    cli();

    if ((PINB & (1 << WAKE_PIN)) == 0) {
        wake_latched = 1;
    }

    wake_disarm();
    cli();
}

/* ---------- PIN 6 ANALOG GATE SEQUENCE ---------- */

static void pin6_precharge_sequence(void) {
    __builtin_avr_delay_cycles(WAKE_TO_PRECHARGE_CYCLES);

    PORTB |= (1 << TX_EN_PIN);     // physical pin 6 high
    __builtin_avr_delay_cycles(PRECHARGE_HIGH_CYCLES);

    PORTB &= ~(1 << TX_EN_PIN);    // physical pin 6 low
    __builtin_avr_delay_cycles(PRE_TX_LOW_PAUSE_CYCLES);
}

static void post_burst_pause(void) {
    PORTB &= ~(1 << TX_EN_PIN);    // physical pin 6 low

    for (uint16_t i = 0; i < POST_BURST_MS; i++) {
        _delay_ms(1);
    }
}

/* ---------- INTERVAL-CODED REAL PIN 5 TRANSMIT CADENCE ---------- */

static inline __attribute__((always_inline)) void wait_match_set_next(uint8_t next_ocr) {
    while ((TIFR & (1 << OCF0A)) == 0);
    TIFR = (1 << OCF0A);
    OCR0A = next_ocr;
}

static inline __attribute__((always_inline)) void emit_cadence8_repeat(void) {
    /*
     * cadence 8 repeat physical intervals:
     * 4,4,4,4,4,4,8 us
     *
     * Because OCR0A written after a match affects the NEXT interval,
     * the correct command sequence is:
     * five 4-us reloads, then request 8 us, then reset to 4 us.
     */
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(31);
    wait_match_set_next(15);
}

static inline __attribute__((always_inline)) void emit_cadence10_repeat(void) {
    /*
     * cadence 10 repeat physical intervals:
     * 4,4,4,4,4,4,4,4,8 us
     *
     * Seven 4-us reloads, then request 8 us, then reset to 4 us.
     */
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(15);
    wait_match_set_next(31);
    wait_match_set_next(15);
}

static inline __attribute__((always_inline)) void emit_cadence8_cell(void) {
    for (uint8_t r = 0; r < 12; r++) {
        emit_cadence8_repeat();
    }
}

static inline __attribute__((always_inline)) void emit_cadence10_cell(void) {
    for (uint8_t r = 0; r < 10; r++) {
        emit_cadence10_repeat();
    }
}

static inline __attribute__((always_inline)) void emit_frame_inline(void) {
    uint8_t *p = frame_cadence;
    uint8_t i = 96;

    do {
        uint8_t cadence = *p++;

        if (cadence == 8) {
            emit_cadence8_cell();
        } else {
            emit_cadence10_cell();
        }
    } while (--i);
}

static void emit_6_frames(void) {
    emit_frame_inline();
    emit_frame_inline();
    emit_frame_inline();
    emit_frame_inline();
    emit_frame_inline();
    emit_frame_inline();
}

static void emit_burst(void) {
    /*
     * Physical pin 6 goes high before physical pin 5 starts.
     * Physical pin 5 starts from idle HIGH.
     */
    PORTB |= (1 << TX_EN_PIN);     // physical pin 6 high
    PORTB |= (1 << TX_PIN);        // physical pin 5 idle high

    __builtin_avr_delay_cycles(PIN6_LEAD_CYCLES);

    /*
     * Timer0 toggles physical pin 5 on every compare.
     * FSK is encoded by changing compare interval:
     * OCR0A = 15 -> 4 us
     * OCR0A = 31 -> 8 us
     */
    TCCR0A = (1 << WGM01);
    TCNT0 = 0;
    TIFR = (1 << OCF0A);
    OCR0A = 15;

    TCCR0A = (1 << WGM01) | (1 << COM0A0);

    emit_6_frames();

    /*
     * End of physical pin 5 burst.
     * Force physical pin 5 idle HIGH, then drop physical pin 6.
     */
    TCCR0A &= ~(1 << COM0A0);
    OCR0A = 15;
    PORTB |= (1 << TX_PIN);        // physical pin 5 idle high

#if PIN6_TAIL_CYCLES > 0
    __builtin_avr_delay_cycles(PIN6_TAIL_CYCLES);
#endif

    PORTB &= ~(1 << TX_EN_PIN);    // physical pin 6 low
}


int main(void) {
    cli();

    force_clock_div1();
    cache_frame_cadence_to_sram();

    DDRB |= (1 << TX_PIN) | (1 << TX_EN_PIN);
    DDRB &= ~(1 << WAKE_PIN);

    PORTB |= (1 << WAKE_PIN);      // physical pin 7 pull-up enabled
    PORTB |= (1 << TX_PIN);        // physical pin 5 idles high
    PORTB &= ~(1 << TX_EN_PIN);    // physical pin 6 normally low

    timer0_init_hw_ctc();

    set_sleep_mode(SLEEP_MODE_PWR_DOWN);

    while (1) {
        sleep_until_pin7_low();

        /*
         * Ignore pin 7 during transmit actions.
         * Pin 7 may change because the circuit can no longer hear the carrier.
         */
        do {
            pin6_precharge_sequence();
            emit_burst();
            post_burst_pause();

            /*
             * After the pause, inspect pin 7:
             * - If pin 7 is still low, repeat.
             * - If pin 7 is high, return to sleep and wait for next field event.
             */
        } while ((PINB & (1 << WAKE_PIN)) == 0);
    }
}

RESULT

Very hacky rebrain done with very blunt soldering iron and solder. I was able to pick up all feeds except the crystals and pin 5 (bit bang pin) off the P1 EEPROM programmer interface.

Supplemental ProxPass II Connectivity & Functional Summary

Educational / interoperability research only.

P1 Programming Header (ICSP‑like)

Pin Net Connection
1 NC
2 GP1 / ICSPCLK U3 pin6, U2 pin3
3 GP0 / ICSPDAT U3 pin7, R16 → U2 pin2
4 MCLR / VPP / WAKE U3 pin4, R12 → U2 pin12
5 VDD (Vlogic) U3 pin1, U2 pin14
6 GND U3 pin8, U2 pin7, board ground

Core ICs

U3 = PIC12CE519 (4 MHz crystal on pins 2‑3)
U2 = 74HC14 (hex Schmitt inverter)

PIC → 74HC14 connections (the logic that matters)

From U3 To U2 Function
pin6 (GP1) pin3 (gate2 input) Primary RF drive control
pin5 (GP2) pin11 (gate5 input) Soft‑drive / damping control
pin7 (GP0) via R16 from U2 pin2 Feedback from sense network
pin4 (MCLR) via R12 from U2 pin12 Wake signal (inverted detector)

74HC14 outputs

U2 pin Drives
pin4 (gate2 out) Q3+Q4 (hard RF switching)
pin10 (gate5 out) R1→antenna clamp node (soft drive)
pin2 (gate1 out) R16→U3 pin7 (sense return)
pin12 (gate6 out) R12→U3 pin4 (wake)

Unused inputs (pins5,9) grounded.

Wake / Field Detector Path

Antenna right node (E2) → R14 (100k) → diode detector (CR4/CR5/CR1) → common node (CR4 pin3, C11, R15, R13) → R13 (10k) → U2 pin13 (gate6 input) → U2 pin12 (inverted) → R12 (10k) → U3 pin4 / P1_4.

Shunt: R15 (10k) + C11 (~84pF) to ground → low‑pass filter.
When field present, U2 pin13 goes high, pin12 low → PIC sees wake (pin4 low for ≥1.19s).
Rearm requires field‑off ≥520 ms.

Transmit / Antenna Drive

Two parallel paths from U2:

  1. Hard switch: U2 pin4 → Q3+Q4 → antenna left node (E1) and tuned capacitors.
  2. Soft drive: U2 pin10 → R1 (330Ω) → C1 (1.456µF) + VR clamp network → antenna right node (E2).

Q4: pin2 ground, pin3 → E1.
Q3: pin2 ground, pin3 → C13/C5/C4/C12 tuning network.

Capacitor clusters (in‑circuit):

  • C12+C4 = 3.877 nF (parallel)
  • C5+C13 = 3.680 nF (parallel)
    C5/C13 other side to ground; C12 other side to E2.

Antenna: rectangle 3.25″×2.3″, 0.008″ wire, 19.2 Ω DC, ~858 µH.
Resonant near 125 kHz receive, detuned to ~139 kHz for transmit upper sideband.

Q1/Q2 Sense / Bias Network

U2 pin1 (gate1 input) connected to Q1 pad3, R6→Q2 pad2.
Q1: pad2=Vlogic, pad1→Q2 pad3.
Q2: pad1→C2 (98nF)+R4 (6.8MΩ), pad2→R6 (470k), pad3→Q1 pad1+R5 (50Ω).
C7 (10.4µF) local decoupling.

U2 pin2 (gate1 output) → R16 (1k) → U3 pin7 (GP0) / P1_3.
This provides a cleaned logic state from the analog bias network.

Key Component Values (Measured in situ)

Ref Value Function
R1 330Ω Soft‑drive damping
R3 10k RF pickup sense
R4 6.8MΩ High‑value bias
R5 50Ω Local damping
R7 1MΩ Top RF region bias
R12,13,15 10k Detector conditioning
R14 100k Antenna pickup feed
R16 1k Feedback from U2 pin2
C1 1.456µF Drive node bypass
C2 98.4nF Q2 bias timing
C7 10.4µF Local reservoir
C11 84pF Detector filter
C12+C4 3.877nF Tuning bank
C5+C13 3.680nF Tuning bank

Useful Links

Original Proxpass Repair: HID ProxPass 拆解 & 修理-PIGOO 痞酷網 - Powered by Discuz!
Original PowerProx FCC FCC ID JQ61351
Proxpass 2 FCC: FCC ID JQ61351B

4 Likes

ADDENDUM: Pin 4 / Pin 7 Correction and Drive-By Behavior

I need to correct the wake/range portion of the original post.

I originally wired the ATtiny wake input to P1:4. That worked at very close range, but it was the wrong signal if the goal is long-range MaxiProx-style behavior. After more logic-analyzer captures on original processor pins 4, 5, 6, and 7, I moved the ATtiny wake wire from P1:4 to P1:3.

The corrected practical wiring is:

P1:3 / original MCU pin 7  →  ATtiny wake input

not:

P1:4 / original MCU pin 4  →  ATtiny wake input

Why pin 4 limited range

Original pin 4 appears to be a conditioned / squelched field-detect path. It is not the best “field is present” signal.

In the captures, pin 4 behaves like a slower threshold signal. It responds cleanly when the reader field is strong enough, but at weaker field strengths it can lag badly or fail to assert quickly. That makes sense if pin 4 is behind an analog conditioning path rather than being the raw sensitive field-present line.

So if the ATtiny uses only pin 4 as wake, it only wakes when the reader field is already very strong. That explains the “works at about a foot but not at range” behavior.

What pin 7 does differently

Original pin 7 is the fast field-present / wake path.

On field entry, pin 7 leads. Across the clean entry captures, the sequence is:

reader field appears
        ↓
original pin 7 drops LOW first
        ↓
about 3 µs later, pin 6 goes HIGH
        ↓
original pin 4 may drop LOW shortly after, if the field is strong enough

In strong-field captures, pin 4 follows pin 7 after roughly 30–40 µs. In weak-field captures, pin 7 still reacts, but pin 4 does not follow quickly. That difference appears to be important.

So the current model is:

pin 7 = fast field-present / entry wake
pin 4 = slower conditioned / squelch / field-strength indicator

The distance / strength branch

The original firmware appears to use the relationship between pin 7 and pin 4 to choose transmit behavior.

Strong field / close-range behavior

When the field is strong, pin 7 drops first and pin 4 follows quickly:

pin 7 ↓
~30–40 µs later pin 4 ↓

The tag then uses the short transmit strategy:

pin 6 HIGH precharge: roughly 43–50 ms
pin 6 LOW pause:      roughly 47.5 ms
pin 5 burst:          about 225.814 ms
burst length:         6 repeated frames

That is the battery-saving / normal-field behavior.

Weak field / long-range behavior

At much weaker coupling, pin 7 still detects the field, but pin 4 does not confirm quickly.

In those captures, the tag changes behavior:

pin 7 ↓
pin 4 does not drop quickly
pin 6 HIGH precharge: roughly 43–45 ms
pin 6 LOW pause:      about 175.7 ms
pin 5 burst:          about 376.343 ms
burst length:         10 repeated frames

That looks like a long-range “wait and shout” mode: the tag waits much longer before transmitting and then sends a longer burst.

Exit / drive-away behavior

Field exit is different from field entry.

On field entry, pin 7 falling is the leading event.

On field exit, pin 4 rising is the leading event.

The exit sequence looks like:

vehicle leaves reader field
        ↓
pin 4 eventually rises HIGH
        ↓
about 3 µs later, pin 6 goes HIGH
        ↓
normal short transmit sequence runs

That produces what I’m calling the exit or drive-away burst. It is not triggered the same way as entry. Entry is led by pin 7; exit is led by pin 4 rising.

The 0.1 second pulse test

The 0.1 s ON / 1.0 s OFF test was useful because it showed that once the original MCU commits to the transmit sequence, the field can disappear and the tag still fires.

In that capture, pin 4 went HIGH before the burst, meaning the external field had already vanished or collapsed from the tag’s perspective. The tag still completed the precharge/pause sequence and transmitted anyway.

So the transmit sequence is not continuously conditional on the field staying present. Once the MCU commits, the burst runs.

What this means for the ATtiny rebrain

With the ATtiny85, I only have one practical input available if I keep the external 4 MHz crystal and leave reset usable for programming. That means I cannot fully reproduce the original two-input pin 7 / pin 4 decision tree without changing the hardware strategy.

The practical repair choice is therefore:

Use P1:3 / original pin 7 as the ATtiny wake input.
Do not use P1:4 / original pin 4 as the only wake input.

For maximum range, the ATtiny should then mimic the weak-field behavior directly:

wake from original pin 7
run the longer pre-transmit pause
send the longer 10-frame burst

For closer factory behavior, the replacement MCU would need both original pin 7 and original pin 4 as inputs:

pin 7 = fast entry wake
pin 4 = strength / squelch / exit trigger

Then firmware could branch:

pin 4 follows quickly  → short 6-frame burst
pin 4 delayed/absent   → long 10-frame burst
pin 4 rises on exit    → exit burst

Corrected takeaway

The correction to the original post is simple:

I originally woke the ATtiny from the wrong sense path.
P1:4 / original pin 4 is too conditioned and range-limiting as the only input.
P1:3 / original pin 7 is the better wake / field-present input.

The RF output path is unchanged:

ATtiny physical pin 5
→ original MCU pin 5 pad
→ 74HC14 U2 pin 3
→ transmit driver path

This addendum only corrects the wake, range, and drive-by behavior.

2 Likes