mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-05-14 23:55:03 +00:00
Fix Fiat V1
Add CRC check from ARF, remove retx path, add confirmed button mapping
This commit is contained in:
+233
-45
@@ -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);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user