mirror of
https://github.com/D4C1-Labs/Flipper-ARF.git
synced 2026-03-29 18:40:01 +00:00
Compare commits
2 Commits
dev-76fbf7
...
dev-03897a
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
03897a406e | ||
|
|
09a7668fe7 |
@@ -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 = {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
Reference in New Issue
Block a user