Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion firmware/application/src/app_cmd.c
Original file line number Diff line number Diff line change
Expand Up @@ -627,7 +627,7 @@ static data_frame_tx_t *cmd_processor_mf1_manipulate_value_block(uint16_t cmd, u
}

static data_frame_tx_t *cmd_processor_em410x_scan(uint16_t cmd, uint16_t status, uint16_t length, uint8_t *data) {
uint8_t card_buffer[7] = {0x00};
uint8_t card_buffer[15] = {0x00};
status = scan_em410x(card_buffer);
if (status != STATUS_LF_TAG_OK) {
return data_frame_make(cmd, status, 0, NULL);
Expand Down
2 changes: 1 addition & 1 deletion firmware/application/src/rfid/nfctag/lf/lf_tag_em.c
Original file line number Diff line number Diff line change
Expand Up @@ -268,7 +268,7 @@ bool lf_tag_data_factory(uint8_t slot, tag_specific_type_t tag_type, uint8_t *ta
*/
bool lf_tag_em410x_data_factory(uint8_t slot, tag_specific_type_t tag_type) {
// default id, must to align(4), more word...
uint8_t tag_id[5] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88};
uint8_t tag_id[13] = {0xDE, 0xAD, 0xBE, 0xEF, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
return lf_tag_data_factory(slot, tag_type, tag_id, sizeof(tag_id));
}

Expand Down
2 changes: 1 addition & 1 deletion firmware/application/src/rfid/nfctag/lf/lf_tag_em.h
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
#include "rfid_main.h"
#include "tag_emulation.h"

#define LF_EM410X_TAG_ID_SIZE 5
#define LF_EM410X_TAG_ID_SIZE 13
#define LF_HIDPROX_TAG_ID_SIZE 13
#define LF_VIKING_TAG_ID_SIZE 4

Expand Down
77 changes: 53 additions & 24 deletions firmware/application/src/rfid/nfctag/lf/protocols/em410x.c
Original file line number Diff line number Diff line change
Expand Up @@ -15,11 +15,15 @@
#define EM_BITS_PER_ROW_COUNT (EM_COLUMN_COUNT + 1)

#define EM_RAW_SIZE (64)
#define EM_DATA_SIZE (5)
#define EM_DECODED_BASE_DATA_SIZE (5)
#define EM_DECODED_EPILOGUE_SIZE (8)
#define EM_DATA_SIZE (EM_DECODED_BASE_DATA_SIZE + EM_DECODED_EPILOGUE_SIZE)
#define EM_ROW_COUNT (10)
#define EM_COLUMN_COUNT (4)
#define EM_HEADER (0x1ff) // 9 bits of 1

#define EM_ENCODED_DATA_HEADER (0xFF80000000000000ULL)

#define EM_T55XX_BLOCK_COUNT (3)

#define EM_READ_TIME1_BASE (0x40)
Expand All @@ -33,7 +37,7 @@
#include "nrf_log_default_backends.h"
NRF_LOG_MODULE_REGISTER();

static nrf_pwm_values_wave_form_t m_em410x_pwm_seq_vals[EM_RAW_SIZE] = {};
static nrf_pwm_values_wave_form_t m_em410x_pwm_seq_vals[EM_RAW_SIZE + EM_RAW_SIZE] = {};

nrf_pwm_sequence_t const m_em410x_pwm_seq = {
.values.p_wave_form = m_em410x_pwm_seq_vals,
Expand All @@ -53,7 +57,8 @@ size_t em410x_protocols_size = ARRAY_SIZE(em410x_protocols);
typedef struct {
uint8_t data[EM_DATA_SIZE];
uint64_t raw;
uint8_t raw_length;
uint64_t epilogue;
uint8_t length;
manchester *modem;
} em410x_codec;

Expand All @@ -80,6 +85,17 @@ uint64_t em410x_raw_data(uint8_t *uid) {
return raw;
}

uint64_t em410x_raw_epilogue(uint8_t *uid) {
uint64_t raw = 0;

for (int i = 0; i < EM_DECODED_EPILOGUE_SIZE; i++) {
raw <<= 8;
raw |= uid[EM_DECODED_BASE_DATA_SIZE + i];
}

return raw;
}

bool em410x_get_time(uint16_t divisor, uint8_t interval, uint8_t base) {
return interval >= (base - EM_READ_JITTER_TIME_BASE) / divisor &&
interval <= (base + EM_READ_JITTER_TIME_BASE) / divisor;
Expand Down Expand Up @@ -144,27 +160,24 @@ uint8_t *em410x_get_data(em410x_codec *d) { return d->data; };
void em410x_decoder_start(em410x_codec *d, uint8_t format) {
memset(d->data, 0, EM_DATA_SIZE);
d->raw = 0;
d->raw_length = 0;
d->length = 0;
d->epilogue = 0;
manchester_reset(d->modem);
};

bool em410x_decode_feed(em410x_codec *d, bool bit) {
d->raw <<= 1;
d->raw_length++;
if (bit) {
d->raw |= 0x01;
}
if (d->raw_length < EM_RAW_SIZE) {
bool carry_bit = (d->epilogue >> 63) & 0b1;

d->length++;
d->raw = (d->raw << 1) | carry_bit;
d->epilogue = (d->epilogue << 1) | bit;

if (d->length < EM_RAW_SIZE + EM_RAW_SIZE) {
return false;
}

// check header
uint8_t v = (d->raw >> (EM_RAW_SIZE - 8)) & 0xff;
if (v != 0xff) {
return false;
}
v = (d->raw >> (EM_RAW_SIZE - 9)) & 0xff;
if (v != 0xff) {
if ((d->raw & EM_ENCODED_DATA_HEADER) != EM_ENCODED_DATA_HEADER) {
return false;
}

Expand Down Expand Up @@ -192,6 +205,15 @@ bool em410x_decode_feed(em410x_codec *d, bool bit) {
d->data[i >> 1] = data << 4;
}
}

for (int i = 0; i < EM_DECODED_EPILOGUE_SIZE; i++) {
if ((d->epilogue & EM_ENCODED_DATA_HEADER) == EM_ENCODED_DATA_HEADER) {
d->data[EM_DECODED_BASE_DATA_SIZE + i] = 0;
} else {
d->data[EM_DECODED_BASE_DATA_SIZE + i] = (d->epilogue >> ((EM_DECODED_EPILOGUE_SIZE - 1 - i) * 8)) & 0XFF;
}
}

return pc == 0x00; // column parity
}

Expand All @@ -201,7 +223,8 @@ bool em410x_decoder_feed(em410x_codec *d, uint16_t interval) {
manchester_feed(d->modem, (uint8_t)interval, bits, &bitlen);
if (bitlen == -1) {
d->raw = 0;
d->raw_length = 0;
d->length = 0;
d->epilogue = 0;
return false;
}
for (int i = 0; i < bitlen; i++) {
Expand All @@ -213,15 +236,21 @@ bool em410x_decoder_feed(em410x_codec *d, uint16_t interval) {
};

const nrf_pwm_sequence_t *em410x_modulator(em410x_codec *d, uint8_t *buf) {
uint64_t lo = em410x_raw_data(buf);
for (int i = 0; i < EM_RAW_SIZE; i++) {
uint16_t msb = 0x00;
if (IS_SET(lo, EM_RAW_SIZE - i - 1)) {
msb = (1 << 15);
uint64_t data[] = {em410x_raw_data(buf), em410x_raw_epilogue(buf)};
uint8_t output_index = 0;

for (int d = 0; d < 2; d++) {
for (int i = 0; i < EM_RAW_SIZE; i++) {
uint16_t msb = 0x00;
if (IS_SET(data[d], EM_RAW_SIZE - i - 1)) {
msb = (1 << 15);
}
m_em410x_pwm_seq_vals[output_index].channel_0 = msb | 32;
m_em410x_pwm_seq_vals[output_index].counter_top = 64;
output_index++;
}
m_em410x_pwm_seq_vals[i].channel_0 = msb | 32;
m_em410x_pwm_seq_vals[i].counter_top = 64;
}

return &m_em410x_pwm_seq;
};

Expand Down
4 changes: 2 additions & 2 deletions software/script/chameleon_cli_unit.py
Original file line number Diff line number Diff line change
Expand Up @@ -405,8 +405,8 @@ def add_card_arg(parser: ArgumentParserNoExit, required=False):
def before_exec(self, args: argparse.Namespace):
if not super().before_exec(args):
return False
if args.id is None or not re.match(r"^[a-fA-F0-9]{10}$", args.id):
raise ArgsParserError("ID must include 10 HEX symbols")
if args.id is None or not re.match(r"^[a-fA-F0-9]{26}$", args.id):
raise ArgsParserError("ID must include 26 HEX symbols")
Copy link
Contributor

@Foxushka Foxushka Oct 24, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't like idea that user is required to use 13 bytes everywhere, when regular 5 byte EM410X are WAY more common (and it will confuse users). Maybe better to add another tag type?

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not familiar enough with the codebase to create a new tag type without duplicating a significant amount of code (and I don't consider code duplication to be a solution). That said, other collaborators are welcome to use this PR as a starting point or reference.

return True

def args_parser(self) -> ArgumentParserNoExit:
Expand Down
8 changes: 4 additions & 4 deletions software/script/chameleon_cmd.py
Original file line number Diff line number Diff line change
Expand Up @@ -434,7 +434,7 @@ def em410x_scan(self):
"""
resp = self.device.send_cmd_sync(Command.EM410X_SCAN)
if resp.status == Status.LF_TAG_OK:
resp.parsed = struct.unpack('!h5s', resp.data) # tag type + uid
resp.parsed = struct.unpack('!h13s', resp.data) # tag type + uid
return resp

@expect_response(Status.LF_TAG_OK)
Expand Down Expand Up @@ -599,9 +599,9 @@ def em410x_set_emu_id(self, id: bytes):
:param id_bytes: byte of the card number
:return:
"""
if len(id) != 5:
raise ValueError("The id bytes length must equal 5")
data = struct.pack('5s', id)
if len(id) != 13:
raise ValueError("The id bytes length must equal 13")
data = struct.pack('13s', id)
return self.device.send_cmd_sync(Command.EM410X_SET_EMU_ID, data)

@expect_response(Status.SUCCESS)
Expand Down