mirror of
https://protopirate.net/ProtoPirate/ProtoPirate.git
synced 2026-08-28 03:14:09 +00:00
Merge remote-tracking branch 'pp_net/zero-mega' into pp_main
This commit is contained in:
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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));
|
||||
}
|
||||
@@ -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);
|
||||
@@ -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",
|
||||
|
||||
@@ -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"
|
||||
|
||||
Reference in New Issue
Block a user