diff --git a/lib/subghz/protocols/kia.c b/lib/subghz/protocols/kia.c index 6988a0ef22..a5fffb746a 100644 --- a/lib/subghz/protocols/kia.c +++ b/lib/subghz/protocols/kia.c @@ -5,6 +5,7 @@ #include "../blocks/encoder.h" #include "../blocks/generic.h" #include "../blocks/math.h" +#include "../blocks/custom_btn_i.h" #define TAG "SubGhzProtocoKia" @@ -52,19 +53,210 @@ const SubGhzProtocolDecoder subghz_protocol_kia_decoder = { }; const SubGhzProtocolEncoder subghz_protocol_kia_encoder = { - .alloc = NULL, - .free = NULL, + .alloc = subghz_protocol_encoder_kia_alloc, + .free = subghz_protocol_encoder_kia_free, - .deserialize = NULL, - .stop = NULL, - .yield = NULL, + .deserialize = subghz_protocol_encoder_kia_deserialize, + .stop = subghz_protocol_encoder_kia_stop, + .yield = subghz_protocol_encoder_kia_yield, }; +void* subghz_protocol_encoder_kia_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderKIA* instance = malloc(sizeof(SubGhzProtocolDecoderKIA)); + instance->base.protocol = &subghz_protocol_kia; + instance->generic.protocol_name = instance->base.protocol->name; + instance->encoder.size_upload = 848; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.repeat = 1; + instance->encoder.is_running = false; + + return instance; +} + +/** + * Free SubGhzProtocolEncoderKIA. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + */ +void subghz_protocol_encoder_kia_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderKIA* instance = context; + free(instance->encoder.upload); + free(instance); +} + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderKIA instance + */ +void subghz_protocol_encoder_kia_stop(void* context) { + SubGhzProtocolEncoderKIA* instance = context; + instance->encoder.is_running = false; +} + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderKIA instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_kia_yield(void* context) { + SubGhzProtocolEncoderKIA* 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; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_kia_check_remote_controller(SubGhzBlockGeneric* instance); + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderKIA instance + * @return true On success + */ +static bool subghz_protocol_encoder_kia_get_upload(SubGhzProtocolEncoderKIA* instance) { + furi_assert(instance); + + // Save original button + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->generic.btn); + } + + size_t index = 0; + size_t size_upload = (instance->generic.data_count_bit * 2 + 32) * 2 + 540; + if(size_upload > instance->encoder.size_upload) { + FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer. %i", instance->generic.data_count_bit); + return false; + } else { + instance->encoder.size_upload = size_upload; + } + + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if(instance->generic.cnt >= 0xFFFF) { + instance->generic.cnt = 0; + } + + uint8_t btn = subghz_custom_btn_get() == SUBGHZ_CUSTOM_BTN_OK + ? subghz_custom_btn_get_original() + : subghz_custom_btn_get(); + + uint64_t reversed = 0, value = ((uint64_t)(0x0F) << 56) | + ((uint64_t)(instance->generic.cnt & 0xFFFF) << 40) | + ((uint64_t)(instance->generic.serial & 0x0FFFFFFF) << 12) | + ((uint64_t)(btn & 0x0F) << 8); + for(uint8_t i = 0; i < sizeof(uint64_t); ++i) { + reversed = (reversed << 8) | ((value >> (i * 8)) & 0xFF); + } + + instance->generic.data = value | (uint64_t)subghz_protocol_blocks_crc8((uint8_t*)&reversed, 7, 0x7F, 0x0F); + + //Send header + for(uint16_t i = 270; i > 0; i--) { + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_kia_const.te_short); + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)subghz_protocol_kia_const.te_short); + } + + for(uint8_t h = 2; h > 0; h--) { + for(uint8_t i = 15; i > 0; i--) { + instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)subghz_protocol_kia_const.te_short); + instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)subghz_protocol_kia_const.te_short); + } + + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + //send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kia_const.te_long); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_kia_const.te_long); + } else { + //send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kia_const.te_short); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_kia_const.te_short); + } + } + + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_kia_const.te_long * 3); + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_kia_const.te_long * 3); + + } + + return true; +} + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderKIA instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_kia_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderKIA* instance = context; + + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_kia_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { + break; + } + + subghz_protocol_kia_check_remote_controller(&instance->generic); + if(!subghz_protocol_encoder_kia_get_upload(instance)) { + ret = SubGhzProtocolStatusErrorEncoderGetUpload; + break; + } + + if(!flipper_format_rewind(flipper_format)) { + FURI_LOG_E(TAG, "Rewind error"); + 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->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + ret = SubGhzProtocolStatusErrorParserKey; + break; + } + + instance->encoder.is_running = true; + } while (false); + + return ret; +} + const SubGhzProtocol subghz_protocol_kia = { .name = SUBGHZ_PROTOCOL_KIA_NAME, .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable | - SubGhzProtocolFlag_AutoAlarms, + SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send | SubGhzProtocolFlag_AutoAlarms, .decoder = &subghz_protocol_kia_decoder, .encoder = &subghz_protocol_kia_encoder, @@ -190,21 +382,6 @@ void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t durati } } -uint8_t subghz_protocol_kia_crc8(uint8_t* data, size_t len) { - uint8_t crc = 0x08; - size_t i, j; - for(i = 0; i < len; i++) { - crc ^= data[i]; - for(j = 0; j < 8; j++) { - if((crc & 0x80) != 0) - crc = (uint8_t)((crc << 1) ^ 0x7F); - else - crc <<= 1; - } - } - return crc; -} - /** * Analysis of received data * @param instance Pointer to a SubGhzBlockGeneric* instance @@ -216,12 +393,18 @@ static void subghz_protocol_kia_check_remote_controller(SubGhzBlockGeneric* inst * 0x0F 0114 43B04EC 1 30 * 0x0F 0115 43B04EC 2 13 * 0x0F 0116 43B04EC 3 F5 - * CNT Serial K CRC8 Kia (CRC8, poly 0x7f, start_crc 0x08) + * CNT Serial K CRC8 Kia (CRC8, poly 0x7f, start_crc 0x0f) */ instance->serial = (uint32_t)((instance->data >> 12) & 0x0FFFFFFF); instance->btn = (instance->data >> 8) & 0x0F; instance->cnt = (instance->data >> 40) & 0xFFFF; + + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->btn); + } + + subghz_custom_btn_set_max(4); } uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) { @@ -248,6 +431,11 @@ SubGhzProtocolStatus &instance->generic, flipper_format, subghz_protocol_kia_const.min_count_bit_for_found); } +static const char* subghz_protocol_kia_get_name_button(uint8_t btn) { + const char* name_btn[5] = {"Unknown", "Lock", "Unlock", "Trunk", "Horn"}; + return name_btn[btn < 5 ? btn : 0]; +} + void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderKIA* instance = context; @@ -260,12 +448,13 @@ void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) { output, "%s %dbit\r\n" "Key:%08lX%08lX\r\n" - "Sn:%07lX Btn:%X Cnt:%04lX\r\n", + "Sn:%07lX Cnt:%04lX\r\n" + "Btn:%s\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, code_found_hi, code_found_lo, instance->generic.serial, - instance->generic.btn, - instance->generic.cnt); + instance->generic.cnt, + subghz_protocol_kia_get_name_button(instance->generic.btn)); } diff --git a/lib/subghz/protocols/kia.h b/lib/subghz/protocols/kia.h index 749ff8afd2..ab4082b73f 100644 --- a/lib/subghz/protocols/kia.h +++ b/lib/subghz/protocols/kia.h @@ -11,6 +11,41 @@ extern const SubGhzProtocolDecoder subghz_protocol_kia_decoder; extern const SubGhzProtocolEncoder subghz_protocol_kia_encoder; extern const SubGhzProtocol subghz_protocol_kia; +/** + * Allocate SubGhzProtocolEncoderKIA. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderKeeloq* pointer to a SubGhzProtocolEncoderKeeloq instance + */ +void* subghz_protocol_encoder_kia_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderKIA. + * @param context Pointer to a SubGhzProtocolEncoderKeeloq instance + */ +void subghz_protocol_encoder_kia_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderKIA instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_kia_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderKIA instance + */ +void subghz_protocol_encoder_kia_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderKIA instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_kia_yield(void* context); + /** * Allocate SubGhzProtocolDecoderKIA. * @param environment Pointer to a SubGhzEnvironment instance