Compare commits

...

2 Commits

Author SHA1 Message Date
DACI
03897a406e Merge branch 'main' of https://github.com/D4C1-Labs/Flipper-ARF 2026-03-12 10:19:55 +01:00
DACI
09a7668fe7 Refactor Subaru & Suzuki protocols and registry
Update subghz protocol registry and perform a large refactor of Subaru and Suzuki implementations. Rename protocol symbols to subghz_protocol_suzuki / subghz_protocol_subaru and expose SUBGHZ_PROTOCOL_SUBARU_NAME; unify includes and use block helpers (const, decoder, encoder, generic, math, custom_btn_i). Subaru: rewrite decoder/encoder types and logic, add count encode/decode, button mapping and names, adjust timing/deltas, add encoder upload builder, improve (de)serialization, use furi_assert, support additional flags (315/433 AM/FM) and custom button handling. Suzuki: clean up decoder/encoder structs, add CRC calculation/verification, button <-> custom mappings, tighten preamble/gap thresholds and parser logic, update protocol flags and naming. Misc: memory and API consistency fixes, improved formatting and small performance/clarity tweaks across headers and source.
2026-03-12 10:19:45 +01:00
5 changed files with 717 additions and 620 deletions

View File

@@ -42,7 +42,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
&subghz_protocol_kia_v0, &subghz_protocol_kia_v1,
&subghz_protocol_kia_v2, &subghz_protocol_kia_v3_v4,
&subghz_protocol_kia_v5, &subghz_protocol_kia_v6,
&suzuki_protocol, &subghz_protocol_mitsubishi_v0,
&subghz_protocol_suzuki, &subghz_protocol_mitsubishi_v0,
};
const SubGhzProtocolRegistry subghz_protocol_registry = {

View File

@@ -1,60 +1,46 @@
#include "subaru.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#include "../blocks/custom_btn_i.h"
static uint8_t subaru_get_btn_code() {
uint8_t custom_btn = subghz_custom_btn_get();
uint8_t original_btn = subghz_custom_btn_get_original();
if(custom_btn == SUBGHZ_CUSTOM_BTN_OK) return original_btn;
if(custom_btn == SUBGHZ_CUSTOM_BTN_UP) return 0x01; // Lock
if(custom_btn == SUBGHZ_CUSTOM_BTN_DOWN) return 0x02; // Unlock
if(custom_btn == SUBGHZ_CUSTOM_BTN_LEFT) return 0x03; // Boot/Trunk
if(custom_btn == SUBGHZ_CUSTOM_BTN_RIGHT) return 0x03; // Boot/Trunk
return original_btn;
}
#define TAG "SubaruProtocol"
#define TAG "SubGhzProtocolSubaru"
static const SubGhzBlockConst subghz_protocol_subaru_const = {
.te_short = 800,
.te_long = 1600,
.te_delta = 200,
.te_delta = 250,
.min_count_bit_for_found = 64,
};
#define SUBARU_PREAMBLE_PAIRS 80
#define SUBARU_GAP_US 2800
#define SUBARU_SYNC_US 2800
#define SUBARU_TOTAL_BURSTS 3
#define SUBARU_INTER_BURST_GAP 25000
typedef struct SubGhzProtocolDecoderSubaru {
struct SubGhzProtocolDecoderSubaru {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint16_t header_count;
uint16_t bit_count;
uint8_t data[8];
uint64_t key;
uint32_t serial;
uint8_t btn;
uint16_t cnt;
} SubGhzProtocolDecoderSubaru;
uint8_t button;
uint16_t count;
};
typedef struct SubGhzProtocolEncoderSubaru {
struct SubGhzProtocolEncoderSubaru {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
uint64_t key;
uint32_t serial;
uint8_t btn;
uint16_t cnt;
} SubGhzProtocolEncoderSubaru;
uint8_t button;
uint16_t count;
};
typedef enum {
SubaruDecoderStepReset = 0,
@@ -65,6 +51,12 @@ typedef enum {
SubaruDecoderStepCheckDuration,
} SubaruDecoderStep;
static void subaru_decode_count(const uint8_t* KB, uint16_t* count);
static void subaru_encode_count(uint8_t* KB, uint16_t count);
static void subaru_add_bit(SubGhzProtocolDecoderSubaru* instance, bool bit);
static bool subaru_process_data(SubGhzProtocolDecoderSubaru* instance);
static void subghz_protocol_encoder_subaru_get_upload(SubGhzProtocolEncoderSubaru* instance);
const SubGhzProtocolDecoder subghz_protocol_subaru_decoder = {
.alloc = subghz_protocol_decoder_subaru_alloc,
.free = subghz_protocol_decoder_subaru_free,
@@ -85,19 +77,49 @@ const SubGhzProtocolEncoder subghz_protocol_subaru_encoder = {
};
const SubGhzProtocol subghz_protocol_subaru = {
.name = SUBARU_PROTOCOL_NAME,
.name = SUBGHZ_PROTOCOL_SUBARU_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_subaru_decoder,
.encoder = &subghz_protocol_subaru_encoder,
};
// ============================================================================
// DECODER HELPER FUNCTIONS
// ============================================================================
static uint8_t subaru_get_button_code(uint8_t custom_btn) {
switch(custom_btn) {
case 1: return 0x01;
case 2: return 0x02;
case 3: return 0x03;
case 4: return 0x04;
case 5: return 0x08;
default: return 0x01;
}
}
static void subaru_decode_count(const uint8_t* KB, uint16_t* cnt) {
static uint8_t subaru_btn_to_custom(uint8_t btn_code) {
switch(btn_code) {
case 0x01: return 1;
case 0x02: return 2;
case 0x03: return 3;
case 0x04: return 4;
case 0x08: return 5;
default: return 1;
}
}
static const char* subaru_get_button_name(uint8_t btn) {
switch(btn) {
case 0x01: return "Lock";
case 0x02: return "Unlock";
case 0x03: return "Trunk";
case 0x04: return "Panic";
case 0x08: return "0x08";
default: return "??";
}
}
static void subaru_decode_count(const uint8_t* KB, uint16_t* count) {
uint8_t lo = 0;
if((KB[4] & 0x40) == 0) lo |= 0x01;
if((KB[4] & 0x80) == 0) lo |= 0x02;
@@ -107,19 +129,19 @@ static void subaru_decode_count(const uint8_t* KB, uint16_t* cnt) {
if((KB[6] & 0x02) == 0) lo |= 0x20;
if((KB[5] & 0x40) == 0) lo |= 0x40;
if((KB[5] & 0x80) == 0) lo |= 0x80;
uint8_t REG_SH1 = (KB[7] << 4) & 0xF0;
if(KB[5] & 0x04) REG_SH1 |= 0x04;
if(KB[5] & 0x08) REG_SH1 |= 0x08;
if(KB[6] & 0x80) REG_SH1 |= 0x02;
if(KB[6] & 0x40) REG_SH1 |= 0x01;
uint8_t REG_SH2 = ((KB[6] << 2) & 0xF0) | ((KB[7] >> 4) & 0x0F);
uint8_t SER0 = KB[3];
uint8_t SER1 = KB[1];
uint8_t SER2 = KB[2];
uint8_t total_rot = 4 + lo;
for(uint8_t i = 0; i < total_rot; ++i) {
uint8_t t_bit = (SER0 >> 7) & 1;
@@ -127,10 +149,10 @@ static void subaru_decode_count(const uint8_t* KB, uint16_t* cnt) {
SER1 = ((SER1 << 1) & 0xFE) | ((SER2 >> 7) & 1);
SER2 = ((SER2 << 1) & 0xFE) | t_bit;
}
uint8_t T1 = SER1 ^ REG_SH1;
uint8_t T2 = SER2 ^ REG_SH2;
uint8_t hi = 0;
if((T1 & 0x10) == 0) hi |= 0x04;
if((T1 & 0x20) == 0) hi |= 0x08;
@@ -140,8 +162,67 @@ static void subaru_decode_count(const uint8_t* KB, uint16_t* cnt) {
if((T1 & 0x02) == 0) hi |= 0x80;
if((T2 & 0x08) == 0) hi |= 0x20;
if((T2 & 0x04) == 0) hi |= 0x10;
*count = ((hi << 8) | lo) & 0xFFFF;
}
*cnt = ((hi << 8) | lo) & 0xFFFF;
static void subaru_encode_count(uint8_t* KB, uint16_t count) {
uint8_t lo = count & 0xFF;
uint8_t hi = (count >> 8) & 0xFF;
KB[4] &= ~0xC0;
KB[5] &= ~0xC3;
KB[6] &= ~0x03;
if((lo & 0x01) == 0) KB[4] |= 0x40;
if((lo & 0x02) == 0) KB[4] |= 0x80;
if((lo & 0x04) == 0) KB[5] |= 0x01;
if((lo & 0x08) == 0) KB[5] |= 0x02;
if((lo & 0x10) == 0) KB[6] |= 0x01;
if((lo & 0x20) == 0) KB[6] |= 0x02;
if((lo & 0x40) == 0) KB[5] |= 0x40;
if((lo & 0x80) == 0) KB[5] |= 0x80;
uint8_t SER0 = KB[3];
uint8_t SER1 = KB[1];
uint8_t SER2 = KB[2];
uint8_t total_rot = 4 + lo;
for(uint8_t i = 0; i < total_rot; ++i) {
uint8_t t_bit = (SER0 >> 7) & 1;
SER0 = ((SER0 << 1) & 0xFE) | ((SER1 >> 7) & 1);
SER1 = ((SER1 << 1) & 0xFE) | ((SER2 >> 7) & 1);
SER2 = ((SER2 << 1) & 0xFE) | t_bit;
}
uint8_t T1 = 0xFF;
uint8_t T2 = 0xFF;
if(hi & 0x04) T1 &= ~0x10;
if(hi & 0x08) T1 &= ~0x20;
if(hi & 0x02) T2 &= ~0x80;
if(hi & 0x01) T2 &= ~0x40;
if(hi & 0x40) T1 &= ~0x01;
if(hi & 0x80) T1 &= ~0x02;
if(hi & 0x20) T2 &= ~0x08;
if(hi & 0x10) T2 &= ~0x04;
uint8_t new_REG_SH1 = T1 ^ SER1;
uint8_t new_REG_SH2 = T2 ^ SER2;
KB[5] &= ~0x0C;
KB[6] &= ~0xC0;
KB[7] = (KB[7] & 0xF0) | ((new_REG_SH1 >> 4) & 0x0F);
if(new_REG_SH1 & 0x04) KB[5] |= 0x04;
if(new_REG_SH1 & 0x08) KB[5] |= 0x08;
if(new_REG_SH1 & 0x02) KB[6] |= 0x80;
if(new_REG_SH1 & 0x01) KB[6] |= 0x40;
KB[6] = (KB[6] & 0xC3) | ((new_REG_SH2 >> 2) & 0x3C);
KB[7] = (KB[7] & 0x0F) | ((new_REG_SH2 << 4) & 0xF0);
}
static void subaru_add_bit(SubGhzProtocolDecoderSubaru* instance, bool bit) {
@@ -161,230 +242,24 @@ static bool subaru_process_data(SubGhzProtocolDecoderSubaru* instance) {
if(instance->bit_count < 64) {
return false;
}
uint8_t* b = instance->data;
instance->key = ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) | ((uint64_t)b[2] << 40) |
((uint64_t)b[3] << 32) | ((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
((uint64_t)b[6] << 8) | ((uint64_t)b[7]);
instance->key = ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) |
((uint64_t)b[2] << 40) | ((uint64_t)b[3] << 32) |
((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
((uint64_t)b[6] << 8) | ((uint64_t)b[7]);
instance->serial = ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | b[3];
instance->btn = b[0] & 0x0F;
subaru_decode_count(b, &instance->cnt);
instance->button = b[0] & 0x0F;
subaru_decode_count(b, &instance->count);
instance->decoder.decode_data = instance->key;
instance->decoder.decode_count_bit = 64;
return true;
}
// ============================================================================
// ENCODER IMPLEMENTATION
// ============================================================================
void* subghz_protocol_encoder_subaru_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderSubaru* instance = malloc(sizeof(SubGhzProtocolEncoderSubaru));
instance->base.protocol = &subghz_protocol_subaru;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 1024;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
instance->encoder.front = 0;
instance->key = 0;
instance->serial = 0;
instance->btn = 0;
instance->cnt = 0;
return instance;
}
void subghz_protocol_encoder_subaru_free(void* context) {
furi_check(context);
SubGhzProtocolEncoderSubaru* instance = context;
if(instance->encoder.upload) {
free(instance->encoder.upload);
}
free(instance);
}
static void subghz_protocol_encoder_subaru_get_upload(SubGhzProtocolEncoderSubaru* instance) {
furi_check(instance);
size_t index = 0;
uint32_t te_short = subghz_protocol_subaru_const.te_short;
uint32_t te_long = subghz_protocol_subaru_const.te_long;
FURI_LOG_I(
TAG,
"Building upload: key=0x%016llX, serial=0x%06lX, btn=0x%X, cnt=0x%04X",
instance->key,
instance->serial,
subaru_get_btn_code(),
instance->cnt);
for(uint8_t burst = 0; burst < SUBARU_TOTAL_BURSTS; burst++) {
if(burst > 0) {
instance->encoder.upload[index++] = level_duration_make(false, SUBARU_INTER_BURST_GAP);
}
// Preamble: Long HIGH/LOW pairs
for(int i = 0; i < SUBARU_PREAMBLE_PAIRS; i++) {
instance->encoder.upload[index++] = level_duration_make(true, te_long);
instance->encoder.upload[index++] = level_duration_make(false, te_long);
}
// Replace last preamble LOW with gap (to avoid consecutive LOWs combining)
instance->encoder.upload[index - 1] = level_duration_make(false, SUBARU_GAP_US);
// Sync: Long HIGH
instance->encoder.upload[index++] = level_duration_make(true, SUBARU_SYNC_US);
// Sync end: Long LOW
instance->encoder.upload[index++] = level_duration_make(false, te_long);
// Data: 64 bits, PWM encoding
// Short HIGH = 1, Long HIGH = 0
for(int bit = 63; bit >= 0; bit--) {
if((instance->key >> bit) & 1) {
// Bit 1: Short HIGH
instance->encoder.upload[index++] = level_duration_make(true, te_short);
} else {
// Bit 0: Long HIGH
instance->encoder.upload[index++] = level_duration_make(true, te_long);
}
// LOW separator
instance->encoder.upload[index++] = level_duration_make(false, te_short);
}
// End marker: extended LOW
instance->encoder.upload[index++] = level_duration_make(false, te_long * 2);
}
instance->encoder.size_upload = index;
instance->encoder.front = 0;
FURI_LOG_I(TAG, "Upload built: %zu elements", instance->encoder.size_upload);
}
SubGhzProtocolStatus
subghz_protocol_encoder_subaru_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolEncoderSubaru* instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
instance->encoder.is_running = false;
instance->encoder.front = 0;
instance->encoder.repeat = 10;
flipper_format_rewind(flipper_format);
do {
FuriString* temp_str = furi_string_alloc();
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
FURI_LOG_E(TAG, "Missing Protocol");
furi_string_free(temp_str);
break;
}
if(!furi_string_equal(temp_str, instance->base.protocol->name)) {
FURI_LOG_E(TAG, "Wrong protocol: %s", furi_string_get_cstr(temp_str));
furi_string_free(temp_str);
break;
}
furi_string_free(temp_str);
uint32_t bit_count_temp = 0;
if(!flipper_format_read_uint32(flipper_format, "Bit", &bit_count_temp, 1)) {
FURI_LOG_E(TAG, "Missing Bit");
break;
}
// Read key in standard hex format (e.g. "A0 3B F8 68 54 53 62 00")
uint8_t key_data[sizeof(uint64_t)] = {0};
if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
FURI_LOG_E(TAG, "Missing Key");
break;
}
instance->key = 0;
for(int i = 0; i < 8; i++) {
instance->key = (instance->key << 8) | key_data[i];
}
FURI_LOG_I(TAG, "Parsed key: 0x%016llX", instance->key);
if(!flipper_format_read_uint32(flipper_format, "Serial", &instance->serial, 1)) {
instance->serial = (uint32_t)((instance->key >> 8) & 0xFFFFFF);
}
uint32_t btn_temp = 0;
if(flipper_format_read_uint32(flipper_format, "Btn", &btn_temp, 1)) {
instance->btn = (uint8_t)btn_temp;
} else {
instance->btn = (uint8_t)(instance->key >> 56) & 0x0F;
}
if(subghz_custom_btn_get_original() == 0)
subghz_custom_btn_set_original(instance->btn);
subghz_custom_btn_set_max(4);
instance->btn = subaru_get_btn_code();
uint32_t cnt_temp = 0;
if(flipper_format_read_uint32(flipper_format, "Cnt", &cnt_temp, 1)) {
instance->cnt = (uint16_t)cnt_temp;
} else {
instance->cnt = 0;
}
if(!flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1)) {
instance->encoder.repeat = 10;
}
subghz_protocol_encoder_subaru_get_upload(instance);
instance->encoder.is_running = true;
FURI_LOG_I(
TAG,
"Encoder ready: key=0x%016llX, serial=0x%06lX, btn=0x%X",
instance->key,
instance->serial,
instance->btn);
ret = SubGhzProtocolStatusOk;
} while(false);
return ret;
}
void subghz_protocol_encoder_subaru_stop(void* context) {
furi_check(context);
SubGhzProtocolEncoderSubaru* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_subaru_yield(void* context) {
furi_check(context);
SubGhzProtocolEncoderSubaru* instance = context;
if(!instance->encoder.is_running || instance->encoder.repeat == 0) {
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
}
// ============================================================================
// DECODER IMPLEMENTATION
// ============================================================================
void* subghz_protocol_decoder_subaru_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderSubaru* instance = malloc(sizeof(SubGhzProtocolDecoderSubaru));
@@ -394,13 +269,13 @@ void* subghz_protocol_decoder_subaru_alloc(SubGhzEnvironment* environment) {
}
void subghz_protocol_decoder_subaru_free(void* context) {
furi_check(context);
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
free(instance);
}
void subghz_protocol_decoder_subaru_reset(void* context) {
furi_check(context);
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
instance->decoder.parser_step = SubaruDecoderStepReset;
instance->decoder.te_last = 0;
@@ -410,25 +285,21 @@ void subghz_protocol_decoder_subaru_reset(void* context) {
}
void subghz_protocol_decoder_subaru_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
uint32_t te_short = subghz_protocol_subaru_const.te_short;
uint32_t te_long = subghz_protocol_subaru_const.te_long;
uint32_t te_delta = subghz_protocol_subaru_const.te_delta;
switch(instance->decoder.parser_step) {
case SubaruDecoderStepReset:
if(level && (DURATION_DIFF(duration, te_long) < te_delta)) {
if(level && DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->decoder.parser_step = SubaruDecoderStepCheckPreamble;
instance->decoder.te_last = duration;
instance->header_count = 1;
}
break;
case SubaruDecoderStepCheckPreamble:
if(!level) {
if(DURATION_DIFF(duration, te_long) < te_delta) {
if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->header_count++;
} else if(duration > 2000 && duration < 3500) {
if(instance->header_count > 20) {
@@ -440,7 +311,7 @@ void subghz_protocol_decoder_subaru_feed(void* context, bool level, uint32_t dur
instance->decoder.parser_step = SubaruDecoderStepReset;
}
} else {
if(DURATION_DIFF(duration, te_long) < te_delta) {
if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->decoder.te_last = duration;
instance->header_count++;
} else {
@@ -448,7 +319,7 @@ void subghz_protocol_decoder_subaru_feed(void* context, bool level, uint32_t dur
}
}
break;
case SubaruDecoderStepFoundGap:
if(level && duration > 2000 && duration < 3500) {
instance->decoder.parser_step = SubaruDecoderStepFoundSync;
@@ -456,9 +327,9 @@ void subghz_protocol_decoder_subaru_feed(void* context, bool level, uint32_t dur
instance->decoder.parser_step = SubaruDecoderStepReset;
}
break;
case SubaruDecoderStepFoundSync:
if(!level && (DURATION_DIFF(duration, te_long) < te_delta)) {
if(!level && DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->decoder.parser_step = SubaruDecoderStepSaveDuration;
instance->bit_count = 0;
memset(instance->data, 0, sizeof(instance->data));
@@ -466,29 +337,32 @@ void subghz_protocol_decoder_subaru_feed(void* context, bool level, uint32_t dur
instance->decoder.parser_step = SubaruDecoderStepReset;
}
break;
case SubaruDecoderStepSaveDuration:
if(level) {
if(DURATION_DIFF(duration, te_short) < te_delta) {
// Short HIGH = bit 1
if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_short) < subghz_protocol_subaru_const.te_delta) {
subaru_add_bit(instance, true);
instance->decoder.te_last = duration;
instance->decoder.parser_step = SubaruDecoderStepCheckDuration;
} else if(DURATION_DIFF(duration, te_long) < te_delta) {
// Long HIGH = bit 0
} else if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
subaru_add_bit(instance, false);
instance->decoder.te_last = duration;
instance->decoder.parser_step = SubaruDecoderStepCheckDuration;
} else if(duration > 3000) {
// End of transmission
if(instance->bit_count >= 64) {
if(subaru_process_data(instance)) {
instance->generic.data = instance->key;
instance->generic.data_count_bit = 64;
instance->generic.serial = instance->serial;
instance->generic.btn = instance->btn;
instance->generic.cnt = instance->cnt;
instance->generic.btn = instance->button;
instance->generic.cnt = instance->count;
uint8_t custom_btn = subaru_btn_to_custom(instance->button);
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(custom_btn);
}
subghz_custom_btn_set_max(5);
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
@@ -502,22 +376,27 @@ void subghz_protocol_decoder_subaru_feed(void* context, bool level, uint32_t dur
instance->decoder.parser_step = SubaruDecoderStepReset;
}
break;
case SubaruDecoderStepCheckDuration:
if(!level) {
if((DURATION_DIFF(duration, te_short) < te_delta) ||
(DURATION_DIFF(duration, te_long) < te_delta)) {
if(DURATION_DIFF(duration, subghz_protocol_subaru_const.te_short) < subghz_protocol_subaru_const.te_delta ||
DURATION_DIFF(duration, subghz_protocol_subaru_const.te_long) < subghz_protocol_subaru_const.te_delta) {
instance->decoder.parser_step = SubaruDecoderStepSaveDuration;
} else if(duration > 3000) {
// Gap - end of packet
if(instance->bit_count >= 64) {
if(subaru_process_data(instance)) {
instance->generic.data = instance->key;
instance->generic.data_count_bit = 64;
instance->generic.serial = instance->serial;
instance->generic.btn = instance->btn;
instance->generic.cnt = instance->cnt;
instance->generic.btn = instance->button;
instance->generic.cnt = instance->count;
uint8_t custom_btn = subaru_btn_to_custom(instance->button);
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(custom_btn);
}
subghz_custom_btn_set_max(5);
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
@@ -535,7 +414,7 @@ void subghz_protocol_decoder_subaru_feed(void* context, bool level, uint32_t dur
}
uint8_t subghz_protocol_decoder_subaru_get_hash_data(void* context) {
furi_check(context);
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
@@ -545,76 +424,230 @@ SubGhzProtocolStatus subghz_protocol_decoder_subaru_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
}
instance->generic.data = instance->key;
instance->generic.data_count_bit = 64;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
SubGhzProtocolStatus subghz_protocol_decoder_subaru_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, subghz_protocol_subaru_const.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
uint32_t temp = instance->serial;
flipper_format_write_uint32(flipper_format, "Serial", &temp, 1);
temp = instance->btn;
flipper_format_write_uint32(flipper_format, "Btn", &temp, 1);
temp = instance->cnt;
flipper_format_write_uint32(flipper_format, "Cnt", &temp, 1);
instance->key = instance->generic.data;
uint8_t b[8];
for(int i = 0; i < 8; i++) {
b[i] = (uint8_t)(instance->key >> (56 - i * 8));
}
instance->serial = ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | b[3];
instance->button = b[0] & 0x0F;
subaru_decode_count(b, &instance->count);
instance->generic.serial = instance->serial;
instance->generic.btn = instance->button;
instance->generic.cnt = instance->count;
uint8_t custom_btn = subaru_btn_to_custom(instance->button);
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(custom_btn);
}
subghz_custom_btn_set_max(5);
}
return ret;
}
SubGhzProtocolStatus
subghz_protocol_decoder_subaru_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderSubaru* instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, subghz_protocol_subaru_const.min_count_bit_for_found);
if(ret != SubGhzProtocolStatusOk) return ret;
instance->key = instance->generic.data;
flipper_format_rewind(flipper_format);
flipper_format_read_uint32(flipper_format, "Serial", &instance->serial, 1);
uint32_t btn_temp = 0;
flipper_format_read_uint32(flipper_format, "Btn", &btn_temp, 1);
instance->btn = (uint8_t)btn_temp;
uint32_t cnt_temp = 0;
flipper_format_read_uint32(flipper_format, "Cnt", &cnt_temp, 1);
instance->cnt = (uint16_t)cnt_temp;
instance->generic.serial = instance->serial;
instance->generic.btn = instance->btn;
instance->generic.cnt = instance->cnt;
return SubGhzProtocolStatusOk;
}
void subghz_protocol_decoder_subaru_get_string(void* context, FuriString* output) {
furi_check(context);
furi_assert(context);
SubGhzProtocolDecoderSubaru* instance = context;
if(subghz_custom_btn_get_original() == 0)
subghz_custom_btn_set_original(instance->btn);
subghz_custom_btn_set_max(4);
uint32_t key_hi = (uint32_t)(instance->key >> 32);
uint32_t key_lo = (uint32_t)(instance->key & 0xFFFFFFFF);
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Key:%08lX%08lX\r\n"
"Sn:%06lX Btn:%X Cnt:%04X\r\n",
"Sn:%06lX Cnt:%04X\r\n"
"Btn:%X [%s]",
instance->generic.protocol_name,
instance->generic.data_count_bit,
key_hi,
key_lo,
instance->serial,
subaru_get_btn_code(),
instance->cnt);
instance->count,
instance->button,
subaru_get_button_name(instance->button));
}
void* subghz_protocol_encoder_subaru_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderSubaru* instance = malloc(sizeof(SubGhzProtocolEncoderSubaru));
instance->base.protocol = &subghz_protocol_subaru;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 10;
instance->encoder.size_upload = 2048;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
instance->encoder.is_running = false;
instance->encoder.front = 0;
return instance;
}
void subghz_protocol_encoder_subaru_free(void* context) {
furi_assert(context);
SubGhzProtocolEncoderSubaru* instance = context;
free(instance->encoder.upload);
free(instance);
}
void subghz_protocol_encoder_subaru_stop(void* context) {
furi_assert(context);
SubGhzProtocolEncoderSubaru* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_subaru_yield(void* context) {
furi_assert(context);
SubGhzProtocolEncoderSubaru* instance = context;
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0;
}
return ret;
}
static void subghz_protocol_encoder_subaru_get_upload(SubGhzProtocolEncoderSubaru* instance) {
furi_assert(instance);
size_t index = 0;
uint32_t te_short = subghz_protocol_subaru_const.te_short;
uint32_t te_long = subghz_protocol_subaru_const.te_long;
uint32_t gap_duration = 2500;
uint32_t sync_duration = 2500;
for(int i = 0; i < 20; i++) {
instance->encoder.upload[index++] = level_duration_make(true, te_long);
instance->encoder.upload[index++] = level_duration_make(false, te_long);
}
instance->encoder.upload[index++] = level_duration_make(true, te_long);
instance->encoder.upload[index++] = level_duration_make(false, gap_duration);
instance->encoder.upload[index++] = level_duration_make(true, sync_duration);
instance->encoder.upload[index++] = level_duration_make(false, te_long);
for(int i = 63; i >= 0; i--) {
bool bit = (instance->key >> i) & 1;
if(bit) {
instance->encoder.upload[index++] = level_duration_make(true, te_short);
instance->encoder.upload[index++] = level_duration_make(false, te_short);
} else {
instance->encoder.upload[index++] = level_duration_make(true, te_long);
instance->encoder.upload[index++] = level_duration_make(false, te_short);
}
}
instance->encoder.upload[index++] = level_duration_make(true, te_long);
instance->encoder.upload[index++] = level_duration_make(false, gap_duration * 2);
instance->encoder.size_upload = index;
instance->encoder.front = 0;
}
SubGhzProtocolStatus subghz_protocol_encoder_subaru_deserialize(void* context, FlipperFormat* flipper_format) {
furi_assert(context);
SubGhzProtocolEncoderSubaru* instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
do {
ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);
if(ret != SubGhzProtocolStatusOk) {
break;
}
uint64_t original_key = instance->generic.data;
uint8_t b[8];
for(int i = 0; i < 8; i++) {
b[i] = (uint8_t)(original_key >> (56 - i * 8));
}
instance->serial = ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | b[3];
instance->button = b[0] & 0x0F;
subaru_decode_count(b, &instance->count);
uint8_t original_custom_btn = subaru_btn_to_custom(instance->button);
if(subghz_custom_btn_get_original() == 0) {
subghz_custom_btn_set_original(original_custom_btn);
}
subghz_custom_btn_set_max(5);
uint8_t selected_custom_btn;
if(subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK) {
selected_custom_btn = subghz_custom_btn_get_original();
} else {
selected_custom_btn = subghz_custom_btn_get();
}
uint8_t new_button = subaru_get_button_code(selected_custom_btn);
instance->button = new_button;
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->count = (instance->count + mult) & 0xFFFF;
b[0] = (b[0] & 0xF0) | (instance->button & 0x0F);
subaru_encode_count(b, instance->count);
instance->key = 0;
for(int i = 0; i < 8; i++) {
instance->key = (instance->key << 8) | b[i];
}
instance->generic.data = instance->key;
instance->generic.serial = instance->serial;
instance->generic.btn = instance->button;
instance->generic.cnt = instance->count;
subghz_protocol_encoder_subaru_get_upload(instance);
if(!flipper_format_rewind(flipper_format)) {
ret = SubGhzProtocolStatusErrorParserOthers;
break;
}
uint8_t key_data[sizeof(uint64_t)] = {0};
for(size_t i = 0; i < sizeof(uint64_t); i++) {
key_data[sizeof(uint64_t) - i - 1] = (instance->key >> (i * 8)) & 0xFF;
}
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
ret = SubGhzProtocolStatusErrorParserKey;
break;
}
instance->encoder.is_running = true;
ret = SubGhzProtocolStatusOk;
} while(false);
return ret;
}

View File

@@ -1,17 +1,12 @@
#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/encoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format.h>
#include "base.h"
#include "../blocks/math.h"
#define SUBGHZ_PROTOCOL_SUBARU_NAME "SUBARU"
#define SUBARU_PROTOCOL_NAME "Subaru"
typedef struct SubGhzProtocolDecoderSubaru SubGhzProtocolDecoderSubaru;
typedef struct SubGhzProtocolEncoderSubaru SubGhzProtocolEncoderSubaru;
extern const SubGhzProtocol subghz_protocol_subaru;
@@ -25,14 +20,14 @@ SubGhzProtocolStatus subghz_protocol_decoder_subaru_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_subaru_deserialize(void* context, FlipperFormat* flipper_format);
SubGhzProtocolStatus subghz_protocol_decoder_subaru_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_subaru_get_string(void* context, FuriString* output);
// Encoder functions
void* subghz_protocol_encoder_subaru_alloc(SubGhzEnvironment* environment);
void subghz_protocol_encoder_subaru_free(void* context);
SubGhzProtocolStatus
subghz_protocol_encoder_subaru_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_encoder_subaru_stop(void* context);
LevelDuration subghz_protocol_encoder_subaru_yield(void* context);
SubGhzProtocolStatus subghz_protocol_encoder_subaru_deserialize(
void* context,
FlipperFormat* flipper_format);

View File

@@ -1,10 +1,13 @@
#include "suzuki.h"
#define TAG "SuzukiProtocol"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/encoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#include "../blocks/custom_btn_i.h"
// ============================================================================
// PROTOCOL CONSTANTS
// ============================================================================
#define TAG "SuzukiProtocol"
static const SubGhzBlockConst subghz_protocol_suzuki_const = {
.te_short = 250,
@@ -13,68 +16,88 @@ static const SubGhzBlockConst subghz_protocol_suzuki_const = {
.min_count_bit_for_found = 64,
};
#define SUZUKI_PREAMBLE_COUNT 350
#define SUZUKI_GAP_TIME 2000
#define SUZUKI_GAP_DELTA 399
#define SUZUKI_GAP_TIME 2000
#define SUZUKI_GAP_DELTA 399
#define SUZUKI_MIN_PREAMBLE_COUNT 200
#define SUZUKI_ENCODER_PREAMBLE_COUNT 300
// ============================================================================
// DECODER STRUCT
// ============================================================================
typedef struct SubGhzProtocolDecoderSuzuki {
typedef struct SubGhzProtocolDecoderSuzuki
{
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint16_t header_count;
} SubGhzProtocolDecoderSuzuki;
// ============================================================================
// ENCODER STRUCT
// ============================================================================
typedef struct SubGhzProtocolEncoderSuzuki {
typedef struct SubGhzProtocolEncoderSuzuki
{
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
} SubGhzProtocolEncoderSuzuki;
// ============================================================================
// DECODER STATE MACHINE
// ============================================================================
typedef enum {
typedef enum
{
SuzukiDecoderStepReset = 0,
SuzukiDecoderStepCountPreamble = 1,
SuzukiDecoderStepDecodeData = 2,
} SuzukiDecoderStep;
// ============================================================================
// HELPER FUNCTIONS
// ============================================================================
static void suzuki_add_bit(SubGhzProtocolDecoderSuzuki* instance, uint8_t bit) {
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | bit;
instance->decoder.decode_count_bit++;
}
static const char* suzuki_get_button_name(uint8_t btn) {
switch(btn) {
case 1:
return "Panic";
case 2:
return "Boot";
case 3:
return "Lock";
case 4:
return "Unlock";
default:
return "Unknown";
static uint8_t suzuki_crc8(uint8_t* data, size_t len) {
uint8_t crc = 0x00;
for(size_t i = 0; i < len; i++) {
crc ^= data[i];
for(size_t j = 0; j < 8; j++) {
if((crc & 0x80) != 0)
crc = (uint8_t)((crc << 1) ^ 0x7F);
else
crc <<= 1;
}
}
return crc;
}
static uint8_t suzuki_calculate_crc(uint64_t data) {
uint8_t crc_data[6];
crc_data[0] = (data >> 52) & 0xFF;
crc_data[1] = (data >> 44) & 0xFF;
crc_data[2] = (data >> 36) & 0xFF;
crc_data[3] = (data >> 28) & 0xFF;
crc_data[4] = (data >> 20) & 0xFF;
crc_data[5] = (data >> 12) & 0xFF;
return suzuki_crc8(crc_data, 6);
}
static uint8_t suzuki_custom_to_btn(uint8_t custom) {
switch(custom) {
case 1: return 3;
case 2: return 4;
case 3: return 2;
case 4: return 1;
default: return 4;
}
}
// ============================================================================
// PROTOCOL DEFINITION
// ============================================================================
static uint8_t suzuki_btn_to_custom(uint8_t btn) {
switch(btn) {
case 1: return 4;
case 2: return 3;
case 3: return 1;
case 4: return 2;
default: return 2;
}
}
static bool suzuki_verify_crc(uint64_t data) {
uint8_t received_crc = (data >> 4) & 0xFF;
uint8_t calculated_crc = suzuki_calculate_crc(data);
return (received_crc == calculated_crc);
}
const SubGhzProtocolDecoder subghz_protocol_suzuki_decoder = {
.alloc = subghz_protocol_decoder_suzuki_alloc,
@@ -95,55 +118,49 @@ const SubGhzProtocolEncoder subghz_protocol_suzuki_encoder = {
.yield = subghz_protocol_encoder_suzuki_yield,
};
const SubGhzProtocol suzuki_protocol = {
.name = SUZUKI_PROTOCOL_NAME,
const SubGhzProtocol subghz_protocol_suzuki = {
.name = SUBGHZ_PROTOCOL_SUZUKI_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM |
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_suzuki_decoder,
.encoder = &subghz_protocol_suzuki_encoder,
};
// ============================================================================
// DECODER IMPLEMENTATION
// ============================================================================
void* subghz_protocol_decoder_suzuki_alloc(SubGhzEnvironment* environment) {
void *subghz_protocol_decoder_suzuki_alloc(SubGhzEnvironment *environment)
{
UNUSED(environment);
SubGhzProtocolDecoderSuzuki* instance = malloc(sizeof(SubGhzProtocolDecoderSuzuki));
instance->base.protocol = &suzuki_protocol;
SubGhzProtocolDecoderSuzuki *instance = malloc(sizeof(SubGhzProtocolDecoderSuzuki));
instance->base.protocol = &subghz_protocol_suzuki;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_suzuki_free(void* context) {
furi_check(context);
SubGhzProtocolDecoderSuzuki* instance = context;
void subghz_protocol_decoder_suzuki_free(void *context)
{
furi_assert(context);
SubGhzProtocolDecoderSuzuki *instance = context;
free(instance);
}
void subghz_protocol_decoder_suzuki_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderSuzuki* instance = context;
void subghz_protocol_decoder_suzuki_reset(void *context)
{
furi_assert(context);
SubGhzProtocolDecoderSuzuki *instance = context;
instance->decoder.parser_step = SuzukiDecoderStepReset;
}
void subghz_protocol_decoder_suzuki_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderSuzuki* instance = context;
void subghz_protocol_decoder_suzuki_feed(void *context, bool level, uint32_t duration)
{
furi_assert(context);
SubGhzProtocolDecoderSuzuki *instance = context;
switch(instance->decoder.parser_step) {
switch (instance->decoder.parser_step)
{
case SuzukiDecoderStepReset:
// Wait for HIGH pulse (~250µs) to start preamble
if(!level) {
return;
}
if(DURATION_DIFF(duration, subghz_protocol_suzuki_const.te_short) >
subghz_protocol_suzuki_const.te_delta) {
return;
}
if (!level) return;
if (DURATION_DIFF(duration, subghz_protocol_suzuki_const.te_short) > subghz_protocol_suzuki_const.te_delta) return;
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = SuzukiDecoderStepCountPreamble;
@@ -151,78 +168,86 @@ void subghz_protocol_decoder_suzuki_feed(void* context, bool level, uint32_t dur
break;
case SuzukiDecoderStepCountPreamble:
if(level) {
// HIGH pulse
if(instance->header_count >= 300) {
if(DURATION_DIFF(duration, subghz_protocol_suzuki_const.te_long) <=
subghz_protocol_suzuki_const.te_delta) {
if (level)
{
if (instance->header_count >= SUZUKI_MIN_PREAMBLE_COUNT)
{
if (DURATION_DIFF(duration, subghz_protocol_suzuki_const.te_long) <= subghz_protocol_suzuki_const.te_delta)
{
instance->decoder.parser_step = SuzukiDecoderStepDecodeData;
suzuki_add_bit(instance, 1);
}
}
} else {
if(DURATION_DIFF(duration, subghz_protocol_suzuki_const.te_short) <=
subghz_protocol_suzuki_const.te_delta) {
}
else
{
if (DURATION_DIFF(duration, subghz_protocol_suzuki_const.te_short) <= subghz_protocol_suzuki_const.te_delta)
{
instance->decoder.te_last = duration;
instance->header_count++;
} else {
}
else
{
instance->decoder.parser_step = SuzukiDecoderStepReset;
}
}
break;
case SuzukiDecoderStepDecodeData:
if(level) {
// HIGH pulse - determines bit value
if(duration < subghz_protocol_suzuki_const.te_long) {
if (level)
{
if (duration < subghz_protocol_suzuki_const.te_long)
{
uint32_t diff_long = 500 - duration;
if(diff_long > 99) {
uint32_t diff_short;
if(duration < 250) {
diff_short = 250 - duration;
} else {
diff_short = duration - 250;
}
if(diff_short <= 99) {
suzuki_add_bit(instance, 0);
}
} else {
if (diff_long > 99)
{
uint32_t diff_short = (duration < 250) ? (250 - duration) : (duration - 250);
if (diff_short <= 99) suzuki_add_bit(instance, 0);
}
else
{
suzuki_add_bit(instance, 1);
}
} else {
}
else
{
uint32_t diff_long = duration - 500;
if(diff_long <= 99) {
suzuki_add_bit(instance, 1);
}
if (diff_long <= 99) suzuki_add_bit(instance, 1);
}
} else {
// LOW pulse - check for gap (end of transmission)
uint32_t diff_gap;
if(duration < SUZUKI_GAP_TIME) {
diff_gap = SUZUKI_GAP_TIME - duration;
} else {
diff_gap = duration - SUZUKI_GAP_TIME;
}
if(diff_gap <= SUZUKI_GAP_DELTA) {
if(instance->decoder.decode_count_bit == 64) {
}
else
{
uint32_t diff_gap = (duration < SUZUKI_GAP_TIME) ? (SUZUKI_GAP_TIME - duration) : (duration - SUZUKI_GAP_TIME);
if (diff_gap <= SUZUKI_GAP_DELTA)
{
if (instance->decoder.decode_count_bit == 64)
{
instance->generic.data = instance->decoder.decode_data;
instance->generic.data_count_bit = 64;
uint64_t data = instance->generic.data;
uint32_t data_high = (uint32_t)(data >> 32);
uint32_t data_low = (uint32_t)data;
if (suzuki_verify_crc(instance->generic.data))
{
uint64_t data = instance->generic.data;
uint32_t data_high = (uint32_t)(data >> 32);
uint32_t data_low = (uint32_t)data;
instance->generic.serial = ((data_high & 0xFFF) << 16) | (data_low >> 16);
instance->generic.btn = (data_low >> 12) & 0xF;
instance->generic.cnt = (data_high << 4) >> 16;
instance->generic.serial = ((data_high & 0xFFF) << 16) | (data_low >> 16);
instance->generic.btn = (data_low >> 12) & 0xF;
instance->generic.cnt = (data_high << 4) >> 16;
if(subghz_custom_btn_get_original() == 0) {
uint8_t custom = suzuki_btn_to_custom(instance->generic.btn);
subghz_custom_btn_set_original(custom);
}
subghz_custom_btn_set_max(4);
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
if (instance->base.callback)
{
instance->base.callback(&instance->base, instance->base.context);
}
}
}
instance->decoder.decode_data = 0;
instance->decoder.decode_count_bit = 0;
instance->decoder.parser_step = SuzukiDecoderStepReset;
@@ -232,186 +257,238 @@ void subghz_protocol_decoder_suzuki_feed(void* context, bool level, uint32_t dur
}
}
uint8_t subghz_protocol_decoder_suzuki_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderSuzuki* instance = context;
return subghz_protocol_blocks_get_hash_data(
&instance->decoder, (instance->generic.data_count_bit / 8) + 1);
uint8_t subghz_protocol_decoder_suzuki_get_hash_data(void *context)
{
furi_assert(context);
SubGhzProtocolDecoderSuzuki *instance = context;
return subghz_protocol_blocks_get_hash_data(&instance->decoder, (instance->generic.data_count_bit / 8) + 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_suzuki_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderSuzuki* instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
ret = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(ret == SubGhzProtocolStatusOk) {
// Save CRC
uint32_t crc = (instance->generic.data >> 4) & 0xFF;
flipper_format_write_uint32(flipper_format, "CRC", &crc, 1);
// Save decoded fields
flipper_format_write_uint32(flipper_format, "Serial", &instance->generic.serial, 1);
uint32_t temp = instance->generic.btn;
flipper_format_write_uint32(flipper_format, "Btn", &temp, 1);
flipper_format_write_uint32(flipper_format, "Cnt", &instance->generic.cnt, 1);
}
SubGhzProtocolStatus subghz_protocol_decoder_suzuki_serialize(void *context, FlipperFormat *flipper_format, SubGhzRadioPreset *preset)
{
furi_assert(context);
SubGhzProtocolDecoderSuzuki *instance = context;
uint32_t temp_serial = instance->generic.serial;
uint32_t temp_cnt = instance->generic.cnt;
uint32_t temp_btn = instance->generic.btn;
instance->generic.serial = 0;
instance->generic.cnt = 0;
instance->generic.btn = 0;
SubGhzProtocolStatus ret = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
instance->generic.serial = temp_serial;
instance->generic.cnt = temp_cnt;
instance->generic.btn = temp_btn;
return ret;
}
SubGhzProtocolStatus
subghz_protocol_decoder_suzuki_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderSuzuki* instance = context;
return subghz_block_generic_deserialize(&instance->generic, flipper_format);
SubGhzProtocolStatus subghz_protocol_decoder_suzuki_deserialize(void *context, FlipperFormat *flipper_format)
{
furi_assert(context);
SubGhzProtocolDecoderSuzuki *instance = context;
SubGhzProtocolStatus ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);
if(ret == SubGhzProtocolStatusOk) {
uint64_t data = instance->generic.data;
instance->generic.cnt = (uint32_t)((data >> 44) & 0xFFFFF);
instance->generic.serial = (uint32_t)((data >> 16) & 0x0FFFFFFF);
instance->generic.btn = (uint8_t)((data >> 12) & 0xF);
if(subghz_custom_btn_get_original() == 0) {
uint8_t custom = suzuki_btn_to_custom(instance->generic.btn);
subghz_custom_btn_set_original(custom);
}
subghz_custom_btn_set_max(4);
}
return ret;
}
void subghz_protocol_decoder_suzuki_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderSuzuki* instance = context;
static const char *suzuki_get_button_name(uint8_t btn)
{
switch (btn)
{
case 1: return "Panic";
case 2: return "Trunk";
case 3: return "Lock";
case 4: return "Unlock";
default: return "Unknown";
}
}
void subghz_protocol_decoder_suzuki_get_string(void *context, FuriString *output) {
furi_assert(context);
SubGhzProtocolDecoderSuzuki *instance = context;
uint64_t data = instance->generic.data;
uint32_t key_high = (data >> 32) & 0xFFFFFFFF;
uint32_t key_low = data & 0xFFFFFFFF;
uint8_t crc = (data >> 4) & 0xFF;
uint8_t received_crc = (data >> 4) & 0xFF;
uint8_t calculated_crc = suzuki_calculate_crc(data);
bool crc_valid = (received_crc == calculated_crc);
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Key:%08lX%08lX\r\n"
"Sn:%07lX Btn:%X %s\r\n"
"Cnt:%04lX CRC:%02X\r\n",
"Sn:%07lX Cnt:%04lX\r\n"
"Btn:%02X:[%s]\r\n"
"CRC:%02X %s",
instance->generic.protocol_name,
instance->generic.data_count_bit,
key_high,
key_low,
instance->generic.serial,
instance->generic.cnt,
instance->generic.btn,
suzuki_get_button_name(instance->generic.btn),
instance->generic.cnt,
crc);
received_crc,
crc_valid ? "(OK)" : "(FAIL)");
}
// ============================================================================
// ENCODER IMPLEMENTATION
// ============================================================================
void* subghz_protocol_encoder_suzuki_alloc(SubGhzEnvironment* environment) {
void *subghz_protocol_encoder_suzuki_alloc(SubGhzEnvironment *environment)
{
UNUSED(environment);
SubGhzProtocolEncoderSuzuki* instance = malloc(sizeof(SubGhzProtocolEncoderSuzuki));
instance->base.protocol = &suzuki_protocol;
SubGhzProtocolEncoderSuzuki *instance = malloc(sizeof(SubGhzProtocolEncoderSuzuki));
instance->base.protocol = &subghz_protocol_suzuki;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.upload = NULL;
instance->encoder.is_running = false;
return instance;
}
void subghz_protocol_encoder_suzuki_free(void* context) {
furi_check(context);
SubGhzProtocolEncoderSuzuki* instance = context;
void subghz_protocol_encoder_suzuki_free(void *context)
{
furi_assert(context);
SubGhzProtocolEncoderSuzuki *instance = context;
if(instance->encoder.upload) {
free(instance->encoder.upload);
}
free(instance);
}
/**
* Build the upload buffer for transmission
* Signal format: 350 preamble pairs (SHORT HIGH/SHORT LOW) + 64 data bits + gap
* Data encoding: SHORT HIGH = 0, LONG HIGH = 1
*/
static void subghz_protocol_encoder_suzuki_get_upload(SubGhzProtocolEncoderSuzuki* instance) {
furi_check(instance);
size_t index = 0;
// Free old upload if exists
if(instance->encoder.upload) {
free(instance->encoder.upload);
}
// Allocate: preamble pairs + data bits (each has HIGH + LOW) + end gap
instance->encoder.size_upload = (SUZUKI_PREAMBLE_COUNT * 2) + (64 * 2) + 1;
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
// Preamble: SHORT HIGH / SHORT LOW pairs
for(size_t i = 0; i < SUZUKI_PREAMBLE_COUNT; i++) {
instance->encoder.upload[index++] =
level_duration_make(true, subghz_protocol_suzuki_const.te_short);
instance->encoder.upload[index++] =
level_duration_make(false, subghz_protocol_suzuki_const.te_short);
}
// Data: 64 bits, MSB first
// SHORT HIGH (~250µs) = 0, LONG HIGH (~500µs) = 1
for(int bit = 63; bit >= 0; bit--) {
if((instance->generic.data >> bit) & 1) {
instance->encoder.upload[index++] =
level_duration_make(true, subghz_protocol_suzuki_const.te_long);
} else {
instance->encoder.upload[index++] =
level_duration_make(true, subghz_protocol_suzuki_const.te_short);
}
instance->encoder.upload[index++] =
level_duration_make(false, subghz_protocol_suzuki_const.te_short);
}
// End gap
instance->encoder.upload[index++] = level_duration_make(false, SUZUKI_GAP_TIME);
instance->encoder.size_upload = index;
instance->encoder.front = 0;
}
SubGhzProtocolStatus
subghz_protocol_encoder_suzuki_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolEncoderSuzuki* instance = context;
SubGhzProtocolStatus subghz_protocol_encoder_suzuki_deserialize(void *context, FlipperFormat *flipper_format)
{
furi_assert(context);
SubGhzProtocolEncoderSuzuki *instance = context;
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
do {
ret = subghz_block_generic_deserialize(&instance->generic, flipper_format);
if(ret != SubGhzProtocolStatusOk) {
if (ret != SubGhzProtocolStatusOk) break;
uint64_t data = instance->generic.data;
instance->generic.cnt = (uint32_t)((data >> 44) & 0xFFFFF);
instance->generic.serial = (uint32_t)((data >> 16) & 0x0FFFFFFF);
instance->generic.btn = (uint8_t)((data >> 12) & 0xF);
if(subghz_custom_btn_get_original() == 0) {
uint8_t custom = suzuki_btn_to_custom(instance->generic.btn);
subghz_custom_btn_set_original(custom);
}
subghz_custom_btn_set_max(4);
uint32_t mult = furi_hal_subghz_get_rolling_counter_mult();
instance->generic.cnt = (instance->generic.cnt + mult) & 0xFFFFF;
uint8_t selected = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK ?
subghz_custom_btn_get_original() :
subghz_custom_btn_get();
uint8_t btn = suzuki_custom_to_btn(selected);
instance->generic.btn = btn;
uint64_t new_data = 0;
new_data |= ((uint64_t)(instance->generic.cnt & 0xFFFFF) << 44);
new_data |= ((uint64_t)(instance->generic.serial & 0x0FFFFFFF) << 16);
new_data |= ((uint64_t)(instance->generic.btn & 0xF) << 12);
uint8_t crc = suzuki_calculate_crc(new_data);
new_data |= ((uint64_t)crc << 4);
instance->generic.data = new_data;
if(!flipper_format_rewind(flipper_format)) {
ret = SubGhzProtocolStatusErrorParserOthers;
break;
}
subghz_protocol_encoder_suzuki_get_upload(instance);
uint8_t key_data[8];
for(size_t i = 0; i < 8; i++) {
key_data[i] = (instance->generic.data >> (56 - i * 8)) & 0xFF;
}
if(!flipper_format_update_hex(flipper_format, "Key", key_data, 8)) {
ret = SubGhzProtocolStatusErrorParserKey;
break;
}
size_t preamble_count = SUZUKI_ENCODER_PREAMBLE_COUNT;
size_t bit_count = 64;
instance->encoder.size_upload = (preamble_count * 2) + (bit_count * 2) + 1;
if(instance->encoder.upload) {
free(instance->encoder.upload);
}
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
size_t index = 0;
for (size_t i = 0; i < preamble_count; i++) {
instance->encoder.upload[index++] = level_duration_make(true, subghz_protocol_suzuki_const.te_short);
instance->encoder.upload[index++] = level_duration_make(false, subghz_protocol_suzuki_const.te_short);
}
for (size_t i = 0; i < bit_count; i++) {
uint8_t bit = (instance->generic.data >> (63 - i)) & 1;
if (bit) {
instance->encoder.upload[index++] = level_duration_make(true, subghz_protocol_suzuki_const.te_long);
} else {
instance->encoder.upload[index++] = level_duration_make(true, subghz_protocol_suzuki_const.te_short);
}
instance->encoder.upload[index++] = level_duration_make(false, subghz_protocol_suzuki_const.te_short);
}
instance->encoder.upload[index++] = level_duration_make(false, SUZUKI_GAP_TIME);
instance->encoder.is_running = true;
instance->encoder.repeat = 10;
instance->encoder.repeat = 5;
instance->encoder.front = 0;
ret = SubGhzProtocolStatusOk;
} while(false);
} while (false);
return ret;
}
void subghz_protocol_encoder_suzuki_stop(void* context) {
furi_check(context);
SubGhzProtocolEncoderSuzuki* instance = context;
void subghz_protocol_encoder_suzuki_stop(void *context)
{
SubGhzProtocolEncoderSuzuki *instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_suzuki_yield(void* context) {
furi_check(context);
SubGhzProtocolEncoderSuzuki* instance = context;
LevelDuration subghz_protocol_encoder_suzuki_yield(void *context)
{
SubGhzProtocolEncoderSuzuki *instance = context;
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
if (instance->encoder.repeat == 0 || !instance->encoder.is_running)
{
instance->encoder.is_running = false;
return level_duration_reset();
}
LevelDuration ret = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
if (++instance->encoder.front == instance->encoder.size_upload)
{
instance->encoder.repeat--;
instance->encoder.front = 0;
}

View File

@@ -1,20 +1,15 @@
#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/encoder.h>
#include <lib/subghz/blocks/generic.h>
#include <lib/subghz/blocks/math.h>
#include <flipper_format/flipper_format.h>
#include "base.h"
#include "../blocks/math.h"
#define SUZUKI_PROTOCOL_NAME "Suzuki"
#define SUBGHZ_PROTOCOL_SUZUKI_NAME "SUZUKI"
extern const SubGhzProtocol suzuki_protocol;
typedef struct SubGhzProtocolDecoderSuzuki SubGhzProtocolDecoderSuzuki;
typedef struct SubGhzProtocolEncoderSuzuki SubGhzProtocolEncoderSuzuki;
extern const SubGhzProtocol subghz_protocol_suzuki;
// Decoder functions
void* subghz_protocol_decoder_suzuki_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_suzuki_free(void* context);
void subghz_protocol_decoder_suzuki_reset(void* context);
@@ -24,14 +19,11 @@ SubGhzProtocolStatus subghz_protocol_decoder_suzuki_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_suzuki_deserialize(void* context, FlipperFormat* flipper_format);
SubGhzProtocolStatus subghz_protocol_decoder_suzuki_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_suzuki_get_string(void* context, FuriString* output);
// Encoder functions
void* subghz_protocol_encoder_suzuki_alloc(SubGhzEnvironment* environment);
void subghz_protocol_encoder_suzuki_free(void* context);
SubGhzProtocolStatus
subghz_protocol_encoder_suzuki_deserialize(void* context, FlipperFormat* flipper_format);
SubGhzProtocolStatus subghz_protocol_encoder_suzuki_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_encoder_suzuki_stop(void* context);
LevelDuration subghz_protocol_encoder_suzuki_yield(void* context);