Merge remote-tracking branch 'pp_net/zero-mega' into pp_main

This commit is contained in:
MX
2026-03-22 10:08:05 +03:00
10 changed files with 1589 additions and 0 deletions
+440
View File
@@ -0,0 +1,440 @@
#include "fiat_v1.h"
#include <string.h>
// Magneti Marelli BSI keyfob protocol (PCF7946)
// Found on: Fiat Panda, Grande Punto (and possibly other Fiat/Lancia/Alfa ~2003-2012)
//
// RF: 433.92 MHz, Manchester encoding
// Two timing variants with identical frame structure:
// Type A (e.g. Panda): te_short ~260us, te_long ~520us
// 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):
// Bytes 0-1: 0xFFFF/0xFFFC preamble residue
// Bytes 2-5: Serial (32 bits)
// Byte 6: [Button:4 | Epoch:4]
// Byte 7: [Counter:5 | Scramble:2 | Fixed:1]
// Bytes 8-12: Encrypted payload (40 bits)
//
// 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_MAX_DATA_BITS 104
#define FIAT_MARELLI_MIN_DATA_BITS 80
#define FIAT_MARELLI_GAP_TE_MULT 4
#define FIAT_MARELLI_SYNC_TE_MIN_MULT 4
#define FIAT_MARELLI_SYNC_TE_MAX_MULT 12
#define FIAT_MARELLI_RETX_GAP_MIN 5000
#define FIAT_MARELLI_RETX_SYNC_MIN 400
#define FIAT_MARELLI_RETX_SYNC_MAX 2800
#define FIAT_MARELLI_TE_TYPE_AB_BOUNDARY 180
static const SubGhzBlockConst subghz_protocol_fiat_marelli_const = {
.te_short = 260,
.te_long = 520,
.te_delta = 80,
.min_count_bit_for_found = FIAT_MARELLI_MIN_DATA_BITS,
};
typedef enum {
FiatMarelliDecoderStepReset = 0,
FiatMarelliDecoderStepPreamble = 1,
FiatMarelliDecoderStepSync = 2,
FiatMarelliDecoderStepData = 3,
FiatMarelliDecoderStepRetxSync = 4,
} FiatMarelliDecoderStep;
struct SubGhzProtocolDecoderFiatMarelli {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
ManchesterState manchester_state;
uint8_t decoder_state;
uint16_t preamble_count;
uint8_t raw_data[13];
uint8_t bit_count;
uint32_t extra_data;
uint32_t te_last;
uint32_t te_sum;
uint16_t te_count;
uint32_t te_detected;
};
static void fiat_marelli_prepare_data(SubGhzProtocolDecoderFiatMarelli* instance) {
instance->bit_count = 0;
instance->extra_data = 0;
instance->generic.data = 0;
instance->generic.data_count_bit = 0;
memset(instance->raw_data, 0, sizeof(instance->raw_data));
manchester_advance(
instance->manchester_state,
ManchesterEventReset,
&instance->manchester_state,
NULL);
instance->decoder_state = FiatMarelliDecoderStepData;
}
static void fiat_marelli_rebuild_raw_data(SubGhzProtocolDecoderFiatMarelli* instance) {
memset(instance->raw_data, 0, sizeof(instance->raw_data));
uint64_t key = instance->generic.data;
for(uint8_t i = 0; i < 8; i++) {
instance->raw_data[i] = (uint8_t)(key >> (56 - i * 8));
}
uint8_t extra_bits =
(instance->generic.data_count_bit > 64) ? (instance->generic.data_count_bit - 64) : 0;
for(uint8_t i = 0; i < extra_bits && i < 32; i++) {
uint8_t byte_idx = 8 + (i / 8);
uint8_t bit_pos = 7 - (i % 8);
if(instance->extra_data & (1UL << (extra_bits - 1 - i))) {
instance->raw_data[byte_idx] |= (1U << bit_pos);
}
}
instance->bit_count = instance->generic.data_count_bit;
if(instance->bit_count >= 56) {
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;
}
}
static const char* fiat_marelli_button_name(uint8_t btn) {
switch(btn) {
case 0x7:
return "Lock";
case 0xB:
return "Unlock";
case 0xD:
return "Trunk";
default:
return "Unknown";
}
}
const SubGhzProtocolDecoder subghz_protocol_fiat_marelli_decoder = {
.alloc = subghz_protocol_decoder_fiat_marelli_alloc,
.free = subghz_protocol_decoder_fiat_marelli_free,
.feed = subghz_protocol_decoder_fiat_marelli_feed,
.reset = subghz_protocol_decoder_fiat_marelli_reset,
.get_hash_data = subghz_protocol_decoder_fiat_marelli_get_hash_data,
.serialize = subghz_protocol_decoder_fiat_marelli_serialize,
.deserialize = subghz_protocol_decoder_fiat_marelli_deserialize,
.get_string = subghz_protocol_decoder_fiat_marelli_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_fiat_marelli_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol fiat_v1_protocol = {
.name = FIAT_MARELLI_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
.decoder = &subghz_protocol_fiat_marelli_decoder,
.encoder = &subghz_protocol_fiat_marelli_encoder,
};
void* subghz_protocol_decoder_fiat_marelli_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderFiatMarelli* instance =
calloc(1, sizeof(SubGhzProtocolDecoderFiatMarelli));
furi_check(instance);
instance->base.protocol = &fiat_v1_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_fiat_marelli_free(void* context) {
furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = context;
free(instance);
}
void subghz_protocol_decoder_fiat_marelli_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = context;
instance->decoder_state = FiatMarelliDecoderStepReset;
instance->preamble_count = 0;
instance->bit_count = 0;
instance->extra_data = 0;
instance->te_last = 0;
instance->te_sum = 0;
instance->te_count = 0;
instance->te_detected = 0;
instance->generic.data = 0;
instance->generic.data_count_bit = 0;
memset(instance->raw_data, 0, sizeof(instance->raw_data));
instance->manchester_state = ManchesterStateMid1;
}
void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = context;
uint32_t te_short = instance->te_detected ? instance->te_detected
: (uint32_t)subghz_protocol_fiat_marelli_const.te_short;
uint32_t te_long = te_short * 2;
uint32_t te_delta = te_short / 2;
if(te_delta < 30) te_delta = 30;
uint32_t diff;
switch(instance->decoder_state) {
case FiatMarelliDecoderStepReset:
if(level) {
if(duration >= FIAT_MARELLI_PREAMBLE_PULSE_MIN &&
duration <= FIAT_MARELLI_PREAMBLE_PULSE_MAX) {
instance->decoder_state = FiatMarelliDecoderStepPreamble;
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;
case FiatMarelliDecoderStepPreamble:
if(duration >= FIAT_MARELLI_PREAMBLE_PULSE_MIN &&
duration <= FIAT_MARELLI_PREAMBLE_PULSE_MAX) {
instance->preamble_count++;
instance->te_sum += duration;
instance->te_count++;
instance->te_last = duration;
} else if(!level) {
if(instance->preamble_count >= FIAT_MARELLI_PREAMBLE_MIN && instance->te_count > 0) {
instance->te_detected = instance->te_sum / instance->te_count;
uint32_t gap_threshold = instance->te_detected * FIAT_MARELLI_GAP_TE_MULT;
if(duration > gap_threshold) {
instance->decoder_state = FiatMarelliDecoderStepSync;
instance->te_last = duration;
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
}
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
}
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
}
break;
case FiatMarelliDecoderStepSync: {
uint32_t sync_min = instance->te_detected * FIAT_MARELLI_SYNC_TE_MIN_MULT;
uint32_t sync_max = instance->te_detected * FIAT_MARELLI_SYNC_TE_MAX_MULT;
if(level && duration >= sync_min && duration <= sync_max) {
fiat_marelli_prepare_data(instance);
instance->te_last = duration;
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
}
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;
diff = (duration > te_short) ? (duration - te_short) : (te_short - duration);
if(diff < te_delta) {
event = level ? ManchesterEventShortLow : ManchesterEventShortHigh;
} else {
diff = (duration > te_long) ? (duration - te_long) : (te_long - duration);
if(diff < te_delta) {
event = level ? ManchesterEventLongLow : ManchesterEventLongHigh;
}
}
if(event != ManchesterEventReset) {
bool data_bit = false;
if(manchester_advance(
instance->manchester_state,
event,
&instance->manchester_state,
&data_bit)) {
uint32_t new_bit = data_bit ? 1U : 0U;
if(instance->bit_count < FIAT_MARELLI_MAX_DATA_BITS) {
uint8_t byte_idx = instance->bit_count / 8;
uint8_t bit_pos = 7 - (instance->bit_count % 8);
if(new_bit) {
instance->raw_data[byte_idx] |= (1U << bit_pos);
}
}
if(instance->bit_count < 64) {
instance->generic.data = (instance->generic.data << 1) | new_bit;
} else {
instance->extra_data = (instance->extra_data << 1) | new_bit;
}
instance->bit_count++;
if(instance->bit_count >= FIAT_MARELLI_MAX_DATA_BITS) {
frame_complete = true;
}
}
} else if(instance->bit_count >= FIAT_MARELLI_MIN_DATA_BITS) {
frame_complete = true;
} else {
instance->decoder_state = FiatMarelliDecoderStepReset;
}
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);
}
instance->decoder_state = FiatMarelliDecoderStepReset;
}
instance->te_last = duration;
break;
}
}
}
uint8_t subghz_protocol_decoder_fiat_marelli_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = context;
SubGhzBlockDecoder decoder = {
.decode_data = instance->generic.data,
.decode_count_bit =
instance->generic.data_count_bit > 64 ? 64 : instance->generic.data_count_bit,
};
uint8_t hash = subghz_protocol_blocks_get_hash_data(
&decoder, (decoder.decode_count_bit / 8) + 1);
uint32_t x = instance->extra_data;
for(uint8_t i = 0; i < 4; i++) {
hash ^= (uint8_t)(x >> (i * 8));
}
return hash;
}
SubGhzProtocolStatus subghz_protocol_decoder_fiat_marelli_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = context;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(ret == SubGhzProtocolStatusOk) {
flipper_format_write_uint32(flipper_format, "Extra", &instance->extra_data, 1);
uint32_t extra_bits =
(instance->generic.data_count_bit > 64) ? (instance->generic.data_count_bit - 64) : 0;
flipper_format_write_uint32(flipper_format, "Extra_bits", &extra_bits, 1);
uint32_t te = instance->te_detected;
flipper_format_write_uint32(flipper_format, "TE", &te, 1);
}
return ret;
}
SubGhzProtocolStatus
subghz_protocol_decoder_fiat_marelli_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = context;
SubGhzProtocolStatus ret =
subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_fiat_marelli_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
uint32_t extra = 0;
if(flipper_format_read_uint32(flipper_format, "Extra", &extra, 1)) {
instance->extra_data = extra;
}
uint32_t te = 0;
if(flipper_format_read_uint32(flipper_format, "TE", &te, 1)) {
instance->te_detected = te;
}
fiat_marelli_rebuild_raw_data(instance);
}
return ret;
}
void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderFiatMarelli* instance = context;
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;
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Enc:%02X%02X%02X%02X%02X Scr:%02X\r\n"
"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",
instance->generic.protocol_name,
(int)instance->bit_count,
instance->raw_data[8],
instance->raw_data[9],
instance->raw_data[10],
instance->raw_data[11],
instance->raw_data[12],
(unsigned)scramble,
instance->raw_data[6],
instance->raw_data[7],
(unsigned)fixed,
(unsigned int)instance->generic.serial,
(unsigned)counter,
(unsigned)instance->generic.btn,
fiat_marelli_button_name(instance->generic.btn),
(unsigned)epoch,
variant,
(unsigned long)instance->te_detected);
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#include <lib/toolbox/manchester_decoder.h>
#include <flipper_format/flipper_format.h>
#include "../defines.h"
#define FIAT_MARELLI_PROTOCOL_NAME "Fiat V1"
typedef struct SubGhzProtocolDecoderFiatMarelli SubGhzProtocolDecoderFiatMarelli;
extern const SubGhzProtocol fiat_v1_protocol;
void* subghz_protocol_decoder_fiat_marelli_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_fiat_marelli_free(void* context);
void subghz_protocol_decoder_fiat_marelli_reset(void* context);
void subghz_protocol_decoder_fiat_marelli_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_fiat_marelli_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_fiat_marelli_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_fiat_marelli_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_fiat_marelli_get_string(void* context, FuriString* output);
+350
View File
@@ -0,0 +1,350 @@
#include "mazda_v0.h"
#include <string.h>
// Original implementation by @lupettohf
#define MAZDA_PREAMBLE_MIN 13
#define MAZDA_COMPLETION_MIN 80
#define MAZDA_COMPLETION_MAX 105
#define MAZDA_DATA_BUFFER_SIZE 14
static const SubGhzBlockConst subghz_protocol_mazda_const = {
.te_short = 250,
.te_long = 500,
.te_delta = 100,
.min_count_bit_for_found = 64,
};
typedef enum {
MazdaDecoderStepReset = 0,
MazdaDecoderStepPreambleSave,
MazdaDecoderStepPreambleCheck,
MazdaDecoderStepDataSave,
MazdaDecoderStepDataCheck,
} MazdaDecoderStep;
struct SubGhzProtocolDecoderMazda {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint16_t preamble_count;
uint16_t bit_counter;
uint8_t prev_state;
uint8_t data_buffer[MAZDA_DATA_BUFFER_SIZE];
};
// ============================================================================
// Helpers
// ============================================================================
static uint8_t mazda_byte_parity(uint8_t value) {
value ^= value >> 4;
value ^= value >> 2;
value ^= value >> 1;
return value & 1;
}
static void mazda_xor_deobfuscate(uint8_t* data) {
uint8_t parity = mazda_byte_parity(data[7]);
if(parity) {
uint8_t mask = data[6];
for(uint8_t i = 0; i < 6; i++) {
data[i] ^= mask;
}
} else {
uint8_t mask = data[5];
for(uint8_t i = 0; i < 5; i++) {
data[i] ^= mask;
}
data[6] ^= mask;
}
uint8_t old5 = data[5];
uint8_t old6 = data[6];
data[5] = (old5 & 0xAAU) | (old6 & 0x55U);
data[6] = (old5 & 0x55U) | (old6 & 0xAAU);
}
static void mazda_parse_data(SubGhzBlockGeneric* generic) {
generic->serial = (uint32_t)(generic->data >> 32);
generic->btn = (generic->data >> 24) & 0xFF;
generic->cnt = (generic->data >> 8) & 0xFFFF;
}
static const char* mazda_get_btn_name(uint8_t btn) {
switch(btn) {
case 0x10:
return "Lock";
case 0x20:
return "Unlock";
case 0x40:
return "Trunk";
default:
return "Unknown";
}
}
static inline bool mazda_is_short(uint32_t duration) {
return DURATION_DIFF(duration, subghz_protocol_mazda_const.te_short) <
subghz_protocol_mazda_const.te_delta;
}
static inline bool mazda_is_long(uint32_t duration) {
return DURATION_DIFF(duration, subghz_protocol_mazda_const.te_long) <
subghz_protocol_mazda_const.te_delta;
}
static void mazda_collect_bit(SubGhzProtocolDecoderMazda* instance, uint8_t state_bit) {
uint8_t byte_idx = instance->bit_counter >> 3;
if(byte_idx < MAZDA_DATA_BUFFER_SIZE) {
instance->data_buffer[byte_idx] <<= 1;
if(state_bit == 0) {
instance->data_buffer[byte_idx] |= 1;
}
}
instance->bit_counter++;
}
static bool mazda_check_completion(SubGhzProtocolDecoderMazda* instance) {
if(instance->bit_counter < MAZDA_COMPLETION_MIN || instance->bit_counter > MAZDA_COMPLETION_MAX) {
return false;
}
// Shift buffer by 1 byte (discard sync/header byte)
uint8_t data[8];
for(uint8_t i = 0; i < 8; i++) {
data[i] = instance->data_buffer[i + 1];
}
mazda_xor_deobfuscate(data);
uint8_t checksum = 0;
for(uint8_t i = 0; i < 7; i++) {
checksum += data[i];
}
if(checksum != data[7]) {
return false;
}
uint64_t packed = 0;
for(uint8_t i = 0; i < 8; i++) {
packed = (packed << 8) | data[i];
}
instance->generic.data = packed;
instance->generic.data_count_bit = 64;
mazda_parse_data(&instance->generic);
return true;
}
static bool
mazda_process_pair(SubGhzProtocolDecoderMazda* instance, uint32_t dur_first, uint32_t dur_second) {
bool first_short = mazda_is_short(dur_first);
bool first_long = mazda_is_long(dur_first);
bool second_short = mazda_is_short(dur_second);
bool second_long = mazda_is_long(dur_second);
if(first_long && second_short) {
mazda_collect_bit(instance, 0);
mazda_collect_bit(instance, 1);
instance->prev_state = 1;
return true;
}
if(first_short && second_long) {
mazda_collect_bit(instance, 1);
instance->prev_state = 0;
return true;
}
if(first_short && second_short) {
mazda_collect_bit(instance, instance->prev_state);
return true;
}
if(first_long && second_long) {
mazda_collect_bit(instance, 0);
mazda_collect_bit(instance, 1);
instance->prev_state = 0;
return true;
}
return false;
}
const SubGhzProtocolDecoder subghz_protocol_mazda_decoder = {
.alloc = subghz_protocol_decoder_mazda_alloc,
.free = subghz_protocol_decoder_mazda_free,
.feed = subghz_protocol_decoder_mazda_feed,
.reset = subghz_protocol_decoder_mazda_reset,
.get_hash_data = subghz_protocol_decoder_mazda_get_hash_data,
.serialize = subghz_protocol_decoder_mazda_serialize,
.deserialize = subghz_protocol_decoder_mazda_deserialize,
.get_string = subghz_protocol_decoder_mazda_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_mazda_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol mazda_v0_protocol = {
.name = MAZDA_PROTOCOL_NAME,
.type = SubGhzProtocolTypeStatic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
.decoder = &subghz_protocol_mazda_decoder,
.encoder = &subghz_protocol_mazda_encoder,
};
// ============================================================================
// Decoder
// ============================================================================
void* subghz_protocol_decoder_mazda_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderMazda* instance = calloc(1, sizeof(SubGhzProtocolDecoderMazda));
furi_check(instance);
instance->base.protocol = &mazda_v0_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_mazda_free(void* context) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
free(instance);
}
void subghz_protocol_decoder_mazda_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
instance->decoder.parser_step = MazdaDecoderStepReset;
instance->preamble_count = 0;
instance->bit_counter = 0;
instance->prev_state = 0;
instance->generic.data = 0;
instance->generic.data_count_bit = 0;
memset(instance->data_buffer, 0, sizeof(instance->data_buffer));
}
void subghz_protocol_decoder_mazda_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
UNUSED(level);
SubGhzProtocolDecoderMazda* instance = context;
switch(instance->decoder.parser_step) {
case MazdaDecoderStepReset:
if(mazda_is_short(duration)) {
instance->decoder.te_last = duration;
instance->preamble_count = 0;
instance->decoder.parser_step = MazdaDecoderStepPreambleCheck;
}
break;
case MazdaDecoderStepPreambleSave:
instance->decoder.te_last = duration;
instance->decoder.parser_step = MazdaDecoderStepPreambleCheck;
break;
case MazdaDecoderStepPreambleCheck:
if(mazda_is_short(instance->decoder.te_last) && mazda_is_short(duration)) {
instance->preamble_count++;
instance->decoder.parser_step = MazdaDecoderStepPreambleSave;
} else if(
mazda_is_short(instance->decoder.te_last) && mazda_is_long(duration) &&
instance->preamble_count >= MAZDA_PREAMBLE_MIN) {
instance->bit_counter = 1;
memset(instance->data_buffer, 0, sizeof(instance->data_buffer));
mazda_collect_bit(instance, 1);
instance->prev_state = 0;
instance->decoder.parser_step = MazdaDecoderStepDataSave;
} else {
instance->decoder.parser_step = MazdaDecoderStepReset;
}
break;
case MazdaDecoderStepDataSave:
instance->decoder.te_last = duration;
instance->decoder.parser_step = MazdaDecoderStepDataCheck;
break;
case MazdaDecoderStepDataCheck:
if(mazda_process_pair(instance, instance->decoder.te_last, duration)) {
instance->decoder.parser_step = MazdaDecoderStepDataSave;
} else {
if(mazda_check_completion(instance) && instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
instance->decoder.parser_step = MazdaDecoderStepReset;
}
break;
}
}
uint8_t subghz_protocol_decoder_mazda_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
SubGhzBlockDecoder decoder = {
.decode_data = instance->generic.data,
.decode_count_bit = instance->generic.data_count_bit,
};
return subghz_protocol_blocks_get_hash_data(&decoder, (decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_mazda_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
SubGhzProtocolStatus
subghz_protocol_decoder_mazda_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, subghz_protocol_mazda_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
mazda_parse_data(&instance->generic);
}
return ret;
}
void subghz_protocol_decoder_mazda_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderMazda* instance = context;
mazda_parse_data(&instance->generic);
uint8_t data[8];
for(uint8_t i = 0; i < 8; i++) {
data[i] = (instance->generic.data >> (56 - 8 * i)) & 0xFF;
}
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Key:%02X %02X %02X %02X %02X %02X %02X %02X\r\n"
"Sn:%08lX Btn:%s\r\n"
"Cnt:%04lX Chk:%02X\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
data[0],
data[1],
data[2],
data[3],
data[4],
data[5],
data[6],
data[7],
(uint32_t)instance->generic.serial,
mazda_get_btn_name(instance->generic.btn),
(uint32_t)instance->generic.cnt,
data[7]);
}
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format.h>
#include "../defines.h"
#define MAZDA_PROTOCOL_NAME "Mazda V0"
typedef struct SubGhzProtocolDecoderMazda SubGhzProtocolDecoderMazda;
extern const SubGhzProtocol mazda_v0_protocol;
void* subghz_protocol_decoder_mazda_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_mazda_free(void* context);
void subghz_protocol_decoder_mazda_reset(void* context);
void subghz_protocol_decoder_mazda_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_mazda_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_mazda_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_mazda_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_mazda_get_string(void* context, FuriString* output);
+261
View File
@@ -0,0 +1,261 @@
#include "mitsubishi_v0.h"
#include <string.h>
// Original implementation by @lupettohf
#define MITSUBISHI_BIT_COUNT 96
#define MITSUBISHI_DATA_BYTES 12
static const SubGhzBlockConst subghz_protocol_mitsubishi_const = {
.te_short = 250,
.te_long = 500,
.te_delta = 100,
.min_count_bit_for_found = 80,
};
typedef enum {
MitsubishiDecoderStepReset = 0,
MitsubishiDecoderStepDataSave,
MitsubishiDecoderStepDataCheck,
} MitsubishiDecoderStep;
struct SubGhzProtocolDecoderMitsubishi {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint8_t decoder_state;
uint16_t bit_count;
uint8_t decode_data[MITSUBISHI_DATA_BYTES];
};
static void mitsubishi_unscramble_payload(uint8_t* payload) {
for(uint8_t i = 0; i < 8; i++) {
payload[i] = (uint8_t)~payload[i];
}
uint16_t counter = ((uint16_t)payload[4] << 8) | payload[5];
uint8_t hi = (counter >> 8) & 0xFF;
uint8_t lo = counter & 0xFF;
uint8_t mask1 = (hi & 0xAAU) | (lo & 0x55U);
uint8_t mask2 = (lo & 0xAAU) | (hi & 0x55U);
uint8_t mask3 = mask1 ^ mask2;
for(uint8_t i = 0; i < 5; i++) {
payload[i] ^= mask3;
}
}
static inline bool mitsubishi_is_short(uint32_t duration) {
return DURATION_DIFF(duration, subghz_protocol_mitsubishi_const.te_short) <
subghz_protocol_mitsubishi_const.te_delta;
}
static inline bool mitsubishi_is_long(uint32_t duration) {
return DURATION_DIFF(duration, subghz_protocol_mitsubishi_const.te_long) <
subghz_protocol_mitsubishi_const.te_delta;
}
static void mitsubishi_reset_payload(SubGhzProtocolDecoderMitsubishi* instance) {
instance->bit_count = 0;
memset(instance->decode_data, 0, sizeof(instance->decode_data));
}
static bool mitsubishi_collect_pair(SubGhzProtocolDecoderMitsubishi* instance, uint32_t high, uint32_t low) {
bool bit_value;
if(mitsubishi_is_short(high) && mitsubishi_is_long(low)) {
bit_value = true;
} else if(mitsubishi_is_long(high) && mitsubishi_is_short(low)) {
bit_value = false;
} else {
return false;
}
uint16_t bit_index = instance->bit_count;
if(bit_index < MITSUBISHI_BIT_COUNT) {
if(bit_value) {
uint8_t byte_index = bit_index >> 3;
uint8_t bit_position = 7 - (bit_index & 0x07);
instance->decode_data[byte_index] |= (1U << bit_position);
}
instance->bit_count++;
}
return true;
}
static void mitsubishi_publish_frame(SubGhzProtocolDecoderMitsubishi* instance) {
uint8_t payload[MITSUBISHI_DATA_BYTES];
memcpy(payload, instance->decode_data, sizeof(payload));
mitsubishi_unscramble_payload(payload);
instance->generic.data_count_bit = instance->bit_count;
instance->generic.serial =
((uint32_t)payload[0] << 24) | ((uint32_t)payload[1] << 16) | ((uint32_t)payload[2] << 8) |
payload[3];
instance->generic.cnt = ((uint16_t)payload[4] << 8) | payload[5];
instance->generic.btn = payload[6];
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
}
const SubGhzProtocolDecoder subghz_protocol_mitsubishi_decoder = {
.alloc = subghz_protocol_decoder_mitsubishi_alloc,
.free = subghz_protocol_decoder_mitsubishi_free,
.feed = subghz_protocol_decoder_mitsubishi_feed,
.reset = subghz_protocol_decoder_mitsubishi_reset,
.get_hash_data = subghz_protocol_decoder_mitsubishi_get_hash_data,
.serialize = subghz_protocol_decoder_mitsubishi_serialize,
.deserialize = subghz_protocol_decoder_mitsubishi_deserialize,
.get_string = subghz_protocol_decoder_mitsubishi_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_mitsubishi_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol mitsubishi_v0_protocol = {
.name = MITSUBISHI_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_868 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
.decoder = &subghz_protocol_mitsubishi_decoder,
.encoder = &subghz_protocol_mitsubishi_encoder,
};
void* subghz_protocol_decoder_mitsubishi_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderMitsubishi* instance = calloc(1, sizeof(SubGhzProtocolDecoderMitsubishi));
furi_check(instance);
instance->base.protocol = &mitsubishi_v0_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_mitsubishi_free(void* context) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
free(instance);
}
void subghz_protocol_decoder_mitsubishi_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
instance->decoder_state = MitsubishiDecoderStepReset;
instance->decoder.te_last = 0;
instance->generic.data_count_bit = 0;
mitsubishi_reset_payload(instance);
}
void subghz_protocol_decoder_mitsubishi_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
switch(instance->decoder_state) {
case MitsubishiDecoderStepReset:
if(level) {
instance->decoder.te_last = duration;
instance->decoder_state = MitsubishiDecoderStepDataCheck;
}
break;
case MitsubishiDecoderStepDataSave:
if(level) {
instance->decoder.te_last = duration;
instance->decoder_state = MitsubishiDecoderStepDataCheck;
} else {
instance->decoder_state = MitsubishiDecoderStepReset;
mitsubishi_reset_payload(instance);
}
break;
case MitsubishiDecoderStepDataCheck:
if(!level) {
if(mitsubishi_collect_pair(instance, instance->decoder.te_last, duration)) {
if(instance->bit_count >= MITSUBISHI_BIT_COUNT) {
mitsubishi_publish_frame(instance);
mitsubishi_reset_payload(instance);
instance->decoder_state = MitsubishiDecoderStepReset;
} else {
instance->decoder_state = MitsubishiDecoderStepDataSave;
}
} else {
mitsubishi_reset_payload(instance);
instance->decoder_state = MitsubishiDecoderStepReset;
}
} else {
instance->decoder.te_last = duration;
}
break;
}
}
uint8_t subghz_protocol_decoder_mitsubishi_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
uint8_t hash = 0;
for(size_t i = 0; i < sizeof(instance->decode_data); i++) {
hash ^= instance->decode_data[i];
}
return hash;
}
SubGhzProtocolStatus subghz_protocol_decoder_mitsubishi_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(ret == SubGhzProtocolStatusOk) {
flipper_format_write_uint32(flipper_format, "Serial", &instance->generic.serial, 1);
flipper_format_write_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
uint32_t btn = instance->generic.btn;
flipper_format_write_uint32(flipper_format, "Btn", &btn, 1);
}
return ret;
}
SubGhzProtocolStatus
subghz_protocol_decoder_mitsubishi_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_mitsubishi_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
flipper_format_read_uint32(flipper_format, "Serial", &instance->generic.serial, 1);
flipper_format_read_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
uint32_t btn = 0;
flipper_format_read_uint32(flipper_format, "Btn", &btn, 1);
instance->generic.btn = (uint8_t)btn;
}
return ret;
}
void subghz_protocol_decoder_mitsubishi_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderMitsubishi* instance = context;
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Sn:%08lX Cnt:%04lX\r\n"
"Btn:%02X\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
instance->generic.serial,
instance->generic.cnt,
instance->generic.btn);
}
+31
View File
@@ -0,0 +1,31 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format.h>
#include "../defines.h"
#define MITSUBISHI_PROTOCOL_NAME "Mitsubishi V0"
typedef struct SubGhzProtocolDecoderMitsubishi SubGhzProtocolDecoderMitsubishi;
extern const SubGhzProtocol mitsubishi_v0_protocol;
void* subghz_protocol_decoder_mitsubishi_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_mitsubishi_free(void* context);
void subghz_protocol_decoder_mitsubishi_reset(void* context);
void subghz_protocol_decoder_mitsubishi_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_mitsubishi_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_mitsubishi_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_mitsubishi_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_mitsubishi_get_string(void* context, FuriString* output);
+372
View File
@@ -0,0 +1,372 @@
#include "porsche_touareg.h"
#include <string.h>
// Original implementation by @lupettohf
#define PORSCHE_CAYENNE_BIT_COUNT 64
#define PC_TE_SYNC 3370U
#define PC_TE_GAP 5930U
#define PC_SYNC_MIN 15
static const SubGhzBlockConst subghz_protocol_porsche_cayenne_const = {
.te_short = 1680,
.te_long = 3370,
.te_delta = 500,
.min_count_bit_for_found = PORSCHE_CAYENNE_BIT_COUNT,
};
typedef enum {
PorscheCayenneDecoderStepReset = 0,
PorscheCayenneDecoderStepSync,
PorscheCayenneDecoderStepGapHigh,
PorscheCayenneDecoderStepGapLow,
PorscheCayenneDecoderStepData,
} PorscheCayenneDecoderStep;
struct SubGhzProtocolDecoderPorscheCayenne {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint16_t sync_count;
uint64_t raw_data;
uint8_t bit_count;
};
static void porsche_cayenne_compute_frame(
uint32_t serial24,
uint8_t btn,
uint16_t counter,
uint8_t frame_type,
uint8_t* pkt) {
uint8_t b0 = (uint8_t)((btn << 4) | (frame_type & 0x07));
uint8_t b1 = (serial24 >> 16) & 0xFF;
uint8_t b2 = (serial24 >> 8) & 0xFF;
uint8_t b3 = serial24 & 0xFF;
uint16_t cnt = counter + 1;
uint8_t cnt_lo = cnt & 0xFF;
uint8_t cnt_hi = (cnt >> 8) & 0xFF;
uint8_t r_h = b3;
uint8_t r_m = b1;
uint8_t r_l = b2;
#define ROTATE24(rh, rm, rl) \
do { \
uint8_t _ch = ((rh) >> 7) & 1U; \
uint8_t _cm = ((rm) >> 7) & 1U; \
uint8_t _cl = ((rl) >> 7) & 1U; \
(rh) = (uint8_t)(((rh) << 1) | _cm); \
(rm) = (uint8_t)(((rm) << 1) | _cl); \
(rl) = (uint8_t)(((rl) << 1) | _ch); \
} while(0)
for(uint8_t i = 0; i < 4; i++) {
ROTATE24(r_h, r_m, r_l);
}
for(uint16_t i = 0; i < cnt_lo; i++) {
ROTATE24(r_h, r_m, r_l);
}
#undef ROTATE24
uint8_t a9a = r_h ^ b0;
uint8_t nb9b_p1 = (uint8_t)((~cnt_lo << 2) & 0xFC) ^ r_m;
uint8_t nb9b_p2 = (uint8_t)((~cnt_hi << 2) & 0xFC) ^ r_m;
uint8_t nb9b_p3 = (uint8_t)((~cnt_hi >> 6) & 0x03) ^ r_m;
uint8_t a9b = (nb9b_p1 & 0xCC) | (nb9b_p2 & 0x30) | (nb9b_p3 & 0x03);
uint8_t nb9c_p1 = (uint8_t)((~cnt_lo >> 2) & 0x3F) ^ r_l;
uint8_t nb9c_p2 = (uint8_t)((~cnt_hi & 0x03) << 6) ^ r_l;
uint8_t nb9c_p3 = (uint8_t)((~cnt_hi >> 2) & 0x3F) ^ r_l;
uint8_t a9c = (nb9c_p1 & 0x33) | (nb9c_p2 & 0xC0) | (nb9c_p3 & 0x0C);
pkt[0] = b0;
pkt[1] = b1;
pkt[2] = b2;
pkt[3] = b3;
pkt[4] = (uint8_t)(((a9a >> 2) & 0x3F) | ((~cnt_lo & 0x03U) << 6));
pkt[5] = (uint8_t)(
(~cnt_lo & 0xC0U) | ((a9a & 0x03U) << 4) | (a9b & 0x0CU) | ((~cnt_lo >> 2) & 0x03U));
pkt[6] = (uint8_t)(((a9b & 0x03U) << 6) | ((a9c >> 2) & 0x3CU) | ((~cnt_lo >> 4) & 0x03U));
pkt[7] = (uint8_t)(((a9b >> 4) & 0x0FU) | ((a9c & 0x0FU) << 4));
}
static void porsche_cayenne_parse_data(SubGhzProtocolDecoderPorscheCayenne* instance) {
uint8_t pkt[8];
uint64_t raw = instance->generic.data;
for(int8_t i = 7; i >= 0; i--) {
pkt[i] = (uint8_t)(raw & 0xFF);
raw >>= 8;
}
instance->generic.serial = ((uint32_t)pkt[1] << 16) | ((uint32_t)pkt[2] << 8) | pkt[3];
instance->generic.btn = (uint8_t)(pkt[0] >> 4);
instance->generic.cnt = 0;
uint8_t frame_type = pkt[0] & 0x07;
uint8_t try_pkt[8];
for(uint16_t try_cnt = 1; try_cnt <= 256; try_cnt++) {
porsche_cayenne_compute_frame(
instance->generic.serial,
instance->generic.btn,
(uint16_t)(try_cnt - 1),
frame_type,
try_pkt);
if(try_pkt[4] == pkt[4] && try_pkt[5] == pkt[5] && try_pkt[6] == pkt[6] &&
try_pkt[7] == pkt[7]) {
instance->generic.cnt = try_cnt;
break;
}
}
}
static void porsche_cayenne_publish_frame(SubGhzProtocolDecoderPorscheCayenne* instance) {
instance->generic.data = instance->raw_data;
instance->generic.data_count_bit = PORSCHE_CAYENNE_BIT_COUNT;
porsche_cayenne_parse_data(instance);
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
}
const SubGhzProtocolDecoder subghz_protocol_porsche_cayenne_decoder = {
.alloc = subghz_protocol_decoder_porsche_cayenne_alloc,
.free = subghz_protocol_decoder_porsche_cayenne_free,
.feed = subghz_protocol_decoder_porsche_cayenne_feed,
.reset = subghz_protocol_decoder_porsche_cayenne_reset,
.get_hash_data = subghz_protocol_decoder_porsche_cayenne_get_hash_data,
.serialize = subghz_protocol_decoder_porsche_cayenne_serialize,
.deserialize = subghz_protocol_decoder_porsche_cayenne_deserialize,
.get_string = subghz_protocol_decoder_porsche_cayenne_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_porsche_cayenne_encoder = {
.alloc = NULL,
.free = NULL,
.deserialize = NULL,
.stop = NULL,
.yield = NULL,
};
const SubGhzProtocol porsche_touareg_protocol = {
.name = PORSCHE_CAYENNE_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save,
.decoder = &subghz_protocol_porsche_cayenne_decoder,
.encoder = &subghz_protocol_porsche_cayenne_encoder,
};
void* subghz_protocol_decoder_porsche_cayenne_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderPorscheCayenne* instance =
calloc(1, sizeof(SubGhzProtocolDecoderPorscheCayenne));
furi_check(instance);
instance->base.protocol = &porsche_touareg_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_porsche_cayenne_free(void* context) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
free(instance);
}
void subghz_protocol_decoder_porsche_cayenne_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
instance->decoder.te_last = 0;
instance->sync_count = 0;
instance->raw_data = 0;
instance->bit_count = 0;
instance->generic.data = 0;
instance->generic.data_count_bit = 0;
}
void subghz_protocol_decoder_porsche_cayenne_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
const uint32_t te_short = subghz_protocol_porsche_cayenne_const.te_short;
const uint32_t te_long = subghz_protocol_porsche_cayenne_const.te_long;
const uint32_t te_delta = subghz_protocol_porsche_cayenne_const.te_delta;
switch(instance->decoder.parser_step) {
case PorscheCayenneDecoderStepReset:
if(!level && DURATION_DIFF(duration, PC_TE_SYNC) < te_delta) {
instance->sync_count = 1;
instance->decoder.parser_step = PorscheCayenneDecoderStepSync;
}
break;
case PorscheCayenneDecoderStepSync:
if(level) {
if(DURATION_DIFF(duration, PC_TE_SYNC) < te_delta) {
// keep collecting sync pairs
} else if(
instance->sync_count >= PC_SYNC_MIN && DURATION_DIFF(duration, PC_TE_GAP) < te_delta) {
instance->decoder.parser_step = PorscheCayenneDecoderStepGapLow;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
} else {
if(DURATION_DIFF(duration, PC_TE_SYNC) < te_delta) {
instance->sync_count++;
} else if(
instance->sync_count >= PC_SYNC_MIN && DURATION_DIFF(duration, PC_TE_GAP) < te_delta) {
instance->decoder.parser_step = PorscheCayenneDecoderStepGapHigh;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
}
break;
case PorscheCayenneDecoderStepGapHigh:
if(level && DURATION_DIFF(duration, PC_TE_GAP) < te_delta) {
instance->raw_data = 0;
instance->bit_count = 0;
instance->decoder.parser_step = PorscheCayenneDecoderStepData;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
break;
case PorscheCayenneDecoderStepGapLow:
if(!level && DURATION_DIFF(duration, PC_TE_GAP) < te_delta) {
instance->raw_data = 0;
instance->bit_count = 0;
instance->decoder.parser_step = PorscheCayenneDecoderStepData;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
break;
case PorscheCayenneDecoderStepData:
if(level) {
bool bit_value = false;
if(DURATION_DIFF(instance->decoder.te_last, te_short) < te_delta &&
DURATION_DIFF(duration, te_long) < te_delta) {
bit_value = false;
} else if(
DURATION_DIFF(instance->decoder.te_last, te_long) < te_delta &&
DURATION_DIFF(duration, te_short) < te_delta) {
bit_value = true;
} else {
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
break;
}
instance->raw_data = (instance->raw_data << 1) | (bit_value ? 1U : 0U);
instance->bit_count++;
if(instance->bit_count >= PORSCHE_CAYENNE_BIT_COUNT) {
porsche_cayenne_publish_frame(instance);
instance->decoder.parser_step = PorscheCayenneDecoderStepReset;
}
} else {
instance->decoder.te_last = duration;
}
break;
}
}
uint8_t subghz_protocol_decoder_porsche_cayenne_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
SubGhzBlockDecoder decoder = {
.decode_data = instance->generic.data,
.decode_count_bit = instance->generic.data_count_bit,
};
return subghz_protocol_blocks_get_hash_data(&decoder, (decoder.decode_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(ret == SubGhzProtocolStatusOk) {
uint32_t serial = instance->generic.serial & 0xFFFFFF;
flipper_format_write_uint32(flipper_format, "Serial", &serial, 1);
uint32_t cnt = instance->generic.cnt;
flipper_format_write_uint32(flipper_format, "Cnt", &cnt, 1);
uint32_t btn = instance->generic.btn;
flipper_format_write_uint32(flipper_format, "Btn", &btn, 1);
}
return ret;
}
SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_deserialize(
void* context,
FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic,
flipper_format,
subghz_protocol_porsche_cayenne_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
porsche_cayenne_parse_data(instance);
uint32_t serial = 0;
if(flipper_format_read_uint32(flipper_format, "Serial", &serial, 1)) {
instance->generic.serial = serial & 0xFFFFFF;
}
uint32_t cnt = 0;
if(flipper_format_read_uint32(flipper_format, "Cnt", &cnt, 1)) {
instance->generic.cnt = cnt;
}
uint32_t btn = 0;
if(flipper_format_read_uint32(flipper_format, "Btn", &btn, 1)) {
instance->generic.btn = (uint8_t)btn;
}
}
return ret;
}
void subghz_protocol_decoder_porsche_cayenne_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderPorscheCayenne* instance = context;
uint8_t frame_type = (uint8_t)((instance->generic.data >> 56) & 0x07);
const char* frame_type_name = "??";
if(frame_type == 0x02) {
frame_type_name = "First";
} else if(frame_type == 0x01) {
frame_type_name = "Cont";
} else if(frame_type == 0x04) {
frame_type_name = "Final";
}
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Sn:%06lX Btn:%X\r\n"
"Cnt:%04lX FT:%s\r\n"
"Raw:%08lX%08lX\r\n",
instance->generic.protocol_name,
instance->generic.data_count_bit,
(unsigned long)(instance->generic.serial & 0xFFFFFF),
(unsigned int)instance->generic.btn,
(unsigned long)instance->generic.cnt,
frame_type_name,
(unsigned long)(instance->generic.data >> 32),
(unsigned long)(instance->generic.data & 0xFFFFFFFF));
}
+32
View File
@@ -0,0 +1,32 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/const.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format.h>
#include "../defines.h"
#define PORSCHE_CAYENNE_PROTOCOL_NAME "Porsche Touareg"
typedef struct SubGhzProtocolDecoderPorscheCayenne SubGhzProtocolDecoderPorscheCayenne;
extern const SubGhzProtocol porsche_touareg_protocol;
void* subghz_protocol_decoder_porsche_cayenne_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_porsche_cayenne_free(void* context);
void subghz_protocol_decoder_porsche_cayenne_reset(void* context);
void subghz_protocol_decoder_porsche_cayenne_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_porsche_cayenne_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus subghz_protocol_decoder_porsche_cayenne_deserialize(
void* context,
FlipperFormat* flipper_format);
void subghz_protocol_decoder_porsche_cayenne_get_string(void* context, FuriString* output);
+36
View File
@@ -11,6 +11,10 @@ const SubGhzProtocol* protopirate_protocol_registry_items[] = {
&kia_protocol_v6, // Heap: free 18296
&ford_protocol_v0, // Heap: free 19456
&fiat_protocol_v0, // Heap: free 16864
&fiat_v1_protocol,
&mazda_v0_protocol,
&mitsubishi_v0_protocol,
&porsche_touareg_protocol,
&subaru_protocol, // Heap: free 17280
&suzuki_protocol, // Heap: free 16064
&vag_protocol, // Heap: free 29352
@@ -97,6 +101,38 @@ static const ProtoPirateProtocolTiming protocol_timings[] = {
.te_delta = 100,
.min_count_bit = 64,
},
// Fiat V1: Manchester dynamic (baseline Type A 260/520us)
{
.name = "Fiat V1",
.te_short = 260,
.te_long = 520,
.te_delta = 80,
.min_count_bit = 80,
},
// Mazda V0: 250/500us
{
.name = "Mazda V0",
.te_short = 250,
.te_long = 500,
.te_delta = 100,
.min_count_bit = 64,
},
// Mitsubishi V0: 250/500us
{
.name = "Mitsubishi V0",
.te_short = 250,
.te_long = 500,
.te_delta = 100,
.min_count_bit = 80,
},
// Porsche Touareg: 1680/3370us
{
.name = "Porsche Touareg",
.te_short = 1680,
.te_long = 3370,
.te_delta = 500,
.min_count_bit = 64,
},
// Subaru: PPM 800/1600µs
{
.name = "Subaru",
+4
View File
@@ -13,6 +13,10 @@
#include "kia_v6.h"
#include "ford_v0.h"
#include "fiat_v0.h"
#include "fiat_v1.h"
#include "mazda_v0.h"
#include "mitsubishi_v0.h"
#include "porsche_touareg.h"
#include "subaru.h"
#include "suzuki.h"
#include "vag.h"