M7E Hecto — Identical Read Behavior With and Without Antenna Connected

I’m running a SparkFun M7E Hecto (ThingMagic M7E) on a Raspberry Pi via USB-C, communicating over /dev/ttyUSB0 at 115200 baud. The board was previously reading Alien H3 UHF key fobs at approximately 16 inches using an external antenna connected via U.FL pigtail.

I recently swapped the coaxial cable in my antenna chain. Since then I have had zero reads.

I’ve done the following diagnostic work:

  1. All five setup commands return status 0x0000 (OK): GEN2 protocol (0x93), Region NA (0x97), Power 27 dBm (0x92), Antenna Port 1 (0x91), Disable filter (0x9A).
  2. The 0x2F continuous read command fires correctly and returns approximately 34,816 bytes over 3 seconds.
  3. Every tag frame returns status 0x0100 — no tags detected.
  4. Critically: the byte count and frame content are identical whether the external antenna is connected or disconnected.

My question: Does identical read behavior with and without the external antenna connected indicate that the RF stage is not routing signal through the U.FL port — specifically, could this indicate that the R3/R4 routing resistor is on R3 (internal PCB antenna) rather than R4 (U.FL external antenna)?

The R3→R4 mod was performed previously and the board was confirmed working. However given the identical antenna-connected vs antenna-disconnected behavior, I want to confirm whether this symptom is consistent with the resistor being on R3.

You’re on the right track, except the relevant areas are R4 (on-board) and R5 (u.FL)…it’s not reading with the on-board antenna either though, so it doesn’t indicate the default position(R4) but rather that neither the R4 nor R5 paths are working

The resistor in your photo is roughly in the correct spot, but it does look like a cold solder joint that might benefit from de-soldering and re-soldering. The resistor is just a 0-ohm unit, you can replace it with plain wire & solder if need be

The other thing it could be is if your new coax isn’t Reverse Polarity (RP)…is it? It should also be a 50-0hm cable/antenna route. Also check the new cable for continuity (both inner and outer) to ensure it isn’t the problem

Hello, had AI summerize where I’m at:

TODAY'S ACTIONS (June 30):

1. Replaced capacitor (between antenna and wire bridge) with identical value — no change in behavior

2. De-soldered and re-soldered R4 resistor per SparkFun guidance — no change in behavior

3. Verified cable is correct RP spec (Times-7 CA240-PE, RP-TNC connectors)

4. Confirmed DC continuity end-to-end: antenna → cable → pigtail → board

``

---

``

CURRENT BEHAVIOR (after re-solder):

- M7E responds to serial commands at 9600 baud

- All five setup commands (0x93, 0x97, 0x92, 0x91, 0x9A) return status 0x0000 (OK)

- 0x2F continuous read command fires correctly

- Returns 11,000+ bytes per 3-second scan

- **Every frame returns status 0x0100 (no tags found)**

- **Behavior identical whether external antenna is connected or disconnected**

- Internal PCB antenna: cannot read any fobs

- External antenna: zero reads despite correct hardware and connections

``

---

``

DIAGNOSTICS COMPLETED:

✅ Serial port functional (responds to commands)

✅ CRC algorithm correct (ThingMagic nibble CRC verified)

✅ Frame structure correct (FF [LEN] [OPCODE] [data] [CRC])

✅ All setup opcodes accepted by reader

✅ 0x2F read command fires and returns data frames

✅ DC continuity end-to-end (antenna chain to board)

✅ Hardware components correct specs (verified against documentation)

✅ Antenna physically undamaged

✅ Cable RP spec verified

✅ Solder joint re-done (R4)

✅ Capacitor replaced

``

---

``

INTERPRETATION:

The board is responding to serial commands, but RF transmit stage is not generating energy. All frames show 0x0100 status (no tags detected) regardless of antenna connection, indicating RF is either:

1. Not being generated at all

2. Not reaching the antenna paths (per SparkFun: "neither R4 nor R5 paths are working")

``

After hardware corrections (re-solder, capacitor replacement) and verification of correct cable specs and continuity, the issue appears to be at the RF generation stage within the M7E module itself.

``

---

``

REQUEST:

Given that neither R4 nor R5 antenna paths are functional per your previous response, and that all diagnostic steps have been completed:

- Is the M7E module defective (RF stage failure)?

- Is there a firmware setting or initialization command that could restore RF generation? I'd appreciate any direction you can give me. Thanks

Weird that it has Dc continuity but no output…Share an updated photo of the 0-ohm resistor at R5

photo attached

(attachments)

I’d try using some IPA and a nylon brush or cotton swabs to clean the blackened areas (especially clean the u.FL port, it seems to have some debris in there that may be causing issues)

There is one more aspect where I wonder about.

One the continuous reading (0x2f) has been sent you mention

- **Every frame returns status 0x0100 (no tags found)**

That is NOT the error I get nor would expect. In my tests I get 0x400

/**Reader was asked to find tags, but none were detected. */
#define TMR_ERROR_NO_TAGS_FOUND TMR_ERROR_CODE(0x400)

The 0x100 is defined as :

/**Invalid number of arguments */
#define TMR_ERROR_MSG_WRONG_NUMBER_OF_DATA TMR_ERROR_CODE(0x100)

Can you enable creating a trace file in your example and share?

Have you tried the Universal Reader Assistant (URA) on Windows ?

Hi,

Thanks for catching that — you were right, and it turned out to matter a lot.

``

I dug into the actual command bytes being sent for the continuous-read (0x2F) call and found the bug. My "no tags found" claim (0x0100) was wrong because the continuous-read command itself was malformed — one byte in the sub-command blob was incorrect. Comparing against your own SparkFun_UHF_RFID_Reader library (startReading() in SparkFun_UHF_RFID_Reader.cpp), the reference blob is:

``

00 00 01 22 00 00 05 07 22 10 00 1B 03 E8 01 FF

My code had byte 9 wrong — 0x1B instead of 0x10:

00 00 01 22 00 00 05 07 22 1B 00 1B 03 E8 01 FF

``

Once corrected, the reader immediately started returning proper status codes: 0x0 (success) on the 0x2F start command, and 0x400 (genuinely no tag found) on 0x22 sub-frames when no tag was in range — exactly matching your correction. So the "RF stage isn't generating energy" conclusion in my last email was based on a false signal. The protocol layer is now working correctly.

``

Separately, I also found the module was responding at 115200 baud, not the 9600 my script was configured for (it must have reverted to default after the earlier unresponsive/hung episode). Fixed that too.

``

CURRENT STATE (with both bugs fixed):

With the external antenna connected and 12 test tags placed throughout the cabinet — one in each extreme corner, halfway points, and center — only ONE tag reads, consistently, across repeated scans:

``

EPC 30340476F44600D61E81D922 — reads at -63 to -66 dBm across multiple 15-second scans, 100% reproducible.

``

The other 11 tags, spread across the rest of the cabinet, never read at all, in any run.

``

Trace file from a full 15-second scan (all setup commands, start command, raw RX bytes, and parsed tag reads) is attached below.

``

1. Given clean protocol behavior now (correct status codes, no framing errors), does a single consistent -63 to -66 dBm read with everything else silent look like an antenna/RF gain or positioning issue rather than a module problem?

2. Any recommended power/gain settings beyond max (27.00 dBm, which we're already using) worth trying?

``

Re: Universal Reader Assistant — I don't have Windows access (Mac only), so I'm not able to run URA. Happy to provide any additional serial traces you need instead.

``

=== TRACE FILE ===

=== M7E Trace — 2026-07-02 06:38:19 ===

Baud: 115200

TX [Set Gen2 protocol] 0x93: FF02930005517D

RX [Set Gen2 protocol]: FF00930000371A

TX [Set region NA] 0x97: FF0197014BBC

RX [Set region NA]: FF00970000779E

TX [Set power 27.00dBm] 0x92: FF02920A8C4BD5

RX [Set power 27.00dBm]: FF00920000273B

TX [Set antenna port 1] 0x91: FF02910101703B

RX [Set antenna port 1]: FF009100001758

TX [Set filter/config] 0x9A: FF039A010C00A35D

RX [Set filter/config]: FF009A0000A633

TX [Start continuous read]: FF102F00000122000005072210001B03E801FFDD2B

--- Scanning 15 seconds, 12 tags placed at all cabinet extremes + center ---

RX total bytes: 250

RX raw hex: FF042F0000012200006DC3FF0022040084E0FF0022040084E0FF0022040084E0FF2822000010001B01FF0101C0110E2612000000790034050000000080300030340476F44600D61E81D9227C7D11ABFF0022040084E0FF2822000010001B01FF0101BF110E241E000002970004050000000080300030340476F44600D61E81D9227C7DBE47FF0022040084E0FF0022040084E0FF0022040084E0FF0022040084E0FF0022040084E0FF0022040084E0FF0022040084E0FF0022040084E0FF2822000010001B01FF0101C0110E2612000002490022050000000080300030340476F44600D61E81D9227C7D7D8CFF0022040084E0FF0022040084E0

Parsed tag reads: 3

EPC 30340476F44600D61E81D922: -64 dBm

EPC 30340476F44600D61E81D922: -65 dBm

EPC 30340476F44600D61E81D922: -64 dBm

``

Thanks again for the correction — it pointed us straight at the real bug.

First of all great to hear the progress. Indeed the read options that were enabled with setting 0x1B instead of 0x10 do not make sense:

Select options: 0x1b
TMR_SR_GEN2_SINGULATION_OPTION_FLAG_METADATA
TMR_SR_GEN2_SINGULATION_OPTION_INVERSE_SELECT_BIT
TMR_SR_GEN2_SINGULATION_OPTION_SELECT_ON_EPC
TMR_SR_GEN2_SINGULATION_OPTION_SELECT_ON_TID
Start location (bits) 1770472, (bytes) 221309
Maskbit length (bits) 1, (bytes) 0

The M7E will read the first TAG that responds strongest. Hence you get the same EPC. To help on that issue there are GEN2 option to instruct the TAG to either not to respond (if already responded during the current read cycle) and/or instruct the reader to perform a different inventory selection.

These GEN2 options are part of the larger Mercury API, (which can be downloaded and used on a Raspberry PI) but not implemented by Sparkfun in their library. I have spend a lot of time on the Sparkfun library have added many calls/options. You can find that on ThingMagic/Arduino_lib_special at master · paulvha/ThingMagic · GitHub . This also includes the calls for GEN2 options like setGen2Session(), setGen2RFmode(), setGen2RFTarget(), setGen2Encoding(), setGen2Q()

We’ve now tested two separate M7E boards with 100% of the surrounding hardware swapped (Pi 4, power supply, cabling, antenna), and both units follow the exact same degradation path down to a total brick. Power delivery method (powered USB hub vs. direct-to-Pi USB) makes no difference — both show identical behavior.

Setup: Times-7 A5020 antenna via U.FL pigtail → RP-TNC/SMA, with the standard R3→R4 resistor mod applied for the external antenna path. We attempted to use python-mercuryapi first, but the upstream MercuryAPI SDK download link is dead, so we’re driving the module with a custom pyserial script instead — flagging this in case it points to us missing something the official SDK would have handled (init sequence, firmware check, etc.).

Phase 1: RF Broken, Protocol Faults (Serial Active)

  • Phantom Tag Reads: Setup commands (Get Version, Set Region, Set Power) work fine. But continuous inventory mode pumps out phantom tag reads (Opcode 0x22) for non-existent EPCs, even with zero tags in the room. Raw UART captures confirm these frames originate from the M7E itself.
  • Fails Real Reads: Single-shot reads stop the phantoms, but holding a known-good tag 1 inch from the antenna yields 0 tags (0x0100).
  • Persistent Faults: Boot App and temperature commands consistently throw FAULT 0x0101. Buffer clears often throw FAULT 0x0405 / 0x0400.

Phase 2: Total Serial Silence (Bricked State)

  • Eventually, the M7E stops sending bytes back entirely.
  • 0 bytes returned on Get Version across all baud rates (9600 to 921600). Unresponsive to bootloader wake-up sequences (0x00 0xFF).
  • Hardware Isolated: CH340 enumerates fine on USB, but switching to PTH mode and wiring 3V3/GND/TXO/RXI directly to the Pi’s hardware UART pins yields the same total silence. Tested on both Pi OS and macOS. Power delivery (hub vs. direct) ruled out as a factor.

Questions

Since replacing the M7E board and all surrounding hardware reproduces the exact same death cycle, this feels less like a one-off bad board and more like something systemic (RF synth lockup, bootloader memory corruption, or extreme sensitivity during inventory sweeps).

  1. Has anyone seen FAULT 0x0101 cascade into total UART silence on ThingMagic/JADAK modules?
  2. Is there a known firmware recovery/un-brick routine for the M7E over UART, or is the chip hard-bricked once it hits this state?
  3. Could standard RF inventory calls trigger internal brownouts/NVM corruption, and could the R3→R4 antenna mod be a contributing factor to RF front-end stress?

hi,

To bad to here it has not worked yet. Couple of questions/remarks:

Can you share the serial script you use? I maybe have a look of able to try as well.

/**Reader not fully initialized and hasn’t yet turned on its radio. Have you set region?*/
#define TMR_ERROR_AFE_NOT_ON TMR_ERROR_CODE(0x405)

Has anyone seen FAULT 0x0101 cascade into total UART silence on ThingMagic/JADAK modules?

When using a high dBM (> 5) I would suggest a strong external power supply. The M7E is very sensitive to that.

I have had a couple of times that my M6E stopped responding. The TX-line from the reader was high level noise. It was caused by power supply.

Is there a known firmware recovery/un-brick routine for the M7E over UART, or is the chip hard-bricked once it hits this state?

Not sure what dBM you use, but when the M7E gets hot (+85 C), it will stop working. You have to wait until it cools down. A cooling block might be needed

In the URA you can upload new firmware to the M7E. Maybe an help to recover. I am sure you will be able to find somewhere a PC on Windows.

Could standard RF inventory calls trigger internal brownouts/NVM corruption, and could the R3→R4 antenna mod be a contributing factor to RF front-end stress?

Not that I have seen.

regards,

Paul

I have dropped you a personal message as well about the mercuryAPI

Paul — thanks, this has been really helpful. Update on where things landed:

**Loopback, finally done.** Earlier “no echo” results were void — no physical jumper was ever actually in place. This time I bridged TX to RX directly at the module’s header pins with a jumper wire, connected a CP2102 USB-to-TTL adapter to the same header, and confirmed clean echo in a serial terminal — characters appeared with the jumper in, vanished with it out. So the full host/adapter/cable path is confirmed good. Whatever’s silent, it’s not that.

**Frame/CRC validated independently.** I stopped trusting my own hand-rolled implementation and pulled the CRC algorithm straight from SparkFun’s own library, cross-checked opcode 0x03 against four different ThingMagic header files. The GetVersion frame I’m sending is `FF 00 03 1D 0C` — that’s correct, not malformed. Swept it across all six standard baud rates (9600–230400), both TX/RX orientations, after a genuine 30+ second cold power cycle (not a quick unplug-replug — learned that distinction matters, since residual capacitor charge can leave a module in the same wedged state). No response at any combination.

**TX line:** multimeter on the module’s TX pin reads 3.47V DC, idling high. So the driver itself is alive and powered — this isn’t a fully dead/unpowered peripheral.

**Boot chatter:** ran a passive listener during a live power cycle across all six baud rates, capturing zero bytes at any rate. No unsolicited boot-time output on this UART at all.

**On your specific points:**

- Region/AFE_NOT_ON (0x405): region was previously set and read back correctly in earlier sessions before this went silent, and this is bare-metal UART silence — no response to any command, not a partial init state.

- Power supply / TX noise: appreciate this — we were running off a powered USB hub (5V/3A dedicated), later moved to direct Pi USB power for diagnostics, both ruled out for rail sag via `vcgencmd throttled` returning clean. TX line reads a steady idle high, not noisy.

- Thermal (85°C): reads were door-triggered, not continuous, and failure occurred cold — ruled out via a confirmed cold-start silence.

- URA firmware recovery: haven’t tried this yet — no Windows PC in the loop currently, but this is the next thing worth chasing if there’s no other explanation.

- Brownout/NVM corruption from RF calls or the R3→R4 mod: no direct evidence either way on our end. The mod’s routing was physically re-verified against the silkscreen and is placed correctly on both boards.

At this point everything upstream of the module — host, adapter, cable, framing, baud — has real evidence ruling it out. The module itself is the most likely remaining explanation, but I’m not calling it dead without a documented failure mode to match it to. If the URA un-brick route is real and works over the same UART header, that’s probably the next thing to try.

Here’s the passive boot-chatter sweep script, happy to share the loopback/GetVersion scripts too if useful:

```python

#!/usr/bin/env python3

“”"

Boot chatter sweep — listens for any serial output during module power-up,

one baud rate at a time.

“”"

import serial

import time

PORT = “/dev/cu.usbserial-0001” # adjust to your adapter

BAUD_RATES = [9600, 19200, 38400, 57600, 115200, 230400]

LISTEN_SECONDS = 8

def test_baud(baud):

print(f"\\n{'='\*50}\\nBAUD RATE: {baud}\\n{'='\*50}")

input("Module should be UNPLUGGED. Press Enter, then plug in power within 2 seconds...")

try:

    ser = serial.Serial(PORT, baud, timeout=0.2)

except Exception as e:

    print(f"FAILED to open port: {e}")

    return

print(f"Listening {LISTEN_SECONDS}s at {baud} baud...")

ser.reset_input_buffer()

start = time.time()

captured = bytearray()

while time.time() - start < LISTEN_SECONDS:

    chunk = ser.read(256)

    if chunk:

        captured.extend(chunk)

        print(f"  \[{time.time()-start:.2f}s\] {chunk.hex().upper()}")

ser.close()

print(f"Captured {len(captured)} bytes" if captured else "Nothing captured.")

input("Unplug power, press Enter for next rate...")

def main():

for baud in BAUD_RATES:

    test_baud(baud)

if _name_ == “_main_”:

main()

```

Thanks

hard to say what is wrong, maybe update the firmware.

Please share the Getversion script.

Just a remark about baudrate. Unlike the M6E, which always start in 115200 baudrate after reset/restart, the M7E seems to restart in the baudrate that was last set:

source: M7E user manual 5.1.4.2 Supported Baud Rates

Upon initial power up, the default baud rate of 115200 will be used. If that baud rate is changed and saved in the application mode, the new saved baud rate will be used the next time the module is powered up. (Check the firmware release notes to confirm that saving of settings is supported.)

In the boot chatter, changing the baudrate, might result in missing out the one time automatic sending of device information.

Have you tried mercuryAPI examples already ?