mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-23 03:29:57 -07:00
Merge remote-tracking branch 'ul/dev' into mntm-dev --nobuild
This commit is contained in:
@@ -14,6 +14,7 @@
|
||||
- UL: Jarolift protocol full support (72bit dynamic) (with Add manually, and all button codes) (by @xMasterX & d82k & Steffen (bastelbudenbuben de))
|
||||
- UL: Treadmill37 protocol support (37bit static) (by @xMasterX)
|
||||
- UL: Ditec GOL4 protocol (with programming mode, button switch, add manually) (by @xMasterX & @zero-mega)
|
||||
- UL: KeyFinder protocol (24bit static) (by @xMasterX & @mishamyte)
|
||||
- UL: New modulation FSK with 12KHz deviation (by @xMasterX)
|
||||
- UL: KingGates Stylo 4k Add manually and button switch support and refactoring of encoder (by @xMasterX)
|
||||
- UL: Stilmatic (R-Tech) 12bit discr. fix & button 9 support (two buttons hold simulation) (mapped on arrow keys) (by @xMasterX)
|
||||
@@ -25,6 +26,7 @@
|
||||
- UL: TX Power setting (by @LeeroysHub)
|
||||
- UL: Somfy Keytis button switch and Add Manually support (by @xMasterX)
|
||||
- UL: Genius Echo/Bravo add 2 buttons hold simulation (0xB btn code) (by @xMasterX)
|
||||
- OFW: RFID: Add Indala 224-bit (long format) protocol support (by @kuzaxak)
|
||||
- UL: JS: Add IR capabilities to the JS engine (by @LuisMayo)
|
||||
- FBT: Allow apps to specify custom cflags (by @WillyJL)
|
||||
- UL: Docs: Add [full list of supported SubGHz protocols](https://github.com/Next-Flip/Momentum-Firmware/blob/dev/documentation/SubGHzSupportedSystems.md) and their frequencies/modulations that can be used for reading remotes (by @xMasterX)
|
||||
@@ -64,6 +66,7 @@
|
||||
- UL: Signal Settings Improvements (by @Dmitry422)
|
||||
- UL: KeeLoq change delta size (by @xMasterX)
|
||||
- Archive: Support opening and pinning ProtoPirate files from Archive (#510 by @LeeroysHub)
|
||||
- OFW: RFID: Make FDX-B readout more descriptive (by @snowsign)
|
||||
- OFW: API: Make `view_port_send_to_back()` public (by @loftyinclination)
|
||||
|
||||
### Fixed:
|
||||
|
||||
@@ -526,6 +526,134 @@ MU_TEST(test_lfrfid_protocol_fdxb_read_simple) {
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
// Indala224: 224-bit PSK2 frame, no known FC/CN descramble,
|
||||
// test data uses the Proxmark3-verified reference (lf indala reader output)
|
||||
#define INDALA224_TEST_DATA_SIZE 28
|
||||
#define INDALA224_TEST_DATA \
|
||||
{0x80, 0x00, 0x00, 0x01, 0xB2, 0x35, 0x23, 0xA6, 0xC2, 0xE3, 0x1E, 0xBA, 0x3C, 0xBE, \
|
||||
0xE4, 0xAF, 0xB3, 0xC6, 0xAD, 0x1F, 0xCF, 0x64, 0x93, 0x93, 0x92, 0x8C, 0x14, 0xE5}
|
||||
#define INDALA224_BITS_PER_FRAME 224
|
||||
#define INDALA224_US_PER_BIT 255
|
||||
#define INDALA224_FRAMES_TO_DECODE 3
|
||||
|
||||
MU_TEST(test_lfrfid_protocol_indala224_roundtrip) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
mu_assert_int_eq(
|
||||
INDALA224_TEST_DATA_SIZE, protocol_dict_get_data_size(dict, LFRFIDProtocolIndala224));
|
||||
mu_assert_string_eq("Indala224", protocol_dict_get_name(dict, LFRFIDProtocolIndala224));
|
||||
mu_assert_string_eq("Motorola", protocol_dict_get_manufacturer(dict, LFRFIDProtocolIndala224));
|
||||
|
||||
const uint8_t data[INDALA224_TEST_DATA_SIZE] = INDALA224_TEST_DATA;
|
||||
|
||||
// Encode: extract bit-level polarity from encoder output.
|
||||
// PSK encoder yields carrier-level oscillation (duration=1 per half-cycle).
|
||||
// PulseGlue produces ~16us outputs which are below the decoder's 127us threshold,
|
||||
// so we extract the bit-level polarity directly: sample the encoder's phase state
|
||||
// at the start of each bit period (every 32 yields = 16 carrier cycles).
|
||||
protocol_dict_set_data(dict, LFRFIDProtocolIndala224, data, INDALA224_TEST_DATA_SIZE);
|
||||
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIndala224));
|
||||
|
||||
const size_t total_bits = INDALA224_BITS_PER_FRAME * INDALA224_FRAMES_TO_DECODE;
|
||||
bool* bit_levels = malloc(total_bits);
|
||||
mu_check(bit_levels != NULL);
|
||||
|
||||
for(size_t i = 0; i < total_bits; i++) {
|
||||
LevelDuration ld = protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala224);
|
||||
bit_levels[i] = level_duration_get_level(ld);
|
||||
// Skip remaining 31 yields for this bit period
|
||||
for(size_t skip = 0; skip < 31; skip++) {
|
||||
protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala224);
|
||||
}
|
||||
}
|
||||
|
||||
// Decode: convert bit-level polarities to run-length timing pairs
|
||||
// and feed directly to decoder (simulates hardware PSK demodulator output).
|
||||
protocol_dict_decoders_start(dict);
|
||||
ProtocolId protocol = PROTOCOL_NO;
|
||||
|
||||
size_t i = 0;
|
||||
while(i < total_bits) {
|
||||
bool cur = bit_levels[i];
|
||||
size_t run = 1;
|
||||
while(i + run < total_bits && bit_levels[i + run] == cur) {
|
||||
run++;
|
||||
}
|
||||
uint32_t duration = (uint32_t)(run * INDALA224_US_PER_BIT);
|
||||
|
||||
protocol = protocol_dict_decoders_feed(dict, cur, duration);
|
||||
if(protocol != PROTOCOL_NO) break;
|
||||
|
||||
i += run;
|
||||
}
|
||||
|
||||
free(bit_levels);
|
||||
|
||||
mu_assert_int_eq(LFRFIDProtocolIndala224, protocol);
|
||||
uint8_t received_data[INDALA224_TEST_DATA_SIZE] = {0};
|
||||
protocol_dict_get_data(dict, protocol, received_data, INDALA224_TEST_DATA_SIZE);
|
||||
mu_assert_mem_eq(data, received_data, INDALA224_TEST_DATA_SIZE);
|
||||
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
// Indala224 phase-alternating test: data with odd number of 1-bits
|
||||
// causes PSK2 carrier phase to invert between consecutive frames.
|
||||
// Decoder must accept inverted preamble at the second frame boundary.
|
||||
#define INDALA224_ALT_TEST_DATA \
|
||||
{0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, \
|
||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}
|
||||
|
||||
MU_TEST(test_lfrfid_protocol_indala224_alternating_phase) {
|
||||
ProtocolDict* dict = protocol_dict_alloc(lfrfid_protocols, LFRFIDProtocolMax);
|
||||
|
||||
const uint8_t data[INDALA224_TEST_DATA_SIZE] = INDALA224_ALT_TEST_DATA;
|
||||
|
||||
protocol_dict_set_data(dict, LFRFIDProtocolIndala224, data, INDALA224_TEST_DATA_SIZE);
|
||||
mu_check(protocol_dict_encoder_start(dict, LFRFIDProtocolIndala224));
|
||||
|
||||
const size_t total_bits = INDALA224_BITS_PER_FRAME * INDALA224_FRAMES_TO_DECODE;
|
||||
bool* bit_levels = malloc(total_bits);
|
||||
mu_check(bit_levels != NULL);
|
||||
|
||||
for(size_t i = 0; i < total_bits; i++) {
|
||||
LevelDuration ld = protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala224);
|
||||
bit_levels[i] = level_duration_get_level(ld);
|
||||
for(size_t skip = 0; skip < 31; skip++) {
|
||||
protocol_dict_encoder_yield(dict, LFRFIDProtocolIndala224);
|
||||
}
|
||||
}
|
||||
|
||||
// Verify phase alternation: frame 1 and frame 2 start with opposite polarity
|
||||
mu_check(bit_levels[0] != bit_levels[INDALA224_BITS_PER_FRAME]);
|
||||
|
||||
protocol_dict_decoders_start(dict);
|
||||
ProtocolId protocol = PROTOCOL_NO;
|
||||
|
||||
size_t i = 0;
|
||||
while(i < total_bits) {
|
||||
bool cur = bit_levels[i];
|
||||
size_t run = 1;
|
||||
while(i + run < total_bits && bit_levels[i + run] == cur) {
|
||||
run++;
|
||||
}
|
||||
uint32_t duration = (uint32_t)(run * INDALA224_US_PER_BIT);
|
||||
|
||||
protocol = protocol_dict_decoders_feed(dict, cur, duration);
|
||||
if(protocol != PROTOCOL_NO) break;
|
||||
|
||||
i += run;
|
||||
}
|
||||
|
||||
free(bit_levels);
|
||||
|
||||
mu_assert_int_eq(LFRFIDProtocolIndala224, protocol);
|
||||
uint8_t received_data[INDALA224_TEST_DATA_SIZE] = {0};
|
||||
protocol_dict_get_data(dict, protocol, received_data, INDALA224_TEST_DATA_SIZE);
|
||||
mu_assert_mem_eq(data, received_data, INDALA224_TEST_DATA_SIZE);
|
||||
|
||||
protocol_dict_free(dict);
|
||||
}
|
||||
|
||||
MU_TEST_SUITE(test_lfrfid_protocols_suite) {
|
||||
MU_RUN_TEST(test_lfrfid_protocol_em_read_simple);
|
||||
MU_RUN_TEST(test_lfrfid_protocol_em_emulate_simple);
|
||||
@@ -538,6 +666,9 @@ MU_TEST_SUITE(test_lfrfid_protocols_suite) {
|
||||
|
||||
MU_RUN_TEST(test_lfrfid_protocol_inadala26_emulate_simple);
|
||||
|
||||
MU_RUN_TEST(test_lfrfid_protocol_indala224_roundtrip);
|
||||
MU_RUN_TEST(test_lfrfid_protocol_indala224_alternating_phase);
|
||||
|
||||
MU_RUN_TEST(test_lfrfid_protocol_fdxb_read_simple);
|
||||
MU_RUN_TEST(test_lfrfid_protocol_fdxb_emulate_simple);
|
||||
}
|
||||
|
||||
@@ -147,19 +147,19 @@ bool subghz_scene_receiver_info_on_event(void* context, SceneManagerEvent event)
|
||||
}
|
||||
//CC1101 Stop RX -> Start TX
|
||||
subghz_txrx_hopper_pause(subghz->txrx);
|
||||
// key concept: we start endless TX until user release OK button, and after this we send last
|
||||
// protocols repeats - this guarantee that one press OK will
|
||||
// be guarantee send the required minimum protocol data packets
|
||||
// for all of this we use subghz_block_generic_global.endless_tx in protocols _yield function.
|
||||
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||
subghz_block_generic_global.endless_tx = true;
|
||||
if(!subghz_tx_start(
|
||||
subghz,
|
||||
subghz_history_get_raw_data(subghz->history, subghz->idx_menu_chosen))) {
|
||||
subghz_txrx_rx_start(subghz->txrx);
|
||||
subghz_txrx_hopper_unpause(subghz->txrx);
|
||||
subghz->state_notifications = SubGhzNotificationStateRx;
|
||||
} else {
|
||||
// key concept: we start endless TX until user release OK button, and after this we send last
|
||||
// protocols repeats - this guarantee that one press OK will
|
||||
// be guarantee send the required minimum protocol data packets
|
||||
// for all of this we use subghz_block_generic_global.endless_tx in protocols _yield function.
|
||||
subghz->state_notifications = SubGhzNotificationStateTx;
|
||||
subghz_block_generic_global.endless_tx = true;
|
||||
subghz_block_generic_global.endless_tx = false;
|
||||
}
|
||||
return true;
|
||||
} else if(event.event == SubGhzCustomEventSceneReceiverInfoTxStop) {
|
||||
|
||||
@@ -86,6 +86,7 @@ That list is only for default SubGHz app, apps like *Weather Station* have their
|
||||
- SMC5326 `315MHz, 433.92MHz, Any other frequency` `AM650` (25 bits, Static)
|
||||
- Hay21 `433.92MHz` `AM650` (21 bits, Dynamic)
|
||||
- Treadmill37 (QH-433) `433.92MHz` `AM650` (37 bits, Static)
|
||||
- KeyFinder `433.92MHz` `AM650` (24 bits, Static)
|
||||
|
||||
---
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
#include "protocol_h10301.h"
|
||||
#include "protocol_idteck.h"
|
||||
#include "protocol_indala26.h"
|
||||
#include "protocol_indala224.h"
|
||||
#include "protocol_io_prox_xsf.h"
|
||||
#include "protocol_awid.h"
|
||||
#include "protocol_fdx_a.h"
|
||||
@@ -31,6 +32,7 @@ const ProtocolBase* const lfrfid_protocols[] = {
|
||||
[LFRFIDProtocolH10301] = &protocol_h10301,
|
||||
[LFRFIDProtocolIdteck] = &protocol_idteck,
|
||||
[LFRFIDProtocolIndala26] = &protocol_indala26,
|
||||
[LFRFIDProtocolIndala224] = &protocol_indala224,
|
||||
[LFRFIDProtocolIOProxXSF] = &protocol_io_prox_xsf,
|
||||
[LFRFIDProtocolAwid] = &protocol_awid,
|
||||
[LFRFIDProtocolFDXA] = &protocol_fdx_a,
|
||||
|
||||
@@ -16,6 +16,7 @@ typedef enum {
|
||||
LFRFIDProtocolH10301,
|
||||
LFRFIDProtocolIdteck,
|
||||
LFRFIDProtocolIndala26,
|
||||
LFRFIDProtocolIndala224,
|
||||
LFRFIDProtocolIOProxXSF,
|
||||
LFRFIDProtocolAwid,
|
||||
LFRFIDProtocolFDXA,
|
||||
|
||||
@@ -284,9 +284,12 @@ void protocol_fdx_b_render_data(ProtocolFDXB* protocol, FuriString* result) {
|
||||
|
||||
bool block_status = bit_lib_get_bit(protocol->data, 48);
|
||||
bool rudi_bit = bit_lib_get_bit(protocol->data, 49);
|
||||
uint8_t reserved = bit_lib_get_bits(protocol->data, 50, 5);
|
||||
uint8_t user_info = bit_lib_get_bits(protocol->data, 55, 5);
|
||||
uint8_t replacement_number = bit_lib_get_bits(protocol->data, 60, 3);
|
||||
uint8_t visual_start_digit =
|
||||
bit_lib_reverse_8_fast(bit_lib_get_bits(protocol->data, 50, 3) << 5);
|
||||
uint8_t reserved = bit_lib_reverse_8_fast(bit_lib_get_bits(protocol->data, 53, 2) << 6);
|
||||
uint8_t user_info = bit_lib_reverse_8_fast(bit_lib_get_bits(protocol->data, 55, 5) << 3);
|
||||
uint8_t replacement_number =
|
||||
bit_lib_reverse_8_fast(bit_lib_get_bits(protocol->data, 60, 3) << 5);
|
||||
bool animal_flag = bit_lib_get_bit(protocol->data, 63);
|
||||
Storage* storage = furi_record_open(RECORD_STORAGE);
|
||||
FuriString* country_full_name = furi_string_alloc();
|
||||
@@ -320,13 +323,19 @@ void protocol_fdx_b_render_data(ProtocolFDXB* protocol, FuriString* result) {
|
||||
result,
|
||||
"\n"
|
||||
"Animal: %s\n"
|
||||
"Bits: %hhX-%hhX-%hhX-%hhX-%hhX",
|
||||
"Visual Start Digit: %hu\n"
|
||||
"Replacement Number: %hu\n"
|
||||
"User Info: %hhX\n"
|
||||
"Data Block: %s\n"
|
||||
"RUDI Bit: %s\n"
|
||||
"RFU: %hhX\n",
|
||||
animal_flag ? "Yes" : "No",
|
||||
block_status,
|
||||
rudi_bit,
|
||||
reserved,
|
||||
visual_start_digit,
|
||||
replacement_number,
|
||||
user_info,
|
||||
replacement_number);
|
||||
block_status ? "Present" : "Absent",
|
||||
rudi_bit ? "Yes" : "No",
|
||||
reserved);
|
||||
|
||||
furi_string_free(country_full_name);
|
||||
}
|
||||
|
||||
281
lib/lfrfid/protocols/protocol_indala224.c
Normal file
281
lib/lfrfid/protocols/protocol_indala224.c
Normal file
@@ -0,0 +1,281 @@
|
||||
#include <furi.h>
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
#include <bit_lib/bit_lib.h>
|
||||
#include "lfrfid_protocols.h"
|
||||
|
||||
#define INDALA224_PREAMBLE_BIT_SIZE (30)
|
||||
#define INDALA224_PREAMBLE_DATA_SIZE (4)
|
||||
|
||||
#define INDALA224_ENCODED_BIT_SIZE (224)
|
||||
#define INDALA224_ENCODED_DATA_SIZE \
|
||||
(((INDALA224_ENCODED_BIT_SIZE) / 8) + INDALA224_PREAMBLE_DATA_SIZE)
|
||||
#define INDALA224_ENCODED_DATA_LAST ((INDALA224_ENCODED_BIT_SIZE) / 8)
|
||||
|
||||
#define INDALA224_DECODED_BIT_SIZE (224)
|
||||
#define INDALA224_DECODED_DATA_SIZE (28)
|
||||
|
||||
#define INDALA224_US_PER_BIT (255)
|
||||
#define INDALA224_ENCODER_PULSES_PER_BIT (16)
|
||||
|
||||
typedef struct {
|
||||
uint8_t data_index;
|
||||
uint8_t bit_clock_index;
|
||||
bool current_polarity;
|
||||
bool pulse_phase;
|
||||
} ProtocolIndala224Encoder;
|
||||
|
||||
typedef struct {
|
||||
uint8_t encoded_data[INDALA224_ENCODED_DATA_SIZE];
|
||||
uint8_t negative_encoded_data[INDALA224_ENCODED_DATA_SIZE];
|
||||
|
||||
uint8_t data[INDALA224_DECODED_DATA_SIZE];
|
||||
ProtocolIndala224Encoder encoder;
|
||||
} ProtocolIndala224;
|
||||
|
||||
ProtocolIndala224* protocol_indala224_alloc(void) {
|
||||
ProtocolIndala224* protocol = malloc(sizeof(ProtocolIndala224));
|
||||
return protocol;
|
||||
}
|
||||
|
||||
void protocol_indala224_free(ProtocolIndala224* protocol) {
|
||||
free(protocol);
|
||||
}
|
||||
|
||||
uint8_t* protocol_indala224_get_data(ProtocolIndala224* protocol) {
|
||||
return protocol->data;
|
||||
}
|
||||
|
||||
void protocol_indala224_decoder_start(ProtocolIndala224* protocol) {
|
||||
memset(protocol->encoded_data, 0, INDALA224_ENCODED_DATA_SIZE);
|
||||
memset(protocol->negative_encoded_data, 0, INDALA224_ENCODED_DATA_SIZE);
|
||||
}
|
||||
|
||||
static bool protocol_indala224_check_preamble(uint8_t* data, size_t bit_index) {
|
||||
// Normal preamble: 1 followed by 29 zeros
|
||||
if(bit_lib_get_bits(data, bit_index, 8) != 0b10000000) return false;
|
||||
if(bit_lib_get_bits(data, bit_index + 8, 8) != 0) return false;
|
||||
if(bit_lib_get_bits(data, bit_index + 16, 8) != 0) return false;
|
||||
if(bit_lib_get_bits(data, bit_index + 24, 6) != 0) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool protocol_indala224_check_inverted_preamble(uint8_t* data, size_t bit_index) {
|
||||
// Inverted preamble: 0 followed by 29 ones (phase-alternating PSK2 cards)
|
||||
if(bit_lib_get_bits(data, bit_index, 8) != 0b01111111) return false;
|
||||
if(bit_lib_get_bits(data, bit_index + 8, 8) != 0xFF) return false;
|
||||
if(bit_lib_get_bits(data, bit_index + 16, 8) != 0xFF) return false;
|
||||
if(bit_lib_get_bits(data, bit_index + 24, 6) != 0b111111) return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool protocol_indala224_can_be_decoded(uint8_t* data) {
|
||||
if(!protocol_indala224_check_preamble(data, 0)) return false;
|
||||
// Second frame may have same or inverted preamble (PSK2 phase alternation)
|
||||
if(!protocol_indala224_check_preamble(data, INDALA224_ENCODED_BIT_SIZE) &&
|
||||
!protocol_indala224_check_inverted_preamble(data, INDALA224_ENCODED_BIT_SIZE)) {
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool protocol_indala224_decoder_feed_internal(bool polarity, uint32_t time, uint8_t* data) {
|
||||
time += (INDALA224_US_PER_BIT / 2);
|
||||
|
||||
size_t bit_count = (time / INDALA224_US_PER_BIT);
|
||||
bool result = false;
|
||||
|
||||
if(bit_count < INDALA224_ENCODED_BIT_SIZE) {
|
||||
for(size_t i = 0; i < bit_count; i++) {
|
||||
bit_lib_push_bit(data, INDALA224_ENCODED_DATA_SIZE, polarity);
|
||||
if(protocol_indala224_can_be_decoded(data)) {
|
||||
result = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void protocol_indala224_decoder_save(uint8_t* data_to, const uint8_t* data_from) {
|
||||
// PSK2 differential decode: the shift register contains phase values, not data.
|
||||
// T5577 PSK2 encodes data as phase transitions: data[n] = phase[n] XOR phase[n+1].
|
||||
// Bit 224 (start of second frame) provides phase[n+1] for the last data bit.
|
||||
for(size_t i = 0; i < INDALA224_DECODED_BIT_SIZE; i++) {
|
||||
bool phase_current = bit_lib_get_bit(data_from, i);
|
||||
bool phase_next = bit_lib_get_bit(data_from, i + 1);
|
||||
bit_lib_set_bit(data_to, i, phase_current ^ phase_next);
|
||||
}
|
||||
}
|
||||
|
||||
bool protocol_indala224_decoder_feed(ProtocolIndala224* protocol, bool level, uint32_t duration) {
|
||||
bool result = false;
|
||||
|
||||
if(duration > (INDALA224_US_PER_BIT / 2)) {
|
||||
if(protocol_indala224_decoder_feed_internal(level, duration, protocol->encoded_data)) {
|
||||
protocol_indala224_decoder_save(protocol->data, protocol->encoded_data);
|
||||
FURI_LOG_D("Indala224", "Positive");
|
||||
result = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
if(protocol_indala224_decoder_feed_internal(
|
||||
!level, duration, protocol->negative_encoded_data)) {
|
||||
protocol_indala224_decoder_save(protocol->data, protocol->negative_encoded_data);
|
||||
FURI_LOG_D("Indala224", "Negative");
|
||||
result = true;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool protocol_indala224_encoder_start(ProtocolIndala224* protocol) {
|
||||
// Store raw data for PSK2 emulation - the yield function handles PSK2 modulation.
|
||||
memcpy(protocol->encoded_data, protocol->data, INDALA224_DECODED_DATA_SIZE);
|
||||
|
||||
protocol->encoder.data_index = 0;
|
||||
protocol->encoder.current_polarity = true;
|
||||
protocol->encoder.pulse_phase = true;
|
||||
protocol->encoder.bit_clock_index = 0;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LevelDuration protocol_indala224_encoder_yield(ProtocolIndala224* protocol) {
|
||||
LevelDuration level_duration;
|
||||
ProtocolIndala224Encoder* encoder = &protocol->encoder;
|
||||
|
||||
if(encoder->pulse_phase) {
|
||||
level_duration = level_duration_make(encoder->current_polarity, 1);
|
||||
encoder->pulse_phase = false;
|
||||
} else {
|
||||
level_duration = level_duration_make(!encoder->current_polarity, 1);
|
||||
encoder->pulse_phase = true;
|
||||
|
||||
encoder->bit_clock_index++;
|
||||
if(encoder->bit_clock_index >= INDALA224_ENCODER_PULSES_PER_BIT) {
|
||||
encoder->bit_clock_index = 0;
|
||||
|
||||
// PSK2: carrier phase flips when data bit is 1
|
||||
bool current_bit = bit_lib_get_bit(protocol->encoded_data, encoder->data_index);
|
||||
if(current_bit) {
|
||||
encoder->current_polarity = !encoder->current_polarity;
|
||||
}
|
||||
|
||||
bit_lib_increment_index(encoder->data_index, INDALA224_ENCODED_BIT_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
return level_duration;
|
||||
}
|
||||
|
||||
static void protocol_indala224_render_data_internal(
|
||||
ProtocolIndala224* protocol,
|
||||
FuriString* result,
|
||||
bool brief) {
|
||||
if(brief) {
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Raw: %02X%02X%02X%02X\n"
|
||||
" %02X%02X%02X%02X...",
|
||||
protocol->data[0],
|
||||
protocol->data[1],
|
||||
protocol->data[2],
|
||||
protocol->data[3],
|
||||
protocol->data[4],
|
||||
protocol->data[5],
|
||||
protocol->data[6],
|
||||
protocol->data[7]);
|
||||
} else {
|
||||
furi_string_printf(
|
||||
result,
|
||||
"Raw: %02X%02X%02X%02X %02X%02X%02X%02X\n"
|
||||
"%02X%02X%02X%02X %02X%02X%02X%02X\n"
|
||||
"%02X%02X%02X%02X %02X%02X%02X%02X\n"
|
||||
"%02X%02X%02X%02X",
|
||||
protocol->data[0],
|
||||
protocol->data[1],
|
||||
protocol->data[2],
|
||||
protocol->data[3],
|
||||
protocol->data[4],
|
||||
protocol->data[5],
|
||||
protocol->data[6],
|
||||
protocol->data[7],
|
||||
protocol->data[8],
|
||||
protocol->data[9],
|
||||
protocol->data[10],
|
||||
protocol->data[11],
|
||||
protocol->data[12],
|
||||
protocol->data[13],
|
||||
protocol->data[14],
|
||||
protocol->data[15],
|
||||
protocol->data[16],
|
||||
protocol->data[17],
|
||||
protocol->data[18],
|
||||
protocol->data[19],
|
||||
protocol->data[20],
|
||||
protocol->data[21],
|
||||
protocol->data[22],
|
||||
protocol->data[23],
|
||||
protocol->data[24],
|
||||
protocol->data[25],
|
||||
protocol->data[26],
|
||||
protocol->data[27]);
|
||||
}
|
||||
}
|
||||
|
||||
void protocol_indala224_render_data(ProtocolIndala224* protocol, FuriString* result) {
|
||||
protocol_indala224_render_data_internal(protocol, result, false);
|
||||
}
|
||||
|
||||
void protocol_indala224_render_brief_data(ProtocolIndala224* protocol, FuriString* result) {
|
||||
protocol_indala224_render_data_internal(protocol, result, true);
|
||||
}
|
||||
|
||||
bool protocol_indala224_write_data(ProtocolIndala224* protocol, void* data) {
|
||||
LFRFIDWriteRequest* request = (LFRFIDWriteRequest*)data;
|
||||
bool result = false;
|
||||
|
||||
if(request->write_type == LFRFIDWriteTypeT5577) {
|
||||
// Config: PSK2, RF/32, 7 data blocks (matches Proxmark3 T55X7_INDALA_224_CONFIG_BLOCK)
|
||||
// T5577 data blocks contain raw data bits; the chip handles PSK2 modulation.
|
||||
request->t5577.block[0] = LFRFID_T5577_BITRATE_RF_32 | LFRFID_T5577_MODULATION_PSK2 |
|
||||
(7 << LFRFID_T5577_MAXBLOCK_SHIFT);
|
||||
request->t5577.block[1] = bit_lib_get_bits_32(protocol->data, 0, 32);
|
||||
request->t5577.block[2] = bit_lib_get_bits_32(protocol->data, 32, 32);
|
||||
request->t5577.block[3] = bit_lib_get_bits_32(protocol->data, 64, 32);
|
||||
request->t5577.block[4] = bit_lib_get_bits_32(protocol->data, 96, 32);
|
||||
request->t5577.block[5] = bit_lib_get_bits_32(protocol->data, 128, 32);
|
||||
request->t5577.block[6] = bit_lib_get_bits_32(protocol->data, 160, 32);
|
||||
request->t5577.block[7] = bit_lib_get_bits_32(protocol->data, 192, 32);
|
||||
request->t5577.blocks_to_write = 8;
|
||||
result = true;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
const ProtocolBase protocol_indala224 = {
|
||||
.name = "Indala224",
|
||||
.manufacturer = "Motorola",
|
||||
.data_size = INDALA224_DECODED_DATA_SIZE,
|
||||
.features = LFRFIDFeaturePSK,
|
||||
.validate_count = 6,
|
||||
.alloc = (ProtocolAlloc)protocol_indala224_alloc,
|
||||
.free = (ProtocolFree)protocol_indala224_free,
|
||||
.get_data = (ProtocolGetData)protocol_indala224_get_data,
|
||||
.decoder =
|
||||
{
|
||||
.start = (ProtocolDecoderStart)protocol_indala224_decoder_start,
|
||||
.feed = (ProtocolDecoderFeed)protocol_indala224_decoder_feed,
|
||||
},
|
||||
.encoder =
|
||||
{
|
||||
.start = (ProtocolEncoderStart)protocol_indala224_encoder_start,
|
||||
.yield = (ProtocolEncoderYield)protocol_indala224_encoder_yield,
|
||||
},
|
||||
.render_data = (ProtocolRenderData)protocol_indala224_render_data,
|
||||
.render_brief_data = (ProtocolRenderData)protocol_indala224_render_brief_data,
|
||||
.write_data = (ProtocolWriteData)protocol_indala224_write_data,
|
||||
};
|
||||
4
lib/lfrfid/protocols/protocol_indala224.h
Normal file
4
lib/lfrfid/protocols/protocol_indala224.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#pragma once
|
||||
#include <toolbox/protocols/protocol.h>
|
||||
|
||||
extern const ProtocolBase protocol_indala224;
|
||||
363
lib/subghz/protocols/keyfinder.c
Normal file
363
lib/subghz/protocols/keyfinder.c
Normal file
@@ -0,0 +1,363 @@
|
||||
#include "keyfinder.h"
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolKeyFinder"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_keyfinder_const = {
|
||||
.te_short = 400,
|
||||
.te_long = 1200,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 24,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderKeyFinder {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint8_t end_count;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderKeyFinder {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
KeyFinderDecoderStepReset = 0,
|
||||
KeyFinderDecoderStepSaveDuration,
|
||||
KeyFinderDecoderStepCheckDuration,
|
||||
KeyFinderDecoderStepFinish,
|
||||
} KeyFinderDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_keyfinder_decoder = {
|
||||
.alloc = subghz_protocol_decoder_keyfinder_alloc,
|
||||
.free = subghz_protocol_decoder_keyfinder_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_keyfinder_feed,
|
||||
.reset = subghz_protocol_decoder_keyfinder_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_keyfinder_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_keyfinder_serialize,
|
||||
.deserialize = subghz_protocol_decoder_keyfinder_deserialize,
|
||||
.get_string = subghz_protocol_decoder_keyfinder_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_keyfinder_encoder = {
|
||||
.alloc = subghz_protocol_encoder_keyfinder_alloc,
|
||||
.free = subghz_protocol_encoder_keyfinder_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_keyfinder_deserialize,
|
||||
.stop = subghz_protocol_encoder_keyfinder_stop,
|
||||
.yield = subghz_protocol_encoder_keyfinder_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_keyfinder = {
|
||||
.name = SUBGHZ_PROTOCOL_KEYFINDER_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_keyfinder_decoder,
|
||||
.encoder = &subghz_protocol_keyfinder_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_keyfinder_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderKeyFinder* instance = malloc(sizeof(SubGhzProtocolEncoderKeyFinder));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_keyfinder;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 5;
|
||||
instance->encoder.size_upload = 60;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_keyfinder_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderKeyFinder* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderKeyFinder instance
|
||||
*/
|
||||
static void
|
||||
subghz_protocol_encoder_keyfinder_get_upload(SubGhzProtocolEncoderKeyFinder* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
|
||||
// Send key data 24 bit first
|
||||
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_keyfinder_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_keyfinder_const.te_long);
|
||||
|
||||
} else {
|
||||
// Send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_keyfinder_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_keyfinder_const.te_short);
|
||||
}
|
||||
}
|
||||
// End bits (3 times then 1 more with gap 4k us)
|
||||
for(uint8_t i = 0; i < 3; i++) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_keyfinder_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_keyfinder_const.te_short);
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_keyfinder_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_keyfinder_const.te_short * 10);
|
||||
|
||||
instance->encoder.size_upload = index;
|
||||
return;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_keyfinder_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
instance->serial = instance->data >> 4;
|
||||
instance->btn = instance->data & 0xF;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_keyfinder_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderKeyFinder* instance = context;
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
do {
|
||||
ret = subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic,
|
||||
flipper_format,
|
||||
subghz_protocol_keyfinder_const.min_count_bit_for_found);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
// Optional value
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
subghz_protocol_keyfinder_check_remote_controller(&instance->generic);
|
||||
subghz_protocol_encoder_keyfinder_get_upload(instance);
|
||||
instance->encoder.is_running = true;
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_keyfinder_stop(void* context) {
|
||||
SubGhzProtocolEncoderKeyFinder* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_keyfinder_yield(void* context) {
|
||||
SubGhzProtocolEncoderKeyFinder* 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) {
|
||||
if(!subghz_block_generic_global.endless_tx) instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_keyfinder_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderKeyFinder* instance = malloc(sizeof(SubGhzProtocolDecoderKeyFinder));
|
||||
instance->base.protocol = &subghz_protocol_keyfinder;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_keyfinder_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKeyFinder* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_keyfinder_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKeyFinder* instance = context;
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_keyfinder_feed(void* context, bool level, volatile uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKeyFinder* instance = context;
|
||||
|
||||
// KeyFinder Decoder
|
||||
// 2026.03 - @xMasterX (MMX)
|
||||
|
||||
// Key samples
|
||||
//
|
||||
// 433.92 MHz AM650
|
||||
// Serial ID Serial ID
|
||||
// RED - C396F E = 11000011100101101111 1110
|
||||
// PURPLE - C396F B = 11000011100101101111 1011
|
||||
// GREEN - C396F D = 11000011100101101111 1101
|
||||
// BLUE - C396F C = 11000011100101101111 1100
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case KeyFinderDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_keyfinder_const.te_short * 10) <
|
||||
subghz_protocol_keyfinder_const.te_delta * 5)) {
|
||||
//Found GAP
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepSaveDuration;
|
||||
}
|
||||
break;
|
||||
case KeyFinderDecoderStepSaveDuration:
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_keyfinder_const.min_count_bit_for_found) {
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_keyfinder_const.te_short) <
|
||||
subghz_protocol_keyfinder_const.te_delta)) {
|
||||
instance->end_count++;
|
||||
if(instance->end_count == 4) {
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepFinish;
|
||||
instance->end_count = 0;
|
||||
}
|
||||
} else if(
|
||||
(!level) && (DURATION_DIFF(duration, subghz_protocol_keyfinder_const.te_short) <
|
||||
subghz_protocol_keyfinder_const.te_delta)) {
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->end_count = 0;
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case KeyFinderDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
// Bit 1 is short and long timing = 400us HIGH (te_last) and 1200us LOW
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keyfinder_const.te_short) <
|
||||
subghz_protocol_keyfinder_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_keyfinder_const.te_long) <
|
||||
subghz_protocol_keyfinder_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepSaveDuration;
|
||||
// Bit 0 is long and short timing = 1200us HIGH (te_last) and 400us LOW
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_keyfinder_const.te_long) <
|
||||
subghz_protocol_keyfinder_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_keyfinder_const.te_short) <
|
||||
subghz_protocol_keyfinder_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case KeyFinderDecoderStepFinish:
|
||||
// If got 24 bits key reading is finished
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_keyfinder_const.min_count_bit_for_found) {
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->end_count = 0;
|
||||
instance->decoder.parser_step = KeyFinderDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_keyfinder_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKeyFinder* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_keyfinder_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKeyFinder* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_keyfinder_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKeyFinder* instance = context;
|
||||
return subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic,
|
||||
flipper_format,
|
||||
subghz_protocol_keyfinder_const.min_count_bit_for_found);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_keyfinder_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKeyFinder* instance = context;
|
||||
|
||||
subghz_protocol_keyfinder_check_remote_controller(&instance->generic);
|
||||
|
||||
uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
|
||||
// for future use
|
||||
// // push protocol data to global variable
|
||||
// subghz_block_generic_global.btn_is_available = false;
|
||||
// subghz_block_generic_global.current_btn = instance->generic.btn;
|
||||
// subghz_block_generic_global.btn_length_bit = 4;
|
||||
// //
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Key: 0x%06lX\r\n"
|
||||
"Yek: 0x%06lX\r\n"
|
||||
"Serial: 0x%05lX\r\n"
|
||||
"ID: 0x%0X",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFF),
|
||||
(uint32_t)(code_found_reverse & 0xFFFFFF),
|
||||
instance->generic.serial,
|
||||
instance->generic.btn);
|
||||
}
|
||||
109
lib/subghz/protocols/keyfinder.h
Normal file
109
lib/subghz/protocols/keyfinder.h
Normal file
@@ -0,0 +1,109 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_KEYFINDER_NAME "KeyFinder"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderKeyFinder SubGhzProtocolDecoderKeyFinder;
|
||||
typedef struct SubGhzProtocolEncoderKeyFinder SubGhzProtocolEncoderKeyFinder;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_keyfinder_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_keyfinder_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_keyfinder;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderKeyFinder.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderKeyFinder* pointer to a SubGhzProtocolEncoderKeyFinder instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_keyfinder_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderKeyFinder.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeyFinder instance
|
||||
*/
|
||||
void subghz_protocol_encoder_keyfinder_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeyFinder instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_keyfinder_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeyFinder instance
|
||||
*/
|
||||
void subghz_protocol_encoder_keyfinder_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeyFinder instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_keyfinder_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderKeyFinder.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderKeyFinder* pointer to a SubGhzProtocolDecoderKeyFinder instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_keyfinder_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderKeyFinder.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeyFinder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_keyfinder_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderKeyFinder.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeyFinder instance
|
||||
*/
|
||||
void subghz_protocol_decoder_keyfinder_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeyFinder instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_keyfinder_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeyFinder instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_keyfinder_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderKeyFinder.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeyFinder instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_keyfinder_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderKeyFinder.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeyFinder instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_keyfinder_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeyFinder instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_keyfinder_get_string(void* context, FuriString* output);
|
||||
@@ -85,6 +85,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
|
||||
&subghz_protocol_beninca_arc,
|
||||
&subghz_protocol_jarolift,
|
||||
&subghz_protocol_ditec_gol4,
|
||||
&subghz_protocol_keyfinder,
|
||||
};
|
||||
|
||||
const SubGhzProtocolRegistry subghz_protocol_registry = {
|
||||
|
||||
@@ -86,3 +86,4 @@
|
||||
#include "beninca_arc.h"
|
||||
#include "jarolift.h"
|
||||
#include "ditec_gol4.h"
|
||||
#include "keyfinder.h"
|
||||
|
||||
Reference in New Issue
Block a user