Compare commits

...

1 Commits

Author SHA1 Message Date
d4rks1d33 018a5feb29 Add Toyota Keeloq (only decode)
Build Dev Firmware / build (push) Failing after 14m50s
2026-06-07 23:45:08 -03:00
9 changed files with 671 additions and 1685 deletions
-2
View File
@@ -90,5 +90,3 @@ lib/subghz/protocols/honda_rolling.c
lib/subghz/protocols/honda_rolling.h
lib/subghz/protocols/honda_pandora.c
lib/subghz/protocols/honda_pandora.h
lib/subghz/protocols/toyota.c
lib/subghz/protocols/toyota.h
-796
View File
@@ -1,796 +0,0 @@
#include "honda_static.h"
#define HONDA_STATIC_BIT_COUNT 64
#define HONDA_STATIC_MIN_SYMBOLS 36
#define HONDA_STATIC_SHORT_BASE_US 28
#define HONDA_STATIC_SHORT_SPAN_US 70
#define HONDA_STATIC_LONG_BASE_US 61
#define HONDA_STATIC_LONG_SPAN_US 130
#define HONDA_STATIC_SYNC_TIME_US 700
#define HONDA_STATIC_ELEMENT_TIME_US 63
#define HONDA_STATIC_UPLOAD_CAPACITY 512
#define HONDA_STATIC_SYMBOL_CAPACITY 512
#define HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT 160
#define HONDA_STATIC_PREAMBLE_MAX_TRANSITIONS 19
static const uint8_t honda_static_encoder_button_map[4] = {0x02, 0x04, 0x08, 0x05};
static const char* const honda_static_button_names[9] = {
"LOCK",
"UNLOCK",
"UNKNOWN",
"TRUNK",
"REMOTE START",
"UNKNOWN",
"UNKNOWN",
"PANIC",
"LOCK x2",
};
typedef struct {
uint8_t button;
uint8_t _reserved_01[3];
uint32_t serial;
uint32_t counter;
uint8_t checksum;
uint8_t _reserved_0d[3];
} HondaStaticFields;
struct SubGhzProtocolDecoderHondaStatic {
SubGhzProtocolDecoderBase base;
uint32_t _reserved_0c;
SubGhzBlockDecoder decoder;
uint32_t _reserved_20;
SubGhzBlockGeneric generic;
uint16_t packet_bit_count;
uint8_t _reserved_5a;
uint8_t _reserved_5b;
uint8_t symbols[HONDA_STATIC_SYMBOL_CAPACITY];
uint16_t symbols_count;
HondaStaticFields decoded;
uint8_t decoded_valid;
};
struct SubGhzProtocolEncoderHondaStatic {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
HondaStaticFields decoded;
uint8_t tx_button;
uint8_t _reserved_69[3];
};
static uint64_t honda_static_bytes_to_u64_be(const uint8_t bytes[8]) {
uint64_t value = 0;
for(size_t i = 0; i < 8; i++) {
value = (value << 8U) | bytes[i];
}
return value;
}
static void honda_static_u64_to_bytes_be(uint64_t value, uint8_t bytes[8]) {
for(size_t i = 0; i < 8; i++) {
bytes[7U - i] = (uint8_t)(value & 0xFFU);
value >>= 8U;
}
}
static uint8_t honda_static_get_bits(const uint8_t* data, uint8_t start, uint8_t count) {
uint32_t value = 0;
for(uint8_t i = 0; i < count; i++) {
const uint8_t bit_index = start + i;
const uint8_t byte = data[bit_index >> 3U];
const uint8_t shift = (uint8_t)(~bit_index) & 0x07U;
value = (value << 1U) | ((byte >> shift) & 1U);
}
return (uint8_t)value;
}
static uint32_t honda_static_get_bits_u32(const uint8_t* data, uint8_t start, uint8_t count) {
uint32_t value = 0;
for(uint8_t i = 0; i < count; i++) {
const uint8_t bit_index = start + i;
const uint8_t byte = data[bit_index >> 3U];
const uint8_t shift = (uint8_t)(~bit_index) & 0x07U;
value = (value << 1U) | ((byte >> shift) & 1U);
}
return value;
}
static void honda_static_set_bits(uint8_t* data, uint8_t start, uint8_t count, uint32_t value) {
for(uint8_t i = 0; i < count; i++) {
const uint8_t bit_index = start + i;
const uint8_t byte_index = bit_index >> 3U;
const uint8_t shift = ((uint8_t)~bit_index) & 0x07U;
const uint8_t mask = (uint8_t)(1U << shift);
const bool bit = ((value >> (count - 1U - i)) & 1U) != 0U;
if(bit) {
data[byte_index] |= mask;
} else {
data[byte_index] &= (uint8_t)~mask;
}
}
}
static uint8_t honda_static_level_u8(bool level) {
return level ? 1U : 0U;
}
static uint8_t honda_static_sym_u8(uint8_t stored) {
return stored ? 1U : 0U;
}
static uint8_t honda_static_reverse_bits8(uint8_t value) {
value = (uint8_t)(((value >> 4U) | (value << 4U)) & 0xFFU);
value = (uint8_t)(((value & 0x33U) << 2U) | ((value >> 2U) & 0x33U));
value = (uint8_t)(((value & 0x55U) << 1U) | ((value >> 1U) & 0x55U));
return value;
}
static bool honda_static_is_valid_button(uint8_t button) {
if(button > 9U) {
return false;
}
return ((0x336U >> button) & 1U) != 0U;
}
static bool honda_static_is_valid_serial(uint32_t serial) {
return (serial != 0U) && (serial != 0x0FFFFFFFU);
}
static uint8_t honda_static_encoder_remap_button(uint8_t button) {
if(button < 2U) {
return 1U;
}
button -= 2U;
if(button <= 3U) {
return honda_static_encoder_button_map[button];
}
return 1U;
}
static const char* honda_static_button_name(uint8_t button) {
if((button >= 1U) && (button <= COUNT_OF(honda_static_button_names))) {
return honda_static_button_names[button - 1U];
}
return "Unknown";
}
static uint8_t honda_static_compact_bytes_checksum(const uint8_t compact[8]) {
const uint8_t canonical[7] = {
(uint8_t)((compact[0] << 4U) | (compact[1] >> 4U)),
(uint8_t)((compact[1] << 4U) | (compact[2] >> 4U)),
(uint8_t)((compact[2] << 4U) | (compact[3] >> 4U)),
(uint8_t)((compact[3] << 4U) | (compact[4] >> 4U)),
compact[5],
compact[6],
compact[7],
};
uint8_t checksum = 0U;
for(size_t i = 0; i < COUNT_OF(canonical); i++) {
checksum ^= canonical[i];
}
return checksum;
}
static void honda_static_unpack_compact(uint64_t key, HondaStaticFields* fields) {
uint8_t compact[8];
honda_static_u64_to_bytes_be(key, compact);
memset(fields, 0, sizeof(*fields));
fields->button = compact[0] & 0x0FU;
fields->serial = ((uint32_t)compact[1] << 20U) | ((uint32_t)compact[2] << 12U) |
((uint32_t)compact[3] << 4U) | ((uint32_t)compact[4] >> 4U);
fields->counter = ((uint32_t)compact[5] << 16U) | ((uint32_t)compact[6] << 8U) |
(uint32_t)compact[7];
fields->checksum = honda_static_compact_bytes_checksum(compact);
}
static uint64_t honda_static_pack_compact(const HondaStaticFields* fields) {
uint8_t compact[8];
compact[0] = fields->button & 0x0FU;
compact[1] = (uint8_t)(fields->serial >> 20U);
compact[2] = (uint8_t)(fields->serial >> 12U);
compact[3] = (uint8_t)(fields->serial >> 4U);
compact[4] = (uint8_t)(fields->serial << 4U);
compact[5] = (uint8_t)(fields->counter >> 16U);
compact[6] = (uint8_t)(fields->counter >> 8U);
compact[7] = (uint8_t)fields->counter;
return honda_static_bytes_to_u64_be(compact);
}
static void honda_static_build_packet_bytes(const HondaStaticFields* fields, uint8_t packet[8]) {
memset(packet, 0, 8);
honda_static_set_bits(packet, 0, 4, fields->button & 0x0FU);
honda_static_set_bits(packet, 4, 28, fields->serial);
honda_static_set_bits(packet, 32, 24, fields->counter);
uint8_t checksum = 0U;
for(size_t i = 0; i < 7; i++) {
checksum ^= packet[i];
}
honda_static_set_bits(packet, 56, 8, checksum);
}
static bool
honda_static_validate_forward_packet(const uint8_t packet[9], HondaStaticFields* fields) {
const uint8_t button = honda_static_get_bits(packet, 0, 4);
const uint32_t serial = honda_static_get_bits_u32(packet, 4, 28);
const uint32_t counter = honda_static_get_bits_u32(packet, 32, 24);
const uint8_t checksum = honda_static_get_bits(packet, 56, 8);
uint8_t checksum_calc = 0U;
for(size_t i = 0; i < 7; i++) {
checksum_calc ^= packet[i];
}
if(checksum != checksum_calc) {
return false;
}
if(!honda_static_is_valid_button(button)) {
return false;
}
if(!honda_static_is_valid_serial(serial)) {
return false;
}
fields->button = button;
fields->serial = serial;
fields->counter = counter;
fields->checksum = checksum;
return true;
}
static bool
honda_static_validate_reverse_packet(const uint8_t packet[9], HondaStaticFields* fields) {
uint8_t reversed[9];
for(size_t i = 0; i < COUNT_OF(reversed); i++) {
reversed[i] = honda_static_reverse_bits8(packet[i]);
}
const uint8_t button = honda_static_get_bits(reversed, 0, 4);
const uint32_t serial = honda_static_get_bits_u32(reversed, 4, 28);
const uint32_t counter = honda_static_get_bits_u32(reversed, 32, 24);
uint8_t checksum = 0U;
for(size_t i = 0; i < 7; i++) {
checksum ^= reversed[i];
}
if(!honda_static_is_valid_button(button)) {
return false;
}
if(!honda_static_is_valid_serial(serial)) {
return false;
}
fields->button = button;
fields->serial = serial;
fields->counter = counter;
fields->checksum = checksum;
return true;
}
static bool honda_static_manchester_pack_64(
const uint8_t* symbols,
uint16_t count,
uint16_t start_pos,
bool inverted,
uint8_t packet[9],
uint16_t* out_bit_count) {
memset(packet, 0, 9);
uint16_t pos = start_pos;
uint16_t bit_count = 0U;
while((uint16_t)(pos + 1U) < count) {
if(bit_count >= HONDA_STATIC_BIT_COUNT) {
break;
}
const uint8_t a = honda_static_sym_u8(symbols[pos]);
const uint8_t b = honda_static_sym_u8(symbols[pos + 1U]);
if(a == b) {
pos++;
continue;
}
bool bit = false;
if(inverted) {
bit = (a == 0U) && (b == 1U);
} else {
bit = (a == 1U) && (b == 0U);
}
if(bit) {
packet[bit_count >> 3U] |= (uint8_t)(1U << (((uint8_t)~bit_count) & 0x07U));
}
bit_count++;
pos += 2U;
}
if(out_bit_count) {
*out_bit_count = bit_count;
}
return bit_count >= HONDA_STATIC_BIT_COUNT;
}
static bool honda_static_parse_symbols(SubGhzProtocolDecoderHondaStatic* instance, bool inverted) {
const uint16_t count = instance->symbols_count;
const uint8_t* symbols = instance->symbols;
uint16_t index = 1U;
uint16_t transitions = 0U;
while(index < count) {
if(honda_static_sym_u8(symbols[index]) != honda_static_sym_u8(symbols[index - 1U])) {
transitions++;
} else {
if(transitions > HONDA_STATIC_PREAMBLE_MAX_TRANSITIONS) {
break;
}
transitions = 0U;
}
index++;
}
if(index >= count) {
return false;
}
while(((uint16_t)(index + 1U) < count) &&
(honda_static_sym_u8(symbols[index]) == honda_static_sym_u8(symbols[index + 1U]))) {
index++;
}
const uint16_t data_start = index;
uint8_t packet[9] = {0};
uint16_t bit_count = 0U;
if(!honda_static_manchester_pack_64(symbols, count, data_start, inverted, packet, &bit_count)) {
return false;
}
if(honda_static_validate_forward_packet(packet, &instance->decoded)) {
instance->decoded_valid = 1U;
return true;
}
if(inverted) {
return false;
}
if(honda_static_validate_reverse_packet(packet, &instance->decoded)) {
instance->decoded_valid = 1U;
return true;
}
return false;
}
static void honda_static_decoder_commit(SubGhzProtocolDecoderHondaStatic* instance) {
instance->packet_bit_count = HONDA_STATIC_BIT_COUNT;
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
instance->generic.data = honda_static_pack_compact(&instance->decoded);
instance->generic.serial = instance->decoded.serial;
instance->generic.cnt = instance->decoded.counter;
instance->generic.btn = instance->decoded.button;
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
}
static void honda_static_build_upload(SubGhzProtocolEncoderHondaStatic* instance) {
uint8_t packet[8];
honda_static_build_packet_bytes(&instance->decoded, packet);
size_t index = 0U;
instance->encoder.upload[index++] = level_duration_make(true, HONDA_STATIC_SYNC_TIME_US);
for(size_t i = 0; i < HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT; i++) {
instance->encoder.upload[index++] =
level_duration_make((i & 1U) != 0U, HONDA_STATIC_ELEMENT_TIME_US);
}
for(uint8_t bit = 0U; bit < HONDA_STATIC_BIT_COUNT; bit++) {
const bool value = ((packet[bit >> 3U] >> (((uint8_t)~bit) & 0x07U)) & 1U) != 0U;
instance->encoder.upload[index++] =
level_duration_make(!value, HONDA_STATIC_ELEMENT_TIME_US);
instance->encoder.upload[index++] =
level_duration_make(value, HONDA_STATIC_ELEMENT_TIME_US);
}
const bool last_bit = (packet[7] & 1U) != 0U;
instance->encoder.upload[index++] = level_duration_make(!last_bit, HONDA_STATIC_SYNC_TIME_US);
instance->encoder.front = 0U;
instance->encoder.size_upload = index;
}
static bool honda_static_read_hex_u64(FlipperFormat* ff, uint64_t* out_key) {
FuriString* tmp = furi_string_alloc();
if(!tmp) return false;
bool ok = false;
do {
if(!flipper_format_rewind(ff) || !flipper_format_read_string(ff, "Key", tmp)) break;
const char* key_str = furi_string_get_cstr(tmp);
uint64_t key = 0;
size_t hex_pos = 0;
for(size_t i = 0; key_str[i] && hex_pos < 16; i++) {
char c = key_str[i];
if(c == ' ') continue;
uint8_t nibble;
if(c >= '0' && c <= '9')
nibble = c - '0';
else if(c >= 'A' && c <= 'F')
nibble = c - 'A' + 10;
else if(c >= 'a' && c <= 'f')
nibble = c - 'a' + 10;
else
break;
key = (key << 4) | nibble;
hex_pos++;
}
if(hex_pos != 16) break;
*out_key = key;
ok = true;
} while(false);
furi_string_free(tmp);
return ok;
}
const SubGhzProtocolDecoder subghz_protocol_honda_static_decoder = {
.alloc = subghz_protocol_decoder_honda_static_alloc,
.free = subghz_protocol_decoder_honda_static_free,
.feed = subghz_protocol_decoder_honda_static_feed,
.reset = subghz_protocol_decoder_honda_static_reset,
.get_hash_data = subghz_protocol_decoder_honda_static_get_hash_data,
.serialize = subghz_protocol_decoder_honda_static_serialize,
.deserialize = subghz_protocol_decoder_honda_static_deserialize,
.get_string = subghz_protocol_decoder_honda_static_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_honda_static_encoder = {
.alloc = subghz_protocol_encoder_honda_static_alloc,
.free = subghz_protocol_encoder_honda_static_free,
.deserialize = subghz_protocol_encoder_honda_static_deserialize,
.stop = subghz_protocol_encoder_honda_static_stop,
.yield = subghz_protocol_encoder_honda_static_yield,
};
const SubGhzProtocol honda_static_protocol = {
.name = HONDA_STATIC_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 |
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load |
SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_honda_static_decoder,
.encoder = &subghz_protocol_honda_static_encoder,
};
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderHondaStatic* instance = malloc(sizeof(SubGhzProtocolEncoderHondaStatic));
furi_check(instance);
memset(instance, 0, sizeof(*instance));
instance->base.protocol = &honda_static_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 3U;
instance->encoder.upload = malloc(HONDA_STATIC_UPLOAD_CAPACITY * sizeof(LevelDuration));
furi_check(instance->encoder.upload);
return instance;
}
void subghz_protocol_encoder_honda_static_free(void* context) {
furi_check(context);
SubGhzProtocolEncoderHondaStatic* instance = context;
free(instance->encoder.upload);
free(instance);
}
SubGhzProtocolStatus
subghz_protocol_encoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolEncoderHondaStatic* instance = context;
SubGhzProtocolStatus status = SubGhzProtocolStatusError;
instance->encoder.is_running = false;
instance->encoder.front = 0U;
do {
FuriString* pstr = furi_string_alloc();
if(!pstr) break;
flipper_format_rewind(flipper_format);
if(!flipper_format_read_string(flipper_format, "Protocol", pstr)) {
furi_string_free(pstr);
break;
}
if(!furi_string_equal(pstr, instance->base.protocol->name)) {
furi_string_free(pstr);
break;
}
furi_string_free(pstr);
uint64_t key = 0;
if(!honda_static_read_hex_u64(flipper_format, &key)) {
break;
}
honda_static_unpack_compact(key, &instance->decoded);
uint32_t serial = instance->decoded.serial;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Serial", &serial, 1)) {
instance->decoded.serial = serial;
}
uint32_t btn_u32 = 0;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Btn", &btn_u32, 1)) {
uint8_t b = (uint8_t)btn_u32;
if(honda_static_is_valid_button(b)) {
instance->decoded.button = b;
} else if(b >= 2U && b <= 5U) {
instance->decoded.button = honda_static_encoder_remap_button(b);
}
}
uint32_t cnt = instance->decoded.counter & 0x00FFFFFFU;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Cnt", &cnt, 1)) {
instance->decoded.counter = cnt & 0x00FFFFFFU;
}
instance->generic.serial = instance->decoded.serial;
instance->generic.cnt = instance->decoded.counter;
instance->generic.btn = instance->decoded.button;
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
instance->generic.data = honda_static_pack_compact(&instance->decoded);
uint8_t key_data[8];
honda_static_u64_to_bytes_be(instance->generic.data, key_data);
flipper_format_rewind(flipper_format);
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(key_data))) {
status = SubGhzProtocolStatusErrorParserKey;
break;
}
flipper_format_rewind(flipper_format);
if(!flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1)) {
instance->encoder.repeat = 3U;
}
honda_static_build_upload(instance);
instance->encoder.is_running = true;
status = SubGhzProtocolStatusOk;
} while(false);
return status;
}
void subghz_protocol_encoder_honda_static_stop(void* context) {
furi_check(context);
SubGhzProtocolEncoderHondaStatic* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_honda_static_yield(void* context) {
furi_check(context);
SubGhzProtocolEncoderHondaStatic* instance = context;
if((instance->encoder.repeat == 0U) || !instance->encoder.is_running) {
instance->encoder.is_running = false;
return level_duration_reset();
}
const LevelDuration current = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0U;
}
return current;
}
void* subghz_protocol_decoder_honda_static_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderHondaStatic* instance = malloc(sizeof(SubGhzProtocolDecoderHondaStatic));
furi_check(instance);
memset(instance, 0, sizeof(*instance));
instance->base.protocol = &honda_static_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_honda_static_free(void* context) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
free(instance);
}
void subghz_protocol_decoder_honda_static_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
instance->symbols_count = 0U;
instance->decoded_valid = 0U;
}
void subghz_protocol_decoder_honda_static_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
const uint8_t sym = honda_static_level_u8(level);
if((duration >= HONDA_STATIC_SHORT_BASE_US) &&
((duration - HONDA_STATIC_SHORT_BASE_US) <= HONDA_STATIC_SHORT_SPAN_US)) {
if(instance->symbols_count < HONDA_STATIC_SYMBOL_CAPACITY) {
instance->symbols[instance->symbols_count++] = sym;
}
return;
}
if((duration >= HONDA_STATIC_LONG_BASE_US) &&
((duration - HONDA_STATIC_LONG_BASE_US) <= HONDA_STATIC_LONG_SPAN_US)) {
if((uint16_t)(instance->symbols_count + 2U) <= HONDA_STATIC_SYMBOL_CAPACITY) {
instance->symbols[instance->symbols_count++] = sym;
instance->symbols[instance->symbols_count++] = sym;
}
return;
}
const uint16_t sc = instance->symbols_count;
if(sc >= HONDA_STATIC_MIN_SYMBOLS) {
if(honda_static_parse_symbols(instance, true) ||
honda_static_parse_symbols(instance, false)) {
honda_static_decoder_commit(instance);
}
}
instance->symbols_count = 0U;
}
uint8_t subghz_protocol_decoder_honda_static_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
const uint64_t data = instance->generic.data;
return (uint8_t)(data ^ (data >> 8U) ^ (data >> 16U) ^ (data >> 24U) ^ (data >> 32U) ^
(data >> 40U) ^ (data >> 48U) ^ (data >> 56U));
}
void subghz_protocol_decoder_honda_static_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
if(!instance->decoded_valid && (instance->generic.data != 0ULL)) {
honda_static_unpack_compact(instance->generic.data, &instance->decoded);
instance->decoded_valid = 1U;
}
furi_string_cat_printf(
output,
"%s %ubit\r\n"
"Key:%016llX\r\n",
instance->generic.protocol_name,
instance->packet_bit_count ? instance->packet_bit_count : HONDA_STATIC_BIT_COUNT,
(unsigned long long)instance->generic.data);
furi_string_cat_printf(
output,
"Btn:%s (0x%X)\r\n"
"Ser:%07lX\r\n"
"Cnt:%06lX Chk:%02X\r\n",
honda_static_button_name(instance->decoded.button),
instance->decoded.button,
(unsigned long)instance->decoded.serial,
(unsigned long)instance->decoded.counter,
instance->decoded.checksum);
}
SubGhzProtocolStatus subghz_protocol_decoder_honda_static_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
SubGhzProtocolStatus ret =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(ret == SubGhzProtocolStatusOk) {
flipper_format_write_uint32(flipper_format, "Serial", &instance->decoded.serial, 1);
uint32_t temp = instance->decoded.button;
flipper_format_write_uint32(flipper_format, "Btn", &temp, 1);
flipper_format_write_uint32(flipper_format, "Cnt", &instance->decoded.counter, 1);
temp = instance->decoded.checksum;
flipper_format_write_uint32(flipper_format, "Checksum", &temp, 1);
}
return ret;
}
SubGhzProtocolStatus
subghz_protocol_decoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
SubGhzProtocolStatus status = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, HONDA_STATIC_BIT_COUNT);
if(status != SubGhzProtocolStatusOk) {
return status;
}
instance->packet_bit_count = HONDA_STATIC_BIT_COUNT;
honda_static_unpack_compact(instance->generic.data, &instance->decoded);
instance->decoded_valid = 1U;
flipper_format_rewind(flipper_format);
uint32_t s = 0, b = 0, c = 0, k = 0;
if(flipper_format_read_uint32(flipper_format, "Serial", &s, 1)) {
instance->decoded.serial = s;
}
if(flipper_format_read_uint32(flipper_format, "Btn", &b, 1)) {
instance->decoded.button = (uint8_t)b;
}
if(flipper_format_read_uint32(flipper_format, "Cnt", &c, 1)) {
instance->decoded.counter = c & 0x00FFFFFFU;
}
if(flipper_format_read_uint32(flipper_format, "Checksum", &k, 1)) {
instance->decoded.checksum = (uint8_t)k;
}
instance->generic.serial = instance->decoded.serial;
instance->generic.cnt = instance->decoded.counter;
instance->generic.btn = instance->decoded.button;
return status;
}
-811
View File
@@ -1,811 +0,0 @@
#include "honda_static.h"
#define HONDA_STATIC_BIT_COUNT 64
#define HONDA_STATIC_MIN_SYMBOLS 36
#define HONDA_STATIC_SHORT_BASE_US 28
#define HONDA_STATIC_SHORT_SPAN_US 70
#define HONDA_STATIC_LONG_BASE_US 61
#define HONDA_STATIC_LONG_SPAN_US 130
#define HONDA_STATIC_SYNC_TIME_US 700
#define HONDA_STATIC_ELEMENT_TIME_US 63
#define HONDA_STATIC_UPLOAD_CAPACITY \
(1U + HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT + (2U * HONDA_STATIC_BIT_COUNT) + 1U)
#define HONDA_STATIC_SYMBOL_CAPACITY 512
#define HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT 160
#define HONDA_STATIC_PREAMBLE_MAX_TRANSITIONS 19
#define HONDA_STATIC_SYMBOL_BYTE_COUNT ((HONDA_STATIC_SYMBOL_CAPACITY + 7U) / 8U)
static const uint8_t honda_static_encoder_button_map[4] = {0x02, 0x04, 0x08, 0x05};
static const char* const honda_static_button_names[9] = {
"Lock",
"Unlock",
"Unknown",
"Trunk",
"Remote Start",
"Unknown",
"Unknown",
"Panic",
"Lock x2",
};
typedef struct {
uint8_t button;
uint8_t _reserved_01[3];
uint32_t serial;
uint32_t counter;
uint8_t checksum;
uint8_t _reserved_0d[3];
} HondaStaticFields;
struct SubGhzProtocolDecoderHondaStatic {
SubGhzProtocolDecoderBase base;
SubGhzBlockGeneric generic;
uint8_t symbols[HONDA_STATIC_SYMBOL_BYTE_COUNT];
uint16_t symbols_count;
};
struct SubGhzProtocolEncoderHondaStatic {
SubGhzProtocolEncoderBase base;
SubGhzProtocolBlockEncoder encoder;
SubGhzBlockGeneric generic;
HondaStaticFields decoded;
uint8_t tx_button;
uint8_t _reserved_69[3];
};
static void honda_static_decoder_commit(
SubGhzProtocolDecoderHondaStatic* instance,
const HondaStaticFields* decoded);
static uint64_t honda_static_bytes_to_u64_be(const uint8_t bytes[8]) {
uint64_t value = 0;
for(size_t i = 0; i < 8; i++) {
value = (value << 8U) | bytes[i];
}
return value;
}
static void honda_static_u64_to_bytes_be(uint64_t value, uint8_t bytes[8]) {
for(size_t i = 0; i < 8; i++) {
bytes[7U - i] = (uint8_t)(value & 0xFFU);
value >>= 8U;
}
}
static uint8_t honda_static_get_bits(const uint8_t* data, uint8_t start, uint8_t count) {
uint32_t value = 0;
for(uint8_t i = 0; i < count; i++) {
const uint8_t bit_index = start + i;
const uint8_t byte = data[bit_index >> 3U];
const uint8_t shift = (uint8_t)(~bit_index) & 0x07U;
value = (value << 1U) | ((byte >> shift) & 1U);
}
return (uint8_t)value;
}
static uint32_t honda_static_get_bits_u32(const uint8_t* data, uint8_t start, uint8_t count) {
uint32_t value = 0;
for(uint8_t i = 0; i < count; i++) {
const uint8_t bit_index = start + i;
const uint8_t byte = data[bit_index >> 3U];
const uint8_t shift = (uint8_t)(~bit_index) & 0x07U;
value = (value << 1U) | ((byte >> shift) & 1U);
}
return value;
}
static void honda_static_set_bits(uint8_t* data, uint8_t start, uint8_t count, uint32_t value) {
for(uint8_t i = 0; i < count; i++) {
const uint8_t bit_index = start + i;
const uint8_t byte_index = bit_index >> 3U;
const uint8_t shift = ((uint8_t)~bit_index) & 0x07U;
const uint8_t mask = (uint8_t)(1U << shift);
const bool bit = ((value >> (count - 1U - i)) & 1U) != 0U;
if(bit) {
data[byte_index] |= mask;
} else {
data[byte_index] &= (uint8_t)~mask;
}
}
}
static uint8_t honda_static_level_u8(bool level) {
return level ? 1U : 0U;
}
static void honda_static_symbol_set(uint8_t* buf, uint16_t index, uint8_t v) {
const uint8_t byte_index = (uint8_t)(index >> 3U);
const uint8_t shift = (uint8_t)(~index) & 0x07U;
const uint8_t mask = (uint8_t)(1U << shift);
if(v) {
buf[byte_index] |= mask;
} else {
buf[byte_index] &= (uint8_t)~mask;
}
}
static uint8_t honda_static_symbol_get(const uint8_t* buf, uint16_t index) {
const uint8_t byte_index = (uint8_t)(index >> 3U);
const uint8_t shift = (uint8_t)(~index) & 0x07U;
return (uint8_t)((buf[byte_index] >> shift) & 1U);
}
static uint8_t honda_static_reverse_bits8(uint8_t value) {
value = (uint8_t)(((value >> 4U) | (value << 4U)) & 0xFFU);
value = (uint8_t)(((value & 0x33U) << 2U) | ((value >> 2U) & 0x33U));
value = (uint8_t)(((value & 0x55U) << 1U) | ((value >> 1U) & 0x55U));
return value;
}
static bool honda_static_is_valid_button(uint8_t button) {
if(button > 9U) {
return false;
}
return ((0x336U >> button) & 1U) != 0U;
}
static bool honda_static_is_valid_serial(uint32_t serial) {
return (serial != 0U) && (serial != 0x0FFFFFFFU);
}
static uint8_t honda_static_encoder_remap_button(uint8_t button) {
if(button < 2U) {
return 1U;
}
button -= 2U;
if(button <= 3U) {
return honda_static_encoder_button_map[button];
}
return 1U;
}
static const char* honda_static_button_name(uint8_t button) {
if((button >= 1U) && (button <= COUNT_OF(honda_static_button_names))) {
return honda_static_button_names[button - 1U];
}
return "Unknown";
}
static uint8_t honda_static_compact_bytes_checksum(const uint8_t compact[8]) {
const uint8_t canonical[7] = {
(uint8_t)((compact[0] << 4U) | (compact[1] >> 4U)),
(uint8_t)((compact[1] << 4U) | (compact[2] >> 4U)),
(uint8_t)((compact[2] << 4U) | (compact[3] >> 4U)),
(uint8_t)((compact[3] << 4U) | (compact[4] >> 4U)),
compact[5],
compact[6],
compact[7],
};
uint8_t checksum = 0U;
for(size_t i = 0; i < COUNT_OF(canonical); i++) {
checksum ^= canonical[i];
}
return checksum;
}
static void honda_static_unpack_compact(uint64_t key, HondaStaticFields* fields) {
uint8_t compact[8];
honda_static_u64_to_bytes_be(key, compact);
memset(fields, 0, sizeof(*fields));
fields->button = compact[0] & 0x0FU;
fields->serial = ((uint32_t)compact[1] << 20U) | ((uint32_t)compact[2] << 12U) |
((uint32_t)compact[3] << 4U) | ((uint32_t)compact[4] >> 4U);
fields->counter = ((uint32_t)compact[5] << 16U) | ((uint32_t)compact[6] << 8U) |
(uint32_t)compact[7];
fields->checksum = honda_static_compact_bytes_checksum(compact);
}
static uint64_t honda_static_pack_compact(const HondaStaticFields* fields) {
uint8_t compact[8];
compact[0] = fields->button & 0x0FU;
compact[1] = (uint8_t)(fields->serial >> 20U);
compact[2] = (uint8_t)(fields->serial >> 12U);
compact[3] = (uint8_t)(fields->serial >> 4U);
compact[4] = (uint8_t)(fields->serial << 4U);
compact[5] = (uint8_t)(fields->counter >> 16U);
compact[6] = (uint8_t)(fields->counter >> 8U);
compact[7] = (uint8_t)fields->counter;
return honda_static_bytes_to_u64_be(compact);
}
static void honda_static_build_packet_bytes(const HondaStaticFields* fields, uint8_t packet[8]) {
memset(packet, 0, 8);
honda_static_set_bits(packet, 0, 4, fields->button & 0x0FU);
honda_static_set_bits(packet, 4, 28, fields->serial);
honda_static_set_bits(packet, 32, 24, fields->counter);
uint8_t checksum = 0U;
for(size_t i = 0; i < 7; i++) {
checksum ^= packet[i];
}
honda_static_set_bits(packet, 56, 8, checksum);
}
static bool
honda_static_validate_forward_packet(const uint8_t packet[9], HondaStaticFields* fields) {
const uint8_t button = honda_static_get_bits(packet, 0, 4);
const uint32_t serial = honda_static_get_bits_u32(packet, 4, 28);
const uint32_t counter = honda_static_get_bits_u32(packet, 32, 24);
const uint8_t checksum = honda_static_get_bits(packet, 56, 8);
uint8_t checksum_calc = 0U;
for(size_t i = 0; i < 7; i++) {
checksum_calc ^= packet[i];
}
if(checksum != checksum_calc) {
return false;
}
if(!honda_static_is_valid_button(button)) {
return false;
}
if(!honda_static_is_valid_serial(serial)) {
return false;
}
fields->button = button;
fields->serial = serial;
fields->counter = counter;
fields->checksum = checksum;
return true;
}
static bool
honda_static_validate_reverse_packet(const uint8_t packet[9], HondaStaticFields* fields) {
uint8_t reversed[9];
for(size_t i = 0; i < COUNT_OF(reversed); i++) {
reversed[i] = honda_static_reverse_bits8(packet[i]);
}
const uint8_t button = honda_static_get_bits(reversed, 0, 4);
const uint32_t serial = honda_static_get_bits_u32(reversed, 4, 28);
const uint32_t counter = honda_static_get_bits_u32(reversed, 32, 24);
uint8_t checksum = 0U;
for(size_t i = 0; i < 7; i++) {
checksum ^= reversed[i];
}
if(!honda_static_is_valid_button(button)) {
return false;
}
if(!honda_static_is_valid_serial(serial)) {
return false;
}
fields->button = button;
fields->serial = serial;
fields->counter = counter;
fields->checksum = checksum;
return true;
}
static bool honda_static_manchester_pack_64(
const uint8_t* symbol_bits,
uint16_t count,
uint16_t start_pos,
bool inverted,
uint8_t packet[9],
uint16_t* out_bit_count) {
memset(packet, 0, 9);
uint16_t pos = start_pos;
uint16_t bit_count = 0U;
while((uint16_t)(pos + 1U) < count) {
if(bit_count >= HONDA_STATIC_BIT_COUNT) {
break;
}
const uint8_t a = honda_static_symbol_get(symbol_bits, pos);
const uint8_t b = honda_static_symbol_get(symbol_bits, pos + 1U);
if(a == b) {
pos++;
continue;
}
bool bit = false;
if(inverted) {
bit = (a == 0U) && (b == 1U);
} else {
bit = (a == 1U) && (b == 0U);
}
if(bit) {
packet[bit_count >> 3U] |= (uint8_t)(1U << (((uint8_t)~bit_count) & 0x07U));
}
bit_count++;
pos += 2U;
}
if(out_bit_count) {
*out_bit_count = bit_count;
}
return bit_count >= HONDA_STATIC_BIT_COUNT;
}
static bool honda_static_parse_symbols(SubGhzProtocolDecoderHondaStatic* instance, bool inverted) {
const uint16_t count = instance->symbols_count;
const uint8_t* symbol_bits = instance->symbols;
HondaStaticFields decoded;
uint16_t index = 1U;
uint16_t transitions = 0U;
while(index < count) {
if(honda_static_symbol_get(symbol_bits, index) !=
honda_static_symbol_get(symbol_bits, index - 1U)) {
transitions++;
} else {
if(transitions > HONDA_STATIC_PREAMBLE_MAX_TRANSITIONS) {
break;
}
transitions = 0U;
}
index++;
}
if(index >= count) {
return false;
}
while(((uint16_t)(index + 1U) < count) && (honda_static_symbol_get(symbol_bits, index) ==
honda_static_symbol_get(symbol_bits, index + 1U))) {
index++;
}
const uint16_t data_start = index;
uint8_t packet[9] = {0};
uint16_t bit_count = 0U;
if(!honda_static_manchester_pack_64(
symbol_bits, count, data_start, inverted, packet, &bit_count)) {
return false;
}
if(honda_static_validate_forward_packet(packet, &decoded)) {
honda_static_decoder_commit(instance, &decoded);
return true;
}
if(inverted) {
return false;
}
if(honda_static_validate_reverse_packet(packet, &decoded)) {
honda_static_decoder_commit(instance, &decoded);
return true;
}
return false;
}
static void honda_static_decoder_commit(
SubGhzProtocolDecoderHondaStatic* instance,
const HondaStaticFields* decoded) {
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
instance->generic.data = honda_static_pack_compact(decoded);
instance->generic.serial = decoded->serial;
instance->generic.cnt = decoded->counter;
instance->generic.btn = decoded->button;
if(instance->base.callback) {
instance->base.callback(&instance->base, instance->base.context);
}
}
static void honda_static_build_upload(SubGhzProtocolEncoderHondaStatic* instance) {
uint8_t packet[8];
honda_static_build_packet_bytes(&instance->decoded, packet);
size_t index = 0U;
instance->encoder.upload[index++] = level_duration_make(true, HONDA_STATIC_SYNC_TIME_US);
for(size_t i = 0; i < HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT; i++) {
instance->encoder.upload[index++] =
level_duration_make((i & 1U) != 0U, HONDA_STATIC_ELEMENT_TIME_US);
}
for(uint8_t bit = 0U; bit < HONDA_STATIC_BIT_COUNT; bit++) {
const bool value = ((packet[bit >> 3U] >> (((uint8_t)~bit) & 0x07U)) & 1U) != 0U;
instance->encoder.upload[index++] =
level_duration_make(!value, HONDA_STATIC_ELEMENT_TIME_US);
instance->encoder.upload[index++] =
level_duration_make(value, HONDA_STATIC_ELEMENT_TIME_US);
}
const bool last_bit = (packet[7] & 1U) != 0U;
instance->encoder.upload[index++] = level_duration_make(!last_bit, HONDA_STATIC_SYNC_TIME_US);
instance->encoder.front = 0U;
instance->encoder.size_upload = index;
}
static bool honda_static_read_hex_u64(FlipperFormat* ff, uint64_t* out_key) {
FuriString* tmp = furi_string_alloc();
if(!tmp) return false;
bool ok = false;
do {
if(!flipper_format_rewind(ff) || !flipper_format_read_string(ff, "Key", tmp)) break;
const char* key_str = furi_string_get_cstr(tmp);
uint64_t key = 0;
size_t hex_pos = 0;
for(size_t i = 0; key_str[i] && hex_pos < 16; i++) {
char c = key_str[i];
if(c == ' ') continue;
uint8_t nibble;
if(c >= '0' && c <= '9')
nibble = c - '0';
else if(c >= 'A' && c <= 'F')
nibble = c - 'A' + 10;
else if(c >= 'a' && c <= 'f')
nibble = c - 'a' + 10;
else
break;
key = (key << 4) | nibble;
hex_pos++;
}
if(hex_pos != 16) break;
*out_key = key;
ok = true;
} while(false);
furi_string_free(tmp);
return ok;
}
const SubGhzProtocolDecoder subghz_protocol_honda_static_decoder = {
.alloc = subghz_protocol_decoder_honda_static_alloc,
.free = subghz_protocol_decoder_honda_static_free,
.feed = subghz_protocol_decoder_honda_static_feed,
.reset = subghz_protocol_decoder_honda_static_reset,
.get_hash_data = subghz_protocol_decoder_honda_static_get_hash_data,
.serialize = subghz_protocol_decoder_honda_static_serialize,
.deserialize = subghz_protocol_decoder_honda_static_deserialize,
.get_string = subghz_protocol_decoder_honda_static_get_string,
};
const SubGhzProtocolEncoder subghz_protocol_honda_static_encoder = {
.alloc = subghz_protocol_encoder_honda_static_alloc,
.free = subghz_protocol_encoder_honda_static_free,
.deserialize = subghz_protocol_encoder_honda_static_deserialize,
.stop = subghz_protocol_encoder_honda_static_stop,
.yield = subghz_protocol_encoder_honda_static_yield,
};
const SubGhzProtocol honda_static_protocol = {
.name = HONDA_STATIC_PROTOCOL_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 |
SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load |
SubGhzProtocolFlag_Send,
.decoder = &subghz_protocol_honda_static_decoder,
.encoder = &subghz_protocol_honda_static_encoder,
};
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolEncoderHondaStatic* instance = malloc(sizeof(SubGhzProtocolEncoderHondaStatic));
furi_check(instance);
memset(instance, 0, sizeof(*instance));
instance->base.protocol = &honda_static_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
instance->encoder.repeat = 3U;
instance->encoder.upload = malloc(HONDA_STATIC_UPLOAD_CAPACITY * sizeof(LevelDuration));
furi_check(instance->encoder.upload);
return instance;
}
void subghz_protocol_encoder_honda_static_free(void* context) {
furi_check(context);
SubGhzProtocolEncoderHondaStatic* instance = context;
free(instance->encoder.upload);
free(instance);
}
SubGhzProtocolStatus
subghz_protocol_encoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolEncoderHondaStatic* instance = context;
SubGhzProtocolStatus status = SubGhzProtocolStatusError;
instance->encoder.is_running = false;
instance->encoder.front = 0U;
do {
FuriString* pstr = furi_string_alloc();
if(!pstr) break;
flipper_format_rewind(flipper_format);
if(!flipper_format_read_string(flipper_format, "Protocol", pstr)) {
furi_string_free(pstr);
break;
}
if(!furi_string_equal(pstr, instance->base.protocol->name)) {
furi_string_free(pstr);
break;
}
furi_string_free(pstr);
uint64_t key = 0;
if(!honda_static_read_hex_u64(flipper_format, &key)) {
break;
}
honda_static_unpack_compact(key, &instance->decoded);
uint32_t serial = instance->decoded.serial;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Serial", &serial, 1)) {
instance->decoded.serial = serial;
}
uint32_t btn_u32 = 0;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Btn", &btn_u32, 1)) {
uint8_t b = (uint8_t)btn_u32;
if(honda_static_is_valid_button(b)) {
instance->decoded.button = b;
} else if(b >= 2U && b <= 5U) {
instance->decoded.button = honda_static_encoder_remap_button(b);
}
}
uint32_t cnt = instance->decoded.counter & 0x00FFFFFFU;
flipper_format_rewind(flipper_format);
if(flipper_format_read_uint32(flipper_format, "Cnt", &cnt, 1)) {
instance->decoded.counter = cnt & 0x00FFFFFFU;
}
instance->generic.serial = instance->decoded.serial;
instance->generic.cnt = instance->decoded.counter;
instance->generic.btn = instance->decoded.button;
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
instance->generic.data = honda_static_pack_compact(&instance->decoded);
uint8_t key_data[8];
honda_static_u64_to_bytes_be(instance->generic.data, key_data);
flipper_format_rewind(flipper_format);
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(key_data))) {
status = SubGhzProtocolStatusErrorParserKey;
break;
}
flipper_format_rewind(flipper_format);
if(!flipper_format_read_uint32(
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1)) {
instance->encoder.repeat = 3U;
}
honda_static_build_upload(instance);
instance->encoder.is_running = true;
status = SubGhzProtocolStatusOk;
} while(false);
return status;
}
void subghz_protocol_encoder_honda_static_stop(void* context) {
furi_check(context);
SubGhzProtocolEncoderHondaStatic* instance = context;
instance->encoder.is_running = false;
}
LevelDuration subghz_protocol_encoder_honda_static_yield(void* context) {
furi_check(context);
SubGhzProtocolEncoderHondaStatic* instance = context;
if((instance->encoder.repeat == 0U) || !instance->encoder.is_running) {
instance->encoder.is_running = false;
return level_duration_reset();
}
const LevelDuration current = instance->encoder.upload[instance->encoder.front];
if(++instance->encoder.front == instance->encoder.size_upload) {
instance->encoder.repeat--;
instance->encoder.front = 0U;
}
return current;
}
void* subghz_protocol_decoder_honda_static_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderHondaStatic* instance = malloc(sizeof(SubGhzProtocolDecoderHondaStatic));
furi_check(instance);
memset(instance, 0, sizeof(*instance));
instance->base.protocol = &honda_static_protocol;
instance->generic.protocol_name = instance->base.protocol->name;
return instance;
}
void subghz_protocol_decoder_honda_static_free(void* context) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
free(instance);
}
void subghz_protocol_decoder_honda_static_reset(void* context) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
instance->symbols_count = 0U;
}
void subghz_protocol_decoder_honda_static_feed(void* context, bool level, uint32_t duration) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
const uint8_t sym = honda_static_level_u8(level);
if((duration >= HONDA_STATIC_SHORT_BASE_US) &&
((duration - HONDA_STATIC_SHORT_BASE_US) <= HONDA_STATIC_SHORT_SPAN_US)) {
if(instance->symbols_count < HONDA_STATIC_SYMBOL_CAPACITY) {
honda_static_symbol_set(instance->symbols, instance->symbols_count, sym);
instance->symbols_count++;
}
return;
}
if((duration >= HONDA_STATIC_LONG_BASE_US) &&
((duration - HONDA_STATIC_LONG_BASE_US) <= HONDA_STATIC_LONG_SPAN_US)) {
if((uint16_t)(instance->symbols_count + 2U) <= HONDA_STATIC_SYMBOL_CAPACITY) {
honda_static_symbol_set(instance->symbols, instance->symbols_count, sym);
instance->symbols_count++;
honda_static_symbol_set(instance->symbols, instance->symbols_count, sym);
instance->symbols_count++;
}
return;
}
const uint16_t sc = instance->symbols_count;
if(sc >= HONDA_STATIC_MIN_SYMBOLS) {
if(!honda_static_parse_symbols(instance, true)) {
honda_static_parse_symbols(instance, false);
}
}
instance->symbols_count = 0U;
}
uint8_t subghz_protocol_decoder_honda_static_get_hash_data(void* context) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
const uint64_t data = instance->generic.data;
return (uint8_t)(data ^ (data >> 8U) ^ (data >> 16U) ^ (data >> 24U) ^ (data >> 32U) ^
(data >> 40U) ^ (data >> 48U) ^ (data >> 56U));
}
void subghz_protocol_decoder_honda_static_get_string(void* context, FuriString* output) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
HondaStaticFields decoded;
honda_static_unpack_compact(instance->generic.data, &decoded);
furi_string_printf(
output,
"%s\r\n"
"Key:%016llX\r\n"
"Btn:%s\r\n"
"Ser:%07lX Cnt:%06lX",
instance->generic.protocol_name,
(unsigned long long)instance->generic.data,
honda_static_button_name(decoded.button),
(unsigned long)decoded.serial,
(unsigned long)decoded.counter);
}
SubGhzProtocolStatus subghz_protocol_decoder_honda_static_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
HondaStaticFields decoded;
honda_static_unpack_compact(instance->generic.data, &decoded);
SubGhzProtocolStatus status =
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
if(status != SubGhzProtocolStatusOk) {
return status;
}
uint32_t temp = decoded.serial;
if(!flipper_format_write_uint32(flipper_format, "Serial", &temp, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
temp = decoded.button;
if(!flipper_format_write_uint32(flipper_format, "Btn", &temp, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
temp = decoded.counter;
if(!flipper_format_write_uint32(flipper_format, "Cnt", &temp, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
temp = decoded.checksum;
if(!flipper_format_write_uint32(flipper_format, "Checksum", &temp, 1)) {
return SubGhzProtocolStatusErrorParserOthers;
}
return status;
}
SubGhzProtocolStatus
subghz_protocol_decoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format) {
furi_check(context);
SubGhzProtocolDecoderHondaStatic* instance = context;
SubGhzProtocolStatus status = subghz_block_generic_deserialize_check_count_bit(
&instance->generic, flipper_format, HONDA_STATIC_BIT_COUNT);
if(status != SubGhzProtocolStatusOk) {
return status;
}
flipper_format_rewind(flipper_format);
HondaStaticFields decoded;
honda_static_unpack_compact(instance->generic.data, &decoded);
uint32_t s = 0;
uint32_t b = 0;
uint32_t c = 0;
if(flipper_format_read_uint32(flipper_format, "Serial", &s, 1)) {
decoded.serial = s;
}
if(flipper_format_read_uint32(flipper_format, "Btn", &b, 1)) {
decoded.button = (uint8_t)b;
}
if(flipper_format_read_uint32(flipper_format, "Cnt", &c, 1)) {
decoded.counter = c & 0x00FFFFFFU;
}
instance->generic.data = honda_static_pack_compact(&decoded);
instance->generic.serial = decoded.serial;
instance->generic.cnt = decoded.counter;
instance->generic.btn = decoded.button;
return status;
}
-36
View File
@@ -1,36 +0,0 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include <lib/subghz/blocks/generic.h>
#include <flipper_format/flipper_format.h>
#define HONDA_STATIC_PROTOCOL_NAME "Honda Static"
typedef struct SubGhzProtocolDecoderHondaStatic SubGhzProtocolDecoderHondaStatic;
typedef struct SubGhzProtocolEncoderHondaStatic SubGhzProtocolEncoderHondaStatic;
extern const SubGhzProtocol honda_static_protocol;
void* subghz_protocol_decoder_honda_static_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_honda_static_free(void* context);
void subghz_protocol_decoder_honda_static_reset(void* context);
void subghz_protocol_decoder_honda_static_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_honda_static_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_honda_static_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_honda_static_get_string(void* context, FuriString* output);
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment);
void subghz_protocol_encoder_honda_static_free(void* context);
SubGhzProtocolStatus
subghz_protocol_encoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_encoder_honda_static_stop(void* context);
LevelDuration subghz_protocol_encoder_honda_static_yield(void* context);
-37
View File
@@ -1,37 +0,0 @@
#pragma once
#include <furi.h>
#include <lib/subghz/protocols/base.h>
#include <lib/subghz/types.h>
#include <lib/subghz/blocks/decoder.h>
#include <lib/subghz/blocks/encoder.h>
#include <lib/subghz/blocks/generic.h>
#include <flipper_format/flipper_format.h>
#define HONDA_STATIC_PROTOCOL_NAME "Honda Static"
typedef struct SubGhzProtocolDecoderHondaStatic SubGhzProtocolDecoderHondaStatic;
typedef struct SubGhzProtocolEncoderHondaStatic SubGhzProtocolEncoderHondaStatic;
extern const SubGhzProtocol honda_static_protocol;
void* subghz_protocol_decoder_honda_static_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_honda_static_free(void* context);
void subghz_protocol_decoder_honda_static_reset(void* context);
void subghz_protocol_decoder_honda_static_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_honda_static_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_honda_static_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus
subghz_protocol_decoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_decoder_honda_static_get_string(void* context, FuriString* output);
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment);
void subghz_protocol_encoder_honda_static_free(void* context);
SubGhzProtocolStatus
subghz_protocol_encoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
void subghz_protocol_encoder_honda_static_stop(void* context);
LevelDuration subghz_protocol_encoder_honda_static_yield(void* context);
+1 -1
View File
@@ -85,7 +85,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
&ford_protocol_v2,
&ford_protocol_v3,
&subghz_protocol_land_rover_v0,
//&subghz_protocol_toyota,
&subghz_protocol_toyota,
};
+3 -2
View File
@@ -79,11 +79,12 @@
#include "scher_khan.h"
#include "sheriff_cfm.h"
#include "chrysler.h"
#include "honda_static.h"
//#include "honda_static.h"
//#include "honda_v1.h"
#include "mazda_v0.h"
#include "kia_v7.h"
#include "ford_v1.h"
#include "ford_v2.h"
#include "ford_v3.h"
#include "land_rover_v0.h"
//#include "toyota.h"
#include "toyota.h"
+642
View File
@@ -0,0 +1,642 @@
#include "toyota.h"
#include "../blocks/const.h"
#include "../blocks/decoder.h"
#include "../blocks/generic.h"
#include "../blocks/math.h"
#define TAG "SubGhzProtocolToyota"
/*
* TOYOTA KEELOQ — DUAL VARIANT DECODER
*
* VARIANT A — Corolla Verso 2004-2010 (433 MHz)
* TE short : 400 us TE long : 800 us delta: 175 us
* Encoding : PWM pairs — LS=0 (Long HIGH + Short LOW)
* SL=1 (Short HIGH + Long LOW)
* Preamble : repeated SS pairs, ends on first non-SS pair
* Frame : 68 bits
* Repeats : 8x
*
* VARIANT B — Tundra 2011 (315 MHz)
* Encoding : NRZ — each individual pulse encodes one bit:
* pulse <= 287 us -> bit 0
* pulse > 287 us -> bit 1
* Preamble : alternating short/long pulses (~200/~390 us each)
* ends with sync gap ~1938 us (a LOW > 1500 us)
* Frame : 67 bits (each pulse = 1 bit, HIGH and LOW alike)
* Repeats : 30x
* Inter-frame gap: ~51000 us
*
* Confirmed from real capture analysis:
* Preamble HIGHs: ~200 us (short)
* Preamble LOWs : ~390 us (long) — counted as preamble pairs
* Sync gap : ~1938 us LOW after last preamble HIGH
* Data pulses : each pulse independently = 0 if <=287us, 1 if >287us
* Boundary 287 = midpoint between 200 us and 375 us centers
*
* generic.data (64 bits):
* [63..32] = hop (32 bits)
* [31..4] = serial (28 bits)
* [3..0] = button (4 bits)
*
* generic.cnt:
* 0 = Variant A (Corolla/433MHz)
* 1 = Variant B (Tundra/315MHz)
*/
/* ----------------------------------------------------------------
* Physical constants — Variant A
* ---------------------------------------------------------------- */
static const SubGhzBlockConst toyota_const_a = {
.te_short = 400,
.te_long = 800,
.te_delta = 175,
.min_count_bit_for_found = 60,
};
/* ----------------------------------------------------------------
* Physical constants — Variant B (preamble classification only)
* Data phase uses midpoint, not these tolerances.
* ---------------------------------------------------------------- */
static const SubGhzBlockConst toyota_const_b = {
.te_short = 200,
.te_long = 390,
.te_delta = 120,
.min_count_bit_for_found = 60,
};
/*
* NRZ midpoint for Variant B data pulses.
* Pulses <= this value are bit 0, pulses > this value are bit 1.
* Midpoint between short center (200us) and long center (375us).
* Value 287 confirmed correct against real capture:
* 192us->0 181us->0 383us->1 434us->1 380us->1 etc.
*/
#define TOYOTA_B_NRZ_MIDPOINT 287u
/* Sync gap: LOW pulse separating preamble from data */
#define TOYOTA_B_SYNC_GAP_MIN 1500u
#define TOYOTA_B_SYNC_GAP_MAX 2600u
/* Any pulse above this is an inter-frame gap */
#define TOYOTA_INTER_FRAME_GAP 5000u
/* Minimum preamble pairs before accepting sync gap */
#define TOYOTA_B_PREAMBLE_MIN 6u
#define TOYOTA_A_PREAMBLE_MIN 6u
/* Frame lengths in bits */
#define TOYOTA_A_BITS 68u
#define TOYOTA_B_BITS 67u
/* First HIGH duration below this -> Variant B, above -> Variant A */
#define TOYOTA_VARIANT_THRESH 310u
/* ----------------------------------------------------------------
* Button codes
* ---------------------------------------------------------------- */
#define TOYOTA_A_BTN_LOCK 0x08
#define TOYOTA_A_BTN_UNLOCK 0x01
#define TOYOTA_B_BTN_LOCK 0x0A
#define TOYOTA_B_BTN_UNLOCK 0x05
/* ----------------------------------------------------------------
* Parser states
* ---------------------------------------------------------------- */
typedef enum {
ToyotaStepReset = 0,
ToyotaStepPreambleA,
ToyotaStepDataA,
ToyotaStepPreambleB,
ToyotaStepDataB,
} ToyotaDecoderStep;
/* ----------------------------------------------------------------
* Decoder instance
* ---------------------------------------------------------------- */
typedef struct {
SubGhzProtocolDecoderBase base;
SubGhzBlockDecoder decoder;
SubGhzBlockGeneric generic;
uint64_t bits_lo;
uint8_t bits_hi;
uint8_t bit_count;
uint32_t te_last;
bool have_high;
uint16_t preamble_count;
uint8_t variant; /* 0 = Corolla/433MHz, 1 = Tundra/315MHz */
uint32_t hop;
uint32_t serial;
uint8_t button;
} SubGhzProtocolDecoderToyota;
/* ----------------------------------------------------------------
* Protocol descriptor
* ---------------------------------------------------------------- */
const SubGhzProtocolDecoder subghz_protocol_toyota_decoder = {
.alloc = subghz_protocol_decoder_toyota_alloc,
.free = subghz_protocol_decoder_toyota_free,
.feed = subghz_protocol_decoder_toyota_feed,
.reset = subghz_protocol_decoder_toyota_reset,
.get_hash_data = subghz_protocol_decoder_toyota_get_hash_data,
.serialize = subghz_protocol_decoder_toyota_serialize,
.deserialize = subghz_protocol_decoder_toyota_deserialize,
.get_string = subghz_protocol_decoder_toyota_get_string,
};
const SubGhzProtocol subghz_protocol_toyota = {
.name = SUBGHZ_PROTOCOL_TOYOTA_NAME,
.type = SubGhzProtocolTypeDynamic,
.flag = SubGhzProtocolFlag_433 |
SubGhzProtocolFlag_315 |
SubGhzProtocolFlag_AM |
SubGhzProtocolFlag_Decodable |
SubGhzProtocolFlag_Load |
SubGhzProtocolFlag_Save,
.decoder = &subghz_protocol_toyota_decoder,
.encoder = NULL,
};
/* ----------------------------------------------------------------
* TE helpers (preamble phase only)
* ---------------------------------------------------------------- */
static inline bool te_is_short(uint32_t d, const SubGhzBlockConst* c) {
return DURATION_DIFF(d, (uint32_t)c->te_short) < (uint32_t)c->te_delta;
}
static inline bool te_is_long(uint32_t d, const SubGhzBlockConst* c) {
return DURATION_DIFF(d, (uint32_t)c->te_long) < (uint32_t)c->te_delta;
}
/* ----------------------------------------------------------------
* Bit accumulator
* ---------------------------------------------------------------- */
static void toyota_push_bit(SubGhzProtocolDecoderToyota* inst, uint8_t bit) {
uint8_t carry = (uint8_t)(inst->bits_lo >> 63) & 1;
inst->bits_hi = (inst->bits_hi << 1) | carry;
inst->bits_lo = (inst->bits_lo << 1) | (bit & 1);
inst->bit_count++;
}
static uint32_t toyota_extract(
const SubGhzProtocolDecoderToyota* inst,
uint8_t offset,
uint8_t length)
{
uint32_t result = 0;
uint8_t total = inst->bit_count;
for(uint8_t i = 0; i < length; i++) {
int8_t pos = (int8_t)(total - 1) - (int8_t)(offset + i);
uint8_t b = 0;
if(pos >= 64) b = (inst->bits_hi >> (pos - 64)) & 1;
else if(pos >= 0) b = (inst->bits_lo >> pos) & 1;
result = (result << 1) | b;
}
return result;
}
/* ----------------------------------------------------------------
* Name helpers
* ---------------------------------------------------------------- */
static const char* toyota_button_name(uint8_t btn, uint8_t variant) {
if(variant == 1) {
switch(btn & 0x0F) {
case TOYOTA_B_BTN_LOCK: return "Lock";
case TOYOTA_B_BTN_UNLOCK: return "Unlock";
case 0x0F: return "Lock+Unlock";
case 0x04: return "Trunk";
default: return "Unknown";
}
}
switch(btn & 0x0F) {
case TOYOTA_A_BTN_LOCK: return "Lock";
case TOYOTA_A_BTN_UNLOCK: return "Unlock";
case 0x09: return "Lock+Unlock";
case 0x02: return "Trunk";
case 0x04: return "Aux";
default: return "Unknown";
}
}
static const char* toyota_model_name(uint8_t variant) {
return (variant == 1) ? "Tundra" : "Corolla";
}
/* ----------------------------------------------------------------
* Decode and fire callback
* ---------------------------------------------------------------- */
static void toyota_decode_and_fire(SubGhzProtocolDecoderToyota* inst) {
const SubGhzBlockConst* c =
(inst->variant == 1) ? &toyota_const_b : &toyota_const_a;
if(inst->bit_count < (uint8_t)c->min_count_bit_for_found) return;
inst->hop = toyota_extract(inst, 0, 32);
inst->serial = toyota_extract(inst, 32, 28);
inst->button = (uint8_t)toyota_extract(inst, 60, 4);
inst->generic.data =
((uint64_t)inst->hop << 32) |
((uint64_t)inst->serial << 4) |
((uint64_t)inst->button & 0x0F);
inst->generic.data_count_bit = inst->bit_count;
inst->generic.serial = inst->serial;
inst->generic.btn = inst->button;
inst->generic.cnt = inst->variant;
inst->decoder.decode_data = inst->generic.data;
inst->decoder.decode_count_bit = inst->generic.data_count_bit;
FURI_LOG_D(TAG, "FIRE var=%d bits=%d hop=%08lX serial=%07lX btn=%X",
(int)inst->variant, (int)inst->bit_count,
(unsigned long)inst->hop,
(unsigned long)inst->serial,
(unsigned int)inst->button);
if(inst->base.callback)
inst->base.callback(&inst->base, inst->base.context);
}
/* ----------------------------------------------------------------
* Alloc / Free / Reset
* ---------------------------------------------------------------- */
void* subghz_protocol_decoder_toyota_alloc(SubGhzEnvironment* environment) {
UNUSED(environment);
SubGhzProtocolDecoderToyota* inst =
malloc(sizeof(SubGhzProtocolDecoderToyota));
inst->base.protocol = &subghz_protocol_toyota;
inst->generic.protocol_name = inst->base.protocol->name;
return inst;
}
void subghz_protocol_decoder_toyota_free(void* context) {
furi_assert(context);
free(context);
}
void subghz_protocol_decoder_toyota_reset(void* context) {
furi_assert(context);
SubGhzProtocolDecoderToyota* inst = context;
inst->decoder.parser_step = ToyotaStepReset;
inst->decoder.te_last = 0;
inst->bits_lo = 0;
inst->bits_hi = 0;
inst->bit_count = 0;
inst->te_last = 0;
inst->have_high = false;
inst->preamble_count = 0;
inst->hop = 0;
inst->serial = 0;
inst->button = 0;
/* variant intentionally NOT reset — detected once per session */
}
/* ----------------------------------------------------------------
* FEED — Variant A (Corolla 433 MHz)
*
* PWM pair encoding: LS=0, SL=1.
* Preamble: SS pairs until first non-SS pair = first data bit.
* ---------------------------------------------------------------- */
static void toyota_feed_variant_a(
SubGhzProtocolDecoderToyota* inst,
bool level, uint32_t duration)
{
const SubGhzBlockConst* c = &toyota_const_a;
if(inst->decoder.parser_step == ToyotaStepPreambleA) {
if(level) {
inst->te_last = duration;
inst->have_high = true;
return;
}
if(!inst->have_high) {
subghz_protocol_decoder_toyota_reset(inst);
return;
}
inst->have_high = false;
bool hs = te_is_short(inst->te_last, c);
bool hl = te_is_long (inst->te_last, c);
bool ls = te_is_short(duration, c);
bool ll = te_is_long (duration, c);
if(hs && ls) {
inst->preamble_count++;
return;
}
if(inst->preamble_count < TOYOTA_A_PREAMBLE_MIN) {
subghz_protocol_decoder_toyota_reset(inst);
return;
}
inst->bits_lo = 0;
inst->bits_hi = 0;
inst->bit_count = 0;
if (hl && ls) toyota_push_bit(inst, 0);
else if(hs && ll) toyota_push_bit(inst, 1);
inst->decoder.parser_step = ToyotaStepDataA;
return;
}
if(inst->decoder.parser_step == ToyotaStepDataA) {
if(level) {
if(te_is_short(duration, c) || te_is_long(duration, c)) {
inst->te_last = duration;
inst->have_high = true;
} else {
if(inst->bit_count >= (uint8_t)c->min_count_bit_for_found)
toyota_decode_and_fire(inst);
subghz_protocol_decoder_toyota_reset(inst);
}
return;
}
if(!inst->have_high) return;
inst->have_high = false;
bool hs = te_is_short(inst->te_last, c);
bool hl = te_is_long (inst->te_last, c);
bool ls = te_is_short(duration, c);
bool ll = te_is_long (duration, c);
if(hl && ls) {
toyota_push_bit(inst, 0);
} else if(hs && ll) {
toyota_push_bit(inst, 1);
} else {
if(inst->bit_count >= (uint8_t)c->min_count_bit_for_found)
toyota_decode_and_fire(inst);
subghz_protocol_decoder_toyota_reset(inst);
return;
}
if(inst->bit_count >= TOYOTA_A_BITS) {
toyota_decode_and_fire(inst);
subghz_protocol_decoder_toyota_reset(inst);
}
}
}
/* ----------------------------------------------------------------
* FEED — Variant B (Tundra 315 MHz)
*
* PREAMBLE state:
* Processes HIGH+LOW pairs using tight TE matching.
* Each [SHORT HIGH + LONG LOW] pair increments preamble_count.
* A LOW >= TOYOTA_B_SYNC_GAP_MIN after a valid preamble
* transitions to DATA state.
*
* DATA state — TRUE NRZ:
* Every single pulse (HIGH or LOW) independently encodes one bit.
* The Flipper delivers them alternating level=true/false.
* We process EACH pulse regardless of polarity:
* duration <= TOYOTA_B_NRZ_MIDPOINT (287us) -> bit 0
* duration > TOYOTA_B_NRZ_MIDPOINT -> bit 1
* A pulse >= TOYOTA_B_SYNC_GAP_MIN ends the frame.
* ---------------------------------------------------------------- */
static void toyota_feed_variant_b(
SubGhzProtocolDecoderToyota* inst,
bool level, uint32_t duration)
{
const SubGhzBlockConst* c = &toyota_const_b;
/* ── PREAMBLE ── */
if(inst->decoder.parser_step == ToyotaStepPreambleB) {
if(level) {
if(te_is_short(duration, c)) {
inst->te_last = duration;
inst->have_high = true;
} else {
subghz_protocol_decoder_toyota_reset(inst);
}
return;
}
/* Falling edge */
if(!inst->have_high) {
subghz_protocol_decoder_toyota_reset(inst);
return;
}
inst->have_high = false;
/* Sync gap: LOW ~1938us -> transition to data */
if(duration >= TOYOTA_B_SYNC_GAP_MIN &&
duration <= TOYOTA_B_SYNC_GAP_MAX)
{
if(inst->preamble_count >= TOYOTA_B_PREAMBLE_MIN) {
FURI_LOG_D(TAG, "B: sync gap after %d pairs -> NRZ data",
(int)inst->preamble_count);
inst->bits_lo = 0;
inst->bits_hi = 0;
inst->bit_count = 0;
inst->have_high = false;
inst->decoder.parser_step = ToyotaStepDataB;
} else {
subghz_protocol_decoder_toyota_reset(inst);
}
return;
}
/* Normal preamble LOW must be LONG */
if(te_is_long(duration, c)) {
inst->preamble_count++;
return;
}
subghz_protocol_decoder_toyota_reset(inst);
return;
}
/* ── DATA (NRZ) ── */
if(inst->decoder.parser_step == ToyotaStepDataB) {
/*
* Every pulse — HIGH or LOW — encodes one bit independently.
* A pulse >= sync gap minimum signals end of frame.
* A pulse >= inter-frame gap also ends frame.
*/
if(duration >= TOYOTA_B_SYNC_GAP_MIN) {
/* Frame ended by gap */
if(inst->bit_count >= (uint8_t)c->min_count_bit_for_found) {
toyota_decode_and_fire(inst);
}
subghz_protocol_decoder_toyota_reset(inst);
return;
}
/*
* NRZ bit: midpoint classification.
* <= 287us -> bit 0
* > 287us -> bit 1
*/
uint8_t bit = (duration > TOYOTA_B_NRZ_MIDPOINT) ? 1 : 0;
toyota_push_bit(inst, bit);
if(inst->bit_count >= TOYOTA_B_BITS) {
toyota_decode_and_fire(inst);
subghz_protocol_decoder_toyota_reset(inst);
}
}
}
/* ----------------------------------------------------------------
* Public feed — dispatcher
* ---------------------------------------------------------------- */
void subghz_protocol_decoder_toyota_feed(void* context, bool level, uint32_t duration) {
furi_assert(context);
SubGhzProtocolDecoderToyota* inst = context;
if(inst->decoder.parser_step == ToyotaStepReset) {
if(!level) return;
/*
* Variant detection from first SHORT HIGH pulse:
* < 310us -> Variant B (Tundra 315MHz, TE_short~200us)
* >= 310us -> Variant A (Corolla 433MHz, TE_short~400us)
*/
bool fits_b = te_is_short(duration, &toyota_const_b) &&
(duration < TOYOTA_VARIANT_THRESH);
bool fits_a = te_is_short(duration, &toyota_const_a) &&
(duration >= TOYOTA_VARIANT_THRESH);
if(fits_b) {
inst->variant = 1;
inst->te_last = duration;
inst->have_high = true;
inst->preamble_count = 0;
inst->decoder.parser_step = ToyotaStepPreambleB;
FURI_LOG_D(TAG, "Detected Variant B (Tundra), first HIGH=%lu",
(unsigned long)duration);
} else if(fits_a) {
inst->variant = 0;
inst->te_last = duration;
inst->have_high = true;
inst->preamble_count = 0;
inst->decoder.parser_step = ToyotaStepPreambleA;
FURI_LOG_D(TAG, "Detected Variant A (Corolla), first HIGH=%lu",
(unsigned long)duration);
}
return;
}
if(inst->variant == 1) {
toyota_feed_variant_b(inst, level, duration);
} else {
toyota_feed_variant_a(inst, level, duration);
}
}
/* ----------------------------------------------------------------
* Hash
* ---------------------------------------------------------------- */
uint8_t subghz_protocol_decoder_toyota_get_hash_data(void* context) {
furi_assert(context);
SubGhzProtocolDecoderToyota* inst = context;
return subghz_protocol_blocks_get_hash_data(
&inst->decoder,
(inst->decoder.decode_count_bit / 8) + 1);
}
/* ----------------------------------------------------------------
* Serialize
* ---------------------------------------------------------------- */
SubGhzProtocolStatus subghz_protocol_decoder_toyota_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset)
{
furi_assert(context);
SubGhzProtocolDecoderToyota* inst = context;
inst->generic.cnt = inst->variant;
return subghz_block_generic_serialize(&inst->generic, flipper_format, preset);
}
/* ----------------------------------------------------------------
* Deserialize
* ---------------------------------------------------------------- */
SubGhzProtocolStatus subghz_protocol_decoder_toyota_deserialize(
void* context,
FlipperFormat* flipper_format)
{
furi_assert(context);
SubGhzProtocolDecoderToyota* inst = context;
SubGhzProtocolStatus ret =
subghz_block_generic_deserialize_check_count_bit(
&inst->generic,
flipper_format,
toyota_const_b.min_count_bit_for_found);
if(ret == SubGhzProtocolStatusOk) {
inst->hop = (uint32_t)(inst->generic.data >> 32);
inst->serial = (uint32_t)((inst->generic.data >> 4) & 0x0FFFFFFF);
inst->button = (uint8_t)(inst->generic.data & 0x0F);
inst->generic.serial = inst->serial;
inst->generic.btn = inst->button;
inst->variant = (inst->generic.cnt != 0) ? 1 : 0;
inst->generic.cnt = inst->variant;
}
return ret;
}
/* ----------------------------------------------------------------
* get_string
* ---------------------------------------------------------------- */
void subghz_protocol_decoder_toyota_get_string(void* context, FuriString* output) {
furi_assert(context);
SubGhzProtocolDecoderToyota* inst = context;
uint32_t hop = (uint32_t)(inst->generic.data >> 32);
uint32_t serial = (uint32_t)((inst->generic.data >> 4) & 0x0FFFFFFF);
uint8_t button = (uint8_t)(inst->generic.data & 0x0F);
uint8_t var = (inst->generic.cnt != 0) ? 1 : 0;
furi_string_cat_printf(
output,
"%s %dbit\r\n"
"Hop: %08lX\r\n"
"Sn: %07lX\r\n"
"Btn: %X [%s]",
toyota_model_name(var),
inst->generic.data_count_bit,
(unsigned long)hop,
(unsigned long)serial,
button,
toyota_button_name(button, var));
}
+25
View File
@@ -0,0 +1,25 @@
#pragma once
#include "base.h"
#define SUBGHZ_PROTOCOL_TOYOTA_NAME "Toyota"
extern const SubGhzProtocol subghz_protocol_toyota;
void* subghz_protocol_decoder_toyota_alloc(SubGhzEnvironment* environment);
void subghz_protocol_decoder_toyota_free(void* context);
void subghz_protocol_decoder_toyota_reset(void* context);
void subghz_protocol_decoder_toyota_feed(void* context, bool level, uint32_t duration);
uint8_t subghz_protocol_decoder_toyota_get_hash_data(void* context);
SubGhzProtocolStatus subghz_protocol_decoder_toyota_serialize(
void* context,
FlipperFormat* flipper_format,
SubGhzRadioPreset* preset);
SubGhzProtocolStatus subghz_protocol_decoder_toyota_deserialize(
void* context,
FlipperFormat* flipper_format);
void subghz_protocol_decoder_toyota_get_string(void* context, FuriString* output);