Fix Fiat V1

Add CRC check from ARF, remove retx path, add confirmed button mapping
This commit is contained in:
zero-mega
2026-04-21 12:13:17 +02:00
parent 47cbede84b
commit e24f2b3bee
+233 -45
View File
@@ -1,6 +1,8 @@
#include "fiat_v1.h"
#include <string.h>
#define TAG "FiatProtocolV1"
// Magneti Marelli BSI keyfob protocol (PCF7946)
// Found on: Fiat Panda, Grande Punto (and possibly other Fiat/Lancia/Alfa ~2003-2012)
//
@@ -10,7 +12,7 @@
// Type B (e.g. Grande Punto): te_short ~100us, te_long ~200us
// TE is auto-detected from preamble pulse averaging.
//
// Frame layout (103-104 bits = 13 bytes):
// Frame layout (104 bits = 13 bytes):
// Bytes 0-1: 0xFFFF/0xFFFC preamble residue
// Bytes 2-5: Serial (32 bits)
// Byte 6: [Button:4 | Epoch:4]
@@ -19,11 +21,11 @@
//
// Original implementation by @lupettohf
#define FIAT_MARELLI_PREAMBLE_PULSE_MIN 50
#define FIAT_MARELLI_PREAMBLE_PULSE_MAX 350
#define FIAT_MARELLI_PREAMBLE_MIN 80
#define FIAT_MARELLI_PREAMBLE_PULSE_MIN 35
#define FIAT_MARELLI_PREAMBLE_PULSE_MAX 450
#define FIAT_MARELLI_PREAMBLE_MIN 48
#define FIAT_MARELLI_MAX_DATA_BITS 104
#define FIAT_MARELLI_MIN_DATA_BITS 80
#define FIAT_MARELLI_MIN_DATA_BITS 104
#define FIAT_MARELLI_GAP_TE_MULT 4
#define FIAT_MARELLI_SYNC_TE_MIN_MULT 4
#define FIAT_MARELLI_SYNC_TE_MAX_MULT 12
@@ -32,6 +34,17 @@
#define FIAT_MARELLI_RETX_SYNC_MAX 2800
#define FIAT_MARELLI_TE_TYPE_AB_BOUNDARY 180
static uint8_t fiat_marelli_crc8(const uint8_t* data, size_t len) {
uint8_t crc = 0x03;
for(size_t i = 0; i < len; i++) {
crc ^= data[i];
for(uint8_t b = 0; b < 8; b++) {
crc = (crc & 0x80) ? ((crc << 1) ^ 0x01) : (crc << 1);
}
}
return crc;
}
static const SubGhzBlockConst subghz_protocol_fiat_marelli_const = {
.te_short = 260,
.te_long = 520,
@@ -44,7 +57,6 @@ typedef enum {
FiatMarelliDecoderStepPreamble = 1,
FiatMarelliDecoderStepSync = 2,
FiatMarelliDecoderStepData = 3,
FiatMarelliDecoderStepRetxSync = 4,
} FiatMarelliDecoderStep;
struct SubGhzProtocolDecoderFiatMarelli {
@@ -66,6 +78,121 @@ struct SubGhzProtocolDecoderFiatMarelli {
uint32_t te_detected;
};
static const char* fiat_marelli_state_name(uint8_t state) {
switch(state) {
case FiatMarelliDecoderStepReset:
return "Reset";
case FiatMarelliDecoderStepPreamble:
return "Preamble";
case FiatMarelliDecoderStepSync:
return "Sync";
case FiatMarelliDecoderStepData:
return "Data";
default:
return "Unknown";
}
}
static void fiat_marelli_set_state(
SubGhzProtocolDecoderFiatMarelli* instance,
FiatMarelliDecoderStep new_state,
const char* reason) {
bool noisy_transition =
(instance->decoder_state == FiatMarelliDecoderStepReset &&
new_state == FiatMarelliDecoderStepPreamble) ||
(instance->decoder_state == FiatMarelliDecoderStepPreamble &&
new_state == FiatMarelliDecoderStepReset);
if(instance->decoder_state != new_state && !noisy_transition) {
FURI_LOG_D(
TAG,
"State %s -> %s (%s)",
fiat_marelli_state_name(instance->decoder_state),
fiat_marelli_state_name(new_state),
reason);
}
instance->decoder_state = new_state;
}
static bool fiat_marelli_get_raw_bit(const uint8_t* raw, uint8_t bit_index) {
return (raw[bit_index / 8] >> (7 - (bit_index % 8))) & 1U;
}
static void fiat_marelli_set_raw_bit(uint8_t* raw, uint8_t bit_index, bool value) {
uint8_t byte_idx = bit_index / 8;
uint8_t mask = 1U << (7 - (bit_index % 8));
if(value) {
raw[byte_idx] |= mask;
} else {
raw[byte_idx] &= (uint8_t)(~mask);
}
}
static void fiat_marelli_rebuild_data_words_from_raw(SubGhzProtocolDecoderFiatMarelli* instance) {
instance->generic.data = 0;
instance->extra_data = 0;
for(uint8_t i = 0; i < 64; i++) {
instance->generic.data =
(instance->generic.data << 1) | (fiat_marelli_get_raw_bit(instance->raw_data, i) ? 1U : 0U);
}
for(uint8_t i = 64; i < FIAT_MARELLI_MAX_DATA_BITS; i++) {
instance->extra_data =
(instance->extra_data << 1) | (fiat_marelli_get_raw_bit(instance->raw_data, i) ? 1U : 0U);
}
instance->bit_count = FIAT_MARELLI_MAX_DATA_BITS;
instance->generic.data_count_bit = FIAT_MARELLI_MAX_DATA_BITS;
}
static bool fiat_marelli_try_recover_tail_bits(SubGhzProtocolDecoderFiatMarelli* instance) {
if(instance->bit_count >= FIAT_MARELLI_MAX_DATA_BITS) {
return true;
}
if(instance->bit_count < (FIAT_MARELLI_MAX_DATA_BITS - 2)) {
return false;
}
uint8_t missing_bits = FIAT_MARELLI_MAX_DATA_BITS - instance->bit_count;
uint8_t variants = 1U << missing_bits;
uint8_t match_count = 0;
uint8_t matched_variant = 0;
for(uint8_t variant = 0; variant < variants; variant++) {
uint8_t trial[13];
memcpy(trial, instance->raw_data, sizeof(trial));
for(uint8_t i = 0; i < missing_bits; i++) {
bool bit = ((variant >> (missing_bits - 1 - i)) & 1U) != 0;
fiat_marelli_set_raw_bit(trial, instance->bit_count + i, bit);
}
uint8_t calc = fiat_marelli_crc8(trial, 12);
if(calc == trial[12]) {
match_count++;
matched_variant = variant;
}
}
if(match_count != 1) {
return false;
}
for(uint8_t i = 0; i < missing_bits; i++) {
bool bit = ((matched_variant >> (missing_bits - 1 - i)) & 1U) != 0;
fiat_marelli_set_raw_bit(instance->raw_data, instance->bit_count + i, bit);
}
FURI_LOG_D(
TAG,
"Recovered %u tail bit(s) via CRC (variant=%u)",
(unsigned)missing_bits,
(unsigned)matched_variant);
fiat_marelli_rebuild_data_words_from_raw(instance);
return true;
}
static void fiat_marelli_prepare_data(SubGhzProtocolDecoderFiatMarelli* instance) {
instance->bit_count = 0;
instance->extra_data = 0;
@@ -77,7 +204,7 @@ static void fiat_marelli_prepare_data(SubGhzProtocolDecoderFiatMarelli* instance
ManchesterEventReset,
&instance->manchester_state,
NULL);
instance->decoder_state = FiatMarelliDecoderStepData;
fiat_marelli_set_state(instance, FiatMarelliDecoderStepData, "sync accepted");
}
static void fiat_marelli_rebuild_raw_data(SubGhzProtocolDecoderFiatMarelli* instance) {
@@ -112,8 +239,10 @@ static void fiat_marelli_rebuild_raw_data(SubGhzProtocolDecoderFiatMarelli* inst
static const char* fiat_marelli_button_name(uint8_t btn) {
switch(btn) {
case 0x8:
case 0x7:
return "Lock";
case 0x0:
case 0xB:
return "Unlock";
case 0xD:
@@ -170,7 +299,7 @@ void subghz_protocol_decoder_fiat_marelli_free(void* context) {
void subghz_protocol_decoder_fiat_marelli_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = context;
instance->decoder_state = FiatMarelliDecoderStepReset;
fiat_marelli_set_state(instance, FiatMarelliDecoderStepReset, "decoder reset");
instance->preamble_count = 0;
instance->bit_count = 0;
instance->extra_data = 0;
@@ -200,15 +329,12 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
if(level) {
if(duration >= FIAT_MARELLI_PREAMBLE_PULSE_MIN &&
duration <= FIAT_MARELLI_PREAMBLE_PULSE_MAX) {
instance->decoder_state = FiatMarelliDecoderStepPreamble;
fiat_marelli_set_state(instance, FiatMarelliDecoderStepPreamble, "preamble start");
instance->preamble_count = 1;
instance->te_sum = duration;
instance->te_count = 1;
instance->te_last = duration;
}
} else if(duration > FIAT_MARELLI_RETX_GAP_MIN && instance->te_detected) {
instance->decoder_state = FiatMarelliDecoderStepRetxSync;
instance->te_last = duration;
}
break;
@@ -225,16 +351,30 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
uint32_t gap_threshold = instance->te_detected * FIAT_MARELLI_GAP_TE_MULT;
if(duration > gap_threshold) {
instance->decoder_state = FiatMarelliDecoderStepSync;
FURI_LOG_D(
TAG,
"Preamble OK: pulses=%u te=%lu gap=%lu",
(unsigned)instance->preamble_count,
(unsigned long)instance->te_detected,
(unsigned long)duration);
fiat_marelli_set_state(instance, FiatMarelliDecoderStepSync, "gap detected");
instance->te_last = duration;
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
FURI_LOG_D(
TAG,
"Reject: gap too short after preamble (pulses=%u te=%lu gap=%lu need>%lu)",
(unsigned)instance->preamble_count,
(unsigned long)instance->te_detected,
(unsigned long)duration,
(unsigned long)gap_threshold);
fiat_marelli_set_state(instance, FiatMarelliDecoderStepReset, "gap too short");
}
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
fiat_marelli_set_state(
instance, FiatMarelliDecoderStepReset, "preamble too short");
}
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
fiat_marelli_set_state(instance, FiatMarelliDecoderStepReset, "invalid preamble pulse");
}
break;
@@ -244,23 +384,25 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
if(level && duration >= sync_min && duration <= sync_max) {
fiat_marelli_prepare_data(instance);
FURI_LOG_D(
TAG,
"Sync OK: duration=%lu expected=[%lu..%lu]",
(unsigned long)duration,
(unsigned long)sync_min,
(unsigned long)sync_max);
instance->te_last = duration;
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
FURI_LOG_D(
TAG,
"Reject: sync timing mismatch (duration=%lu expected=[%lu..%lu])",
(unsigned long)duration,
(unsigned long)sync_min,
(unsigned long)sync_max);
fiat_marelli_set_state(instance, FiatMarelliDecoderStepReset, "sync timing mismatch");
}
break;
}
case FiatMarelliDecoderStepRetxSync:
if(level && duration >= FIAT_MARELLI_RETX_SYNC_MIN &&
duration <= FIAT_MARELLI_RETX_SYNC_MAX) {
fiat_marelli_prepare_data(instance);
instance->te_last = duration;
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
}
break;
case FiatMarelliDecoderStepData: {
ManchesterEvent event = ManchesterEventReset;
bool frame_complete = false;
@@ -303,27 +445,65 @@ void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32
frame_complete = true;
}
}
} else if(instance->bit_count >= FIAT_MARELLI_MIN_DATA_BITS) {
} else if(instance->bit_count >= (FIAT_MARELLI_MAX_DATA_BITS - 2)) {
frame_complete = true;
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
FURI_LOG_D(
TAG,
"Reject: invalid Manchester timing (bit=%u duration=%lu te=%lu)",
(unsigned)instance->bit_count,
(unsigned long)duration,
(unsigned long)te_short);
fiat_marelli_set_state(
instance, FiatMarelliDecoderStepReset, "invalid manchester timing");
}
if(frame_complete) {
instance->generic.data_count_bit = instance->bit_count;
instance->generic.serial = ((uint32_t)instance->raw_data[2] << 24) |
((uint32_t)instance->raw_data[3] << 16) |
((uint32_t)instance->raw_data[4] << 8) |
((uint32_t)instance->raw_data[5]);
instance->generic.btn = (instance->raw_data[6] >> 4) & 0x0F;
instance->generic.cnt = (instance->raw_data[7] >> 3) & 0x1F;
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
if(!fiat_marelli_try_recover_tail_bits(instance)) {
FURI_LOG_D(
TAG,
"Reject: incomplete frame (bits=%u, unable to recover to %u bits)",
(unsigned)instance->bit_count,
(unsigned)FIAT_MARELLI_MAX_DATA_BITS);
fiat_marelli_set_state(instance, FiatMarelliDecoderStepReset, "frame complete");
instance->te_last = duration;
break;
}
instance->decoder_state = FiatMarelliDecoderStepReset;
bool crc_ok = true;
if(instance->bit_count >= FIAT_MARELLI_MAX_DATA_BITS) {
uint8_t calc = fiat_marelli_crc8(instance->raw_data, 12);
crc_ok = (calc == instance->raw_data[12]);
if(!crc_ok) {
FURI_LOG_D(
TAG,
"Reject: CRC fail (calc=%02X rx=%02X bits=%u)",
calc,
instance->raw_data[12],
(unsigned)instance->bit_count);
}
}
if(crc_ok) {
FURI_LOG_D(TAG, "Frame accepted (%u bits, CRC OK)", instance->bit_count);
instance->generic.serial = ((uint32_t)instance->raw_data[2] << 24) |
((uint32_t)instance->raw_data[3] << 16) |
((uint32_t)instance->raw_data[4] << 8) |
((uint32_t)instance->raw_data[5]);
instance->generic.btn = (instance->raw_data[6] >> 4) & 0x0F;
instance->generic.cnt = (instance->raw_data[7] >> 3) & 0x1F;
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
} else {
FURI_LOG_D(TAG, "Frame rejected (%u bits, CRC FAIL)", instance->bit_count);
}
fiat_marelli_set_state(instance, FiatMarelliDecoderStepReset, "frame complete");
}
instance->te_last = duration;
@@ -383,6 +563,10 @@ SubGhzProtocolStatus
subghz_protocol_fiat_marelli_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
if(instance->generic.data_count_bit != FIAT_MARELLI_MAX_DATA_BITS) {
return SubGhzProtocolStatusErrorValueBitCount;
}
uint32_t extra = 0;
if(flipper_format_read_uint32(flipper_format, "Extra", &extra, 1)) {
instance->extra_data = extra;
@@ -405,11 +589,15 @@ void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString*
uint8_t epoch = instance->raw_data[6] & 0x0F;
uint8_t counter = (instance->raw_data[7] >> 3) & 0x1F;
const char* variant =
(instance->te_detected && instance->te_detected < FIAT_MARELLI_TE_TYPE_AB_BOUNDARY) ? "B" :
"A";
uint8_t scramble = (instance->raw_data[7] >> 1) & 0x03;
uint8_t fixed = instance->raw_data[7] & 0x01;
const char* crc_state = "N/A";
uint8_t crc_value = instance->raw_data[12];
if(instance->bit_count >= 104) {
uint8_t calc = fiat_marelli_crc8(instance->raw_data, 12);
crc_state = (calc == instance->raw_data[12]) ? "OK" : "FAIL";
}
furi_string_cat_printf(
output,
@@ -418,7 +606,7 @@ void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString*
"Raw:%02X%02X Fixed:%X\r\n"
"Sn:%08X Cnt:%02X\r\n"
"Btn:%02X:[%s] Ep:%02X\r\n"
"Tp:%s TE:%lu\r\n",
"CRC:%02X [%s]\r\n",
instance->generic.protocol_name,
(int)instance->bit_count,
instance->raw_data[8],
@@ -435,6 +623,6 @@ void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString*
(unsigned)instance->generic.btn,
fiat_marelli_button_name(instance->generic.btn),
(unsigned)epoch,
variant,
(unsigned long)instance->te_detected);
(unsigned)crc_value,
crc_state);
}