mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Update subghz
This commit is contained in:
32
applications/main/subghz/SConscript
Normal file
32
applications/main/subghz/SConscript
Normal file
@@ -0,0 +1,32 @@
|
||||
Import("env")
|
||||
|
||||
env.Append(
|
||||
CPPPATH=[
|
||||
"#/lib/subghz",
|
||||
],
|
||||
SDK_HEADERS=[
|
||||
File("environment.h"),
|
||||
File("receiver.h"),
|
||||
File("subghz_worker.h"),
|
||||
File("subghz_tx_rx_worker.h"),
|
||||
File("transmitter.h"),
|
||||
File("registry.h"),
|
||||
File("protocols/protocol_items.h"),
|
||||
File("protocols/raw.h"),
|
||||
File("blocks/const.h"),
|
||||
File("blocks/decoder.h"),
|
||||
File("blocks/encoder.h"),
|
||||
File("blocks/generic.h"),
|
||||
File("blocks/math.h"),
|
||||
File("subghz_setting.h"),
|
||||
],
|
||||
)
|
||||
|
||||
libenv = env.Clone(FW_LIB_NAME="subghz")
|
||||
libenv.ApplyLibFlags()
|
||||
|
||||
sources = libenv.GlobRecursive("*.c*")
|
||||
|
||||
lib = libenv.StaticLibrary("${FW_LIB_NAME}", sources)
|
||||
libenv.Install("${LIB_DIST_DIR}", lib)
|
||||
Return("lib")
|
||||
@@ -12,7 +12,7 @@ App(
|
||||
],
|
||||
provides=["subghz_start"],
|
||||
icon="A_Sub1ghz_14",
|
||||
stack_size=2 * 1024,
|
||||
stack_size=3 * 1024,
|
||||
order=10,
|
||||
)
|
||||
|
||||
|
||||
1
applications/main/subghz/blocks/const.c
Normal file
1
applications/main/subghz/blocks/const.c
Normal file
@@ -0,0 +1 @@
|
||||
#include "const.h"
|
||||
20
applications/main/subghz/blocks/const.h
Normal file
20
applications/main/subghz/blocks/const.h
Normal file
@@ -0,0 +1,20 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
const uint16_t te_long;
|
||||
const uint16_t te_short;
|
||||
const uint16_t te_delta;
|
||||
const uint8_t min_count_bit_for_found;
|
||||
} SubGhzBlockConst;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
27
applications/main/subghz/blocks/decoder.c
Normal file
27
applications/main/subghz/blocks/decoder.c
Normal file
@@ -0,0 +1,27 @@
|
||||
#include "decoder.h"
|
||||
|
||||
#define TAG "SubGhzBlockDecoder"
|
||||
|
||||
void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit) {
|
||||
decoder->decode_data = decoder->decode_data << 1 | bit;
|
||||
decoder->decode_count_bit++;
|
||||
}
|
||||
|
||||
void subghz_protocol_blocks_add_to_128_bit(
|
||||
SubGhzBlockDecoder* decoder,
|
||||
uint8_t bit,
|
||||
uint64_t* head_64_bit) {
|
||||
if(++decoder->decode_count_bit > 64) {
|
||||
(*head_64_bit) = ((*head_64_bit) << 1) | (decoder->decode_data >> 63);
|
||||
}
|
||||
decoder->decode_data = decoder->decode_data << 1 | bit;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len) {
|
||||
uint8_t hash = 0;
|
||||
uint8_t* p = (uint8_t*)&decoder->decode_data;
|
||||
for(size_t i = 0; i < len; i++) {
|
||||
hash ^= p[i];
|
||||
}
|
||||
return hash;
|
||||
}
|
||||
47
applications/main/subghz/blocks/decoder.h
Normal file
47
applications/main/subghz/blocks/decoder.h
Normal file
@@ -0,0 +1,47 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SubGhzBlockDecoder SubGhzBlockDecoder;
|
||||
|
||||
struct SubGhzBlockDecoder {
|
||||
uint32_t parser_step;
|
||||
uint32_t te_last;
|
||||
uint64_t decode_data;
|
||||
uint8_t decode_count_bit;
|
||||
};
|
||||
|
||||
/**
|
||||
* Add data bit when decoding.
|
||||
* @param decoder Pointer to a SubGhzBlockDecoder instance
|
||||
* @param bit data, 1bit
|
||||
*/
|
||||
void subghz_protocol_blocks_add_bit(SubGhzBlockDecoder* decoder, uint8_t bit);
|
||||
|
||||
/**
|
||||
* Add data to_128 bit when decoding.
|
||||
* @param decoder Pointer to a SubGhzBlockDecoder instance
|
||||
* @param head_64_bit Pointer to a head_64_bit
|
||||
* @param bit data, 1bit
|
||||
*/
|
||||
void subghz_protocol_blocks_add_to_128_bit(
|
||||
SubGhzBlockDecoder* decoder,
|
||||
uint8_t bit,
|
||||
uint64_t* head_64_bit);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param decoder Pointer to a SubGhzBlockDecoder instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_get_hash_data(SubGhzBlockDecoder* decoder, size_t len);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
58
applications/main/subghz/blocks/encoder.c
Normal file
58
applications/main/subghz/blocks/encoder.c
Normal file
@@ -0,0 +1,58 @@
|
||||
#include "encoder.h"
|
||||
#include "math.h"
|
||||
#include <core/check.h>
|
||||
|
||||
#include "furi.h"
|
||||
|
||||
#define TAG "SubGhzBlockEncoder"
|
||||
|
||||
void subghz_protocol_blocks_set_bit_array(
|
||||
bool bit_value,
|
||||
uint8_t data_array[],
|
||||
size_t set_index_bit,
|
||||
size_t max_size_array) {
|
||||
furi_assert(set_index_bit < max_size_array * 8);
|
||||
bit_write(data_array[set_index_bit >> 3], 7 - (set_index_bit & 0x7), bit_value);
|
||||
}
|
||||
|
||||
bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit) {
|
||||
return bit_read(data_array[read_index_bit >> 3], 7 - (read_index_bit & 0x7));
|
||||
}
|
||||
|
||||
size_t subghz_protocol_blocks_get_upload_from_bit_array(
|
||||
uint8_t data_array[],
|
||||
size_t count_bit_data_array,
|
||||
LevelDuration* upload,
|
||||
size_t max_size_upload,
|
||||
uint32_t duration_bit,
|
||||
SubGhzProtocolBlockAlignBit align_bit) {
|
||||
size_t bias_bit = 0;
|
||||
size_t size_upload = 0;
|
||||
uint32_t duration = duration_bit;
|
||||
|
||||
if(align_bit == SubGhzProtocolBlockAlignBitRight) {
|
||||
if(count_bit_data_array & 0x7) {
|
||||
bias_bit = 8 - (count_bit_data_array & 0x7);
|
||||
}
|
||||
}
|
||||
size_t index_bit = bias_bit;
|
||||
|
||||
bool last_bit = subghz_protocol_blocks_get_bit_array(data_array, index_bit++);
|
||||
for(size_t i = 1 + bias_bit; i < count_bit_data_array + bias_bit; i++) {
|
||||
if(last_bit == subghz_protocol_blocks_get_bit_array(data_array, index_bit)) {
|
||||
duration += duration_bit;
|
||||
} else {
|
||||
if(size_upload > max_size_upload) {
|
||||
furi_crash("SubGhz: Encoder buffer overflow");
|
||||
}
|
||||
upload[size_upload++] = level_duration_make(
|
||||
subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration);
|
||||
last_bit = !last_bit;
|
||||
duration = duration_bit;
|
||||
}
|
||||
index_bit++;
|
||||
}
|
||||
upload[size_upload++] = level_duration_make(
|
||||
subghz_protocol_blocks_get_bit_array(data_array, index_bit - 1), duration);
|
||||
return size_upload;
|
||||
}
|
||||
67
applications/main/subghz/blocks/encoder.h
Normal file
67
applications/main/subghz/blocks/encoder.h
Normal file
@@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <lib/toolbox/level_duration.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct {
|
||||
bool is_running;
|
||||
size_t repeat;
|
||||
size_t front;
|
||||
size_t size_upload;
|
||||
LevelDuration* upload;
|
||||
|
||||
} SubGhzProtocolBlockEncoder;
|
||||
|
||||
typedef enum {
|
||||
SubGhzProtocolBlockAlignBitLeft,
|
||||
SubGhzProtocolBlockAlignBitRight,
|
||||
} SubGhzProtocolBlockAlignBit;
|
||||
|
||||
/**
|
||||
* Set data bit when encoding HEX array.
|
||||
* @param bit_value The value of the bit to be set
|
||||
* @param data_array Pointer to a HEX array
|
||||
* @param set_index_bit Number set a bit in the array starting from the left
|
||||
* @param max_size_array array size, check not to overflow
|
||||
*/
|
||||
void subghz_protocol_blocks_set_bit_array(
|
||||
bool bit_value,
|
||||
uint8_t data_array[],
|
||||
size_t set_index_bit,
|
||||
size_t max_size_array);
|
||||
|
||||
/**
|
||||
* Get data bit when encoding HEX array.
|
||||
* @param data_array Pointer to a HEX array
|
||||
* @param read_index_bit Number get a bit in the array starting from the left
|
||||
* @return bool value bit
|
||||
*/
|
||||
bool subghz_protocol_blocks_get_bit_array(uint8_t data_array[], size_t read_index_bit);
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param data_array Pointer to a HEX array
|
||||
* @param count_bit_data_array How many bits in the array are processed
|
||||
* @param upload Pointer to a LevelDuration
|
||||
* @param max_size_upload upload size, check not to overflow
|
||||
* @param duration_bit duration 1 bit
|
||||
* @param align_bit alignment of useful bits in an array
|
||||
*/
|
||||
size_t subghz_protocol_blocks_get_upload_from_bit_array(
|
||||
uint8_t data_array[],
|
||||
size_t count_bit_data_array,
|
||||
LevelDuration* upload,
|
||||
size_t max_size_upload,
|
||||
uint32_t duration_bit,
|
||||
SubGhzProtocolBlockAlignBit align_bit);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
120
applications/main/subghz/blocks/generic.c
Normal file
120
applications/main/subghz/blocks/generic.c
Normal file
@@ -0,0 +1,120 @@
|
||||
#include "generic.h"
|
||||
#include <lib/toolbox/stream/stream.h>
|
||||
#include <lib/flipper_format/flipper_format_i.h>
|
||||
|
||||
#define TAG "SubGhzBlockGeneric"
|
||||
|
||||
void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str) {
|
||||
const char* preset_name_temp;
|
||||
if(!strcmp(preset_name, "AM270")) {
|
||||
preset_name_temp = "FuriHalSubGhzPresetOok270Async";
|
||||
} else if(!strcmp(preset_name, "AM650")) {
|
||||
preset_name_temp = "FuriHalSubGhzPresetOok650Async";
|
||||
} else if(!strcmp(preset_name, "FM238")) {
|
||||
preset_name_temp = "FuriHalSubGhzPreset2FSKDev238Async";
|
||||
} else if(!strcmp(preset_name, "FM476")) {
|
||||
preset_name_temp = "FuriHalSubGhzPreset2FSKDev476Async";
|
||||
} else {
|
||||
preset_name_temp = "FuriHalSubGhzPresetCustom";
|
||||
}
|
||||
furi_string_set(preset_str, preset_name_temp);
|
||||
}
|
||||
|
||||
bool subghz_block_generic_serialize(
|
||||
SubGhzBlockGeneric* instance,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(instance);
|
||||
bool res = false;
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
do {
|
||||
stream_clean(flipper_format_get_raw_stream(flipper_format));
|
||||
if(!flipper_format_write_header_cstr(
|
||||
flipper_format, SUBGHZ_KEY_FILE_TYPE, SUBGHZ_KEY_FILE_VERSION)) {
|
||||
FURI_LOG_E(TAG, "Unable to add header");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_uint32(flipper_format, "Frequency", &preset->frequency, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Frequency");
|
||||
break;
|
||||
}
|
||||
|
||||
subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str);
|
||||
if(!flipper_format_write_string_cstr(
|
||||
flipper_format, "Preset", furi_string_get_cstr(temp_str))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Preset");
|
||||
break;
|
||||
}
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) {
|
||||
if(!flipper_format_write_string_cstr(
|
||||
flipper_format, "Custom_preset_module", "CC1101")) {
|
||||
FURI_LOG_E(TAG, "Unable to add Custom_preset_module");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_write_hex(
|
||||
flipper_format, "Custom_preset_data", preset->data, preset->data_size)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Custom_preset_data");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!flipper_format_write_string_cstr(flipper_format, "Protocol", instance->protocol_name)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Protocol");
|
||||
break;
|
||||
}
|
||||
uint32_t temp = instance->data_count_bit;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Bit", &temp, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Bit");
|
||||
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->data >> (i * 8)) & 0xFF;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Key");
|
||||
break;
|
||||
}
|
||||
res = true;
|
||||
} while(false);
|
||||
furi_string_free(temp_str);
|
||||
return res;
|
||||
}
|
||||
|
||||
bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format) {
|
||||
furi_assert(instance);
|
||||
bool res = false;
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
uint32_t temp_data = 0;
|
||||
|
||||
do {
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_uint32(flipper_format, "Bit", (uint32_t*)&temp_data, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing Bit");
|
||||
break;
|
||||
}
|
||||
instance->data_count_bit = (uint16_t)temp_data;
|
||||
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
if(!flipper_format_read_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Missing Key");
|
||||
break;
|
||||
}
|
||||
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
instance->data = instance->data << 8 | key_data[i];
|
||||
}
|
||||
|
||||
res = true;
|
||||
} while(0);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return res;
|
||||
}
|
||||
59
applications/main/subghz/blocks/generic.h
Normal file
59
applications/main/subghz/blocks/generic.h
Normal file
@@ -0,0 +1,59 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include <furi.h>
|
||||
#include <furi_hal.h>
|
||||
#include "../types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SubGhzBlockGeneric SubGhzBlockGeneric;
|
||||
|
||||
struct SubGhzBlockGeneric {
|
||||
const char* protocol_name;
|
||||
uint64_t data;
|
||||
uint64_t data_2;
|
||||
uint32_t serial;
|
||||
uint16_t data_count_bit;
|
||||
uint8_t btn;
|
||||
uint32_t cnt;
|
||||
uint8_t cnt_2;
|
||||
uint32_t seed;
|
||||
};
|
||||
|
||||
/**
|
||||
* Get name preset.
|
||||
* @param preset_name name preset
|
||||
* @param preset_str Output name preset
|
||||
*/
|
||||
void subghz_block_generic_get_preset_name(const char* preset_name, FuriString* preset_str);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzBlockGeneric.
|
||||
* @param instance Pointer to a SubGhzBlockGeneric instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_block_generic_serialize(
|
||||
SubGhzBlockGeneric* instance,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzBlockGeneric.
|
||||
* @param instance Pointer to a SubGhzBlockGeneric instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_block_generic_deserialize(SubGhzBlockGeneric* instance, FlipperFormat* flipper_format);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
244
applications/main/subghz/blocks/math.c
Normal file
244
applications/main/subghz/blocks/math.c
Normal file
@@ -0,0 +1,244 @@
|
||||
#include "math.h"
|
||||
|
||||
uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) {
|
||||
uint64_t reverse_key = 0;
|
||||
for(uint8_t i = 0; i < bit_count; i++) {
|
||||
reverse_key = reverse_key << 1 | bit_read(key, i);
|
||||
}
|
||||
return reverse_key;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) {
|
||||
uint8_t parity = 0;
|
||||
for(uint8_t i = 0; i < bit_count; i++) {
|
||||
parity += bit_read(key, i);
|
||||
}
|
||||
return parity & 0x01;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_crc4(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t polynomial,
|
||||
uint8_t init) {
|
||||
uint8_t remainder = init << 4; // LSBs are unused
|
||||
uint8_t poly = polynomial << 4;
|
||||
uint8_t bit;
|
||||
|
||||
while(size--) {
|
||||
remainder ^= *message++;
|
||||
for(bit = 0; bit < 8; bit++) {
|
||||
if(remainder & 0x80) {
|
||||
remainder = (remainder << 1) ^ poly;
|
||||
} else {
|
||||
remainder = (remainder << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return remainder >> 4 & 0x0f; // discard the LSBs
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_crc7(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t polynomial,
|
||||
uint8_t init) {
|
||||
uint8_t remainder = init << 1; // LSB is unused
|
||||
uint8_t poly = polynomial << 1;
|
||||
|
||||
for(size_t byte = 0; byte < size; ++byte) {
|
||||
remainder ^= message[byte];
|
||||
for(uint8_t bit = 0; bit < 8; ++bit) {
|
||||
if(remainder & 0x80) {
|
||||
remainder = (remainder << 1) ^ poly;
|
||||
} else {
|
||||
remainder = (remainder << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return remainder >> 1 & 0x7f; // discard the LSB
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_crc8(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t polynomial,
|
||||
uint8_t init) {
|
||||
uint8_t remainder = init;
|
||||
|
||||
for(size_t byte = 0; byte < size; ++byte) {
|
||||
remainder ^= message[byte];
|
||||
for(uint8_t bit = 0; bit < 8; ++bit) {
|
||||
if(remainder & 0x80) {
|
||||
remainder = (remainder << 1) ^ polynomial;
|
||||
} else {
|
||||
remainder = (remainder << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return remainder;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_crc8le(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t polynomial,
|
||||
uint8_t init) {
|
||||
uint8_t remainder = subghz_protocol_blocks_reverse_key(init, 8);
|
||||
polynomial = subghz_protocol_blocks_reverse_key(polynomial, 8);
|
||||
|
||||
for(size_t byte = 0; byte < size; ++byte) {
|
||||
remainder ^= message[byte];
|
||||
for(uint8_t bit = 0; bit < 8; ++bit) {
|
||||
if(remainder & 1) {
|
||||
remainder = (remainder >> 1) ^ polynomial;
|
||||
} else {
|
||||
remainder = (remainder >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return remainder;
|
||||
}
|
||||
|
||||
uint16_t subghz_protocol_blocks_crc16lsb(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint16_t polynomial,
|
||||
uint16_t init) {
|
||||
uint16_t remainder = init;
|
||||
|
||||
for(size_t byte = 0; byte < size; ++byte) {
|
||||
remainder ^= message[byte];
|
||||
for(uint8_t bit = 0; bit < 8; ++bit) {
|
||||
if(remainder & 1) {
|
||||
remainder = (remainder >> 1) ^ polynomial;
|
||||
} else {
|
||||
remainder = (remainder >> 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return remainder;
|
||||
}
|
||||
|
||||
uint16_t subghz_protocol_blocks_crc16(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint16_t polynomial,
|
||||
uint16_t init) {
|
||||
uint16_t remainder = init;
|
||||
|
||||
for(size_t byte = 0; byte < size; ++byte) {
|
||||
remainder ^= message[byte] << 8;
|
||||
for(uint8_t bit = 0; bit < 8; ++bit) {
|
||||
if(remainder & 0x8000) {
|
||||
remainder = (remainder << 1) ^ polynomial;
|
||||
} else {
|
||||
remainder = (remainder << 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
return remainder;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_lfsr_digest8(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t gen,
|
||||
uint8_t key) {
|
||||
uint8_t sum = 0;
|
||||
for(size_t byte = 0; byte < size; ++byte) {
|
||||
uint8_t data = message[byte];
|
||||
for(int i = 7; i >= 0; --i) {
|
||||
// XOR key into sum if data bit is set
|
||||
if((data >> i) & 1) sum ^= key;
|
||||
|
||||
// roll the key right (actually the LSB is dropped here)
|
||||
// and apply the gen (needs to include the dropped LSB as MSB)
|
||||
if(key & 1)
|
||||
key = (key >> 1) ^ gen;
|
||||
else
|
||||
key = (key >> 1);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_lfsr_digest8_reflect(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t gen,
|
||||
uint8_t key) {
|
||||
uint8_t sum = 0;
|
||||
// Process message from last byte to first byte (reflected)
|
||||
for(int byte = size - 1; byte >= 0; --byte) {
|
||||
uint8_t data = message[byte];
|
||||
// Process individual bits of each byte (reflected)
|
||||
for(uint8_t i = 0; i < 8; ++i) {
|
||||
// XOR key into sum if data bit is set
|
||||
if((data >> i) & 1) {
|
||||
sum ^= key;
|
||||
}
|
||||
|
||||
// roll the key left (actually the LSB is dropped here)
|
||||
// and apply the gen (needs to include the dropped lsb as MSB)
|
||||
if(key & 0x80)
|
||||
key = (key << 1) ^ gen;
|
||||
else
|
||||
key = (key << 1);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint16_t subghz_protocol_blocks_lfsr_digest16(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint16_t gen,
|
||||
uint16_t key) {
|
||||
uint16_t sum = 0;
|
||||
for(size_t byte = 0; byte < size; ++byte) {
|
||||
uint8_t data = message[byte];
|
||||
for(int8_t i = 7; i >= 0; --i) {
|
||||
// if data bit is set then xor with key
|
||||
if((data >> i) & 1) sum ^= key;
|
||||
|
||||
// roll the key right (actually the LSB is dropped here)
|
||||
// and apply the gen (needs to include the dropped LSB as MSB)
|
||||
if(key & 1)
|
||||
key = (key >> 1) ^ gen;
|
||||
else
|
||||
key = (key >> 1);
|
||||
}
|
||||
}
|
||||
return sum;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) {
|
||||
uint32_t result = 0;
|
||||
for(size_t i = 0; i < size; ++i) {
|
||||
result += message[i];
|
||||
}
|
||||
return (uint8_t)result;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_parity8(uint8_t byte) {
|
||||
byte ^= byte >> 4;
|
||||
byte &= 0xf;
|
||||
return (0x6996 >> byte) & 1;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) {
|
||||
uint8_t result = 0;
|
||||
for(size_t i = 0; i < size; ++i) {
|
||||
result ^= subghz_protocol_blocks_parity8(message[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size) {
|
||||
uint8_t result = 0;
|
||||
for(size_t i = 0; i < size; ++i) {
|
||||
result ^= message[i];
|
||||
}
|
||||
return result;
|
||||
}
|
||||
222
applications/main/subghz/blocks/math.h
Normal file
222
applications/main/subghz/blocks/math.h
Normal file
@@ -0,0 +1,222 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdbool.h>
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#define bit_read(value, bit) (((value) >> (bit)) & 0x01)
|
||||
#define bit_set(value, bit) \
|
||||
({ \
|
||||
__typeof__(value) _one = (1); \
|
||||
(value) |= (_one << (bit)); \
|
||||
})
|
||||
#define bit_clear(value, bit) \
|
||||
({ \
|
||||
__typeof__(value) _one = (1); \
|
||||
(value) &= ~(_one << (bit)); \
|
||||
})
|
||||
#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit))
|
||||
#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/** Flip the data bitwise
|
||||
*
|
||||
* @param key In data
|
||||
* @param bit_count number of data bits
|
||||
*
|
||||
* @return Reverse data
|
||||
*/
|
||||
uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count);
|
||||
|
||||
/** Get parity the data bitwise
|
||||
*
|
||||
* @param key In data
|
||||
* @param bit_count number of data bits
|
||||
*
|
||||
* @return parity
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count);
|
||||
|
||||
/** CRC-4
|
||||
*
|
||||
* @param message array of bytes to check
|
||||
* @param size number of bytes in message
|
||||
* @param polynomial CRC polynomial
|
||||
* @param init starting crc value
|
||||
*
|
||||
* @return CRC value
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_crc4(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t polynomial,
|
||||
uint8_t init);
|
||||
|
||||
/** CRC-7
|
||||
*
|
||||
* @param message array of bytes to check
|
||||
* @param size number of bytes in message
|
||||
* @param polynomial CRC polynomial
|
||||
* @param init starting crc value
|
||||
*
|
||||
* @return CRC value
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_crc7(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t polynomial,
|
||||
uint8_t init);
|
||||
|
||||
/** Generic Cyclic Redundancy Check CRC-8. Example polynomial: 0x31 = x8 + x5 +
|
||||
* x4 + 1 (x8 is implicit) Example polynomial: 0x80 = x8 + x7 (a normal
|
||||
* bit-by-bit parity XOR)
|
||||
*
|
||||
* @param message array of bytes to check
|
||||
* @param size number of bytes in message
|
||||
* @param polynomial byte is from x^7 to x^0 (x^8 is implicitly one)
|
||||
* @param init starting crc value
|
||||
*
|
||||
* @return CRC value
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_crc8(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t polynomial,
|
||||
uint8_t init);
|
||||
|
||||
/** "Little-endian" Cyclic Redundancy Check CRC-8 LE Input and output are
|
||||
* reflected, i.e. least significant bit is shifted in first
|
||||
*
|
||||
* @param message array of bytes to check
|
||||
* @param size number of bytes in message
|
||||
* @param polynomial CRC polynomial
|
||||
* @param init starting crc value
|
||||
*
|
||||
* @return CRC value
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_crc8le(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t polynomial,
|
||||
uint8_t init);
|
||||
|
||||
/** CRC-16 LSB. Input and output are reflected, i.e. least significant bit is
|
||||
* shifted in first. Note that poly and init already need to be reflected
|
||||
*
|
||||
* @param message array of bytes to check
|
||||
* @param size number of bytes in message
|
||||
* @param polynomial CRC polynomial
|
||||
* @param init starting crc value
|
||||
*
|
||||
* @return CRC value
|
||||
*/
|
||||
uint16_t subghz_protocol_blocks_crc16lsb(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint16_t polynomial,
|
||||
uint16_t init);
|
||||
|
||||
/** CRC-16
|
||||
*
|
||||
* @param message array of bytes to check
|
||||
* @param size number of bytes in message
|
||||
* @param polynomial CRC polynomial
|
||||
* @param init starting crc value
|
||||
*
|
||||
* @return CRC value
|
||||
*/
|
||||
uint16_t subghz_protocol_blocks_crc16(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint16_t polynomial,
|
||||
uint16_t init);
|
||||
|
||||
/** Digest-8 by "LFSR-based Toeplitz hash"
|
||||
*
|
||||
* @param message bytes of message data
|
||||
* @param size number of bytes to digest
|
||||
* @param gen key stream generator, needs to includes the MSB if the
|
||||
* LFSR is rolling
|
||||
* @param key initial key
|
||||
*
|
||||
* @return digest value
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_lfsr_digest8(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t gen,
|
||||
uint8_t key);
|
||||
|
||||
/** Digest-8 by "LFSR-based Toeplitz hash", byte reflect, bit reflect
|
||||
*
|
||||
* @param message bytes of message data
|
||||
* @param size number of bytes to digest
|
||||
* @param gen key stream generator, needs to includes the MSB if the
|
||||
* LFSR is rolling
|
||||
* @param key initial key
|
||||
*
|
||||
* @return digest value
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_lfsr_digest8_reflect(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint8_t gen,
|
||||
uint8_t key);
|
||||
|
||||
/** Digest-16 by "LFSR-based Toeplitz hash"
|
||||
*
|
||||
* @param message bytes of message data
|
||||
* @param size number of bytes to digest
|
||||
* @param gen key stream generator, needs to includes the MSB if the
|
||||
* LFSR is rolling
|
||||
* @param key initial key
|
||||
*
|
||||
* @return digest value
|
||||
*/
|
||||
uint16_t subghz_protocol_blocks_lfsr_digest16(
|
||||
uint8_t const message[],
|
||||
size_t size,
|
||||
uint16_t gen,
|
||||
uint16_t key);
|
||||
|
||||
/** Compute Addition of a number of bytes
|
||||
*
|
||||
* @param message bytes of message data
|
||||
* @param size number of bytes to sum
|
||||
*
|
||||
* @return summation value
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size);
|
||||
|
||||
/** Compute bit parity of a single byte (8 bits)
|
||||
*
|
||||
* @param byte single byte to check
|
||||
*
|
||||
* @return 1 odd parity, 0 even parity
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_parity8(uint8_t byte);
|
||||
|
||||
/** Compute bit parity of a number of bytes
|
||||
*
|
||||
* @param message bytes of message data
|
||||
* @param size number of bytes to sum
|
||||
*
|
||||
* @return 1 odd parity, 0 even parity
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size);
|
||||
|
||||
/** Compute XOR (byte-wide parity) of a number of bytes
|
||||
*
|
||||
* @param message bytes of message data
|
||||
* @param size number of bytes to sum
|
||||
*
|
||||
* @return summation value, per bit-position 1 odd parity, 0 even parity
|
||||
*/
|
||||
uint8_t subghz_protocol_blocks_xor_bytes(uint8_t const message[], size_t size);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
116
applications/main/subghz/environment.c
Normal file
116
applications/main/subghz/environment.c
Normal file
@@ -0,0 +1,116 @@
|
||||
#include "environment.h"
|
||||
#include "registry.h"
|
||||
|
||||
struct SubGhzEnvironment {
|
||||
SubGhzKeystore* keystore;
|
||||
const SubGhzProtocolRegistry* protocol_registry;
|
||||
const char* came_atomo_rainbow_table_file_name;
|
||||
const char* nice_flor_s_rainbow_table_file_name;
|
||||
const char* alutech_at_4n_rainbow_table_file_name;
|
||||
};
|
||||
|
||||
SubGhzEnvironment* subghz_environment_alloc() {
|
||||
SubGhzEnvironment* instance = malloc(sizeof(SubGhzEnvironment));
|
||||
|
||||
instance->keystore = subghz_keystore_alloc();
|
||||
instance->protocol_registry = NULL;
|
||||
instance->came_atomo_rainbow_table_file_name = NULL;
|
||||
instance->nice_flor_s_rainbow_table_file_name = NULL;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_environment_free(SubGhzEnvironment* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->protocol_registry = NULL;
|
||||
instance->came_atomo_rainbow_table_file_name = NULL;
|
||||
instance->nice_flor_s_rainbow_table_file_name = NULL;
|
||||
subghz_keystore_free(instance->keystore);
|
||||
|
||||
free(instance);
|
||||
}
|
||||
|
||||
bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename) {
|
||||
furi_assert(instance);
|
||||
|
||||
return subghz_keystore_load(instance->keystore, filename);
|
||||
}
|
||||
|
||||
SubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->keystore;
|
||||
}
|
||||
|
||||
void subghz_environment_set_came_atomo_rainbow_table_file_name(
|
||||
SubGhzEnvironment* instance,
|
||||
const char* filename) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->came_atomo_rainbow_table_file_name = filename;
|
||||
}
|
||||
|
||||
const char*
|
||||
subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->came_atomo_rainbow_table_file_name;
|
||||
}
|
||||
|
||||
void subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
|
||||
SubGhzEnvironment* instance,
|
||||
const char* filename) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->alutech_at_4n_rainbow_table_file_name = filename;
|
||||
}
|
||||
|
||||
const char*
|
||||
subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->alutech_at_4n_rainbow_table_file_name;
|
||||
}
|
||||
|
||||
void subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||
SubGhzEnvironment* instance,
|
||||
const char* filename) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->nice_flor_s_rainbow_table_file_name = filename;
|
||||
}
|
||||
|
||||
const char*
|
||||
subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
return instance->nice_flor_s_rainbow_table_file_name;
|
||||
}
|
||||
|
||||
void subghz_environment_set_protocol_registry(
|
||||
SubGhzEnvironment* instance,
|
||||
void* protocol_registry_items) {
|
||||
furi_assert(instance);
|
||||
const SubGhzProtocolRegistry* protocol_registry = protocol_registry_items;
|
||||
instance->protocol_registry = protocol_registry;
|
||||
}
|
||||
|
||||
void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->protocol_registry);
|
||||
return (void*)instance->protocol_registry;
|
||||
}
|
||||
|
||||
const char*
|
||||
subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx) {
|
||||
furi_assert(instance);
|
||||
furi_assert(instance->protocol_registry);
|
||||
const SubGhzProtocol* protocol =
|
||||
subghz_protocol_registry_get_by_index(instance->protocol_registry, idx);
|
||||
if(protocol != NULL) {
|
||||
return protocol->name;
|
||||
} else {
|
||||
return NULL;
|
||||
}
|
||||
}
|
||||
115
applications/main/subghz/environment.h
Normal file
115
applications/main/subghz/environment.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include "subghz_keystore.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SubGhzEnvironment SubGhzEnvironment;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzEnvironment.
|
||||
* @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance
|
||||
*/
|
||||
SubGhzEnvironment* subghz_environment_alloc();
|
||||
|
||||
/**
|
||||
* Free SubGhzEnvironment.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
*/
|
||||
void subghz_environment_free(SubGhzEnvironment* instance);
|
||||
|
||||
/**
|
||||
* Downloading the manufacture key file.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @param filename Full path to the file
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_environment_load_keystore(SubGhzEnvironment* instance, const char* filename);
|
||||
|
||||
/**
|
||||
* Get pointer to a SubGhzKeystore* instance.
|
||||
* @return SubGhzEnvironment* pointer to a SubGhzEnvironment instance
|
||||
*/
|
||||
SubGhzKeystore* subghz_environment_get_keystore(SubGhzEnvironment* instance);
|
||||
|
||||
/**
|
||||
* Set filename to work with Came Atomo.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @param filename Full path to the file
|
||||
*/
|
||||
void subghz_environment_set_came_atomo_rainbow_table_file_name(
|
||||
SubGhzEnvironment* instance,
|
||||
const char* filename);
|
||||
|
||||
/**
|
||||
* Get filename to work with Came Atomo.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @return Full path to the file
|
||||
*/
|
||||
const char* subghz_environment_get_came_atomo_rainbow_table_file_name(SubGhzEnvironment* instance);
|
||||
|
||||
/**
|
||||
* Set filename to work with Alutech at-4n.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @param filename Full path to the file
|
||||
*/
|
||||
void subghz_environment_set_alutech_at_4n_rainbow_table_file_name(
|
||||
SubGhzEnvironment* instance,
|
||||
const char* filename);
|
||||
|
||||
/**
|
||||
* Get filename to work with Alutech at-4n.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @return Full path to the file
|
||||
*/
|
||||
const char*
|
||||
subghz_environment_get_alutech_at_4n_rainbow_table_file_name(SubGhzEnvironment* instance);
|
||||
|
||||
/**
|
||||
* Set filename to work with Nice Flor-S.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @param filename Full path to the file
|
||||
*/
|
||||
void subghz_environment_set_nice_flor_s_rainbow_table_file_name(
|
||||
SubGhzEnvironment* instance,
|
||||
const char* filename);
|
||||
|
||||
/**
|
||||
* Get filename to work with Nice Flor-S.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @return Full path to the file
|
||||
*/
|
||||
const char*
|
||||
subghz_environment_get_nice_flor_s_rainbow_table_file_name(SubGhzEnvironment* instance);
|
||||
|
||||
/**
|
||||
* Set list of protocols to work.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @param protocol_registry_items Pointer to a SubGhzProtocolRegistry
|
||||
*/
|
||||
void subghz_environment_set_protocol_registry(
|
||||
SubGhzEnvironment* instance,
|
||||
void* protocol_registry_items);
|
||||
|
||||
/**
|
||||
* Get list of protocols to work.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @return Pointer to a SubGhzProtocolRegistry
|
||||
*/
|
||||
void* subghz_environment_get_protocol_registry(SubGhzEnvironment* instance);
|
||||
|
||||
/**
|
||||
* Get list of protocols names.
|
||||
* @param instance Pointer to a SubGhzEnvironment instance
|
||||
* @param idx index protocols
|
||||
* @return Pointer to a SubGhzProtocolRegistry
|
||||
*/
|
||||
const char* subghz_environment_get_protocol_name_registry(SubGhzEnvironment* instance, size_t idx);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
@@ -50,7 +50,6 @@ typedef enum {
|
||||
SubGhzCustomEventSceneAnalyzerLock,
|
||||
SubGhzCustomEventSceneAnalyzerUnlock,
|
||||
SubGhzCustomEventSceneSettingLock,
|
||||
SubGhzCustomEventSceneSettingError,
|
||||
|
||||
SubGhzCustomEventSceneExit,
|
||||
SubGhzCustomEventSceneStay,
|
||||
|
||||
@@ -36,13 +36,13 @@ struct SubGhzFrequencyAnalyzerWorker {
|
||||
};
|
||||
|
||||
static void subghz_frequency_analyzer_worker_load_registers(const uint8_t data[][2]) {
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
|
||||
furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
|
||||
size_t i = 0;
|
||||
while(data[i][0]) {
|
||||
cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, data[i][0], data[i][1]);
|
||||
cc1101_write_reg(furi_hal_subghz.spi_bus_handle, data[i][0], data[i][1]);
|
||||
i++;
|
||||
}
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
|
||||
furi_hal_spi_release(furi_hal_subghz.spi_bus_handle);
|
||||
}
|
||||
|
||||
// running average with adaptive coefficient
|
||||
@@ -80,26 +80,26 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
||||
//Start CC1101
|
||||
furi_hal_subghz_reset();
|
||||
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
|
||||
cc1101_flush_rx(&furi_hal_spi_bus_handle_subghz);
|
||||
cc1101_flush_tx(&furi_hal_spi_bus_handle_subghz);
|
||||
cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_IOCFG0, CC1101IocfgHW);
|
||||
cc1101_write_reg(&furi_hal_spi_bus_handle_subghz, CC1101_MDMCFG3,
|
||||
furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
|
||||
cc1101_flush_rx(furi_hal_subghz.spi_bus_handle);
|
||||
cc1101_flush_tx(furi_hal_subghz.spi_bus_handle);
|
||||
cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_IOCFG0, CC1101IocfgHW);
|
||||
cc1101_write_reg(furi_hal_subghz.spi_bus_handle, CC1101_MDMCFG3,
|
||||
0b01111111); // symbol rate
|
||||
cc1101_write_reg(
|
||||
&furi_hal_spi_bus_handle_subghz,
|
||||
furi_hal_subghz.spi_bus_handle,
|
||||
CC1101_AGCCTRL2,
|
||||
0b00000111); // 00 - DVGA all; 000 - MAX LNA+LNA2; 111 - MAGN_TARGET 42 dB
|
||||
cc1101_write_reg(
|
||||
&furi_hal_spi_bus_handle_subghz,
|
||||
furi_hal_subghz.spi_bus_handle,
|
||||
CC1101_AGCCTRL1,
|
||||
0b00001000); // 0; 0 - LNA 2 gain is decreased to minimum before decreasing LNA gain; 00 - Relative carrier sense threshold disabled; 1000 - Absolute carrier sense threshold disabled
|
||||
cc1101_write_reg(
|
||||
&furi_hal_spi_bus_handle_subghz,
|
||||
furi_hal_subghz.spi_bus_handle,
|
||||
CC1101_AGCCTRL0,
|
||||
0b00110000); // 00 - No hysteresis, medium asymmetric dead zone, medium gain ; 11 - 64 samples agc; 00 - Normal AGC, 00 - 4dB boundary
|
||||
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
|
||||
furi_hal_spi_release(furi_hal_subghz.spi_bus_handle);
|
||||
|
||||
furi_hal_subghz_set_path(FuriHalSubGhzPathIsolate);
|
||||
|
||||
@@ -118,20 +118,23 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
||||
// First stage: coarse scan
|
||||
for(size_t i = 0; i < subghz_setting_get_frequency_count(instance->setting); i++) {
|
||||
if(furi_hal_subghz_is_frequency_valid(
|
||||
subghz_setting_get_frequency(instance->setting, i))) {
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
|
||||
cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
|
||||
subghz_setting_get_frequency(instance->setting, i)) &&
|
||||
!((furi_hal_subghz.radio_type == SubGhzRadioExternal) &&
|
||||
(subghz_setting_get_frequency(instance->setting, i) >= 311900000 &&
|
||||
subghz_setting_get_frequency(instance->setting, i) <= 312200000))) {
|
||||
furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
|
||||
cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle);
|
||||
frequency = cc1101_set_frequency(
|
||||
&furi_hal_spi_bus_handle_subghz,
|
||||
furi_hal_subghz.spi_bus_handle,
|
||||
subghz_setting_get_frequency(instance->setting, i));
|
||||
|
||||
cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);
|
||||
cc1101_calibrate(furi_hal_subghz.spi_bus_handle);
|
||||
do {
|
||||
status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz);
|
||||
status = cc1101_get_status(furi_hal_subghz.spi_bus_handle);
|
||||
} while(status.STATE != CC1101StateIDLE);
|
||||
|
||||
cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
|
||||
cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle);
|
||||
furi_hal_spi_release(furi_hal_subghz.spi_bus_handle);
|
||||
|
||||
furi_delay_ms(2);
|
||||
|
||||
@@ -166,17 +169,17 @@ static int32_t subghz_frequency_analyzer_worker_thread(void* context) {
|
||||
i < frequency_rssi.frequency_coarse + 300000;
|
||||
i += 20000) {
|
||||
if(furi_hal_subghz_is_frequency_valid(i)) {
|
||||
furi_hal_spi_acquire(&furi_hal_spi_bus_handle_subghz);
|
||||
cc1101_switch_to_idle(&furi_hal_spi_bus_handle_subghz);
|
||||
frequency = cc1101_set_frequency(&furi_hal_spi_bus_handle_subghz, i);
|
||||
furi_hal_spi_acquire(furi_hal_subghz.spi_bus_handle);
|
||||
cc1101_switch_to_idle(furi_hal_subghz.spi_bus_handle);
|
||||
frequency = cc1101_set_frequency(furi_hal_subghz.spi_bus_handle, i);
|
||||
|
||||
cc1101_calibrate(&furi_hal_spi_bus_handle_subghz);
|
||||
cc1101_calibrate(furi_hal_subghz.spi_bus_handle);
|
||||
do {
|
||||
status = cc1101_get_status(&furi_hal_spi_bus_handle_subghz);
|
||||
status = cc1101_get_status(furi_hal_subghz.spi_bus_handle);
|
||||
} while(status.STATE != CC1101StateIDLE);
|
||||
|
||||
cc1101_switch_to_rx(&furi_hal_spi_bus_handle_subghz);
|
||||
furi_hal_spi_release(&furi_hal_spi_bus_handle_subghz);
|
||||
cc1101_switch_to_rx(furi_hal_subghz.spi_bus_handle);
|
||||
furi_hal_spi_release(furi_hal_subghz.spi_bus_handle);
|
||||
|
||||
furi_delay_ms(2);
|
||||
|
||||
|
||||
455
applications/main/subghz/protocols/alutech_at_4n.c
Normal file
455
applications/main/subghz/protocols/alutech_at_4n.c
Normal file
@@ -0,0 +1,455 @@
|
||||
#include "alutech_at_4n.h"
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocoAlutech_at_4n"
|
||||
|
||||
#define SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE 0xFFFFFFFF
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_alutech_at_4n_const = {
|
||||
.te_short = 400,
|
||||
.te_long = 800,
|
||||
.te_delta = 140,
|
||||
.min_count_bit_for_found = 72,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderAlutech_at_4n {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint64_t data;
|
||||
uint32_t crc;
|
||||
uint16_t header_count;
|
||||
|
||||
const char* alutech_at_4n_rainbow_table_file_name;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderAlutech_at_4n {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Alutech_at_4nDecoderStepReset = 0,
|
||||
Alutech_at_4nDecoderStepCheckPreambula,
|
||||
Alutech_at_4nDecoderStepSaveDuration,
|
||||
Alutech_at_4nDecoderStepCheckDuration,
|
||||
} Alutech_at_4nDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder = {
|
||||
.alloc = subghz_protocol_decoder_alutech_at_4n_alloc,
|
||||
.free = subghz_protocol_decoder_alutech_at_4n_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_alutech_at_4n_feed,
|
||||
.reset = subghz_protocol_decoder_alutech_at_4n_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_alutech_at_4n_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_alutech_at_4n_serialize,
|
||||
.deserialize = subghz_protocol_decoder_alutech_at_4n_deserialize,
|
||||
.get_string = subghz_protocol_decoder_alutech_at_4n_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_alutech_at_4n = {
|
||||
.name = SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable,
|
||||
|
||||
.decoder = &subghz_protocol_alutech_at_4n_decoder,
|
||||
.encoder = &subghz_protocol_alutech_at_4n_encoder,
|
||||
};
|
||||
|
||||
/**
|
||||
* Read bytes from rainbow table
|
||||
* @param file_name Full path to rainbow table the file
|
||||
* @param number_alutech_at_4n_magic_data number in the array
|
||||
* @return alutech_at_4n_magic_data
|
||||
*/
|
||||
static uint32_t subghz_protocol_alutech_at_4n_get_magic_data_in_file(
|
||||
const char* file_name,
|
||||
uint8_t number_alutech_at_4n_magic_data) {
|
||||
if(!strcmp(file_name, "")) return SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE;
|
||||
|
||||
uint8_t buffer[sizeof(uint32_t)] = {0};
|
||||
uint32_t address = number_alutech_at_4n_magic_data * sizeof(uint32_t);
|
||||
uint32_t alutech_at_4n_magic_data = 0;
|
||||
|
||||
if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint32_t))) {
|
||||
for(size_t i = 0; i < sizeof(uint32_t); i++) {
|
||||
alutech_at_4n_magic_data = (alutech_at_4n_magic_data << 8) | buffer[i];
|
||||
}
|
||||
} else {
|
||||
alutech_at_4n_magic_data = SUBGHZ_NO_ALUTECH_AT_4N_RAINBOW_TABLE;
|
||||
}
|
||||
return alutech_at_4n_magic_data;
|
||||
}
|
||||
|
||||
static uint8_t subghz_protocol_alutech_at_4n_crc(uint64_t data) {
|
||||
uint8_t* p = (uint8_t*)&data;
|
||||
uint8_t crc = 0xff;
|
||||
for(uint8_t y = 0; y < 8; y++) {
|
||||
crc = crc ^ p[y];
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
if((crc & 0x80) != 0) {
|
||||
crc <<= 1;
|
||||
crc ^= 0x31;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static uint8_t subghz_protocol_alutech_at_4n_decrypt_data_crc(uint8_t data) {
|
||||
uint8_t crc = data;
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
if((crc & 0x80) != 0) {
|
||||
crc <<= 1;
|
||||
crc ^= 0x31;
|
||||
} else {
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return ~crc;
|
||||
}
|
||||
|
||||
static uint64_t subghz_protocol_alutech_at_4n_decrypt(uint64_t data, const char* file_name) {
|
||||
uint8_t* p = (uint8_t*)&data;
|
||||
uint32_t data1 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
|
||||
uint32_t data2 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7];
|
||||
uint32_t data3 = 0;
|
||||
uint32_t magic_data[] = {
|
||||
subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0),
|
||||
subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1),
|
||||
subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2),
|
||||
subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 3),
|
||||
subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4),
|
||||
subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5)};
|
||||
|
||||
uint32_t i = magic_data[0];
|
||||
do {
|
||||
data2 = data2 -
|
||||
((magic_data[1] + (data1 << 4)) ^ ((magic_data[2] + (data1 >> 5)) ^ (data1 + i)));
|
||||
data3 = data2 + i;
|
||||
i += magic_data[3];
|
||||
data1 =
|
||||
data1 - ((magic_data[4] + (data2 << 4)) ^ ((magic_data[5] + (data2 >> 5)) ^ data3));
|
||||
} while(i != 0);
|
||||
|
||||
p[0] = (uint8_t)(data1 >> 24);
|
||||
p[1] = (uint8_t)(data1 >> 16);
|
||||
p[3] = (uint8_t)data1;
|
||||
p[4] = (uint8_t)(data2 >> 24);
|
||||
p[5] = (uint8_t)(data2 >> 16);
|
||||
p[2] = (uint8_t)(data1 >> 8);
|
||||
p[6] = (uint8_t)(data2 >> 8);
|
||||
p[7] = (uint8_t)data2;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
// static uint64_t subghz_protocol_alutech_at_4n_encrypt(uint64_t data, const char* file_name) {
|
||||
// uint8_t* p = (uint8_t*)&data;
|
||||
// uint32_t data1 = 0;
|
||||
// uint32_t data2 = p[0] << 24 | p[1] << 16 | p[2] << 8 | p[3];
|
||||
// uint32_t data3 = p[4] << 24 | p[5] << 16 | p[6] << 8 | p[7];
|
||||
// uint32_t magic_data[] = {
|
||||
// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 6),
|
||||
// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 4),
|
||||
// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 5),
|
||||
// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 1),
|
||||
// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 2),
|
||||
// subghz_protocol_alutech_at_4n_get_magic_data_in_file(file_name, 0)};
|
||||
|
||||
// do {
|
||||
// data1 = data1 + magic_data[0];
|
||||
// data2 = data2 + ((magic_data[1] + (data3 << 4)) ^
|
||||
// ((magic_data[2] + (data3 >> 5)) ^ (data1 + data3)));
|
||||
// data3 = data3 + ((magic_data[3] + (data2 << 4)) ^
|
||||
// ((magic_data[4] + (data2 >> 5)) ^ (data1 + data2)));
|
||||
// } while(data1 != magic_data[5]);
|
||||
// p[0] = (uint8_t)(data2 >> 24);
|
||||
// p[1] = (uint8_t)(data2 >> 16);
|
||||
// p[3] = (uint8_t)data2;
|
||||
// p[4] = (uint8_t)(data3 >> 24);
|
||||
// p[5] = (uint8_t)(data3 >> 16);
|
||||
// p[2] = (uint8_t)(data2 >> 8);
|
||||
// p[6] = (uint8_t)(data3 >> 8);
|
||||
// p[7] = (uint8_t)data3;
|
||||
|
||||
// return data;
|
||||
// }
|
||||
|
||||
void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolDecoderAlutech_at_4n* instance =
|
||||
malloc(sizeof(SubGhzProtocolDecoderAlutech_at_4n));
|
||||
instance->base.protocol = &subghz_protocol_alutech_at_4n;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->alutech_at_4n_rainbow_table_file_name =
|
||||
subghz_environment_get_alutech_at_4n_rainbow_table_file_name(environment);
|
||||
if(instance->alutech_at_4n_rainbow_table_file_name) {
|
||||
FURI_LOG_I(
|
||||
TAG, "Loading rainbow table from %s", instance->alutech_at_4n_rainbow_table_file_name);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_alutech_at_4n_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAlutech_at_4n* instance = context;
|
||||
instance->alutech_at_4n_rainbow_table_file_name = NULL;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_alutech_at_4n_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAlutech_at_4n* instance = context;
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAlutech_at_4n* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case Alutech_at_4nDecoderStepReset:
|
||||
if((level) && DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) <
|
||||
subghz_protocol_alutech_at_4n_const.te_delta) {
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckPreambula;
|
||||
instance->header_count++;
|
||||
}
|
||||
break;
|
||||
case Alutech_at_4nDecoderStepCheckPreambula:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) <
|
||||
subghz_protocol_alutech_at_4n_const.te_delta)) {
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
if((instance->header_count > 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short * 10) <
|
||||
subghz_protocol_alutech_at_4n_const.te_delta * 10)) {
|
||||
// Found header
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
case Alutech_at_4nDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepCheckDuration;
|
||||
}
|
||||
break;
|
||||
case Alutech_at_4nDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_alutech_at_4n_const.te_short * 2 +
|
||||
subghz_protocol_alutech_at_4n_const.te_delta)) {
|
||||
//add last bit
|
||||
if(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) <
|
||||
subghz_protocol_alutech_at_4n_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
} else if(
|
||||
DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) <
|
||||
subghz_protocol_alutech_at_4n_const.te_delta * 2) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
}
|
||||
|
||||
// Found end TX
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) {
|
||||
if(instance->generic.data != instance->data) {
|
||||
instance->generic.data = instance->data;
|
||||
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
instance->crc = instance->decoder.decode_data;
|
||||
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_short) <
|
||||
subghz_protocol_alutech_at_4n_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_long) <
|
||||
subghz_protocol_alutech_at_4n_const.te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
if(instance->decoder.decode_count_bit == 64) {
|
||||
instance->data = instance->decoder.decode_data;
|
||||
instance->decoder.decode_data = 0;
|
||||
}
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_alutech_at_4n_const.te_long) <
|
||||
subghz_protocol_alutech_at_4n_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_alutech_at_4n_const.te_short) <
|
||||
subghz_protocol_alutech_at_4n_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
if(instance->decoder.decode_count_bit == 64) {
|
||||
instance->data = instance->decoder.decode_data;
|
||||
instance->decoder.decode_data = 0;
|
||||
}
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = Alutech_at_4nDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
* @param file_name Full path to rainbow table the file
|
||||
*/
|
||||
static void subghz_protocol_alutech_at_4n_remote_controller(
|
||||
SubGhzBlockGeneric* instance,
|
||||
uint8_t crc,
|
||||
const char* file_name) {
|
||||
/**
|
||||
* Message format 72bit LSB first
|
||||
* data crc
|
||||
* XXXXXXXXXXXXXXXX CC
|
||||
*
|
||||
* For analysis, you need to turn the package MSB
|
||||
* in decoded messages format
|
||||
*
|
||||
* crc1 serial cnt key
|
||||
* cc SSSSSSSS XXxx BB
|
||||
*
|
||||
* crc1 is calculated from the lower part of cnt
|
||||
* key 1=0xff, 2=0x11, 3=0x22, 4=0x33, 5=0x44
|
||||
*
|
||||
*/
|
||||
|
||||
bool status = false;
|
||||
uint64_t data = subghz_protocol_blocks_reverse_key(instance->data, 64);
|
||||
crc = subghz_protocol_blocks_reverse_key(crc, 8);
|
||||
|
||||
if(crc == subghz_protocol_alutech_at_4n_crc(data)) {
|
||||
data = subghz_protocol_alutech_at_4n_decrypt(data, file_name);
|
||||
status = true;
|
||||
}
|
||||
|
||||
if(status && ((uint8_t)(data >> 56) ==
|
||||
subghz_protocol_alutech_at_4n_decrypt_data_crc((uint8_t)((data >> 8) & 0xFF)))) {
|
||||
instance->btn = (uint8_t)data & 0xFF;
|
||||
instance->cnt = (uint16_t)(data >> 8) & 0xFFFF;
|
||||
instance->serial = (uint32_t)(data >> 24) & 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
if(!status) {
|
||||
instance->btn = 0;
|
||||
instance->cnt = 0;
|
||||
instance->serial = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAlutech_at_4n* instance = context;
|
||||
return (uint8_t)instance->crc;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_alutech_at_4n_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAlutech_at_4n* instance = context;
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(res && !flipper_format_write_uint32(flipper_format, "CRC", &instance->crc, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add CRC");
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_alutech_at_4n_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAlutech_at_4n* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_alutech_at_4n_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_uint32(flipper_format, "CRC", (uint32_t*)&instance->crc, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing CRC");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAlutech_at_4n* instance = context;
|
||||
subghz_protocol_alutech_at_4n_remote_controller(
|
||||
&instance->generic, instance->crc, instance->alutech_at_4n_rainbow_table_file_name);
|
||||
uint32_t code_found_hi = instance->generic.data >> 32;
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %d\r\n"
|
||||
"Key:0x%08lX%08lX%02X\r\n"
|
||||
"Sn:0x%08lX Btn:0x%01X\r\n"
|
||||
"Cnt:0x%03lX\r\n",
|
||||
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
code_found_hi,
|
||||
code_found_lo,
|
||||
(uint8_t)instance->crc,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->generic.cnt);
|
||||
}
|
||||
74
applications/main/subghz/protocols/alutech_at_4n.h
Normal file
74
applications/main/subghz/protocols/alutech_at_4n.h
Normal file
@@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_ALUTECH_AT_4N_NAME "Alutech at-4n"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderAlutech_at_4n SubGhzProtocolDecoderAlutech_at_4n;
|
||||
typedef struct SubGhzProtocolEncoderAlutech_at_4n SubGhzProtocolEncoderAlutech_at_4n;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_alutech_at_4n_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_alutech_at_4n_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_alutech_at_4n;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderAlutech_at_4n.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderAlutech_at_4n* pointer to a SubGhzProtocolDecoderAlutech_at_4n instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_alutech_at_4n_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderAlutech_at_4n.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance
|
||||
*/
|
||||
void subghz_protocol_decoder_alutech_at_4n_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderAlutech_at_4n.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance
|
||||
*/
|
||||
void subghz_protocol_decoder_alutech_at_4n_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_alutech_at_4n_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_alutech_at_4n_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderAlutech_at_4n.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_alutech_at_4n_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderAlutech_at_4n.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_alutech_at_4n_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAlutech_at_4n instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_alutech_at_4n_get_string(void* context, FuriString* output);
|
||||
346
applications/main/subghz/protocols/ansonic.c
Normal file
346
applications/main/subghz/protocols/ansonic.c
Normal file
@@ -0,0 +1,346 @@
|
||||
#include "ansonic.h"
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolAnsonic"
|
||||
|
||||
#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c"
|
||||
#define CNT_TO_DIP(dip) \
|
||||
(dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \
|
||||
(dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \
|
||||
(dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \
|
||||
(dip & 0x0008 ? '1' : '0')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_ansonic_const = {
|
||||
.te_short = 555,
|
||||
.te_long = 1111,
|
||||
.te_delta = 120,
|
||||
.min_count_bit_for_found = 12,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderAnsonic {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderAnsonic {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
AnsonicDecoderStepReset = 0,
|
||||
AnsonicDecoderStepFoundStartBit,
|
||||
AnsonicDecoderStepSaveDuration,
|
||||
AnsonicDecoderStepCheckDuration,
|
||||
} AnsonicDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder = {
|
||||
.alloc = subghz_protocol_decoder_ansonic_alloc,
|
||||
.free = subghz_protocol_decoder_ansonic_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_ansonic_feed,
|
||||
.reset = subghz_protocol_decoder_ansonic_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_ansonic_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_ansonic_serialize,
|
||||
.deserialize = subghz_protocol_decoder_ansonic_deserialize,
|
||||
.get_string = subghz_protocol_decoder_ansonic_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder = {
|
||||
.alloc = subghz_protocol_encoder_ansonic_alloc,
|
||||
.free = subghz_protocol_encoder_ansonic_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_ansonic_deserialize,
|
||||
.stop = subghz_protocol_encoder_ansonic_stop,
|
||||
.yield = subghz_protocol_encoder_ansonic_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_ansonic = {
|
||||
.name = SUBGHZ_PROTOCOL_ANSONIC_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_FM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_ansonic_decoder,
|
||||
.encoder = &subghz_protocol_ansonic_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderAnsonic* instance = malloc(sizeof(SubGhzProtocolEncoderAnsonic));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_ansonic;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 52;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_ansonic_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderAnsonic* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderAnsonic instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_ansonic_get_upload(SubGhzProtocolEncoderAnsonic* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_short * 35);
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short);
|
||||
//Send key data
|
||||
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(false, (uint32_t)subghz_protocol_ansonic_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_long);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_ansonic_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_ansonic_const.te_short);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderAnsonic* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_ansonic_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_ansonic_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_ansonic_stop(void* context) {
|
||||
SubGhzProtocolEncoderAnsonic* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_ansonic_yield(void* context) {
|
||||
SubGhzProtocolEncoderAnsonic* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderAnsonic* instance = malloc(sizeof(SubGhzProtocolDecoderAnsonic));
|
||||
instance->base.protocol = &subghz_protocol_ansonic;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ansonic_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAnsonic* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ansonic_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAnsonic* instance = context;
|
||||
instance->decoder.parser_step = AnsonicDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAnsonic* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case AnsonicDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short * 35) <
|
||||
subghz_protocol_ansonic_const.te_delta * 35)) {
|
||||
//Found header Ansonic
|
||||
instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case AnsonicDecoderStepFoundStartBit:
|
||||
if(!level) {
|
||||
break;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) <
|
||||
subghz_protocol_ansonic_const.te_delta) {
|
||||
//Found start bit Ansonic
|
||||
instance->decoder.parser_step = AnsonicDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = AnsonicDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case AnsonicDecoderStepSaveDuration:
|
||||
if(!level) { //save interval
|
||||
if(duration >= (subghz_protocol_ansonic_const.te_short * 4)) {
|
||||
instance->decoder.parser_step = AnsonicDecoderStepFoundStartBit;
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_ansonic_const.min_count_bit_for_found) {
|
||||
instance->generic.serial = 0x0;
|
||||
instance->generic.btn = 0x0;
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = AnsonicDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = AnsonicDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case AnsonicDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_short) <
|
||||
subghz_protocol_ansonic_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_long) <
|
||||
subghz_protocol_ansonic_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = AnsonicDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ansonic_const.te_long) <
|
||||
subghz_protocol_ansonic_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_ansonic_const.te_short) <
|
||||
subghz_protocol_ansonic_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = AnsonicDecoderStepSaveDuration;
|
||||
} else
|
||||
instance->decoder.parser_step = AnsonicDecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.parser_step = AnsonicDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_ansonic_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/*
|
||||
* 12345678(10) k 9
|
||||
* AAA => 10101010 1 01 0
|
||||
*
|
||||
* 1...10 - DIP
|
||||
* k- KEY
|
||||
*/
|
||||
instance->cnt = instance->data & 0xFFF;
|
||||
instance->btn = ((instance->data >> 1) & 0x3);
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAnsonic* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_ansonic_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAnsonic* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAnsonic* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_ansonic_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderAnsonic* instance = context;
|
||||
subghz_protocol_ansonic_check_remote_controller(&instance->generic);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%03lX\r\n"
|
||||
"Btn:%X\r\n"
|
||||
"DIP:" DIP_PATTERN "\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
|
||||
instance->generic.btn,
|
||||
CNT_TO_DIP(instance->generic.cnt));
|
||||
}
|
||||
107
applications/main/subghz/protocols/ansonic.h
Normal file
107
applications/main/subghz/protocols/ansonic.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_ANSONIC_NAME "Ansonic"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderAnsonic SubGhzProtocolDecoderAnsonic;
|
||||
typedef struct SubGhzProtocolEncoderAnsonic SubGhzProtocolEncoderAnsonic;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_ansonic_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_ansonic_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_ansonic;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderAnsonic.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderAnsonic* pointer to a SubGhzProtocolEncoderAnsonic instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_ansonic_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderAnsonic.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderAnsonic instance
|
||||
*/
|
||||
void subghz_protocol_encoder_ansonic_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderAnsonic instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderAnsonic instance
|
||||
*/
|
||||
void subghz_protocol_encoder_ansonic_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderAnsonic instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_ansonic_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderAnsonic.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderAnsonic* pointer to a SubGhzProtocolDecoderAnsonic instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_ansonic_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderAnsonic.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
|
||||
*/
|
||||
void subghz_protocol_decoder_ansonic_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderAnsonic.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
|
||||
*/
|
||||
void subghz_protocol_decoder_ansonic_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_ansonic_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_ansonic_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderAnsonic.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_ansonic_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderAnsonic.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_ansonic_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderAnsonic instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_ansonic_get_string(void* context, FuriString* output);
|
||||
62
applications/main/subghz/protocols/base.c
Normal file
62
applications/main/subghz/protocols/base.c
Normal file
@@ -0,0 +1,62 @@
|
||||
#include "base.h"
|
||||
#include "registry.h"
|
||||
|
||||
void subghz_protocol_decoder_base_set_decoder_callback(
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
SubGhzProtocolDecoderBaseRxCallback callback,
|
||||
void* context) {
|
||||
decoder_base->callback = callback;
|
||||
decoder_base->context = context;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_base_get_string(
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
FuriString* output) {
|
||||
bool status = false;
|
||||
|
||||
if(decoder_base->protocol && decoder_base->protocol->decoder &&
|
||||
decoder_base->protocol->decoder->get_string) {
|
||||
decoder_base->protocol->decoder->get_string(decoder_base, output);
|
||||
status = true;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_base_serialize(
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
bool status = false;
|
||||
|
||||
if(decoder_base->protocol && decoder_base->protocol->decoder &&
|
||||
decoder_base->protocol->decoder->serialize) {
|
||||
status = decoder_base->protocol->decoder->serialize(decoder_base, flipper_format, preset);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_base_deserialize(
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
FlipperFormat* flipper_format) {
|
||||
bool status = false;
|
||||
|
||||
if(decoder_base->protocol && decoder_base->protocol->decoder &&
|
||||
decoder_base->protocol->decoder->deserialize) {
|
||||
status = decoder_base->protocol->decoder->deserialize(decoder_base, flipper_format);
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base) {
|
||||
uint8_t hash = 0;
|
||||
|
||||
if(decoder_base->protocol && decoder_base->protocol->decoder &&
|
||||
decoder_base->protocol->decoder->get_hash_data) {
|
||||
hash = decoder_base->protocol->decoder->get_hash_data(decoder_base);
|
||||
}
|
||||
|
||||
return hash;
|
||||
}
|
||||
88
applications/main/subghz/protocols/base.h
Normal file
88
applications/main/subghz/protocols/base.h
Normal file
@@ -0,0 +1,88 @@
|
||||
#pragma once
|
||||
|
||||
#include "../types.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SubGhzProtocolDecoderBase SubGhzProtocolDecoderBase;
|
||||
|
||||
typedef void (
|
||||
*SubGhzProtocolDecoderBaseRxCallback)(SubGhzProtocolDecoderBase* instance, void* context);
|
||||
|
||||
typedef void (*SubGhzProtocolDecoderBaseSerialize)(
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
FuriString* output);
|
||||
|
||||
struct SubGhzProtocolDecoderBase {
|
||||
// Decoder general section
|
||||
const SubGhzProtocol* protocol;
|
||||
|
||||
// Callback section
|
||||
SubGhzProtocolDecoderBaseRxCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
/**
|
||||
* Set a callback upon completion of successful decoding of one of the protocols.
|
||||
* @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance
|
||||
* @param callback Callback, SubGhzProtocolDecoderBaseRxCallback
|
||||
* @param context Context
|
||||
*/
|
||||
void subghz_protocol_decoder_base_set_decoder_callback(
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
SubGhzProtocolDecoderBaseRxCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
bool subghz_protocol_decoder_base_get_string(
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
FuriString* output);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderBase.
|
||||
* @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_base_serialize(
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderBase.
|
||||
* @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_base_deserialize(
|
||||
SubGhzProtocolDecoderBase* decoder_base,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param decoder_base Pointer to a SubGhzProtocolDecoderBase instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_base_get_hash_data(SubGhzProtocolDecoderBase* decoder_base);
|
||||
|
||||
// Encoder Base
|
||||
typedef struct SubGhzProtocolEncoderBase SubGhzProtocolEncoderBase;
|
||||
|
||||
struct SubGhzProtocolEncoderBase {
|
||||
// Decoder general section
|
||||
const SubGhzProtocol* protocol;
|
||||
|
||||
// Callback section
|
||||
};
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
342
applications/main/subghz/protocols/bett.c
Normal file
342
applications/main/subghz/protocols/bett.c
Normal file
@@ -0,0 +1,342 @@
|
||||
#include "bett.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
// protocol BERNER / ELKA / TEDSEN / TELETASTER
|
||||
#define TAG "SubGhzProtocolBETT"
|
||||
|
||||
#define DIP_P 0b11 //(+)
|
||||
#define DIP_O 0b10 //(0)
|
||||
#define DIP_N 0b00 //(-)
|
||||
|
||||
#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c"
|
||||
#define SHOW_DIP_P(dip, check_dip) \
|
||||
((((dip >> 0x8) >> 0x8) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_bett_const = {
|
||||
.te_short = 340,
|
||||
.te_long = 2000,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 18,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderBETT {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderBETT {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
BETTDecoderStepReset = 0,
|
||||
BETTDecoderStepSaveDuration,
|
||||
BETTDecoderStepCheckDuration,
|
||||
} BETTDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_bett_decoder = {
|
||||
.alloc = subghz_protocol_decoder_bett_alloc,
|
||||
.free = subghz_protocol_decoder_bett_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_bett_feed,
|
||||
.reset = subghz_protocol_decoder_bett_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_bett_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_bett_serialize,
|
||||
.deserialize = subghz_protocol_decoder_bett_deserialize,
|
||||
.get_string = subghz_protocol_decoder_bett_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_bett_encoder = {
|
||||
.alloc = subghz_protocol_encoder_bett_alloc,
|
||||
.free = subghz_protocol_encoder_bett_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_bett_deserialize,
|
||||
.stop = subghz_protocol_encoder_bett_stop,
|
||||
.yield = subghz_protocol_encoder_bett_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_bett = {
|
||||
.name = SUBGHZ_PROTOCOL_BETT_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_bett_decoder,
|
||||
.encoder = &subghz_protocol_bett_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderBETT* instance = malloc(sizeof(SubGhzProtocolEncoderBETT));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_bett;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 52;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_bett_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderBETT* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderBETT instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_bett_get_upload(SubGhzProtocolEncoderBETT* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2);
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
|
||||
if(bit_read(instance->generic.data, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_bett_const.te_long);
|
||||
}
|
||||
}
|
||||
if(bit_read(instance->generic.data, 0)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_long);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false,
|
||||
(uint32_t)subghz_protocol_bett_const.te_short +
|
||||
subghz_protocol_bett_const.te_long * 7);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_bett_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false,
|
||||
(uint32_t)subghz_protocol_bett_const.te_long + subghz_protocol_bett_const.te_long * 7);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderBETT* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_bett_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_bett_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_bett_stop(void* context) {
|
||||
SubGhzProtocolEncoderBETT* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_bett_yield(void* context) {
|
||||
SubGhzProtocolEncoderBETT* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderBETT* instance = malloc(sizeof(SubGhzProtocolDecoderBETT));
|
||||
instance->base.protocol = &subghz_protocol_bett;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_bett_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBETT* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_bett_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBETT* instance = context;
|
||||
instance->decoder.parser_step = BETTDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBETT* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case BETTDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) <
|
||||
(subghz_protocol_bett_const.te_delta * 15))) {
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->decoder.parser_step = BETTDecoderStepCheckDuration;
|
||||
}
|
||||
break;
|
||||
case BETTDecoderStepSaveDuration:
|
||||
if(!level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_short * 44) <
|
||||
(subghz_protocol_bett_const.te_delta * 15)) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_bett_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);
|
||||
} else {
|
||||
instance->decoder.parser_step = BETTDecoderStepReset;
|
||||
}
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
break;
|
||||
} else {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) <
|
||||
subghz_protocol_bett_const.te_delta) ||
|
||||
(DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) <
|
||||
subghz_protocol_bett_const.te_delta * 3)) {
|
||||
instance->decoder.parser_step = BETTDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = BETTDecoderStepReset;
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case BETTDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_bett_const.te_long) <
|
||||
subghz_protocol_bett_const.te_delta * 3) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = BETTDecoderStepSaveDuration;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_bett_const.te_short) <
|
||||
subghz_protocol_bett_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = BETTDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = BETTDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = BETTDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBETT* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_bett_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBETT* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBETT* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_bett_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_bett_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderBETT* instance = context;
|
||||
uint32_t data = (uint32_t)(instance->generic.data & 0x3FFFF);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%05lX\r\n"
|
||||
" +: " DIP_PATTERN "\r\n"
|
||||
" o: " DIP_PATTERN "\r\n"
|
||||
" -: " DIP_PATTERN "\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
data,
|
||||
SHOW_DIP_P(data, DIP_P),
|
||||
SHOW_DIP_P(data, DIP_O),
|
||||
SHOW_DIP_P(data, DIP_N));
|
||||
}
|
||||
107
applications/main/subghz/protocols/bett.h
Normal file
107
applications/main/subghz/protocols/bett.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_BETT_NAME "BETT"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderBETT SubGhzProtocolDecoderBETT;
|
||||
typedef struct SubGhzProtocolEncoderBETT SubGhzProtocolEncoderBETT;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_bett_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_bett_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_bett;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderBETT.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderBETT* pointer to a SubGhzProtocolEncoderBETT instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_bett_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderBETT.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderBETT instance
|
||||
*/
|
||||
void subghz_protocol_encoder_bett_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderBETT instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_bett_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderBETT instance
|
||||
*/
|
||||
void subghz_protocol_encoder_bett_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderBETT instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_bett_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderBETT.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderBETT* pointer to a SubGhzProtocolDecoderBETT instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_bett_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderBETT.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBETT instance
|
||||
*/
|
||||
void subghz_protocol_decoder_bett_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderBETT.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBETT instance
|
||||
*/
|
||||
void subghz_protocol_decoder_bett_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBETT instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_bett_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBETT instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_bett_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderBETT.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBETT instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_bett_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderBETT.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBETT instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_bett_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBETT instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_bett_get_string(void* context, FuriString* output);
|
||||
1120
applications/main/subghz/protocols/bin_raw.c
Normal file
1120
applications/main/subghz/protocols/bin_raw.c
Normal file
File diff suppressed because it is too large
Load Diff
111
applications/main/subghz/protocols/bin_raw.h
Normal file
111
applications/main/subghz/protocols/bin_raw.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_BIN_RAW_NAME "BinRAW"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderBinRAW SubGhzProtocolDecoderBinRAW;
|
||||
typedef struct SubGhzProtocolEncoderBinRAW SubGhzProtocolEncoderBinRAW;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_bin_raw_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_bin_raw_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_bin_raw;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderBinRAW.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderBinRAW* pointer to a SubGhzProtocolEncoderBinRAW instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_bin_raw_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderBinRAW.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderBinRAW instance
|
||||
*/
|
||||
void subghz_protocol_encoder_bin_raw_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderBinRAW instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderBinRAW instance
|
||||
*/
|
||||
void subghz_protocol_encoder_bin_raw_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderBinRAW instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_bin_raw_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderBinRAW.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderBinRAW* pointer to a SubGhzProtocolDecoderBinRAW instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_bin_raw_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderBinRAW.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
|
||||
*/
|
||||
void subghz_protocol_decoder_bin_raw_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderBinRAW.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
|
||||
*/
|
||||
void subghz_protocol_decoder_bin_raw_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_bin_raw_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_bin_raw_get_hash_data(void* context);
|
||||
|
||||
void subghz_protocol_decoder_bin_raw_data_input_rssi(
|
||||
SubGhzProtocolDecoderBinRAW* instance,
|
||||
float rssi);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderBinRAW.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_bin_raw_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderBinRAW.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_bin_raw_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderBinRAW instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_bin_raw_get_string(void* context, FuriString* output);
|
||||
347
applications/main/subghz/protocols/came.c
Normal file
347
applications/main/subghz/protocols/came.c
Normal file
@@ -0,0 +1,347 @@
|
||||
#include "came.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://phreakerclub.com/447
|
||||
*
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocolCAME"
|
||||
#define CAME_24_COUNT_BIT 24
|
||||
#define PRASTEL_COUNT_BIT 25
|
||||
#define PRASTEL_NAME "Prastel"
|
||||
#define AIRFORCE_COUNT_BIT 18
|
||||
#define AIRFORCE_NAME "Airforce"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_came_const = {
|
||||
.te_short = 320,
|
||||
.te_long = 640,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 12,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderCame {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderCame {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
CameDecoderStepReset = 0,
|
||||
CameDecoderStepFoundStartBit,
|
||||
CameDecoderStepSaveDuration,
|
||||
CameDecoderStepCheckDuration,
|
||||
} CameDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_came_decoder = {
|
||||
.alloc = subghz_protocol_decoder_came_alloc,
|
||||
.free = subghz_protocol_decoder_came_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_came_feed,
|
||||
.reset = subghz_protocol_decoder_came_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_came_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_came_serialize,
|
||||
.deserialize = subghz_protocol_decoder_came_deserialize,
|
||||
.get_string = subghz_protocol_decoder_came_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_came_encoder = {
|
||||
.alloc = subghz_protocol_encoder_came_alloc,
|
||||
.free = subghz_protocol_encoder_came_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_came_deserialize,
|
||||
.stop = subghz_protocol_encoder_came_stop,
|
||||
.yield = subghz_protocol_encoder_came_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_came = {
|
||||
.name = SUBGHZ_PROTOCOL_CAME_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_came_decoder,
|
||||
.encoder = &subghz_protocol_came_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderCame* instance = malloc(sizeof(SubGhzProtocolEncoderCame));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_came;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_came_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderCame* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderCame instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_came_get_upload(SubGhzProtocolEncoderCame* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
//Send header
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false,
|
||||
(((instance->generic.data_count_bit == CAME_24_COUNT_BIT) ||
|
||||
(instance->generic.data_count_bit ==
|
||||
subghz_protocol_came_const.min_count_bit_for_found)) ?
|
||||
(uint32_t)subghz_protocol_came_const.te_short * 76 :
|
||||
(uint32_t)subghz_protocol_came_const.te_short * 39));
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short);
|
||||
//Send key data
|
||||
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(false, (uint32_t)subghz_protocol_came_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_came_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_came_const.te_long);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderCame* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_came_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_came_stop(void* context) {
|
||||
SubGhzProtocolEncoderCame* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_came_yield(void* context) {
|
||||
SubGhzProtocolEncoderCame* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_came_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderCame* instance = malloc(sizeof(SubGhzProtocolDecoderCame));
|
||||
instance->base.protocol = &subghz_protocol_came;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCame* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCame* instance = context;
|
||||
instance->decoder.parser_step = CameDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCame* instance = context;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case CameDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_const.te_short * 56) <
|
||||
subghz_protocol_came_const.te_delta * 47)) {
|
||||
//Found header CAME
|
||||
instance->decoder.parser_step = CameDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case CameDecoderStepFoundStartBit:
|
||||
if(!level) {
|
||||
break;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_came_const.te_short) <
|
||||
subghz_protocol_came_const.te_delta) {
|
||||
//Found start bit CAME
|
||||
instance->decoder.parser_step = CameDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = CameDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case CameDecoderStepSaveDuration:
|
||||
if(!level) { //save interval
|
||||
if(duration >= (subghz_protocol_came_const.te_short * 4)) {
|
||||
instance->decoder.parser_step = CameDecoderStepFoundStartBit;
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_came_const.min_count_bit_for_found) {
|
||||
instance->generic.serial = 0x0;
|
||||
instance->generic.btn = 0x0;
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = CameDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = CameDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case CameDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_short) <
|
||||
subghz_protocol_came_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_came_const.te_long) <
|
||||
subghz_protocol_came_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = CameDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_came_const.te_long) <
|
||||
subghz_protocol_came_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_came_const.te_short) <
|
||||
subghz_protocol_came_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = CameDecoderStepSaveDuration;
|
||||
} else
|
||||
instance->decoder.parser_step = CameDecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.parser_step = CameDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_came_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCame* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_came_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCame* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCame* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if((instance->generic.data_count_bit > PRASTEL_COUNT_BIT)) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCame* instance = context;
|
||||
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
|
||||
uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
|
||||
uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%08lX\r\n"
|
||||
"Yek:0x%08lX\r\n",
|
||||
(instance->generic.data_count_bit == PRASTEL_COUNT_BIT ?
|
||||
PRASTEL_NAME :
|
||||
(instance->generic.data_count_bit == AIRFORCE_COUNT_BIT ?
|
||||
AIRFORCE_NAME :
|
||||
instance->generic.protocol_name)),
|
||||
instance->generic.data_count_bit,
|
||||
code_found_lo,
|
||||
code_found_reverse_lo);
|
||||
}
|
||||
107
applications/main/subghz/protocols/came.h
Normal file
107
applications/main/subghz/protocols/came.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_CAME_NAME "CAME"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderCame SubGhzProtocolDecoderCame;
|
||||
typedef struct SubGhzProtocolEncoderCame SubGhzProtocolEncoderCame;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_came_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_came_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_came;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderCame.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderCame* pointer to a SubGhzProtocolEncoderCame instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_came_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderCame.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCame instance
|
||||
*/
|
||||
void subghz_protocol_encoder_came_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCame instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_came_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCame instance
|
||||
*/
|
||||
void subghz_protocol_encoder_came_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCame instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_came_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderCame.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderCame* pointer to a SubGhzProtocolDecoderCame instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_came_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderCame.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCame instance
|
||||
*/
|
||||
void subghz_protocol_decoder_came_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderCame.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCame instance
|
||||
*/
|
||||
void subghz_protocol_decoder_came_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCame instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_came_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCame instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_came_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderCame.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCame instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_came_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderCame.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCame instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_came_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCame instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_came_get_string(void* context, FuriString* output);
|
||||
598
applications/main/subghz/protocols/came_atomo.c
Normal file
598
applications/main/subghz/protocols/came_atomo.c
Normal file
@@ -0,0 +1,598 @@
|
||||
#include "came_atomo.h"
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include <lib/toolbox/manchester_encoder.h>
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocoCameAtomo"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_came_atomo_const = {
|
||||
.te_short = 600,
|
||||
.te_long = 1200,
|
||||
.te_delta = 250,
|
||||
.min_count_bit_for_found = 62,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderCameAtomo {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
ManchesterState manchester_saved_state;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderCameAtomo {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
CameAtomoDecoderStepReset = 0,
|
||||
CameAtomoDecoderStepDecoderData,
|
||||
} CameAtomoDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder = {
|
||||
.alloc = subghz_protocol_decoder_came_atomo_alloc,
|
||||
.free = subghz_protocol_decoder_came_atomo_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_came_atomo_feed,
|
||||
.reset = subghz_protocol_decoder_came_atomo_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_came_atomo_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_came_atomo_serialize,
|
||||
.deserialize = subghz_protocol_decoder_came_atomo_deserialize,
|
||||
.get_string = subghz_protocol_decoder_came_atomo_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder = {
|
||||
.alloc = subghz_protocol_encoder_came_atomo_alloc,
|
||||
.free = subghz_protocol_encoder_came_atomo_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_came_atomo_deserialize,
|
||||
.stop = subghz_protocol_encoder_came_atomo_stop,
|
||||
.yield = subghz_protocol_encoder_came_atomo_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_came_atomo = {
|
||||
.name = SUBGHZ_PROTOCOL_CAME_ATOMO_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_came_atomo_decoder,
|
||||
.encoder = &subghz_protocol_came_atomo_encoder,
|
||||
};
|
||||
|
||||
static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* instance);
|
||||
|
||||
void* subghz_protocol_encoder_came_atomo_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolEncoderCameAtomo));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_came_atomo;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 900; //actual size 766+
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_came_atomo_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderCameAtomo* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static LevelDuration
|
||||
subghz_protocol_encoder_came_atomo_add_duration_to_upload(ManchesterEncoderResult result) {
|
||||
LevelDuration data = {.duration = 0, .level = 0};
|
||||
switch(result) {
|
||||
case ManchesterEncoderResultShortLow:
|
||||
data.duration = subghz_protocol_came_atomo_const.te_short;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongLow:
|
||||
data.duration = subghz_protocol_came_atomo_const.te_long;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongHigh:
|
||||
data.duration = subghz_protocol_came_atomo_const.te_long;
|
||||
data.level = true;
|
||||
break;
|
||||
case ManchesterEncoderResultShortHigh:
|
||||
data.duration = subghz_protocol_came_atomo_const.te_short;
|
||||
data.level = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
FURI_LOG_E(TAG, "SubGhz: ManchesterEncoderResult is incorrect.");
|
||||
break;
|
||||
}
|
||||
return level_duration_make(data.level, data.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderCameAtomo instance
|
||||
*/
|
||||
static void
|
||||
subghz_protocol_encoder_came_atomo_get_upload(SubGhzProtocolEncoderCameAtomo* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
|
||||
ManchesterEncoderState enc_state;
|
||||
manchester_encoder_reset(&enc_state);
|
||||
ManchesterEncoderResult result;
|
||||
|
||||
uint8_t pack[8] = {};
|
||||
|
||||
if(instance->generic.cnt < 0xFFFF) {
|
||||
instance->generic.cnt++;
|
||||
} else if(instance->generic.cnt >= 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
}
|
||||
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_came_atomo_const.te_long * 15);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_long * 60);
|
||||
|
||||
for(uint8_t i = 0; i < 8; i++) {
|
||||
pack[0] = (instance->generic.data_2 >> 56);
|
||||
pack[1] = (instance->generic.cnt >> 8);
|
||||
pack[2] = (instance->generic.cnt & 0xFF);
|
||||
pack[3] = ((instance->generic.data_2 >> 32) & 0xFF);
|
||||
pack[4] = ((instance->generic.data_2 >> 24) & 0xFF);
|
||||
pack[5] = ((instance->generic.data_2 >> 16) & 0xFF);
|
||||
pack[6] = ((instance->generic.data_2 >> 8) & 0xFF);
|
||||
pack[7] = (instance->generic.data_2 & 0xFF);
|
||||
|
||||
if(pack[0] == 0x7F) {
|
||||
pack[0] = 0;
|
||||
} else {
|
||||
pack[0] += (i + 1);
|
||||
}
|
||||
|
||||
atomo_encrypt(pack);
|
||||
uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3];
|
||||
uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7];
|
||||
instance->generic.data = (uint64_t)hi << 32 | lo;
|
||||
|
||||
instance->generic.data ^= 0xFFFFFFFFFFFFFFFF;
|
||||
instance->generic.data >>= 4;
|
||||
instance->generic.data &= 0xFFFFFFFFFFFFFFF;
|
||||
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_came_atomo_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_short);
|
||||
|
||||
for(uint8_t i = (instance->generic.data_count_bit - 2); i > 0; i--) {
|
||||
if(!manchester_encoder_advance(
|
||||
&enc_state, !bit_read(instance->generic.data, i - 1), &result)) {
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_came_atomo_add_duration_to_upload(result);
|
||||
manchester_encoder_advance(
|
||||
&enc_state, !bit_read(instance->generic.data, i - 1), &result);
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_came_atomo_add_duration_to_upload(result);
|
||||
}
|
||||
instance->encoder.upload[index] =
|
||||
subghz_protocol_encoder_came_atomo_add_duration_to_upload(
|
||||
manchester_encoder_finish(&enc_state));
|
||||
if(level_duration_get_level(instance->encoder.upload[index])) {
|
||||
index++;
|
||||
}
|
||||
//Send pause
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_came_atomo_const.te_delta * 272);
|
||||
}
|
||||
instance->encoder.size_upload = index;
|
||||
instance->generic.cnt_2++;
|
||||
pack[0] = (instance->generic.cnt_2);
|
||||
pack[1] = (instance->generic.cnt >> 8);
|
||||
pack[2] = (instance->generic.cnt & 0xFF);
|
||||
pack[3] = ((instance->generic.data_2 >> 32) & 0xFF);
|
||||
pack[4] = ((instance->generic.data_2 >> 24) & 0xFF);
|
||||
pack[5] = ((instance->generic.data_2 >> 16) & 0xFF);
|
||||
pack[6] = ((instance->generic.data_2 >> 8) & 0xFF);
|
||||
pack[7] = (instance->generic.data_2 & 0xFF);
|
||||
|
||||
atomo_encrypt(pack);
|
||||
uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3];
|
||||
uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7];
|
||||
instance->generic.data = (uint64_t)hi << 32 | lo;
|
||||
|
||||
instance->generic.data ^= 0xFFFFFFFFFFFFFFFF;
|
||||
instance->generic.data >>= 4;
|
||||
instance->generic.data &= 0xFFFFFFFFFFFFFFF;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderCameAtomo* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
subghz_protocol_came_atomo_remote_controller(&instance->generic);
|
||||
subghz_protocol_encoder_came_atomo_get_upload(instance);
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
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");
|
||||
break;
|
||||
}
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_came_atomo_stop(void* context) {
|
||||
SubGhzProtocolEncoderCameAtomo* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_came_atomo_yield(void* context) {
|
||||
SubGhzProtocolEncoderCameAtomo* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_came_atomo_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderCameAtomo* instance = malloc(sizeof(SubGhzProtocolDecoderCameAtomo));
|
||||
instance->base.protocol = &subghz_protocol_came_atomo;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_atomo_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameAtomo* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_atomo_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameAtomo* instance = context;
|
||||
instance->decoder.parser_step = CameAtomoDecoderStepReset;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameAtomo* instance = context;
|
||||
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case CameAtomoDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long * 60) <
|
||||
subghz_protocol_came_atomo_const.te_delta * 40)) {
|
||||
//Found header CAME
|
||||
instance->decoder.parser_step = CameAtomoDecoderStepDecoderData;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 1;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventShortLow,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
case CameAtomoDecoderStepDecoderData:
|
||||
if(!level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) <
|
||||
subghz_protocol_came_atomo_const.te_delta) {
|
||||
event = ManchesterEventShortLow;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) <
|
||||
subghz_protocol_came_atomo_const.te_delta) {
|
||||
event = ManchesterEventLongLow;
|
||||
} else if(
|
||||
duration >= ((uint32_t)subghz_protocol_came_atomo_const.te_long * 2 +
|
||||
subghz_protocol_came_atomo_const.te_delta)) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_came_atomo_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 = 1;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventShortLow,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
} else {
|
||||
instance->decoder.parser_step = CameAtomoDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_short) <
|
||||
subghz_protocol_came_atomo_const.te_delta) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_came_atomo_const.te_long) <
|
||||
subghz_protocol_came_atomo_const.te_delta) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
instance->decoder.parser_step = CameAtomoDecoderStepReset;
|
||||
}
|
||||
}
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = manchester_advance(
|
||||
instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data;
|
||||
instance->decoder.decode_count_bit++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
* @param file_name Full path to rainbow table the file
|
||||
*/
|
||||
static void subghz_protocol_came_atomo_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/*
|
||||
* ***SkorP ver.***
|
||||
* 0x1fafef3ed0f7d9ef
|
||||
* 0x185fcc1531ee86e7
|
||||
* 0x184fa96912c567ff
|
||||
* 0x187f8a42f3dc38f7
|
||||
* 0x186f63915492a5cd
|
||||
* 0x181f40bab58bfac5
|
||||
* 0x180f25c696a01bdd
|
||||
* 0x183f06ed77b944d5
|
||||
* 0x182ef661d83d21a9
|
||||
* 0x18ded54a39247ea1
|
||||
* 0x18ceb0361a0f9fb9
|
||||
* 0x18fe931dfb16c0b1
|
||||
* 0x18ee7ace5c585d8b
|
||||
* ........
|
||||
* transmission consists of 99 parcels with increasing counter while holding down the button
|
||||
* with each new press, the counter in the encrypted part increases
|
||||
*
|
||||
* 0x1FAFF13ED0F7D9EF
|
||||
* 0x1FAFF11ED0F7D9EF
|
||||
* 0x1FAFF10ED0F7D9EF
|
||||
* 0x1FAFF0FED0F7D9EF
|
||||
* 0x1FAFF0EED0F7D9EF
|
||||
* 0x1FAFF0DED0F7D9EF
|
||||
* 0x1FAFF0CED0F7D9EF
|
||||
* 0x1FAFF0BED0F7D9EF
|
||||
* 0x1FAFF0AED0F7D9EF
|
||||
*
|
||||
* where 0x1FAF - parcel counter, 0хF0A - button press counter,
|
||||
* 0xED0F7D9E - serial number, 0хF - key
|
||||
* 0x1FAF parcel counter - 1 in the parcel queue ^ 0x185F = 0x07F0
|
||||
* 0x185f ^ 0x185F = 0x0000
|
||||
* 0x184f ^ 0x185F = 0x0010
|
||||
* 0x187f ^ 0x185F = 0x0020
|
||||
* .....
|
||||
* 0x182e ^ 0x185F = 0x0071
|
||||
* 0x18de ^ 0x185F = 0x0081
|
||||
* .....
|
||||
* 0x1e43 ^ 0x185F = 0x061C
|
||||
* where the last nibble is incremented every 8 samples
|
||||
*
|
||||
* Decode
|
||||
*
|
||||
* 0x1cf6931dfb16c0b1 => 0x1cf6
|
||||
* 0x1cf6 ^ 0x185F = 0x04A9
|
||||
* 0x04A9 => 0x04A = 74 (dec)
|
||||
* 74+1 % 32(atomo_magic_xor) = 11
|
||||
* GET atomo_magic_xor[11] = 0xXXXXXXXXXXXXXXXX
|
||||
* 0x931dfb16c0b1 ^ 0xXXXXXXXXXXXXXXXX = 0xEF3ED0F7D9EF
|
||||
* 0xEF3 ED0F7D9E F => 0xEF3 - CNT, 0xED0F7D9E - SN, 0xF - key
|
||||
*
|
||||
* ***Eng1n33r ver. (actual)***
|
||||
* 0x1FF08D9924984115 - received data
|
||||
* 0x00F7266DB67BEEA0 - inverted data
|
||||
* 0x0501FD0000A08300 - decrypted data,
|
||||
* where: 0x05 - Button hold-cycle counter (8-bit, from 0 to 0x7F)
|
||||
* 0x01FD - Parcel counter (normal 16-bit counter)
|
||||
* 0x0000A083 - Serial number (32-bit)
|
||||
* 0x0 - Button code (4-bit, 0x0 - #1 left-up; 0x2 - #2 right-up; 0x4 - #3 left-down; 0x6 - #4 right-down)
|
||||
* 0x0 - Last zero nibble
|
||||
* */
|
||||
|
||||
instance->data ^= 0xFFFFFFFFFFFFFFFF;
|
||||
instance->data <<= 4;
|
||||
|
||||
uint8_t pack[8] = {};
|
||||
pack[0] = (instance->data >> 56);
|
||||
pack[1] = ((instance->data >> 48) & 0xFF);
|
||||
pack[2] = ((instance->data >> 40) & 0xFF);
|
||||
pack[3] = ((instance->data >> 32) & 0xFF);
|
||||
pack[4] = ((instance->data >> 24) & 0xFF);
|
||||
pack[5] = ((instance->data >> 16) & 0xFF);
|
||||
pack[6] = ((instance->data >> 8) & 0xFF);
|
||||
pack[7] = (instance->data & 0xFF);
|
||||
|
||||
atomo_decrypt(pack);
|
||||
|
||||
instance->cnt_2 = pack[0];
|
||||
instance->cnt = (uint16_t)pack[1] << 8 | pack[2];
|
||||
instance->serial = (uint32_t)(pack[3]) << 24 | pack[4] << 16 | pack[5] << 8 | pack[6];
|
||||
|
||||
uint8_t btn_decode = (pack[7] >> 4);
|
||||
if(btn_decode == 0x0) {
|
||||
instance->btn = 0x1;
|
||||
}
|
||||
if(btn_decode == 0x2) {
|
||||
instance->btn = 0x2;
|
||||
}
|
||||
if(btn_decode == 0x4) {
|
||||
instance->btn = 0x3;
|
||||
}
|
||||
if(btn_decode == 0x6) {
|
||||
instance->btn = 0x4;
|
||||
}
|
||||
|
||||
uint32_t hi = pack[0] << 24 | pack[1] << 16 | pack[2] << 8 | pack[3];
|
||||
uint32_t lo = pack[4] << 24 | pack[5] << 16 | pack[6] << 8 | pack[7];
|
||||
instance->data_2 = (uint64_t)hi << 32 | lo;
|
||||
}
|
||||
|
||||
void atomo_encrypt(uint8_t* buff) {
|
||||
uint8_t tmpB = (~buff[0] + 1) & 0x7F;
|
||||
|
||||
uint8_t bitCnt = 8;
|
||||
while(bitCnt < 59) {
|
||||
if((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) {
|
||||
tmpB = ((tmpB << 1) & 0xFF) | 1;
|
||||
} else {
|
||||
tmpB = (tmpB << 1) & 0xFF;
|
||||
}
|
||||
|
||||
if(tmpB & 0x80) {
|
||||
buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7));
|
||||
}
|
||||
|
||||
bitCnt++;
|
||||
}
|
||||
|
||||
buff[0] = (buff[0] ^ 5) & 0x7F;
|
||||
}
|
||||
|
||||
void atomo_decrypt(uint8_t* buff) {
|
||||
buff[0] = (buff[0] ^ 5) & 0x7F;
|
||||
uint8_t tmpB = (-buff[0]) & 0x7F;
|
||||
|
||||
uint8_t bitCnt = 8;
|
||||
while(bitCnt < 59) {
|
||||
if((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) {
|
||||
tmpB = ((tmpB << 1) & 0xFF) | 1;
|
||||
} else {
|
||||
tmpB = (tmpB << 1) & 0xFF;
|
||||
}
|
||||
|
||||
if(tmpB & 0x80) {
|
||||
buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7));
|
||||
}
|
||||
|
||||
bitCnt++;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameAtomo* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_came_atomo_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameAtomo* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameAtomo* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_came_atomo_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameAtomo* instance = context;
|
||||
subghz_protocol_came_atomo_remote_controller(&instance->generic);
|
||||
uint32_t code_found_hi = instance->generic.data >> 32;
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Key:0x%08lX%08lX\r\n"
|
||||
"Sn:0x%08lX Btn:0x%01X\r\n"
|
||||
"Pcl_Cnt:0x%04lX\r\n"
|
||||
"Btn_Cnt:0x%02X",
|
||||
|
||||
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_2);
|
||||
}
|
||||
110
applications/main/subghz/protocols/came_atomo.h
Normal file
110
applications/main/subghz/protocols/came_atomo.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_CAME_ATOMO_NAME "CAME Atomo"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderCameAtomo SubGhzProtocolDecoderCameAtomo;
|
||||
typedef struct SubGhzProtocolEncoderCameAtomo SubGhzProtocolEncoderCameAtomo;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_came_atomo_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_came_atomo_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_came_atomo;
|
||||
|
||||
void atomo_decrypt(uint8_t* buff);
|
||||
|
||||
void atomo_encrypt(uint8_t* buff);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderCameAtomo.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderCameAtomo* pointer to a SubGhzProtocolEncoderCameAtomo instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_came_atomo_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderCameAtomo.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance
|
||||
*/
|
||||
void subghz_protocol_encoder_came_atomo_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance
|
||||
*/
|
||||
void subghz_protocol_encoder_came_atomo_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCameAtomo instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_came_atomo_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderCameAtomo.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderCameAtomo* pointer to a SubGhzProtocolDecoderCameAtomo instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_came_atomo_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderCameAtomo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance
|
||||
*/
|
||||
void subghz_protocol_decoder_came_atomo_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderCameAtomo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance
|
||||
*/
|
||||
void subghz_protocol_decoder_came_atomo_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_came_atomo_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_came_atomo_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderCameAtomo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_came_atomo_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderCameAtomo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_came_atomo_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameAtomo instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_came_atomo_get_string(void* context, FuriString* output);
|
||||
468
applications/main/subghz/protocols/came_twee.c
Normal file
468
applications/main/subghz/protocols/came_twee.c
Normal file
@@ -0,0 +1,468 @@
|
||||
#include "came_twee.h"
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include <lib/toolbox/manchester_encoder.h>
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://phreakerclub.com/forum/showthread.php?t=635&highlight=came+twin
|
||||
*
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocolCAME_Twee"
|
||||
|
||||
#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c"
|
||||
#define CNT_TO_DIP(dip) \
|
||||
(dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \
|
||||
(dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \
|
||||
(dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \
|
||||
(dip & 0x0001 ? '1' : '0')
|
||||
|
||||
/**
|
||||
* Rainbow table Came Twee.
|
||||
*/
|
||||
static const uint32_t came_twee_magic_numbers_xor[15] = {
|
||||
0x0E0E0E00,
|
||||
0x1D1D1D11,
|
||||
0x2C2C2C22,
|
||||
0x3B3B3B33,
|
||||
0x4A4A4A44,
|
||||
0x59595955,
|
||||
0x68686866,
|
||||
0x77777777,
|
||||
0x86868688,
|
||||
0x95959599,
|
||||
0xA4A4A4AA,
|
||||
0xB3B3B3BB,
|
||||
0xC2C2C2CC,
|
||||
0xD1D1D1DD,
|
||||
0xE0E0E0EE,
|
||||
};
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_came_twee_const = {
|
||||
.te_short = 500,
|
||||
.te_long = 1000,
|
||||
.te_delta = 250,
|
||||
.min_count_bit_for_found = 54,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderCameTwee {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
ManchesterState manchester_saved_state;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderCameTwee {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
CameTweeDecoderStepReset = 0,
|
||||
CameTweeDecoderStepDecoderData,
|
||||
} CameTweeDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder = {
|
||||
.alloc = subghz_protocol_decoder_came_twee_alloc,
|
||||
.free = subghz_protocol_decoder_came_twee_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_came_twee_feed,
|
||||
.reset = subghz_protocol_decoder_came_twee_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_came_twee_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_came_twee_serialize,
|
||||
.deserialize = subghz_protocol_decoder_came_twee_deserialize,
|
||||
.get_string = subghz_protocol_decoder_came_twee_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder = {
|
||||
.alloc = subghz_protocol_encoder_came_twee_alloc,
|
||||
.free = subghz_protocol_encoder_came_twee_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_came_twee_deserialize,
|
||||
.stop = subghz_protocol_encoder_came_twee_stop,
|
||||
.yield = subghz_protocol_encoder_came_twee_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_came_twee = {
|
||||
.name = SUBGHZ_PROTOCOL_CAME_TWEE_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_came_twee_decoder,
|
||||
.encoder = &subghz_protocol_came_twee_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderCameTwee* instance = malloc(sizeof(SubGhzProtocolEncoderCameTwee));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_came_twee;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 1536; //max upload 92*14 = 1288 !!!!
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_came_twee_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderCameTwee* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static LevelDuration
|
||||
subghz_protocol_encoder_came_twee_add_duration_to_upload(ManchesterEncoderResult result) {
|
||||
LevelDuration data = {.duration = 0, .level = 0};
|
||||
switch(result) {
|
||||
case ManchesterEncoderResultShortLow:
|
||||
data.duration = subghz_protocol_came_twee_const.te_short;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongLow:
|
||||
data.duration = subghz_protocol_came_twee_const.te_long;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongHigh:
|
||||
data.duration = subghz_protocol_came_twee_const.te_long;
|
||||
data.level = true;
|
||||
break;
|
||||
case ManchesterEncoderResultShortHigh:
|
||||
data.duration = subghz_protocol_came_twee_const.te_short;
|
||||
data.level = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
furi_crash("SubGhz: ManchesterEncoderResult is incorrect.");
|
||||
break;
|
||||
}
|
||||
return level_duration_make(data.level, data.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderCameTwee instance
|
||||
*/
|
||||
static void subghz_protocol_encoder_came_twee_get_upload(SubGhzProtocolEncoderCameTwee* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
|
||||
ManchesterEncoderState enc_state;
|
||||
manchester_encoder_reset(&enc_state);
|
||||
ManchesterEncoderResult result;
|
||||
|
||||
uint64_t temp_parcel = 0x003FFF7200000000; //parcel mask
|
||||
|
||||
for(int i = 14; i >= 0; i--) {
|
||||
temp_parcel = (temp_parcel & 0xFFFFFFFF00000000) |
|
||||
(instance->generic.serial ^ came_twee_magic_numbers_xor[i]);
|
||||
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
||||
if(!manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result)) {
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_came_twee_add_duration_to_upload(result);
|
||||
manchester_encoder_advance(&enc_state, !bit_read(temp_parcel, i - 1), &result);
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_came_twee_add_duration_to_upload(result);
|
||||
}
|
||||
instance->encoder.upload[index] = subghz_protocol_encoder_came_twee_add_duration_to_upload(
|
||||
manchester_encoder_finish(&enc_state));
|
||||
if(level_duration_get_level(instance->encoder.upload[index])) {
|
||||
index++;
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_came_twee_const.te_long * 51);
|
||||
}
|
||||
instance->encoder.size_upload = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_came_twee_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/* Came Twee 54 bit, rolling code 15 parcels with
|
||||
* a decreasing counter from 0xE to 0x0
|
||||
* with originally coded dip switches on the console 10 bit code
|
||||
*
|
||||
* 0x003FFF72E04A6FEE
|
||||
* 0x003FFF72D17B5EDD
|
||||
* 0x003FFF72C2684DCC
|
||||
* 0x003FFF72B3193CBB
|
||||
* 0x003FFF72A40E2BAA
|
||||
* 0x003FFF72953F1A99
|
||||
* 0x003FFF72862C0988
|
||||
* 0x003FFF7277DDF877
|
||||
* 0x003FFF7268C2E766
|
||||
* 0x003FFF7259F3D655
|
||||
* 0x003FFF724AE0C544
|
||||
* 0x003FFF723B91B433
|
||||
* 0x003FFF722C86A322
|
||||
* 0x003FFF721DB79211
|
||||
* 0x003FFF720EA48100
|
||||
*
|
||||
* decryption
|
||||
* the last 32 bits, do XOR by the desired number, divide the result by 4,
|
||||
* convert the first 16 bits of the resulting 32-bit number to bin and do
|
||||
* bit-by-bit mirroring, adding up to 10 bits
|
||||
*
|
||||
* Example
|
||||
* Step 1. 0x003FFF721DB79211 => 0x1DB79211
|
||||
* Step 4. 0x1DB79211 xor 0x1D1D1D11 => 0x00AA8F00
|
||||
* Step 4. 0x00AA8F00 / 4 => 0x002AA3C0
|
||||
* Step 5. 0x002AA3C0 => 0x002A
|
||||
* Step 6. 0x002A bin => b101010
|
||||
* Step 7. b101010 => b0101010000
|
||||
* Step 8. b0101010000 => (Dip) Off ON Off ON Off ON Off Off Off Off
|
||||
*/
|
||||
|
||||
uint8_t cnt_parcel = (uint8_t)(instance->data & 0xF);
|
||||
uint32_t data = (uint32_t)(instance->data & 0x0FFFFFFFF);
|
||||
|
||||
data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]);
|
||||
instance->serial = data;
|
||||
data /= 4;
|
||||
instance->btn = (data >> 4) & 0x0F;
|
||||
data >>= 16;
|
||||
data = (uint16_t)subghz_protocol_blocks_reverse_key(data, 16);
|
||||
instance->cnt = data >> 6;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderCameTwee* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_came_twee_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
subghz_protocol_came_twee_remote_controller(&instance->generic);
|
||||
subghz_protocol_encoder_came_twee_get_upload(instance);
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_came_twee_stop(void* context) {
|
||||
SubGhzProtocolEncoderCameTwee* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_came_twee_yield(void* context) {
|
||||
SubGhzProtocolEncoderCameTwee* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_came_twee_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderCameTwee* instance = malloc(sizeof(SubGhzProtocolDecoderCameTwee));
|
||||
instance->base.protocol = &subghz_protocol_came_twee;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_twee_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameTwee* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_twee_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameTwee* instance = context;
|
||||
instance->decoder.parser_step = CameTweeDecoderStepReset;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameTwee* instance = context;
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case CameTweeDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long * 51) <
|
||||
subghz_protocol_came_twee_const.te_delta * 20)) {
|
||||
//Found header CAME
|
||||
instance->decoder.parser_step = CameTweeDecoderStepDecoderData;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventLongLow,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventLongHigh,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventShortLow,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
case CameTweeDecoderStepDecoderData:
|
||||
if(!level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) <
|
||||
subghz_protocol_came_twee_const.te_delta) {
|
||||
event = ManchesterEventShortLow;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) <
|
||||
subghz_protocol_came_twee_const.te_delta) {
|
||||
event = ManchesterEventLongLow;
|
||||
} else if(
|
||||
duration >= ((uint32_t)subghz_protocol_came_twee_const.te_long * 2 +
|
||||
subghz_protocol_came_twee_const.te_delta)) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_came_twee_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;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventLongLow,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventLongHigh,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventShortLow,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
} else {
|
||||
instance->decoder.parser_step = CameTweeDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_short) <
|
||||
subghz_protocol_came_twee_const.te_delta) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_came_twee_const.te_long) <
|
||||
subghz_protocol_came_twee_const.te_delta) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
instance->decoder.parser_step = CameTweeDecoderStepReset;
|
||||
}
|
||||
}
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = manchester_advance(
|
||||
instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data;
|
||||
instance->decoder.decode_count_bit++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameTwee* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_came_twee_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameTwee* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameTwee* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_came_twee_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderCameTwee* instance = context;
|
||||
subghz_protocol_came_twee_remote_controller(&instance->generic);
|
||||
uint32_t code_found_hi = instance->generic.data >> 32;
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"Btn:%X\r\n"
|
||||
"DIP:" DIP_PATTERN "\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
code_found_hi,
|
||||
code_found_lo,
|
||||
instance->generic.btn,
|
||||
CNT_TO_DIP(instance->generic.cnt));
|
||||
}
|
||||
107
applications/main/subghz/protocols/came_twee.h
Normal file
107
applications/main/subghz/protocols/came_twee.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_CAME_TWEE_NAME "CAME TWEE"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderCameTwee SubGhzProtocolDecoderCameTwee;
|
||||
typedef struct SubGhzProtocolEncoderCameTwee SubGhzProtocolEncoderCameTwee;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_came_twee_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_came_twee_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_came_twee;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderCameTwee.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderCameTwee* pointer to a SubGhzProtocolEncoderCameTwee instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_came_twee_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderCameTwee.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCameTwee instance
|
||||
*/
|
||||
void subghz_protocol_encoder_came_twee_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCameTwee instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCameTwee instance
|
||||
*/
|
||||
void subghz_protocol_encoder_came_twee_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderCameTwee instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_came_twee_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderCameTwee.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderCameTwee* pointer to a SubGhzProtocolDecoderCameTwee instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_came_twee_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderCameTwee.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameTwee instance
|
||||
*/
|
||||
void subghz_protocol_decoder_came_twee_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderCameTwee.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameTwee instance
|
||||
*/
|
||||
void subghz_protocol_decoder_came_twee_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameTwee instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_came_twee_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameTwee instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_came_twee_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderCameTwee.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameTwee instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_came_twee_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderCameTwee.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameTwee instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_came_twee_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderCameTwee instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_came_twee_get_string(void* context, FuriString* output);
|
||||
499
applications/main/subghz/protocols/chamberlain_code.c
Normal file
499
applications/main/subghz/protocols/chamberlain_code.c
Normal file
@@ -0,0 +1,499 @@
|
||||
#include "chamberlain_code.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolChamb_Code"
|
||||
|
||||
#define CHAMBERLAIN_CODE_BIT_STOP 0b0001
|
||||
#define CHAMBERLAIN_CODE_BIT_1 0b0011
|
||||
#define CHAMBERLAIN_CODE_BIT_0 0b0111
|
||||
|
||||
#define CHAMBERLAIN_7_CODE_MASK 0xF000000FF0F
|
||||
#define CHAMBERLAIN_8_CODE_MASK 0xF00000F00F
|
||||
#define CHAMBERLAIN_9_CODE_MASK 0xF000000000F
|
||||
|
||||
#define CHAMBERLAIN_7_CODE_MASK_CHECK 0x10000001101
|
||||
#define CHAMBERLAIN_8_CODE_MASK_CHECK 0x1000001001
|
||||
#define CHAMBERLAIN_9_CODE_MASK_CHECK 0x10000000001
|
||||
|
||||
#define CHAMBERLAIN_7_CODE_DIP_PATTERN "%c%c%c%c%c%c%c"
|
||||
#define CHAMBERLAIN_7_CODE_DATA_TO_DIP(dip) \
|
||||
(dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \
|
||||
(dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \
|
||||
(dip & 0x0001 ? '1' : '0')
|
||||
|
||||
#define CHAMBERLAIN_8_CODE_DIP_PATTERN "%c%c%c%c%cx%c%c"
|
||||
#define CHAMBERLAIN_8_CODE_DATA_TO_DIP(dip) \
|
||||
(dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \
|
||||
(dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0001 ? '1' : '0'), \
|
||||
(dip & 0x0002 ? '1' : '0')
|
||||
|
||||
#define CHAMBERLAIN_9_CODE_DIP_PATTERN "%c%c%c%c%c%c%c%c%c"
|
||||
#define CHAMBERLAIN_9_CODE_DATA_TO_DIP(dip) \
|
||||
(dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), \
|
||||
(dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), \
|
||||
(dip & 0x0001 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), (dip & 0x0004 ? '1' : '0')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_chamb_code_const = {
|
||||
.te_short = 1000,
|
||||
.te_long = 3000,
|
||||
.te_delta = 200,
|
||||
.min_count_bit_for_found = 10,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderChamb_Code {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderChamb_Code {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Chamb_CodeDecoderStepReset = 0,
|
||||
Chamb_CodeDecoderStepFoundStartBit,
|
||||
Chamb_CodeDecoderStepSaveDuration,
|
||||
Chamb_CodeDecoderStepCheckDuration,
|
||||
} Chamb_CodeDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder = {
|
||||
.alloc = subghz_protocol_decoder_chamb_code_alloc,
|
||||
.free = subghz_protocol_decoder_chamb_code_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_chamb_code_feed,
|
||||
.reset = subghz_protocol_decoder_chamb_code_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_chamb_code_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_chamb_code_serialize,
|
||||
.deserialize = subghz_protocol_decoder_chamb_code_deserialize,
|
||||
.get_string = subghz_protocol_decoder_chamb_code_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder = {
|
||||
.alloc = subghz_protocol_encoder_chamb_code_alloc,
|
||||
.free = subghz_protocol_encoder_chamb_code_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_chamb_code_deserialize,
|
||||
.stop = subghz_protocol_encoder_chamb_code_stop,
|
||||
.yield = subghz_protocol_encoder_chamb_code_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_chamb_code = {
|
||||
.name = SUBGHZ_PROTOCOL_CHAMB_CODE_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_chamb_code_decoder,
|
||||
.encoder = &subghz_protocol_chamb_code_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolEncoderChamb_Code));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_chamb_code;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 24;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_chamb_code_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderChamb_Code* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static uint64_t subghz_protocol_chamb_bit_to_code(uint64_t data, uint8_t size) {
|
||||
uint64_t data_res = 0;
|
||||
for(uint8_t i = 0; i < size; i++) {
|
||||
if(!(bit_read(data, size - i - 1))) {
|
||||
data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_0;
|
||||
} else {
|
||||
data_res = data_res << 4 | CHAMBERLAIN_CODE_BIT_1;
|
||||
}
|
||||
}
|
||||
return data_res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderChamb_Code instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_chamb_code_get_upload(SubGhzProtocolEncoderChamb_Code* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
uint64_t data = subghz_protocol_chamb_bit_to_code(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
|
||||
switch(instance->generic.data_count_bit) {
|
||||
case 7:
|
||||
data = ((data >> 4) << 16) | (data & 0xF) << 4 | CHAMBERLAIN_7_CODE_MASK_CHECK;
|
||||
break;
|
||||
case 8:
|
||||
data = ((data >> 12) << 16) | (data & 0xFF) << 4 | CHAMBERLAIN_8_CODE_MASK_CHECK;
|
||||
break;
|
||||
case 9:
|
||||
data = (data << 4) | CHAMBERLAIN_9_CODE_MASK_CHECK;
|
||||
break;
|
||||
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Invalid bits count");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
#define UPLOAD_HEX_DATA_SIZE 10
|
||||
uint8_t upload_hex_data[UPLOAD_HEX_DATA_SIZE] = {0};
|
||||
size_t upload_hex_count_bit = 0;
|
||||
|
||||
//insert guard time
|
||||
for(uint8_t i = 0; i < 36; i++) {
|
||||
subghz_protocol_blocks_set_bit_array(
|
||||
0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);
|
||||
}
|
||||
|
||||
//insert data
|
||||
switch(instance->generic.data_count_bit) {
|
||||
case 7:
|
||||
case 9:
|
||||
for(uint8_t i = 44; i > 0; i--) {
|
||||
if(!bit_read(data, i - 1)) {
|
||||
subghz_protocol_blocks_set_bit_array(
|
||||
0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);
|
||||
} else {
|
||||
subghz_protocol_blocks_set_bit_array(
|
||||
1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 8:
|
||||
for(uint8_t i = 40; i > 0; i--) {
|
||||
if(!bit_read(data, i - 1)) {
|
||||
subghz_protocol_blocks_set_bit_array(
|
||||
0, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);
|
||||
} else {
|
||||
subghz_protocol_blocks_set_bit_array(
|
||||
1, upload_hex_data, upload_hex_count_bit++, UPLOAD_HEX_DATA_SIZE);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
instance->encoder.size_upload = subghz_protocol_blocks_get_upload_from_bit_array(
|
||||
upload_hex_data,
|
||||
upload_hex_count_bit,
|
||||
instance->encoder.upload,
|
||||
instance->encoder.size_upload,
|
||||
subghz_protocol_chamb_code_const.te_short,
|
||||
SubGhzProtocolBlockAlignBitLeft);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderChamb_Code* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit >
|
||||
subghz_protocol_chamb_code_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_chamb_code_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_chamb_code_stop(void* context) {
|
||||
SubGhzProtocolEncoderChamb_Code* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_chamb_code_yield(void* context) {
|
||||
SubGhzProtocolEncoderChamb_Code* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_chamb_code_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderChamb_Code* instance = malloc(sizeof(SubGhzProtocolDecoderChamb_Code));
|
||||
instance->base.protocol = &subghz_protocol_chamb_code;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_chamb_code_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderChamb_Code* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_chamb_code_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderChamb_Code* instance = context;
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepReset;
|
||||
}
|
||||
|
||||
static bool subghz_protocol_chamb_code_to_bit(uint64_t* data, uint8_t size) {
|
||||
uint64_t data_tmp = data[0];
|
||||
uint64_t data_res = 0;
|
||||
for(uint8_t i = 0; i < size; i++) {
|
||||
if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_0) {
|
||||
bit_write(data_res, i, 0);
|
||||
} else if((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_1) {
|
||||
bit_write(data_res, i, 1);
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
data_tmp >>= 4;
|
||||
}
|
||||
data[0] = data_res;
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool subghz_protocol_decoder_chamb_code_check_mask_and_parse(
|
||||
SubGhzProtocolDecoderChamb_Code* instance) {
|
||||
furi_assert(instance);
|
||||
if(instance->decoder.decode_count_bit >
|
||||
subghz_protocol_chamb_code_const.min_count_bit_for_found + 1)
|
||||
return false;
|
||||
|
||||
if((instance->decoder.decode_data & CHAMBERLAIN_7_CODE_MASK) ==
|
||||
CHAMBERLAIN_7_CODE_MASK_CHECK) {
|
||||
instance->decoder.decode_count_bit = 7;
|
||||
instance->decoder.decode_data &= ~CHAMBERLAIN_7_CODE_MASK;
|
||||
instance->decoder.decode_data = (instance->decoder.decode_data >> 12) |
|
||||
((instance->decoder.decode_data >> 4) & 0xF);
|
||||
} else if(
|
||||
(instance->decoder.decode_data & CHAMBERLAIN_8_CODE_MASK) ==
|
||||
CHAMBERLAIN_8_CODE_MASK_CHECK) {
|
||||
instance->decoder.decode_count_bit = 8;
|
||||
instance->decoder.decode_data &= ~CHAMBERLAIN_8_CODE_MASK;
|
||||
instance->decoder.decode_data = instance->decoder.decode_data >> 4 |
|
||||
CHAMBERLAIN_CODE_BIT_0 << 8; //DIP 6 no use
|
||||
} else if(
|
||||
(instance->decoder.decode_data & CHAMBERLAIN_9_CODE_MASK) ==
|
||||
CHAMBERLAIN_9_CODE_MASK_CHECK) {
|
||||
instance->decoder.decode_count_bit = 9;
|
||||
instance->decoder.decode_data &= ~CHAMBERLAIN_9_CODE_MASK;
|
||||
instance->decoder.decode_data >>= 4;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
return subghz_protocol_chamb_code_to_bit(
|
||||
&instance->decoder.decode_data, instance->decoder.decode_count_bit);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderChamb_Code* instance = context;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case Chamb_CodeDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 39) <
|
||||
subghz_protocol_chamb_code_const.te_delta * 20)) {
|
||||
//Found header Chamb_Code
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case Chamb_CodeDecoderStepFoundStartBit:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) <
|
||||
subghz_protocol_chamb_code_const.te_delta)) {
|
||||
//Found start bit Chamb_Code
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->decoder.decode_data = instance->decoder.decode_data << 4 |
|
||||
CHAMBERLAIN_CODE_BIT_STOP;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case Chamb_CodeDecoderStepSaveDuration:
|
||||
if(!level) { //save interval
|
||||
if(duration > subghz_protocol_chamb_code_const.te_short * 5) {
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_chamb_code_const.min_count_bit_for_found) {
|
||||
instance->generic.serial = 0x0;
|
||||
instance->generic.btn = 0x0;
|
||||
if(subghz_protocol_decoder_chamb_code_check_mask_and_parse(instance)) {
|
||||
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.parser_step = Chamb_CodeDecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case Chamb_CodeDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF( //Found stop bit Chamb_Code
|
||||
instance->decoder.te_last,
|
||||
subghz_protocol_chamb_code_const.te_short * 3) <
|
||||
subghz_protocol_chamb_code_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short) <
|
||||
subghz_protocol_chamb_code_const.te_delta)) {
|
||||
instance->decoder.decode_data = instance->decoder.decode_data << 4 |
|
||||
CHAMBERLAIN_CODE_BIT_STOP;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short * 2) <
|
||||
subghz_protocol_chamb_code_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 2) <
|
||||
subghz_protocol_chamb_code_const.te_delta)) {
|
||||
instance->decoder.decode_data = instance->decoder.decode_data << 4 |
|
||||
CHAMBERLAIN_CODE_BIT_1;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_chamb_code_const.te_short) <
|
||||
subghz_protocol_chamb_code_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_chamb_code_const.te_short * 3) <
|
||||
subghz_protocol_chamb_code_const.te_delta)) {
|
||||
instance->decoder.decode_data = instance->decoder.decode_data << 4 |
|
||||
CHAMBERLAIN_CODE_BIT_0;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepReset;
|
||||
}
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = Chamb_CodeDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderChamb_Code* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_chamb_code_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderChamb_Code* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderChamb_Code* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit >
|
||||
subghz_protocol_chamb_code_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderChamb_Code* instance = context;
|
||||
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
|
||||
uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
|
||||
uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Key:0x%03lX\r\n"
|
||||
"Yek:0x%03lX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
code_found_lo,
|
||||
code_found_reverse_lo);
|
||||
|
||||
switch(instance->generic.data_count_bit) {
|
||||
case 7:
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"DIP:" CHAMBERLAIN_7_CODE_DIP_PATTERN "\r\n",
|
||||
CHAMBERLAIN_7_CODE_DATA_TO_DIP(code_found_lo));
|
||||
break;
|
||||
case 8:
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"DIP:" CHAMBERLAIN_8_CODE_DIP_PATTERN "\r\n",
|
||||
CHAMBERLAIN_8_CODE_DATA_TO_DIP(code_found_lo));
|
||||
break;
|
||||
case 9:
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"DIP:" CHAMBERLAIN_9_CODE_DIP_PATTERN "\r\n",
|
||||
CHAMBERLAIN_9_CODE_DATA_TO_DIP(code_found_lo));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
107
applications/main/subghz/protocols/chamberlain_code.h
Normal file
107
applications/main/subghz/protocols/chamberlain_code.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_CHAMB_CODE_NAME "Cham_Code"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderChamb_Code SubGhzProtocolDecoderChamb_Code;
|
||||
typedef struct SubGhzProtocolEncoderChamb_Code SubGhzProtocolEncoderChamb_Code;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_chamb_code_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_chamb_code_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_chamb_code;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderChamb_Code.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderChamb_Code* pointer to a SubGhzProtocolEncoderChamb_Code instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_chamb_code_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderChamb_Code.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance
|
||||
*/
|
||||
void subghz_protocol_encoder_chamb_code_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance
|
||||
*/
|
||||
void subghz_protocol_encoder_chamb_code_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderChamb_Code instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_chamb_code_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderChamb_Code.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderChamb_Code* pointer to a SubGhzProtocolDecoderChamb_Code instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_chamb_code_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderChamb_Code.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance
|
||||
*/
|
||||
void subghz_protocol_decoder_chamb_code_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderChamb_Code.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance
|
||||
*/
|
||||
void subghz_protocol_decoder_chamb_code_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_chamb_code_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_chamb_code_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderChamb_Code.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_chamb_code_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderChamb_Code.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_chamb_code_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderChamb_Code instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_chamb_code_get_string(void* context, FuriString* output);
|
||||
365
applications/main/subghz/protocols/clemsa.c
Normal file
365
applications/main/subghz/protocols/clemsa.c
Normal file
@@ -0,0 +1,365 @@
|
||||
#include "clemsa.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
// protocol BERNER / ELKA / TEDSEN / TELETASTER
|
||||
#define TAG "SubGhzProtocolClemsa"
|
||||
|
||||
#define DIP_P 0b11 //(+)
|
||||
#define DIP_O 0b10 //(0)
|
||||
#define DIP_N 0b00 //(-)
|
||||
|
||||
#define DIP_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define SHOW_DIP_P(dip, check_dip) \
|
||||
((((dip >> 0xE) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0xC) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0xA) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x8) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x6) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x4) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x2) & 0x3) == check_dip) ? '*' : '_'), \
|
||||
((((dip >> 0x0) & 0x3) == check_dip) ? '*' : '_')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_clemsa_const = {
|
||||
.te_short = 385,
|
||||
.te_long = 2695,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 18,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderClemsa {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderClemsa {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ClemsaDecoderStepReset = 0,
|
||||
ClemsaDecoderStepSaveDuration,
|
||||
ClemsaDecoderStepCheckDuration,
|
||||
} ClemsaDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder = {
|
||||
.alloc = subghz_protocol_decoder_clemsa_alloc,
|
||||
.free = subghz_protocol_decoder_clemsa_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_clemsa_feed,
|
||||
.reset = subghz_protocol_decoder_clemsa_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_clemsa_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_clemsa_serialize,
|
||||
.deserialize = subghz_protocol_decoder_clemsa_deserialize,
|
||||
.get_string = subghz_protocol_decoder_clemsa_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder = {
|
||||
.alloc = subghz_protocol_encoder_clemsa_alloc,
|
||||
.free = subghz_protocol_encoder_clemsa_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_clemsa_deserialize,
|
||||
.stop = subghz_protocol_encoder_clemsa_stop,
|
||||
.yield = subghz_protocol_encoder_clemsa_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_clemsa = {
|
||||
.name = SUBGHZ_PROTOCOL_CLEMSA_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_clemsa_decoder,
|
||||
.encoder = &subghz_protocol_clemsa_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderClemsa* instance = malloc(sizeof(SubGhzProtocolEncoderClemsa));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_clemsa;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 52;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_clemsa_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderClemsa* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderClemsa instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_clemsa_get_upload(SubGhzProtocolEncoderClemsa* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2);
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
|
||||
if(bit_read(instance->generic.data, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_clemsa_const.te_long);
|
||||
}
|
||||
}
|
||||
if(bit_read(instance->generic.data, 0)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_long);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false,
|
||||
(uint32_t)subghz_protocol_clemsa_const.te_short +
|
||||
subghz_protocol_clemsa_const.te_long * 7);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_clemsa_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false,
|
||||
(uint32_t)subghz_protocol_clemsa_const.te_long +
|
||||
subghz_protocol_clemsa_const.te_long * 7);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderClemsa* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_clemsa_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_clemsa_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_clemsa_stop(void* context) {
|
||||
SubGhzProtocolEncoderClemsa* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_clemsa_yield(void* context) {
|
||||
SubGhzProtocolEncoderClemsa* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderClemsa* instance = malloc(sizeof(SubGhzProtocolDecoderClemsa));
|
||||
instance->base.protocol = &subghz_protocol_clemsa;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_clemsa_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderClemsa* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_clemsa_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderClemsa* instance = context;
|
||||
instance->decoder.parser_step = ClemsaDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderClemsa* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case ClemsaDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) <
|
||||
subghz_protocol_clemsa_const.te_delta * 25)) {
|
||||
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case ClemsaDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = ClemsaDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = ClemsaDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case ClemsaDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) <
|
||||
subghz_protocol_clemsa_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_long) <
|
||||
subghz_protocol_clemsa_const.te_delta * 3)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) <
|
||||
subghz_protocol_clemsa_const.te_delta * 3) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short) <
|
||||
subghz_protocol_clemsa_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = ClemsaDecoderStepSaveDuration;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_clemsa_const.te_short * 51) <
|
||||
subghz_protocol_clemsa_const.te_delta * 25) {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_clemsa_const.te_short) <
|
||||
subghz_protocol_clemsa_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
} else if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_clemsa_const.te_long) <
|
||||
subghz_protocol_clemsa_const.te_delta * 3)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
} else {
|
||||
instance->decoder.parser_step = ClemsaDecoderStepReset;
|
||||
}
|
||||
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_clemsa_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.parser_step = ClemsaDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = ClemsaDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = ClemsaDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_clemsa_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
instance->serial = (instance->data >> 2) & 0xFFFF;
|
||||
instance->btn = (instance->data & 0x03);
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderClemsa* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_clemsa_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderClemsa* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderClemsa* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_clemsa_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_clemsa_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderClemsa* instance = context;
|
||||
subghz_protocol_clemsa_check_remote_controller(&instance->generic);
|
||||
//uint32_t data = (uint32_t)(instance->generic.data & 0xFFFFFF);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%05lX Btn %X\r\n"
|
||||
" +: " DIP_PATTERN "\r\n"
|
||||
" o: " DIP_PATTERN "\r\n"
|
||||
" -: " DIP_PATTERN "\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data & 0x3FFFF),
|
||||
instance->generic.btn,
|
||||
SHOW_DIP_P(instance->generic.serial, DIP_P),
|
||||
SHOW_DIP_P(instance->generic.serial, DIP_O),
|
||||
SHOW_DIP_P(instance->generic.serial, DIP_N));
|
||||
}
|
||||
107
applications/main/subghz/protocols/clemsa.h
Normal file
107
applications/main/subghz/protocols/clemsa.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_CLEMSA_NAME "Clemsa"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderClemsa SubGhzProtocolDecoderClemsa;
|
||||
typedef struct SubGhzProtocolEncoderClemsa SubGhzProtocolEncoderClemsa;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_clemsa_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_clemsa_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_clemsa;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderClemsa.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderClemsa* pointer to a SubGhzProtocolEncoderClemsa instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_clemsa_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderClemsa.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
|
||||
*/
|
||||
void subghz_protocol_encoder_clemsa_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
|
||||
*/
|
||||
void subghz_protocol_encoder_clemsa_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderClemsa instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_clemsa_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderClemsa.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderClemsa* pointer to a SubGhzProtocolDecoderClemsa instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_clemsa_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderClemsa.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
|
||||
*/
|
||||
void subghz_protocol_decoder_clemsa_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderClemsa.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
|
||||
*/
|
||||
void subghz_protocol_decoder_clemsa_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_clemsa_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_clemsa_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderClemsa.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_clemsa_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderClemsa.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_clemsa_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderClemsa instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_clemsa_get_string(void* context, FuriString* output);
|
||||
356
applications/main/subghz/protocols/doitrand.c
Normal file
356
applications/main/subghz/protocols/doitrand.c
Normal file
@@ -0,0 +1,356 @@
|
||||
#include "doitrand.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolDoitrand"
|
||||
|
||||
#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c"
|
||||
#define CNT_TO_DIP(dip) \
|
||||
(dip & 0x0001 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \
|
||||
(dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x1000 ? '1' : '0'), \
|
||||
(dip & 0x0800 ? '1' : '0'), (dip & 0x0400 ? '1' : '0'), (dip & 0x0200 ? '1' : '0'), \
|
||||
(dip & 0x0002 ? '1' : '0')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_doitrand_const = {
|
||||
.te_short = 400,
|
||||
.te_long = 1100,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 37,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderDoitrand {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderDoitrand {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
DoitrandDecoderStepReset = 0,
|
||||
DoitrandDecoderStepFoundStartBit,
|
||||
DoitrandDecoderStepSaveDuration,
|
||||
DoitrandDecoderStepCheckDuration,
|
||||
} DoitrandDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder = {
|
||||
.alloc = subghz_protocol_decoder_doitrand_alloc,
|
||||
.free = subghz_protocol_decoder_doitrand_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_doitrand_feed,
|
||||
.reset = subghz_protocol_decoder_doitrand_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_doitrand_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_doitrand_serialize,
|
||||
.deserialize = subghz_protocol_decoder_doitrand_deserialize,
|
||||
.get_string = subghz_protocol_decoder_doitrand_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder = {
|
||||
.alloc = subghz_protocol_encoder_doitrand_alloc,
|
||||
.free = subghz_protocol_encoder_doitrand_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_doitrand_deserialize,
|
||||
.stop = subghz_protocol_encoder_doitrand_stop,
|
||||
.yield = subghz_protocol_encoder_doitrand_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_doitrand = {
|
||||
.name = SUBGHZ_PROTOCOL_DOITRAND_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_doitrand_decoder,
|
||||
.encoder = &subghz_protocol_doitrand_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderDoitrand* instance = malloc(sizeof(SubGhzProtocolEncoderDoitrand));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_doitrand;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_doitrand_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderDoitrand* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderDoitrand instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_doitrand_get_upload(SubGhzProtocolEncoderDoitrand* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short * 62);
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short * 2 - 100);
|
||||
//Send key data
|
||||
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(false, (uint32_t)subghz_protocol_doitrand_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_doitrand_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_doitrand_const.te_long);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderDoitrand* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_doitrand_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_doitrand_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_doitrand_stop(void* context) {
|
||||
SubGhzProtocolEncoderDoitrand* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_doitrand_yield(void* context) {
|
||||
SubGhzProtocolEncoderDoitrand* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderDoitrand* instance = malloc(sizeof(SubGhzProtocolDecoderDoitrand));
|
||||
instance->base.protocol = &subghz_protocol_doitrand;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_doitrand_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDoitrand* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_doitrand_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDoitrand* instance = context;
|
||||
instance->decoder.parser_step = DoitrandDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDoitrand* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case DoitrandDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short * 62) <
|
||||
subghz_protocol_doitrand_const.te_delta * 30)) {
|
||||
//Found Preambula
|
||||
instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case DoitrandDecoderStepFoundStartBit:
|
||||
if(level && ((DURATION_DIFF(duration, (subghz_protocol_doitrand_const.te_short * 2)) <
|
||||
subghz_protocol_doitrand_const.te_delta * 3))) {
|
||||
//Found start bit
|
||||
instance->decoder.parser_step = DoitrandDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = DoitrandDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case DoitrandDecoderStepSaveDuration:
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_doitrand_const.te_short * 10 +
|
||||
subghz_protocol_doitrand_const.te_delta)) {
|
||||
instance->decoder.parser_step = DoitrandDecoderStepFoundStartBit;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_doitrand_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;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = DoitrandDecoderStepCheckDuration;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case DoitrandDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_short) <
|
||||
subghz_protocol_doitrand_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_long) <
|
||||
subghz_protocol_doitrand_const.te_delta * 3)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = DoitrandDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_doitrand_const.te_long) <
|
||||
subghz_protocol_doitrand_const.te_delta * 3) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_doitrand_const.te_short) <
|
||||
subghz_protocol_doitrand_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = DoitrandDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = DoitrandDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = DoitrandDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_doitrand_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/*
|
||||
* 67892345 0 k 1
|
||||
* 0000082F5F => 00000000000000000 10 000010111101011111
|
||||
* 0002082F5F => 00000000000100000 10 000010111101011111
|
||||
* 0200082F5F => 00010000000000000 10 000010111101011111
|
||||
* 0400082F5F => 00100000000000000 10 000010111101011111
|
||||
* 0800082F5F => 01000000000000000 10 000010111101011111
|
||||
* 1000082F5F => 10000000000000000 10 000010111101011111
|
||||
* 0020082F5F => 00000001000000000 10 000010111101011111
|
||||
* 0040082F5F => 00000010000000000 10 000010111101011111
|
||||
* 0080082F5F => 00000100000000000 10 000010111101011111
|
||||
* 0100082F5F => 00001000000000000 10 000010111101011111
|
||||
* 000008AF5F => 00000000000000000 10 001010111101011111
|
||||
* 1FE208AF5F => 11111111000100000 10 001010111101011111
|
||||
*
|
||||
* 0...9 - DIP
|
||||
* k- KEY
|
||||
*/
|
||||
instance->cnt = (instance->data >> 24) | ((instance->data >> 15) & 0x1);
|
||||
instance->btn = ((instance->data >> 18) & 0x3);
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDoitrand* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_doitrand_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDoitrand* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDoitrand* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_doitrand_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDoitrand* instance = context;
|
||||
subghz_protocol_doitrand_check_remote_controller(&instance->generic);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%02lX%08lX\r\n"
|
||||
"Btn:%X\r\n"
|
||||
"DIP:" DIP_PATTERN "\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF,
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
|
||||
instance->generic.btn,
|
||||
CNT_TO_DIP(instance->generic.cnt));
|
||||
}
|
||||
107
applications/main/subghz/protocols/doitrand.h
Normal file
107
applications/main/subghz/protocols/doitrand.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_DOITRAND_NAME "Doitrand"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderDoitrand SubGhzProtocolDecoderDoitrand;
|
||||
typedef struct SubGhzProtocolEncoderDoitrand SubGhzProtocolEncoderDoitrand;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_doitrand_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_doitrand_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_doitrand;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderDoitrand.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderDoitrand* pointer to a SubGhzProtocolEncoderDoitrand instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_doitrand_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderDoitrand.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDoitrand instance
|
||||
*/
|
||||
void subghz_protocol_encoder_doitrand_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDoitrand instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDoitrand instance
|
||||
*/
|
||||
void subghz_protocol_encoder_doitrand_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDoitrand instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_doitrand_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderDoitrand.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderDoitrand* pointer to a SubGhzProtocolDecoderDoitrand instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_doitrand_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderDoitrand.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
|
||||
*/
|
||||
void subghz_protocol_decoder_doitrand_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderDoitrand.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
|
||||
*/
|
||||
void subghz_protocol_decoder_doitrand_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_doitrand_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_doitrand_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderDoitrand.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_doitrand_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderDoitrand.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_doitrand_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDoitrand instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_doitrand_get_string(void* context, FuriString* output);
|
||||
447
applications/main/subghz/protocols/dooya.c
Normal file
447
applications/main/subghz/protocols/dooya.c
Normal file
@@ -0,0 +1,447 @@
|
||||
#include "dooya.h"
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolDooya"
|
||||
|
||||
#define DOYA_SINGLE_CHANNEL 0xFF
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_dooya_const = {
|
||||
.te_short = 366,
|
||||
.te_long = 733,
|
||||
.te_delta = 120,
|
||||
.min_count_bit_for_found = 40,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderDooya {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderDooya {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
DooyaDecoderStepReset = 0,
|
||||
DooyaDecoderStepFoundStartBit,
|
||||
DooyaDecoderStepSaveDuration,
|
||||
DooyaDecoderStepCheckDuration,
|
||||
} DooyaDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_dooya_decoder = {
|
||||
.alloc = subghz_protocol_decoder_dooya_alloc,
|
||||
.free = subghz_protocol_decoder_dooya_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_dooya_feed,
|
||||
.reset = subghz_protocol_decoder_dooya_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_dooya_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_dooya_serialize,
|
||||
.deserialize = subghz_protocol_decoder_dooya_deserialize,
|
||||
.get_string = subghz_protocol_decoder_dooya_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_dooya_encoder = {
|
||||
.alloc = subghz_protocol_encoder_dooya_alloc,
|
||||
.free = subghz_protocol_encoder_dooya_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_dooya_deserialize,
|
||||
.stop = subghz_protocol_encoder_dooya_stop,
|
||||
.yield = subghz_protocol_encoder_dooya_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_dooya = {
|
||||
.name = SUBGHZ_PROTOCOL_DOOYA_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_dooya_decoder,
|
||||
.encoder = &subghz_protocol_dooya_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderDooya* instance = malloc(sizeof(SubGhzProtocolEncoderDooya));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_dooya;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_dooya_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderDooya* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderDooya instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_dooya_get_upload(SubGhzProtocolEncoderDooya* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send header
|
||||
if(bit_read(instance->generic.data, 0)) {
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false,
|
||||
(uint32_t)subghz_protocol_dooya_const.te_long * 12 +
|
||||
subghz_protocol_dooya_const.te_long);
|
||||
} else {
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false,
|
||||
(uint32_t)subghz_protocol_dooya_const.te_long * 12 +
|
||||
subghz_protocol_dooya_const.te_short);
|
||||
}
|
||||
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short * 13);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long * 2);
|
||||
|
||||
//Send key data
|
||||
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_dooya_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_dooya_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_dooya_const.te_long);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderDooya* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_dooya_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_dooya_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_dooya_stop(void* context) {
|
||||
SubGhzProtocolEncoderDooya* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_dooya_yield(void* context) {
|
||||
SubGhzProtocolEncoderDooya* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderDooya* instance = malloc(sizeof(SubGhzProtocolDecoderDooya));
|
||||
instance->base.protocol = &subghz_protocol_dooya;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_dooya_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDooya* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_dooya_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDooya* instance = context;
|
||||
instance->decoder.parser_step = DooyaDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDooya* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case DooyaDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 12) <
|
||||
subghz_protocol_dooya_const.te_delta * 20)) {
|
||||
instance->decoder.parser_step = DooyaDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
|
||||
case DooyaDecoderStepFoundStartBit:
|
||||
if(!level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long * 2) <
|
||||
subghz_protocol_dooya_const.te_delta * 3) {
|
||||
instance->decoder.parser_step = DooyaDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = DooyaDecoderStepReset;
|
||||
}
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short * 13) <
|
||||
subghz_protocol_dooya_const.te_delta * 5) {
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.parser_step = DooyaDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case DooyaDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = DooyaDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = DooyaDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case DooyaDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if(duration >= (subghz_protocol_dooya_const.te_long * 4)) {
|
||||
//add last bit
|
||||
if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) <
|
||||
subghz_protocol_dooya_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
} else if(
|
||||
DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) <
|
||||
subghz_protocol_dooya_const.te_delta * 2) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
} else {
|
||||
instance->decoder.parser_step = DooyaDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
instance->decoder.parser_step = DooyaDecoderStepFoundStartBit;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_dooya_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);
|
||||
}
|
||||
break;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_short) <
|
||||
subghz_protocol_dooya_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_long) <
|
||||
subghz_protocol_dooya_const.te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = DooyaDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_dooya_const.te_long) <
|
||||
subghz_protocol_dooya_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_dooya_const.te_short) <
|
||||
subghz_protocol_dooya_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = DooyaDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = DooyaDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = DooyaDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_somfy_telis_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/*
|
||||
* serial s/m ch key
|
||||
* long press down X * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011
|
||||
*
|
||||
* short press down 3 * E1DC030533, 40b 111000011101110000000011 0000 0101 0011 0011
|
||||
* 3 * E1DC03053C, 40b 111000011101110000000011 0000 0101 0011 1100
|
||||
*
|
||||
* press stop X * E1DC030555, 40b 111000011101110000000011 0000 0101 0101 0101
|
||||
*
|
||||
* long press up X * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001
|
||||
*
|
||||
* short press up 3 * E1DC030511, 40b 111000011101110000000011 0000 0101 0001 0001
|
||||
* 3 * E1DC03051E, 40b 111000011101110000000011 0000 0101 0001 1110
|
||||
*
|
||||
* serial: 3 byte serial number
|
||||
* s/m: single (b0000) / multi (b0001) channel console
|
||||
* ch: channel if single (always b0101) or multi
|
||||
* key: 0b00010001 - long press up
|
||||
* 0b00011110 - short press up
|
||||
* 0b00110011 - long press down
|
||||
* 0b00111100 - short press down
|
||||
* 0b01010101 - press stop
|
||||
* 0b01111001 - press up + down
|
||||
* 0b10000000 - press up + stop
|
||||
* 0b10000001 - press down + stop
|
||||
* 0b11001100 - press P2
|
||||
*
|
||||
*/
|
||||
|
||||
instance->serial = (instance->data >> 16);
|
||||
if((instance->data >> 12) & 0x0F) {
|
||||
instance->cnt = (instance->data >> 8) & 0x0F;
|
||||
} else {
|
||||
instance->cnt = DOYA_SINGLE_CHANNEL;
|
||||
}
|
||||
instance->btn = instance->data & 0xFF;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDooya* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_dooya_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDooya* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDooya* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_dooya_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get button name.
|
||||
* @param btn Button number, 8 bit
|
||||
*/
|
||||
static const char* subghz_protocol_dooya_get_name_button(uint8_t btn) {
|
||||
const char* btn_name;
|
||||
switch(btn) {
|
||||
case 0b00010001:
|
||||
btn_name = "Up_Long";
|
||||
break;
|
||||
case 0b00011110:
|
||||
btn_name = "Up_Short";
|
||||
break;
|
||||
case 0b00110011:
|
||||
btn_name = "Down_Long";
|
||||
break;
|
||||
case 0b00111100:
|
||||
btn_name = "Down_Short";
|
||||
break;
|
||||
case 0b01010101:
|
||||
btn_name = "Stop";
|
||||
break;
|
||||
case 0b01111001:
|
||||
btn_name = "Up+Down";
|
||||
break;
|
||||
case 0b10000000:
|
||||
btn_name = "Up+Stop";
|
||||
break;
|
||||
case 0b10000001:
|
||||
btn_name = "Down+Stop";
|
||||
break;
|
||||
case 0b11001100:
|
||||
btn_name = "P2";
|
||||
break;
|
||||
default:
|
||||
btn_name = "Unknown";
|
||||
break;
|
||||
}
|
||||
return btn_name;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderDooya* instance = context;
|
||||
|
||||
subghz_protocol_somfy_telis_check_remote_controller(&instance->generic);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%010llX\r\n"
|
||||
"Sn:0x%08lX\r\n"
|
||||
"Btn:%s\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
instance->generic.data,
|
||||
instance->generic.serial,
|
||||
subghz_protocol_dooya_get_name_button(instance->generic.btn));
|
||||
if(instance->generic.cnt == DOYA_SINGLE_CHANNEL) {
|
||||
furi_string_cat_printf(output, "Ch:Single\r\n");
|
||||
} else {
|
||||
furi_string_cat_printf(output, "Ch:%lu\r\n", instance->generic.cnt);
|
||||
}
|
||||
}
|
||||
107
applications/main/subghz/protocols/dooya.h
Normal file
107
applications/main/subghz/protocols/dooya.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_DOOYA_NAME "Dooya"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderDooya SubGhzProtocolDecoderDooya;
|
||||
typedef struct SubGhzProtocolEncoderDooya SubGhzProtocolEncoderDooya;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_dooya_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_dooya_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_dooya;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderDooya.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderDooya* pointer to a SubGhzProtocolEncoderDooya instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_dooya_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderDooya.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDooya instance
|
||||
*/
|
||||
void subghz_protocol_encoder_dooya_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDooya instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_dooya_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDooya instance
|
||||
*/
|
||||
void subghz_protocol_encoder_dooya_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderDooya instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_dooya_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderDooya.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderDooya* pointer to a SubGhzProtocolDecoderDooya instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_dooya_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderDooya.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDooya instance
|
||||
*/
|
||||
void subghz_protocol_decoder_dooya_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderDooya.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDooya instance
|
||||
*/
|
||||
void subghz_protocol_decoder_dooya_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDooya instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_dooya_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDooya instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_dooya_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderDooya.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDooya instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_dooya_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderDooya.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDooya instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_dooya_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderDooya instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_dooya_get_string(void* context, FuriString* output);
|
||||
512
applications/main/subghz/protocols/faac_slh.c
Normal file
512
applications/main/subghz/protocols/faac_slh.c
Normal file
@@ -0,0 +1,512 @@
|
||||
#include "faac_slh.h"
|
||||
#include "../subghz_keystore.h"
|
||||
#include <m-array.h>
|
||||
#include "keeloq_common.h"
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolFaacSLH"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_faac_slh_const = {
|
||||
.te_short = 255,
|
||||
.te_long = 595,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 64,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderFaacSLH {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
SubGhzKeystore* keystore;
|
||||
const char* manufacture_name;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderFaacSLH {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
SubGhzKeystore* keystore;
|
||||
const char* manufacture_name;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
FaacSLHDecoderStepReset = 0,
|
||||
FaacSLHDecoderStepFoundPreambula,
|
||||
FaacSLHDecoderStepSaveDuration,
|
||||
FaacSLHDecoderStepCheckDuration,
|
||||
} FaacSLHDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder = {
|
||||
.alloc = subghz_protocol_decoder_faac_slh_alloc,
|
||||
.free = subghz_protocol_decoder_faac_slh_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_faac_slh_feed,
|
||||
.reset = subghz_protocol_decoder_faac_slh_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_faac_slh_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_faac_slh_serialize,
|
||||
.deserialize = subghz_protocol_decoder_faac_slh_deserialize,
|
||||
.get_string = subghz_protocol_decoder_faac_slh_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder = {
|
||||
.alloc = subghz_protocol_encoder_faac_slh_alloc,
|
||||
.free = subghz_protocol_encoder_faac_slh_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_faac_slh_deserialize,
|
||||
.stop = subghz_protocol_encoder_faac_slh_stop,
|
||||
.yield = subghz_protocol_encoder_faac_slh_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_faac_slh = {
|
||||
.name = SUBGHZ_PROTOCOL_FAAC_SLH_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_faac_slh_decoder,
|
||||
.encoder = &subghz_protocol_faac_slh_encoder,
|
||||
};
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
* @param keystore Pointer to a SubGhzKeystore* instance
|
||||
* @param manufacture_name
|
||||
*/
|
||||
static void subghz_protocol_faac_slh_check_remote_controller(
|
||||
SubGhzBlockGeneric* instance,
|
||||
SubGhzKeystore* keystore,
|
||||
const char** manufacture_name);
|
||||
|
||||
void* subghz_protocol_encoder_faac_slh_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolEncoderFaacSLH* instance = malloc(sizeof(SubGhzProtocolEncoderFaacSLH));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_faac_slh;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->keystore = subghz_environment_get_keystore(environment);
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 256;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_faac_slh_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderFaacSLH* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static bool subghz_protocol_faac_slh_gen_data(SubGhzProtocolEncoderFaacSLH* instance) {
|
||||
instance->generic.cnt++;
|
||||
uint32_t fix = instance->generic.serial << 4 | instance->generic.btn;
|
||||
uint32_t hop = 0;
|
||||
uint32_t decrypt = 0;
|
||||
uint64_t man = 0;
|
||||
int res = 0;
|
||||
char fixx[8] = {};
|
||||
int shiftby = 32;
|
||||
for(int i = 0; i < 8; i++) {
|
||||
fixx[i] = (fix >> (shiftby -= 4)) & 0xF;
|
||||
}
|
||||
if((instance->generic.cnt % 2) == 0) {
|
||||
decrypt = fixx[6] << 28 | fixx[7] << 24 | fixx[5] << 20 |
|
||||
(instance->generic.cnt & 0xFFFFF);
|
||||
} else {
|
||||
decrypt = fixx[2] << 28 | fixx[3] << 24 | fixx[4] << 20 |
|
||||
(instance->generic.cnt & 0xFFFFF);
|
||||
}
|
||||
for
|
||||
M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
|
||||
res = strcmp(furi_string_get_cstr(manufacture_code->name), instance->manufacture_name);
|
||||
if(res == 0) {
|
||||
switch(manufacture_code->type) {
|
||||
case KEELOQ_LEARNING_FAAC:
|
||||
//FAAC Learning
|
||||
man = subghz_protocol_keeloq_common_faac_learning(
|
||||
instance->generic.seed, manufacture_code->key);
|
||||
hop = subghz_protocol_keeloq_common_encrypt(decrypt, man);
|
||||
break;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(hop) {
|
||||
instance->generic.data = (uint64_t)fix << 32 | hop;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_faac_slh_create_data(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
uint32_t serial,
|
||||
uint8_t btn,
|
||||
uint32_t cnt,
|
||||
uint32_t seed,
|
||||
const char* manufacture_name,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
// roguemaster don't steal!!!
|
||||
SubGhzProtocolEncoderFaacSLH* instance = context;
|
||||
instance->generic.serial = serial;
|
||||
instance->generic.btn = btn;
|
||||
instance->generic.cnt = (cnt & 0xFFFFF);
|
||||
instance->generic.seed = seed;
|
||||
instance->manufacture_name = manufacture_name;
|
||||
instance->generic.data_count_bit = 64;
|
||||
bool res = subghz_protocol_faac_slh_gen_data(instance);
|
||||
if(res) {
|
||||
res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderFaacSLH instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_faac_slh_get_upload(SubGhzProtocolEncoderFaacSLH* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
subghz_protocol_faac_slh_gen_data(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = 2 + (instance->generic.data_count_bit * 2);
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_long * 2);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_long * 2);
|
||||
|
||||
//Send key data
|
||||
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_faac_slh_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_faac_slh_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_faac_slh_const.te_long);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderFaacSLH* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
uint8_t seed_data[sizeof(uint32_t)] = {0};
|
||||
for(size_t i = 0; i < sizeof(uint32_t); i++) {
|
||||
seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF;
|
||||
}
|
||||
if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) {
|
||||
FURI_LOG_E(TAG, "Missing Seed");
|
||||
break;
|
||||
}
|
||||
instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 |
|
||||
seed_data[3];
|
||||
|
||||
subghz_protocol_faac_slh_check_remote_controller(
|
||||
&instance->generic, instance->keystore, &instance->manufacture_name);
|
||||
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
subghz_protocol_encoder_faac_slh_get_upload(instance);
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
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");
|
||||
break;
|
||||
}
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_faac_slh_stop(void* context) {
|
||||
SubGhzProtocolEncoderFaacSLH* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_faac_slh_yield(void* context) {
|
||||
SubGhzProtocolEncoderFaacSLH* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_faac_slh_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderFaacSLH* instance = malloc(sizeof(SubGhzProtocolDecoderFaacSLH));
|
||||
instance->base.protocol = &subghz_protocol_faac_slh;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->keystore = subghz_environment_get_keystore(environment);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_faac_slh_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderFaacSLH* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_faac_slh_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderFaacSLH* instance = context;
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderFaacSLH* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case FaacSLHDecoderStepReset:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) <
|
||||
subghz_protocol_faac_slh_const.te_delta * 3)) {
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula;
|
||||
}
|
||||
break;
|
||||
case FaacSLHDecoderStepFoundPreambula:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long * 2) <
|
||||
subghz_protocol_faac_slh_const.te_delta * 3)) {
|
||||
//Found Preambula
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case FaacSLHDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_faac_slh_const.te_short * 3 +
|
||||
subghz_protocol_faac_slh_const.te_delta)) {
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepFoundPreambula;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_faac_slh_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;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepCheckDuration;
|
||||
}
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case FaacSLHDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_short) <
|
||||
subghz_protocol_faac_slh_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_long) <
|
||||
subghz_protocol_faac_slh_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_faac_slh_const.te_long) <
|
||||
subghz_protocol_faac_slh_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_faac_slh_const.te_short) <
|
||||
subghz_protocol_faac_slh_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = FaacSLHDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
* @param keystore Pointer to a SubGhzKeystore* instance
|
||||
* @param manifacture_name Manufacturer name
|
||||
*/
|
||||
static void subghz_protocol_faac_slh_check_remote_controller(
|
||||
SubGhzBlockGeneric* instance,
|
||||
SubGhzKeystore* keystore,
|
||||
const char** manufacture_name) {
|
||||
uint32_t code_fix = instance->data >> 32;
|
||||
uint32_t code_hop = instance->data & 0xFFFFFFFF;
|
||||
instance->serial = code_fix >> 4;
|
||||
instance->btn = code_fix & 0xF;
|
||||
uint32_t decrypt = 0;
|
||||
uint64_t man;
|
||||
|
||||
for
|
||||
M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) {
|
||||
switch(manufacture_code->type) {
|
||||
case KEELOQ_LEARNING_FAAC:
|
||||
// FAAC Learning
|
||||
man = subghz_protocol_keeloq_common_faac_learning(
|
||||
instance->seed, manufacture_code->key);
|
||||
decrypt = subghz_protocol_keeloq_common_decrypt(code_hop, man);
|
||||
*manufacture_name = furi_string_get_cstr(manufacture_code->name);
|
||||
break;
|
||||
}
|
||||
}
|
||||
instance->cnt = decrypt & 0xFFFFF;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderFaacSLH* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_faac_slh_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderFaacSLH* instance = context;
|
||||
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
|
||||
uint8_t seed_data[sizeof(uint32_t)] = {0};
|
||||
for(size_t i = 0; i < sizeof(uint32_t); i++) {
|
||||
seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF;
|
||||
}
|
||||
if(res && !flipper_format_write_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Seed");
|
||||
res = false;
|
||||
}
|
||||
instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 |
|
||||
seed_data[3];
|
||||
|
||||
subghz_protocol_faac_slh_check_remote_controller(
|
||||
&instance->generic, instance->keystore, &instance->manufacture_name);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderFaacSLH* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_faac_slh_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t seed_data[sizeof(uint32_t)] = {0};
|
||||
for(size_t i = 0; i < sizeof(uint32_t); i++) {
|
||||
seed_data[sizeof(uint32_t) - i - 1] = (instance->generic.seed >> i * 8) & 0xFF;
|
||||
}
|
||||
if(!flipper_format_read_hex(flipper_format, "Seed", seed_data, sizeof(uint32_t))) {
|
||||
FURI_LOG_E(TAG, "Missing Seed");
|
||||
break;
|
||||
}
|
||||
instance->generic.seed = seed_data[0] << 24 | seed_data[1] << 16 | seed_data[2] << 8 |
|
||||
seed_data[3];
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderFaacSLH* instance = context;
|
||||
subghz_protocol_faac_slh_check_remote_controller(
|
||||
&instance->generic, instance->keystore, &instance->manufacture_name);
|
||||
uint32_t code_fix = instance->generic.data >> 32;
|
||||
uint32_t code_hop = instance->generic.data & 0xFFFFFFFF;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%lX%08lX\r\n"
|
||||
"Fix:%08lX Cnt:%05lX\r\n"
|
||||
"Hop:%08lX Btn:%X\r\n"
|
||||
"Sn:%07lX Sd:%08lX",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data >> 32),
|
||||
(uint32_t)instance->generic.data,
|
||||
code_fix,
|
||||
instance->generic.cnt,
|
||||
code_hop,
|
||||
instance->generic.btn,
|
||||
instance->generic.serial,
|
||||
instance->generic.seed);
|
||||
}
|
||||
129
applications/main/subghz/protocols/faac_slh.h
Normal file
129
applications/main/subghz/protocols/faac_slh.h
Normal file
@@ -0,0 +1,129 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_FAAC_SLH_NAME "Faac SLH"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderFaacSLH SubGhzProtocolDecoderFaacSLH;
|
||||
typedef struct SubGhzProtocolEncoderFaacSLH SubGhzProtocolEncoderFaacSLH;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_faac_slh_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_faac_slh_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_faac_slh;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderFaacSLH.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderFaacSLH* pointer to a SubGhzProtocolEncoderFaacSLH instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_faac_slh_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderFaacSLH.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance
|
||||
*/
|
||||
void subghz_protocol_encoder_faac_slh_free(void* context);
|
||||
|
||||
/**
|
||||
* Key generation from simple data.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param serial Serial number, 28 bit
|
||||
* @param btn Button number, 4 bit
|
||||
* @param cnt Counter value, 16 bit
|
||||
* @param seed Seed value, 32 bit
|
||||
* @param manufacture_name Name of manufacturer's key
|
||||
* @param preset Modulation, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_faac_slh_create_data(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
uint32_t serial,
|
||||
uint8_t btn,
|
||||
uint32_t cnt,
|
||||
uint32_t seed,
|
||||
const char* manufacture_name,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance
|
||||
*/
|
||||
void subghz_protocol_encoder_faac_slh_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderFaacSLH instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_faac_slh_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderFaacSLH.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderFaacSLH* pointer to a SubGhzProtocolDecoderFaacSLH instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_faac_slh_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderFaacSLH.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance
|
||||
*/
|
||||
void subghz_protocol_decoder_faac_slh_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderFaacSLH.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance
|
||||
*/
|
||||
void subghz_protocol_decoder_faac_slh_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_faac_slh_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_faac_slh_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderFaacSLH.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_faac_slh_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderFaacSLH.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_faac_slh_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderFaacSLH instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_faac_slh_get_string(void* context, FuriString* output);
|
||||
334
applications/main/subghz/protocols/gate_tx.c
Normal file
334
applications/main/subghz/protocols/gate_tx.c
Normal file
@@ -0,0 +1,334 @@
|
||||
#include "gate_tx.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolGateTx"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_gate_tx_const = {
|
||||
.te_short = 350,
|
||||
.te_long = 700,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 24,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderGateTx {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderGateTx {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
GateTXDecoderStepReset = 0,
|
||||
GateTXDecoderStepFoundStartBit,
|
||||
GateTXDecoderStepSaveDuration,
|
||||
GateTXDecoderStepCheckDuration,
|
||||
} GateTXDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder = {
|
||||
.alloc = subghz_protocol_decoder_gate_tx_alloc,
|
||||
.free = subghz_protocol_decoder_gate_tx_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_gate_tx_feed,
|
||||
.reset = subghz_protocol_decoder_gate_tx_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_gate_tx_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_gate_tx_serialize,
|
||||
.deserialize = subghz_protocol_decoder_gate_tx_deserialize,
|
||||
.get_string = subghz_protocol_decoder_gate_tx_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder = {
|
||||
.alloc = subghz_protocol_encoder_gate_tx_alloc,
|
||||
.free = subghz_protocol_encoder_gate_tx_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_gate_tx_deserialize,
|
||||
.stop = subghz_protocol_encoder_gate_tx_stop,
|
||||
.yield = subghz_protocol_encoder_gate_tx_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_gate_tx = {
|
||||
.name = SUBGHZ_PROTOCOL_GATE_TX_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_gate_tx_decoder,
|
||||
.encoder = &subghz_protocol_gate_tx_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderGateTx* instance = malloc(sizeof(SubGhzProtocolEncoderGateTx));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_gate_tx;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_gate_tx_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderGateTx* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderGateTx instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_gate_tx_get_upload(SubGhzProtocolEncoderGateTx* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short * 49);
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long);
|
||||
//Send key data
|
||||
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(false, (uint32_t)subghz_protocol_gate_tx_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_gate_tx_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_gate_tx_const.te_long);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderGateTx* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_gate_tx_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_gate_tx_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_gate_tx_stop(void* context) {
|
||||
SubGhzProtocolEncoderGateTx* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_gate_tx_yield(void* context) {
|
||||
SubGhzProtocolEncoderGateTx* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_gate_tx_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderGateTx* instance = malloc(sizeof(SubGhzProtocolDecoderGateTx));
|
||||
instance->base.protocol = &subghz_protocol_gate_tx;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_gate_tx_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderGateTx* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_gate_tx_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderGateTx* instance = context;
|
||||
instance->decoder.parser_step = GateTXDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderGateTx* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case GateTXDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short * 47) <
|
||||
subghz_protocol_gate_tx_const.te_delta * 47)) {
|
||||
//Found Preambula
|
||||
instance->decoder.parser_step = GateTXDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case GateTXDecoderStepFoundStartBit:
|
||||
if(level && ((DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) <
|
||||
subghz_protocol_gate_tx_const.te_delta * 3))) {
|
||||
//Found start bit
|
||||
instance->decoder.parser_step = GateTXDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = GateTXDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case GateTXDecoderStepSaveDuration:
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_gate_tx_const.te_short * 10 +
|
||||
subghz_protocol_gate_tx_const.te_delta)) {
|
||||
instance->decoder.parser_step = GateTXDecoderStepFoundStartBit;
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_gate_tx_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;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = GateTXDecoderStepCheckDuration;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case GateTXDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_short) <
|
||||
subghz_protocol_gate_tx_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_long) <
|
||||
subghz_protocol_gate_tx_const.te_delta * 3)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = GateTXDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_gate_tx_const.te_long) <
|
||||
subghz_protocol_gate_tx_const.te_delta * 3) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_gate_tx_const.te_short) <
|
||||
subghz_protocol_gate_tx_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = GateTXDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = GateTXDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = GateTXDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_gate_tx_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
uint32_t code_found_reverse =
|
||||
subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);
|
||||
|
||||
instance->serial = (code_found_reverse & 0xFF) << 12 |
|
||||
((code_found_reverse >> 8) & 0xFF) << 4 |
|
||||
((code_found_reverse >> 20) & 0x0F);
|
||||
instance->btn = ((code_found_reverse >> 16) & 0x0F);
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderGateTx* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_gate_tx_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderGateTx* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderGateTx* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_gate_tx_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderGateTx* instance = context;
|
||||
subghz_protocol_gate_tx_check_remote_controller(&instance->generic);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%06lX\r\n"
|
||||
"Sn:%05lX Btn:%X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFF),
|
||||
instance->generic.serial,
|
||||
instance->generic.btn);
|
||||
}
|
||||
107
applications/main/subghz/protocols/gate_tx.h
Normal file
107
applications/main/subghz/protocols/gate_tx.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_GATE_TX_NAME "GateTX"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderGateTx SubGhzProtocolDecoderGateTx;
|
||||
typedef struct SubGhzProtocolEncoderGateTx SubGhzProtocolEncoderGateTx;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_gate_tx_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_gate_tx_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_gate_tx;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderGateTx.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderGateTx* pointer to a SubGhzProtocolEncoderGateTx instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_gate_tx_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderGateTx.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderGateTx instance
|
||||
*/
|
||||
void subghz_protocol_encoder_gate_tx_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderGateTx instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderGateTx instance
|
||||
*/
|
||||
void subghz_protocol_encoder_gate_tx_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderGateTx instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_gate_tx_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderGateTx.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderGateTx* pointer to a SubGhzProtocolDecoderGateTx instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_gate_tx_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderGateTx.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderGateTx instance
|
||||
*/
|
||||
void subghz_protocol_decoder_gate_tx_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderGateTx.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderGateTx instance
|
||||
*/
|
||||
void subghz_protocol_decoder_gate_tx_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderGateTx instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_gate_tx_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderGateTx instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_gate_tx_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderGateTx.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderGateTx instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_gate_tx_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderGateTx.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderGateTx instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_gate_tx_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderGateTx instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_gate_tx_get_string(void* context, FuriString* output);
|
||||
374
applications/main/subghz/protocols/holtek.c
Normal file
374
applications/main/subghz/protocols/holtek.c
Normal file
@@ -0,0 +1,374 @@
|
||||
#include "holtek.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://pdf1.alldatasheet.com/datasheet-pdf/view/82103/HOLTEK/HT640.html
|
||||
* https://fccid.io/OJM-CMD-HHLR-XXXA
|
||||
*
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocolHoltek"
|
||||
|
||||
#define HOLTEK_HEADER_MASK 0xF000000000
|
||||
#define HOLTEK_HEADER 0x5000000000
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_holtek_const = {
|
||||
.te_short = 430,
|
||||
.te_long = 870,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 40,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderHoltek {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderHoltek {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
HoltekDecoderStepReset = 0,
|
||||
HoltekDecoderStepFoundStartBit,
|
||||
HoltekDecoderStepSaveDuration,
|
||||
HoltekDecoderStepCheckDuration,
|
||||
} HoltekDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_holtek_decoder = {
|
||||
.alloc = subghz_protocol_decoder_holtek_alloc,
|
||||
.free = subghz_protocol_decoder_holtek_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_holtek_feed,
|
||||
.reset = subghz_protocol_decoder_holtek_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_holtek_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_holtek_serialize,
|
||||
.deserialize = subghz_protocol_decoder_holtek_deserialize,
|
||||
.get_string = subghz_protocol_decoder_holtek_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_holtek_encoder = {
|
||||
.alloc = subghz_protocol_encoder_holtek_alloc,
|
||||
.free = subghz_protocol_encoder_holtek_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_holtek_deserialize,
|
||||
.stop = subghz_protocol_encoder_holtek_stop,
|
||||
.yield = subghz_protocol_encoder_holtek_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_holtek = {
|
||||
.name = SUBGHZ_PROTOCOL_HOLTEK_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
|
||||
SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_holtek_decoder,
|
||||
.encoder = &subghz_protocol_holtek_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderHoltek* instance = malloc(sizeof(SubGhzProtocolEncoderHoltek));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_holtek;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_holtek_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHoltek* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderHoltek instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_holtek_get_upload(SubGhzProtocolEncoderHoltek* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short * 36);
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short);
|
||||
//Send key data
|
||||
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(false, (uint32_t)subghz_protocol_holtek_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_holtek_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_holtek_const.te_long);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHoltek* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_holtek_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_holtek_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_holtek_stop(void* context) {
|
||||
SubGhzProtocolEncoderHoltek* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_holtek_yield(void* context) {
|
||||
SubGhzProtocolEncoderHoltek* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_holtek_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderHoltek* instance = malloc(sizeof(SubGhzProtocolDecoderHoltek));
|
||||
instance->base.protocol = &subghz_protocol_holtek;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek* instance = context;
|
||||
instance->decoder.parser_step = HoltekDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case HoltekDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short * 36) <
|
||||
subghz_protocol_holtek_const.te_delta * 36)) {
|
||||
//Found Preambula
|
||||
instance->decoder.parser_step = HoltekDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case HoltekDecoderStepFoundStartBit:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) <
|
||||
subghz_protocol_holtek_const.te_delta)) {
|
||||
//Found StartBit
|
||||
instance->decoder.parser_step = HoltekDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = HoltekDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case HoltekDecoderStepSaveDuration:
|
||||
//save duration
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_holtek_const.te_short * 10 +
|
||||
subghz_protocol_holtek_const.te_delta)) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_holtek_const.min_count_bit_for_found) {
|
||||
if((instance->decoder.decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) {
|
||||
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->decoder.parser_step = HoltekDecoderStepFoundStartBit;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
|
||||
instance->decoder.parser_step = HoltekDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = HoltekDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case HoltekDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_short) <
|
||||
subghz_protocol_holtek_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_holtek_const.te_long) <
|
||||
subghz_protocol_holtek_const.te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = HoltekDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_holtek_const.te_long) <
|
||||
subghz_protocol_holtek_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_holtek_const.te_short) <
|
||||
subghz_protocol_holtek_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = HoltekDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = HoltekDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = HoltekDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_holtek_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
if((instance->data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) {
|
||||
instance->serial =
|
||||
subghz_protocol_blocks_reverse_key((instance->data >> 16) & 0xFFFFF, 20);
|
||||
uint16_t btn = instance->data & 0xFFFF;
|
||||
if((btn & 0xf) != 0xA) {
|
||||
instance->btn = 0x1 << 4 | (btn & 0xF);
|
||||
} else if(((btn >> 4) & 0xF) != 0xA) {
|
||||
instance->btn = 0x2 << 4 | ((btn >> 4) & 0xF);
|
||||
} else if(((btn >> 8) & 0xF) != 0xA) {
|
||||
instance->btn = 0x3 << 4 | ((btn >> 8) & 0xF);
|
||||
} else if(((btn >> 12) & 0xF) != 0xA) {
|
||||
instance->btn = 0x4 << 4 | ((btn >> 12) & 0xF);
|
||||
} else {
|
||||
instance->btn = 0;
|
||||
}
|
||||
} else {
|
||||
instance->serial = 0;
|
||||
instance->btn = 0;
|
||||
instance->cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_holtek_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_holtek_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek* instance = context;
|
||||
subghz_protocol_holtek_check_remote_controller(&instance->generic);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"Sn:0x%05lX Btn:%X ",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF),
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
|
||||
instance->generic.serial,
|
||||
instance->generic.btn >> 4);
|
||||
|
||||
if((instance->generic.btn & 0xF) == 0xE) {
|
||||
furi_string_cat_printf(output, "ON\r\n");
|
||||
} else if(((instance->generic.btn & 0xF) == 0xB)) {
|
||||
furi_string_cat_printf(output, "OFF\r\n");
|
||||
}
|
||||
}
|
||||
107
applications/main/subghz/protocols/holtek.h
Normal file
107
applications/main/subghz/protocols/holtek.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_HOLTEK_NAME "Holtek"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderHoltek SubGhzProtocolDecoderHoltek;
|
||||
typedef struct SubGhzProtocolEncoderHoltek SubGhzProtocolEncoderHoltek;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_holtek_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_holtek_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_holtek;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderHoltek.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderHoltek* pointer to a SubGhzProtocolEncoderHoltek instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_holtek_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderHoltek.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek instance
|
||||
*/
|
||||
void subghz_protocol_encoder_holtek_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_holtek_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek instance
|
||||
*/
|
||||
void subghz_protocol_encoder_holtek_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_holtek_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderHoltek.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderHoltek* pointer to a SubGhzProtocolDecoderHoltek instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_holtek_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderHoltek.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek instance
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderHoltek.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek instance
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_holtek_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderHoltek.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_holtek_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderHoltek.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_holtek_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_get_string(void* context, FuriString* output);
|
||||
400
applications/main/subghz/protocols/holtek_ht12x.c
Normal file
400
applications/main/subghz/protocols/holtek_ht12x.c
Normal file
@@ -0,0 +1,400 @@
|
||||
#include "holtek_ht12x.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://www.holtek.com/documents/10179/116711/HT12A_Ev130.pdf
|
||||
*
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocolHoltek_HT12X"
|
||||
|
||||
#define DIP_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define CNT_TO_DIP(dip) \
|
||||
(dip & 0x0080 ? '0' : '1'), (dip & 0x0040 ? '0' : '1'), (dip & 0x0020 ? '0' : '1'), \
|
||||
(dip & 0x0010 ? '0' : '1'), (dip & 0x0008 ? '0' : '1'), (dip & 0x0004 ? '0' : '1'), \
|
||||
(dip & 0x0002 ? '0' : '1'), (dip & 0x0001 ? '0' : '1')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_holtek_th12x_const = {
|
||||
.te_short = 320,
|
||||
.te_long = 640,
|
||||
.te_delta = 200,
|
||||
.min_count_bit_for_found = 12,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderHoltek_HT12X {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint32_t te;
|
||||
uint32_t last_data;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderHoltek_HT12X {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint32_t te;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Holtek_HT12XDecoderStepReset = 0,
|
||||
Holtek_HT12XDecoderStepFoundStartBit,
|
||||
Holtek_HT12XDecoderStepSaveDuration,
|
||||
Holtek_HT12XDecoderStepCheckDuration,
|
||||
} Holtek_HT12XDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder = {
|
||||
.alloc = subghz_protocol_decoder_holtek_th12x_alloc,
|
||||
.free = subghz_protocol_decoder_holtek_th12x_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_holtek_th12x_feed,
|
||||
.reset = subghz_protocol_decoder_holtek_th12x_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_holtek_th12x_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_holtek_th12x_serialize,
|
||||
.deserialize = subghz_protocol_decoder_holtek_th12x_deserialize,
|
||||
.get_string = subghz_protocol_decoder_holtek_th12x_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder = {
|
||||
.alloc = subghz_protocol_encoder_holtek_th12x_alloc,
|
||||
.free = subghz_protocol_encoder_holtek_th12x_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_holtek_th12x_deserialize,
|
||||
.stop = subghz_protocol_encoder_holtek_th12x_stop,
|
||||
.yield = subghz_protocol_encoder_holtek_th12x_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_holtek_th12x = {
|
||||
.name = SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_holtek_th12x_decoder,
|
||||
.encoder = &subghz_protocol_holtek_th12x_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderHoltek_HT12X* instance =
|
||||
malloc(sizeof(SubGhzProtocolEncoderHoltek_HT12X));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_holtek_th12x;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_holtek_th12x_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHoltek_HT12X* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_holtek_th12x_get_upload(SubGhzProtocolEncoderHoltek_HT12X* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send header
|
||||
instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 36);
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);
|
||||
//Send key data
|
||||
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(false, (uint32_t)instance->te * 2);
|
||||
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)instance->te * 2);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHoltek_HT12X* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing TE");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_holtek_th12x_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_holtek_th12x_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_holtek_th12x_stop(void* context) {
|
||||
SubGhzProtocolEncoderHoltek_HT12X* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context) {
|
||||
SubGhzProtocolEncoderHoltek_HT12X* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance =
|
||||
malloc(sizeof(SubGhzProtocolDecoderHoltek_HT12X));
|
||||
instance->base.protocol = &subghz_protocol_holtek_th12x;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_th12x_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_th12x_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case Holtek_HT12XDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short * 36) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta * 36)) {
|
||||
//Found Preambula
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case Holtek_HT12XDecoderStepFoundStartBit:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta)) {
|
||||
//Found StartBit
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->te = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case Holtek_HT12XDecoderStepSaveDuration:
|
||||
//save duration
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_holtek_th12x_const.te_short * 10 +
|
||||
subghz_protocol_holtek_th12x_const.te_delta)) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_holtek_th12x_const.min_count_bit_for_found) {
|
||||
if((instance->last_data == instance->decoder.decode_data) &&
|
||||
instance->last_data) {
|
||||
instance->te /= (instance->decoder.decode_count_bit * 3 + 1);
|
||||
|
||||
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->last_data = instance->decoder.decode_data;
|
||||
}
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->te = 0;
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepFoundStartBit;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->te += duration;
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case Holtek_HT12XDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
instance->te += duration;
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_long) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_short) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_holtek_th12x_const.te_short) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_holtek_th12x_const.te_long) <
|
||||
subghz_protocol_holtek_th12x_const.te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = Holtek_HT12XDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_holtek_th12x_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
instance->btn = instance->data & 0x0F;
|
||||
instance->cnt = (instance->data >> 4) & 0xFF;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_holtek_th12x_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add TE");
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_holtek_th12x_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing TE");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void subghz_protocol_holtek_th12x_event_serialize(uint8_t event, FuriString* output) {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s%s%s%s\r\n",
|
||||
(((event >> 3) & 0x1) == 0x0 ? "B1 " : ""),
|
||||
(((event >> 2) & 0x1) == 0x0 ? "B2 " : ""),
|
||||
(((event >> 1) & 0x1) == 0x0 ? "B3 " : ""),
|
||||
(((event >> 0) & 0x1) == 0x0 ? "B4 " : ""));
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoltek_HT12X* instance = context;
|
||||
subghz_protocol_holtek_th12x_check_remote_controller(&instance->generic);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Key:0x%03lX\r\n"
|
||||
"Btn: ",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data & 0xFFF));
|
||||
subghz_protocol_holtek_th12x_event_serialize(instance->generic.btn, output);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"DIP:" DIP_PATTERN "\r\n"
|
||||
"Te:%luus\r\n",
|
||||
CNT_TO_DIP(instance->generic.cnt),
|
||||
instance->te);
|
||||
}
|
||||
107
applications/main/subghz/protocols/holtek_ht12x.h
Normal file
107
applications/main/subghz/protocols/holtek_ht12x.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_HOLTEK_HT12X_NAME "Holtek_HT12X"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderHoltek_HT12X SubGhzProtocolDecoderHoltek_HT12X;
|
||||
typedef struct SubGhzProtocolEncoderHoltek_HT12X SubGhzProtocolEncoderHoltek_HT12X;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_holtek_th12x_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_holtek_th12x_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_holtek_th12x;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderHoltek_HT12X.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderHoltek_HT12X* pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_holtek_th12x_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
*/
|
||||
void subghz_protocol_encoder_holtek_th12x_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
*/
|
||||
void subghz_protocol_encoder_holtek_th12x_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoltek_HT12X instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_holtek_th12x_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderHoltek_HT12X* pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_holtek_th12x_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_th12x_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_th12x_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_th12x_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_holtek_th12x_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_holtek_th12x_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderHoltek_HT12X.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_holtek_th12x_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoltek_HT12X instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_holtek_th12x_get_string(void* context, FuriString* output);
|
||||
399
applications/main/subghz/protocols/honeywell_wdb.c
Normal file
399
applications/main/subghz/protocols/honeywell_wdb.c
Normal file
@@ -0,0 +1,399 @@
|
||||
#include "honeywell_wdb.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolHoneywellWDB"
|
||||
|
||||
/*
|
||||
*
|
||||
* https://github.com/klohner/honeywell-wireless-doorbell
|
||||
*
|
||||
*/
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_honeywell_wdb_const = {
|
||||
.te_short = 160,
|
||||
.te_long = 320,
|
||||
.te_delta = 60,
|
||||
.min_count_bit_for_found = 48,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderHoneywell_WDB {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
const char* device_type;
|
||||
const char* alert;
|
||||
uint8_t secret_knock;
|
||||
uint8_t relay;
|
||||
uint8_t lowbat;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderHoneywell_WDB {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Honeywell_WDBDecoderStepReset = 0,
|
||||
Honeywell_WDBDecoderStepFoundStartBit,
|
||||
Honeywell_WDBDecoderStepSaveDuration,
|
||||
Honeywell_WDBDecoderStepCheckDuration,
|
||||
} Honeywell_WDBDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder = {
|
||||
.alloc = subghz_protocol_decoder_honeywell_wdb_alloc,
|
||||
.free = subghz_protocol_decoder_honeywell_wdb_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_honeywell_wdb_feed,
|
||||
.reset = subghz_protocol_decoder_honeywell_wdb_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_honeywell_wdb_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_honeywell_wdb_serialize,
|
||||
.deserialize = subghz_protocol_decoder_honeywell_wdb_deserialize,
|
||||
.get_string = subghz_protocol_decoder_honeywell_wdb_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder = {
|
||||
.alloc = subghz_protocol_encoder_honeywell_wdb_alloc,
|
||||
.free = subghz_protocol_encoder_honeywell_wdb_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_honeywell_wdb_deserialize,
|
||||
.stop = subghz_protocol_encoder_honeywell_wdb_stop,
|
||||
.yield = subghz_protocol_encoder_honeywell_wdb_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_honeywell_wdb = {
|
||||
.name = SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_honeywell_wdb_decoder,
|
||||
.encoder = &subghz_protocol_honeywell_wdb_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderHoneywell_WDB* instance =
|
||||
malloc(sizeof(SubGhzProtocolEncoderHoneywell_WDB));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_honeywell_wdb;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_honeywell_wdb_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHoneywell_WDB* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_honeywell_wdb_get_upload(
|
||||
SubGhzProtocolEncoderHoneywell_WDB* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3);
|
||||
//Send key data
|
||||
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_honeywell_wdb_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_honeywell_wdb_const.te_long);
|
||||
}
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_honeywell_wdb_const.te_short * 3);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_honeywell_wdb_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHoneywell_WDB* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_honeywell_wdb_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_honeywell_wdb_stop(void* context) {
|
||||
SubGhzProtocolEncoderHoneywell_WDB* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context) {
|
||||
SubGhzProtocolEncoderHoneywell_WDB* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_honeywell_wdb_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderHoneywell_WDB* instance =
|
||||
malloc(sizeof(SubGhzProtocolDecoderHoneywell_WDB));
|
||||
instance->base.protocol = &subghz_protocol_honeywell_wdb;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honeywell_wdb_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoneywell_WDB* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honeywell_wdb_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoneywell_WDB* instance = context;
|
||||
instance->decoder.parser_step = Honeywell_WDBDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoneywell_WDB* instance = context;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case Honeywell_WDBDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) <
|
||||
subghz_protocol_honeywell_wdb_const.te_delta)) {
|
||||
//Found header Honeywell_WDB
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration;
|
||||
}
|
||||
break;
|
||||
case Honeywell_WDBDecoderStepSaveDuration:
|
||||
if(level) { //save interval
|
||||
if(DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short * 3) <
|
||||
subghz_protocol_honeywell_wdb_const.te_delta) {
|
||||
if((instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) &&
|
||||
((instance->decoder.decode_data & 0x01) ==
|
||||
subghz_protocol_blocks_get_parity(
|
||||
instance->decoder.decode_data >> 1,
|
||||
subghz_protocol_honeywell_wdb_const.min_count_bit_for_found - 1))) {
|
||||
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.parser_step = Honeywell_WDBDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = Honeywell_WDBDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = Honeywell_WDBDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case Honeywell_WDBDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_short) <
|
||||
subghz_protocol_honeywell_wdb_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_long) <
|
||||
subghz_protocol_honeywell_wdb_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_honeywell_wdb_const.te_long) <
|
||||
subghz_protocol_honeywell_wdb_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_honeywell_wdb_const.te_short) <
|
||||
subghz_protocol_honeywell_wdb_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = Honeywell_WDBDecoderStepSaveDuration;
|
||||
} else
|
||||
instance->decoder.parser_step = Honeywell_WDBDecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.parser_step = Honeywell_WDBDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzProtocolDecoderHoneywell_WDB* instance
|
||||
*/
|
||||
static void subghz_protocol_honeywell_wdb_check_remote_controller(
|
||||
SubGhzProtocolDecoderHoneywell_WDB* instance) {
|
||||
/*
|
||||
*
|
||||
* Frame bits used in Honeywell RCWL300A, RCWL330A, Series 3, 5, 9 and all Decor Series Wireless Chimes
|
||||
* 0000 0000 1111 1111 2222 2222 3333 3333 4444 4444 5555 5555
|
||||
* 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210 7654 3210
|
||||
* XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XXXX XX.. XXX. .... KEY DATA (any change and receiver doesn't seem to recognize signal)
|
||||
* XXXX XXXX XXXX XXXX XXXX .... .... .... .... .... .... .... KEY ID (different for each transmitter)
|
||||
* .... .... .... .... .... 0000 00.. 0000 0000 00.. 000. .... KEY UNKNOWN 0 (always 0 in devices I've tested)
|
||||
* .... .... .... .... .... .... ..XX .... .... .... .... .... DEVICE TYPE (10 = doorbell, 01 = PIR Motion sensor)
|
||||
* .... .... .... .... .... .... .... .... .... ..XX ...X XXX. FLAG DATA (may be modified for possible effects on receiver)
|
||||
* .... .... .... .... .... .... .... .... .... ..XX .... .... ALERT (00 = normal, 01 or 10 = right-left halo light pattern, 11 = full volume alarm)
|
||||
* .... .... .... .... .... .... .... .... .... .... ...X .... SECRET KNOCK (0 = default, 1 if doorbell is pressed 3x rapidly)
|
||||
* .... .... .... .... .... .... .... .... .... .... .... X... RELAY (1 if signal is a retransmission of a received transmission, only some models)
|
||||
* .... .... .... .... .... .... .... .... .... .... .... .X.. FLAG UNKNOWN (0 = default, but 1 is accepted and I don't observe any effects)
|
||||
* .... .... .... .... .... .... .... .... .... .... .... ..X. LOWBAT (1 if battery is low, receiver gives low battery alert)
|
||||
* .... .... .... .... .... .... .... .... .... .... .... ...X PARITY (LSB of count of set bits in previous 47 bits)
|
||||
*
|
||||
*/
|
||||
|
||||
instance->generic.serial = (instance->generic.data >> 28) & 0xFFFFF;
|
||||
switch((instance->generic.data >> 20) & 0x3) {
|
||||
case 0x02:
|
||||
instance->device_type = "Doorbell";
|
||||
break;
|
||||
case 0x01:
|
||||
instance->device_type = "PIR-Motion";
|
||||
break;
|
||||
default:
|
||||
instance->device_type = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
switch((instance->generic.data >> 16) & 0x3) {
|
||||
case 0x00:
|
||||
instance->alert = "Normal";
|
||||
break;
|
||||
case 0x01:
|
||||
case 0x02:
|
||||
instance->alert = "High";
|
||||
break;
|
||||
case 0x03:
|
||||
instance->alert = "Full";
|
||||
break;
|
||||
default:
|
||||
instance->alert = "Unknown";
|
||||
break;
|
||||
}
|
||||
|
||||
instance->secret_knock = (uint8_t)((instance->generic.data >> 4) & 0x1);
|
||||
instance->relay = (uint8_t)((instance->generic.data >> 3) & 0x1);
|
||||
instance->lowbat = (uint8_t)((instance->generic.data >> 1) & 0x1);
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoneywell_WDB* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_honeywell_wdb_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoneywell_WDB* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_honeywell_wdb_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoneywell_WDB* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_honeywell_wdb_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHoneywell_WDB* instance = context;
|
||||
subghz_protocol_honeywell_wdb_check_remote_controller(instance);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"Sn:0x%05lX\r\n"
|
||||
"DT:%s Al:%s\r\n"
|
||||
"SK:%01X R:%01X LBat:%01X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)((instance->generic.data >> 32) & 0xFFFFFFFF),
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
|
||||
instance->generic.serial,
|
||||
instance->device_type,
|
||||
instance->alert,
|
||||
instance->secret_knock,
|
||||
instance->relay,
|
||||
instance->lowbat);
|
||||
}
|
||||
111
applications/main/subghz/protocols/honeywell_wdb.h
Normal file
111
applications/main/subghz/protocols/honeywell_wdb.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_HONEYWELL_WDB_NAME "Honeywell"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderHoneywell_WDB SubGhzProtocolDecoderHoneywell_WDB;
|
||||
typedef struct SubGhzProtocolEncoderHoneywell_WDB SubGhzProtocolEncoderHoneywell_WDB;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_honeywell_wdb_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_honeywell_wdb_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_honeywell_wdb;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderHoneywell_WDB.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderHoneywell_WDB* pointer to a SubGhzProtocolEncoderHoneywell_WDB instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_honeywell_wdb_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderHoneywell_WDB.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance
|
||||
*/
|
||||
void subghz_protocol_encoder_honeywell_wdb_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_honeywell_wdb_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance
|
||||
*/
|
||||
void subghz_protocol_encoder_honeywell_wdb_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHoneywell_WDB instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_honeywell_wdb_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderHoneywell_WDB.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderHoneywell_WDB* pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_honeywell_wdb_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderHoneywell_WDB.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
||||
*/
|
||||
void subghz_protocol_decoder_honeywell_wdb_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderHoneywell_WDB.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
||||
*/
|
||||
void subghz_protocol_decoder_honeywell_wdb_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_honeywell_wdb_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_honeywell_wdb_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderHoneywell_WDB.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_honeywell_wdb_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderHoneywell_WDB.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_honeywell_wdb_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHoneywell_WDB instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_honeywell_wdb_get_string(void* context, FuriString* output);
|
||||
341
applications/main/subghz/protocols/hormann.c
Normal file
341
applications/main/subghz/protocols/hormann.c
Normal file
@@ -0,0 +1,341 @@
|
||||
#include "hormann.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolHormannHSM"
|
||||
|
||||
#define HORMANN_HSM_PATTERN 0xFF000000003
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_hormann_const = {
|
||||
.te_short = 500,
|
||||
.te_long = 1000,
|
||||
.te_delta = 200,
|
||||
.min_count_bit_for_found = 44,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderHormann {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderHormann {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
HormannDecoderStepReset = 0,
|
||||
HormannDecoderStepFoundStartHeader,
|
||||
HormannDecoderStepFoundHeader,
|
||||
HormannDecoderStepFoundStartBit,
|
||||
HormannDecoderStepSaveDuration,
|
||||
HormannDecoderStepCheckDuration,
|
||||
} HormannDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_hormann_decoder = {
|
||||
.alloc = subghz_protocol_decoder_hormann_alloc,
|
||||
.free = subghz_protocol_decoder_hormann_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_hormann_feed,
|
||||
.reset = subghz_protocol_decoder_hormann_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_hormann_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_hormann_serialize,
|
||||
.deserialize = subghz_protocol_decoder_hormann_deserialize,
|
||||
.get_string = subghz_protocol_decoder_hormann_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_hormann_encoder = {
|
||||
.alloc = subghz_protocol_encoder_hormann_alloc,
|
||||
.free = subghz_protocol_encoder_hormann_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_hormann_deserialize,
|
||||
.stop = subghz_protocol_encoder_hormann_stop,
|
||||
.yield = subghz_protocol_encoder_hormann_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_hormann = {
|
||||
.name = SUBGHZ_PROTOCOL_HORMANN_HSM_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_hormann_decoder,
|
||||
.encoder = &subghz_protocol_hormann_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderHormann* instance = malloc(sizeof(SubGhzProtocolEncoderHormann));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_hormann;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 2048;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_hormann_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHormann* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderHormann instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_hormann_get_upload(SubGhzProtocolEncoderHormann* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2 + 2) * 20 + 1;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
instance->encoder.repeat = 10; //original remote does 10 repeats
|
||||
|
||||
for(size_t repeat = 0; repeat < 20; repeat++) {
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short);
|
||||
//Send key data
|
||||
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_hormann_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_hormann_const.te_long);
|
||||
}
|
||||
}
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_hormann_const.te_short * 24);
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHormann* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_hormann_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_hormann_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_hormann_stop(void* context) {
|
||||
SubGhzProtocolEncoderHormann* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_hormann_yield(void* context) {
|
||||
SubGhzProtocolEncoderHormann* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_hormann_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderHormann* instance = malloc(sizeof(SubGhzProtocolDecoderHormann));
|
||||
instance->base.protocol = &subghz_protocol_hormann;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormann* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static bool subghz_protocol_decoder_hormann_check_pattern(SubGhzProtocolDecoderHormann* instance) {
|
||||
return (instance->decoder.decode_data & HORMANN_HSM_PATTERN) == HORMANN_HSM_PATTERN;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormann* instance = context;
|
||||
instance->decoder.parser_step = HormannDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormann* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case HormannDecoderStepReset:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short * 24) <
|
||||
subghz_protocol_hormann_const.te_delta * 24)) {
|
||||
instance->decoder.parser_step = HormannDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case HormannDecoderStepFoundStartBit:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) <
|
||||
subghz_protocol_hormann_const.te_delta)) {
|
||||
instance->decoder.parser_step = HormannDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = HormannDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case HormannDecoderStepSaveDuration:
|
||||
if(level) { //save interval
|
||||
if(duration >= (subghz_protocol_hormann_const.te_short * 5) &&
|
||||
subghz_protocol_decoder_hormann_check_pattern(instance)) {
|
||||
instance->decoder.parser_step = HormannDecoderStepFoundStartBit;
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_hormann_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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = HormannDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = HormannDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case HormannDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_short) <
|
||||
subghz_protocol_hormann_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_hormann_const.te_long) <
|
||||
subghz_protocol_hormann_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = HormannDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_hormann_const.te_long) <
|
||||
subghz_protocol_hormann_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_hormann_const.te_short) <
|
||||
subghz_protocol_hormann_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = HormannDecoderStepSaveDuration;
|
||||
} else
|
||||
instance->decoder.parser_step = HormannDecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.parser_step = HormannDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_hormann_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
instance->btn = (instance->data >> 4) & 0xF;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormann* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_hormann_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormann* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormann* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_hormann_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormann* instance = context;
|
||||
subghz_protocol_hormann_check_remote_controller(&instance->generic);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s\r\n"
|
||||
"%dbit\r\n"
|
||||
"Key:0x%03lX%08lX\r\n"
|
||||
"Btn:0x%01X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data >> 32),
|
||||
(uint32_t)instance->generic.data,
|
||||
instance->generic.btn);
|
||||
}
|
||||
107
applications/main/subghz/protocols/hormann.h
Normal file
107
applications/main/subghz/protocols/hormann.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_HORMANN_HSM_NAME "Hormann HSM"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderHormann SubGhzProtocolDecoderHormann;
|
||||
typedef struct SubGhzProtocolEncoderHormann SubGhzProtocolEncoderHormann;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_hormann_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_hormann_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_hormann;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderHormann.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderHormann* pointer to a SubGhzProtocolEncoderHormann instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_hormann_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderHormann.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHormann instance
|
||||
*/
|
||||
void subghz_protocol_encoder_hormann_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHormann instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_hormann_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHormann instance
|
||||
*/
|
||||
void subghz_protocol_encoder_hormann_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHormann instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_hormann_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderHormann.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderHormann* pointer to a SubGhzProtocolDecoderHormann instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_hormann_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderHormann.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormann instance
|
||||
*/
|
||||
void subghz_protocol_decoder_hormann_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderHormann.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormann instance
|
||||
*/
|
||||
void subghz_protocol_decoder_hormann_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormann instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_hormann_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormann instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_hormann_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderHormann.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormann instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_hormann_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderHormann.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormann instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_hormann_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormann instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_hormann_get_string(void* context, FuriString* output);
|
||||
234
applications/main/subghz/protocols/ido.c
Normal file
234
applications/main/subghz/protocols/ido.c
Normal file
@@ -0,0 +1,234 @@
|
||||
#include "ido.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocol_iDo_117/111"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_ido_const = {
|
||||
.te_short = 450,
|
||||
.te_long = 1450,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 48,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderIDo {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderIDo {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
IDoDecoderStepReset = 0,
|
||||
IDoDecoderStepFoundPreambula,
|
||||
IDoDecoderStepSaveDuration,
|
||||
IDoDecoderStepCheckDuration,
|
||||
} IDoDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_ido_decoder = {
|
||||
.alloc = subghz_protocol_decoder_ido_alloc,
|
||||
.free = subghz_protocol_decoder_ido_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_ido_feed,
|
||||
.reset = subghz_protocol_decoder_ido_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_ido_get_hash_data,
|
||||
.deserialize = subghz_protocol_decoder_ido_deserialize,
|
||||
.serialize = subghz_protocol_decoder_ido_serialize,
|
||||
.get_string = subghz_protocol_decoder_ido_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_ido_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_ido = {
|
||||
.name = SUBGHZ_PROTOCOL_IDO_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Save,
|
||||
|
||||
.decoder = &subghz_protocol_ido_decoder,
|
||||
.encoder = &subghz_protocol_ido_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderIDo* instance = malloc(sizeof(SubGhzProtocolDecoderIDo));
|
||||
instance->base.protocol = &subghz_protocol_ido;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ido_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIDo* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ido_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIDo* instance = context;
|
||||
instance->decoder.parser_step = IDoDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIDo* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case IDoDecoderStepReset:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) <
|
||||
subghz_protocol_ido_const.te_delta * 5)) {
|
||||
instance->decoder.parser_step = IDoDecoderStepFoundPreambula;
|
||||
}
|
||||
break;
|
||||
case IDoDecoderStepFoundPreambula:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_ido_const.te_short * 10) <
|
||||
subghz_protocol_ido_const.te_delta * 5)) {
|
||||
//Found Preambula
|
||||
instance->decoder.parser_step = IDoDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = IDoDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case IDoDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_ido_const.te_short * 5 +
|
||||
subghz_protocol_ido_const.te_delta)) {
|
||||
instance->decoder.parser_step = IDoDecoderStepFoundPreambula;
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_ido_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;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = IDoDecoderStepCheckDuration;
|
||||
}
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = IDoDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case IDoDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) <
|
||||
subghz_protocol_ido_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_ido_const.te_long) <
|
||||
subghz_protocol_ido_const.te_delta * 3)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = IDoDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_ido_const.te_short) <
|
||||
subghz_protocol_ido_const.te_delta * 3) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_ido_const.te_short) <
|
||||
subghz_protocol_ido_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = IDoDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = IDoDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = IDoDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_ido_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
uint64_t code_found_reverse =
|
||||
subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit);
|
||||
uint32_t code_fix = code_found_reverse & 0xFFFFFF;
|
||||
|
||||
instance->serial = code_fix & 0xFFFFF;
|
||||
instance->btn = (code_fix >> 20) & 0x0F;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIDo* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_ido_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIDo* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIDo* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit != subghz_protocol_ido_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_ido_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIDo* instance = context;
|
||||
|
||||
subghz_protocol_ido_check_remote_controller(&instance->generic);
|
||||
uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
uint32_t code_fix = code_found_reverse & 0xFFFFFF;
|
||||
uint32_t code_hop = (code_found_reverse >> 24) & 0xFFFFFF;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"Fix:%06lX \r\n"
|
||||
"Hop:%06lX \r\n"
|
||||
"Sn:%05lX Btn:%X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data >> 32),
|
||||
(uint32_t)instance->generic.data,
|
||||
code_fix,
|
||||
code_hop,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn);
|
||||
}
|
||||
73
applications/main/subghz/protocols/ido.h
Normal file
73
applications/main/subghz/protocols/ido.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_IDO_NAME "iDo 117/111"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderIDo SubGhzProtocolDecoderIDo;
|
||||
typedef struct SubGhzProtocolEncoderIDo SubGhzProtocolEncoderIDo;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_ido_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_ido_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_ido;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderIDo.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderIDo* pointer to a SubGhzProtocolDecoderIDo instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_ido_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderIDo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIDo instance
|
||||
*/
|
||||
void subghz_protocol_decoder_ido_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderIDo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIDo instance
|
||||
*/
|
||||
void subghz_protocol_decoder_ido_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIDo instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_ido_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIDo instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_ido_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderIDo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIDo instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_ido_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderIDo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIDo instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_ido_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIDo instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_ido_get_string(void* context, FuriString* output);
|
||||
472
applications/main/subghz/protocols/intertechno_v3.c
Normal file
472
applications/main/subghz/protocols/intertechno_v3.c
Normal file
@@ -0,0 +1,472 @@
|
||||
#include "intertechno_v3.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolIntertechnoV3"
|
||||
|
||||
#define CH_PATTERN "%c%c%c%c"
|
||||
#define CNT_TO_CH(ch) \
|
||||
(ch & 0x8 ? '1' : '0'), (ch & 0x4 ? '1' : '0'), (ch & 0x2 ? '1' : '0'), (ch & 0x1 ? '1' : '0')
|
||||
|
||||
#define INTERTECHNO_V3_DIMMING_COUNT_BIT 36
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_intertechno_v3_const = {
|
||||
.te_short = 275,
|
||||
.te_long = 1375,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 32,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderIntertechno_V3 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderIntertechno_V3 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
IntertechnoV3DecoderStepReset = 0,
|
||||
IntertechnoV3DecoderStepStartSync,
|
||||
IntertechnoV3DecoderStepFoundSync,
|
||||
IntertechnoV3DecoderStepStartDuration,
|
||||
IntertechnoV3DecoderStepSaveDuration,
|
||||
IntertechnoV3DecoderStepCheckDuration,
|
||||
IntertechnoV3DecoderStepEndDuration,
|
||||
} IntertechnoV3DecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder = {
|
||||
.alloc = subghz_protocol_decoder_intertechno_v3_alloc,
|
||||
.free = subghz_protocol_decoder_intertechno_v3_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_intertechno_v3_feed,
|
||||
.reset = subghz_protocol_decoder_intertechno_v3_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_intertechno_v3_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_intertechno_v3_serialize,
|
||||
.deserialize = subghz_protocol_decoder_intertechno_v3_deserialize,
|
||||
.get_string = subghz_protocol_decoder_intertechno_v3_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder = {
|
||||
.alloc = subghz_protocol_encoder_intertechno_v3_alloc,
|
||||
.free = subghz_protocol_encoder_intertechno_v3_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_intertechno_v3_deserialize,
|
||||
.stop = subghz_protocol_encoder_intertechno_v3_stop,
|
||||
.yield = subghz_protocol_encoder_intertechno_v3_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_intertechno_v3 = {
|
||||
.name = SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_868 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
|
||||
SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_intertechno_v3_decoder,
|
||||
.encoder = &subghz_protocol_intertechno_v3_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderIntertechno_V3* instance =
|
||||
malloc(sizeof(SubGhzProtocolEncoderIntertechno_V3));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_intertechno_v3;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 256;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_intertechno_v3_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderIntertechno_V3* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_intertechno_v3_get_upload(
|
||||
SubGhzProtocolEncoderIntertechno_V3* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 38);
|
||||
//Send sync
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short * 10);
|
||||
//Send key data
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
||||
if((instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) && (i == 9)) {
|
||||
//send bit dimm
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
} else if(bit_read(instance->generic.data, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_intertechno_v3_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_intertechno_v3_const.te_long);
|
||||
}
|
||||
}
|
||||
instance->encoder.size_upload = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_intertechno_v3_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderIntertechno_V3* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if((instance->generic.data_count_bit !=
|
||||
subghz_protocol_intertechno_v3_const.min_count_bit_for_found) &&
|
||||
(instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_intertechno_v3_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_intertechno_v3_stop(void* context) {
|
||||
SubGhzProtocolEncoderIntertechno_V3* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context) {
|
||||
SubGhzProtocolEncoderIntertechno_V3* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderIntertechno_V3* instance =
|
||||
malloc(sizeof(SubGhzProtocolDecoderIntertechno_V3));
|
||||
instance->base.protocol = &subghz_protocol_intertechno_v3;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_intertechno_v3_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIntertechno_V3* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_intertechno_v3_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIntertechno_V3* instance = context;
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIntertechno_V3* instance = context;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case IntertechnoV3DecoderStepReset:
|
||||
if((!level) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 37) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta * 15)) {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync;
|
||||
}
|
||||
break;
|
||||
case IntertechnoV3DecoderStepStartSync:
|
||||
if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta)) {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepFoundSync;
|
||||
} else {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case IntertechnoV3DecoderStepFoundSync:
|
||||
if(!level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short * 10) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta * 3)) {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case IntertechnoV3DecoderStepStartDuration:
|
||||
if(level && (DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta)) {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case IntertechnoV3DecoderStepSaveDuration:
|
||||
if(!level) { //save interval
|
||||
if(duration >= (subghz_protocol_intertechno_v3_const.te_short * 11)) {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepStartSync;
|
||||
if((instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_intertechno_v3_const.min_count_bit_for_found) ||
|
||||
(instance->decoder.decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) {
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case IntertechnoV3DecoderStepCheckDuration:
|
||||
if(level) {
|
||||
//Add 0 bit
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration;
|
||||
} else if(
|
||||
//Add 1 bit
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_long) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration;
|
||||
|
||||
} else if(
|
||||
//Add dimm_state
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_intertechno_v3_const.te_short) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta) &&
|
||||
(instance->decoder.decode_count_bit == 27)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepEndDuration;
|
||||
|
||||
} else
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case IntertechnoV3DecoderStepEndDuration:
|
||||
if(!level && ((DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_short) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta) ||
|
||||
(DURATION_DIFF(duration, subghz_protocol_intertechno_v3_const.te_long) <
|
||||
subghz_protocol_intertechno_v3_const.te_delta * 2))) {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepStartDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = IntertechnoV3DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_intertechno_v3_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/*
|
||||
* A frame is either 32 or 36 bits:
|
||||
*
|
||||
* _
|
||||
* start bit: | |__________ (T,10T)
|
||||
* _ _
|
||||
* '0': | |_| |_____ (T,T,T,5T)
|
||||
* _ _
|
||||
* '1': | |_____| |_ (T,5T,T,T)
|
||||
* _ _
|
||||
* dimm: | |_| |_ (T,T,T,T)
|
||||
*
|
||||
* _
|
||||
* stop bit: | |____...____ (T,38T)
|
||||
*
|
||||
* if frame 32 bits
|
||||
* SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch on/off ~ch
|
||||
* Key:0x3F86C59F => 00111111100001101100010110 0 1 1111
|
||||
*
|
||||
* if frame 36 bits
|
||||
* SSSSSSSSSSSSSSSSSSSSSSSSSS all_ch dimm ~ch dimm_level
|
||||
* Key:0x42D2E8856 => 01000010110100101110100010 0 X 0101 0110
|
||||
*
|
||||
*/
|
||||
|
||||
if(instance->data_count_bit == subghz_protocol_intertechno_v3_const.min_count_bit_for_found) {
|
||||
instance->serial = (instance->data >> 6) & 0x3FFFFFF;
|
||||
if((instance->data >> 5) & 0x1) {
|
||||
instance->cnt = 1 << 5;
|
||||
} else {
|
||||
instance->cnt = (~instance->data & 0xF);
|
||||
}
|
||||
instance->btn = (instance->data >> 4) & 0x1;
|
||||
} else if(instance->data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) {
|
||||
instance->serial = (instance->data >> 10) & 0x3FFFFFF;
|
||||
if((instance->data >> 9) & 0x1) {
|
||||
instance->cnt = 1 << 5;
|
||||
} else {
|
||||
instance->cnt = (~(instance->data >> 4) & 0xF);
|
||||
}
|
||||
instance->btn = (instance->data) & 0xF;
|
||||
} else {
|
||||
instance->serial = 0;
|
||||
instance->cnt = 0;
|
||||
instance->btn = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIntertechno_V3* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_intertechno_v3_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIntertechno_V3* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_intertechno_v3_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIntertechno_V3* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if((instance->generic.data_count_bit !=
|
||||
subghz_protocol_intertechno_v3_const.min_count_bit_for_found) &&
|
||||
(instance->generic.data_count_bit != INTERTECHNO_V3_DIMMING_COUNT_BIT)) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderIntertechno_V3* instance = context;
|
||||
|
||||
subghz_protocol_intertechno_v3_check_remote_controller(&instance->generic);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%.11s %db\r\n"
|
||||
"Key:0x%08llX\r\n"
|
||||
"Sn:%07lX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
instance->generic.data,
|
||||
instance->generic.serial);
|
||||
|
||||
if(instance->generic.data_count_bit ==
|
||||
subghz_protocol_intertechno_v3_const.min_count_bit_for_found) {
|
||||
if(instance->generic.cnt >> 5) {
|
||||
furi_string_cat_printf(
|
||||
output, "Ch: All Btn:%s\r\n", (instance->generic.btn ? "On" : "Off"));
|
||||
} else {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"Ch:" CH_PATTERN " Btn:%s\r\n",
|
||||
CNT_TO_CH(instance->generic.cnt),
|
||||
(instance->generic.btn ? "On" : "Off"));
|
||||
}
|
||||
} else if(instance->generic.data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"Ch:" CH_PATTERN " Dimm:%d%%\r\n",
|
||||
CNT_TO_CH(instance->generic.cnt),
|
||||
(int)(6.67 * (float)instance->generic.btn));
|
||||
}
|
||||
}
|
||||
111
applications/main/subghz/protocols/intertechno_v3.h
Normal file
111
applications/main/subghz/protocols/intertechno_v3.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_INTERTECHNO_V3_NAME "Intertechno_V3"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderIntertechno_V3 SubGhzProtocolDecoderIntertechno_V3;
|
||||
typedef struct SubGhzProtocolEncoderIntertechno_V3 SubGhzProtocolEncoderIntertechno_V3;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_intertechno_v3_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_intertechno_v3_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_intertechno_v3;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderIntertechno_V3.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderIntertechno_V3* pointer to a SubGhzProtocolEncoderIntertechno_V3 instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_intertechno_v3_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderIntertechno_V3.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_intertechno_v3_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_intertechno_v3_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_intertechno_v3_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderIntertechno_V3 instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_intertechno_v3_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderIntertechno_V3.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderIntertechno_V3* pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_intertechno_v3_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderIntertechno_V3.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_intertechno_v3_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderIntertechno_V3.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_intertechno_v3_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_intertechno_v3_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_intertechno_v3_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderIntertechno_V3.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_intertechno_v3_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderIntertechno_V3.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_intertechno_v3_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderIntertechno_V3 instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_intertechno_v3_get_string(void* context, FuriString* output);
|
||||
1115
applications/main/subghz/protocols/keeloq.c
Normal file
1115
applications/main/subghz/protocols/keeloq.c
Normal file
File diff suppressed because it is too large
Load Diff
153
applications/main/subghz/protocols/keeloq.h
Normal file
153
applications/main/subghz/protocols/keeloq.h
Normal file
@@ -0,0 +1,153 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_KEELOQ_NAME "KeeLoq"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderKeeloq SubGhzProtocolDecoderKeeloq;
|
||||
typedef struct SubGhzProtocolEncoderKeeloq SubGhzProtocolEncoderKeeloq;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_keeloq_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_keeloq_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_keeloq;
|
||||
|
||||
void keeloq_reset_mfname();
|
||||
|
||||
void keeloq_reset_kl_type();
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderKeeloq.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderKeeloq* pointer to a SubGhzProtocolEncoderKeeloq instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_keeloq_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderKeeloq.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeeloq instance
|
||||
*/
|
||||
void subghz_protocol_encoder_keeloq_free(void* context);
|
||||
|
||||
/**
|
||||
* Key generation from simple data.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeeloq instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param serial Serial number, 28 bit
|
||||
* @param btn Button number, 4 bit
|
||||
* @param cnt Counter value, 16 bit
|
||||
* @param manufacture_name Name of manufacturer's key
|
||||
* @param preset Modulation, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_keeloq_create_data(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
uint32_t serial,
|
||||
uint8_t btn,
|
||||
uint16_t cnt,
|
||||
const char* manufacture_name,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Key generation for BFT.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeeloq instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param serial Serial number, 28 bit
|
||||
* @param btn Button number, 4 bit
|
||||
* @param cnt Counter value, 16 bit
|
||||
* @param seed Seed value, 32 bit
|
||||
* @param manufacture_name Name of manufacturer's key
|
||||
* @param preset Modulation, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_keeloq_bft_create_data(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
uint32_t serial,
|
||||
uint8_t btn,
|
||||
uint16_t cnt,
|
||||
uint32_t seed,
|
||||
const char* manufacture_name,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeeloq instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeeloq instance
|
||||
*/
|
||||
void subghz_protocol_encoder_keeloq_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKeeloq instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_keeloq_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderKeeloq.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderKeeloq* pointer to a SubGhzProtocolDecoderKeeloq instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_keeloq_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderKeeloq.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeeloq instance
|
||||
*/
|
||||
void subghz_protocol_decoder_keeloq_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderKeeloq.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeeloq instance
|
||||
*/
|
||||
void subghz_protocol_decoder_keeloq_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeeloq instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_keeloq_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeeloq instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_keeloq_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderKeeloq.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeeloq instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_keeloq_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderKeeloq.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeeloq instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_keeloq_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKeeloq instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_keeloq_get_string(void* context, FuriString* output);
|
||||
142
applications/main/subghz/protocols/keeloq_common.c
Normal file
142
applications/main/subghz/protocols/keeloq_common.c
Normal file
@@ -0,0 +1,142 @@
|
||||
#include "keeloq_common.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
#include <m-array.h>
|
||||
|
||||
#define bit(x, n) (((x) >> (n)) & 1)
|
||||
#define g5(x, a, b, c, d, e) \
|
||||
(bit(x, a) + bit(x, b) * 2 + bit(x, c) * 4 + bit(x, d) * 8 + bit(x, e) * 16)
|
||||
|
||||
/** Simple Learning Encrypt
|
||||
* @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
|
||||
* @param key - manufacture (64bit)
|
||||
* @return keeloq encrypt data
|
||||
*/
|
||||
inline uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key) {
|
||||
uint32_t x = data, r;
|
||||
for(r = 0; r < 528; r++)
|
||||
x = (x >> 1) ^ ((bit(x, 0) ^ bit(x, 16) ^ (uint32_t)bit(key, r & 63) ^
|
||||
bit(KEELOQ_NLF, g5(x, 1, 9, 20, 26, 31)))
|
||||
<< 31);
|
||||
return x;
|
||||
}
|
||||
|
||||
/** Simple Learning Decrypt
|
||||
* @param data - keelog encrypt data
|
||||
* @param key - manufacture (64bit)
|
||||
* @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
|
||||
*/
|
||||
inline uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key) {
|
||||
uint32_t x = data, r;
|
||||
for(r = 0; r < 528; r++)
|
||||
x = (x << 1) ^ bit(x, 31) ^ bit(x, 15) ^ (uint32_t)bit(key, (15 - r) & 63) ^
|
||||
bit(KEELOQ_NLF, g5(x, 0, 8, 19, 25, 30));
|
||||
return x;
|
||||
}
|
||||
|
||||
/** Normal Learning
|
||||
* @param data - serial number (28bit)
|
||||
* @param key - manufacture (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
inline uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key) {
|
||||
uint32_t k1, k2;
|
||||
|
||||
data &= 0x0FFFFFFF;
|
||||
data |= 0x20000000;
|
||||
k1 = subghz_protocol_keeloq_common_decrypt(data, key);
|
||||
|
||||
data &= 0x0FFFFFFF;
|
||||
data |= 0x60000000;
|
||||
k2 = subghz_protocol_keeloq_common_decrypt(data, key);
|
||||
|
||||
return ((uint64_t)k2 << 32) | k1; // key - shifrovanoya
|
||||
}
|
||||
|
||||
/** Secure Learning
|
||||
* @param data - serial number (28bit)
|
||||
* @param seed - seed number (32bit)
|
||||
* @param key - manufacture (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
inline uint64_t subghz_protocol_keeloq_common_secure_learning(
|
||||
uint32_t data,
|
||||
uint32_t seed,
|
||||
const uint64_t key) {
|
||||
uint32_t k1, k2;
|
||||
|
||||
data &= 0x0FFFFFFF;
|
||||
k1 = subghz_protocol_keeloq_common_decrypt(data, key);
|
||||
k2 = subghz_protocol_keeloq_common_decrypt(seed, key);
|
||||
|
||||
return ((uint64_t)k1 << 32) | k2;
|
||||
}
|
||||
|
||||
/** Magic_xor_type1 Learning
|
||||
* @param data - serial number (28bit)
|
||||
* @param xor - magic xor (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
inline uint64_t
|
||||
subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor) {
|
||||
data &= 0x0FFFFFFF;
|
||||
return (((uint64_t)data << 32) | data) ^ xor;
|
||||
}
|
||||
|
||||
/** Faac SLH (Spa) Learning
|
||||
* @param seed - seed number (32bit)
|
||||
* @param key - mfkey (64bit)
|
||||
* @return man_learning for this seed number (64bit)
|
||||
*/
|
||||
|
||||
inline uint64_t
|
||||
subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key) {
|
||||
uint16_t hs = seed >> 16;
|
||||
const uint16_t ending = 0x544D;
|
||||
uint32_t lsb = (uint32_t)hs << 16 | ending;
|
||||
uint64_t man = (uint64_t)subghz_protocol_keeloq_common_encrypt(seed, key) << 32 |
|
||||
subghz_protocol_keeloq_common_encrypt(lsb, key);
|
||||
return man;
|
||||
}
|
||||
/** Magic_serial_type1 Learning
|
||||
* @param data - serial number (28bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
inline uint64_t
|
||||
subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man) {
|
||||
return (man & 0xFFFFFFFF) | ((uint64_t)data << 40) |
|
||||
((uint64_t)(((data & 0xff) + ((data >> 8) & 0xFF)) & 0xFF) << 32);
|
||||
}
|
||||
|
||||
/** Magic_serial_type2 Learning
|
||||
* @param data - btn+serial number (32bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
inline uint64_t
|
||||
subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man) {
|
||||
uint8_t* p = (uint8_t*)&data;
|
||||
uint8_t* m = (uint8_t*)&man;
|
||||
m[7] = p[0];
|
||||
m[6] = p[1];
|
||||
m[5] = p[2];
|
||||
m[4] = p[3];
|
||||
return man;
|
||||
}
|
||||
|
||||
/** Magic_serial_type3 Learning
|
||||
* @param data - serial number (24bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
inline uint64_t
|
||||
subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man) {
|
||||
return (man & 0xFFFFFFFFFF000000) | (data & 0xFFFFFF);
|
||||
}
|
||||
100
applications/main/subghz/protocols/keeloq_common.h
Normal file
100
applications/main/subghz/protocols/keeloq_common.h
Normal file
@@ -0,0 +1,100 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#include <furi.h>
|
||||
|
||||
/*
|
||||
* Keeloq
|
||||
* https://ru.wikipedia.org/wiki/KeeLoq
|
||||
* https://phreakerclub.com/forum/showthread.php?t=1094
|
||||
*
|
||||
*/
|
||||
#define KEELOQ_NLF 0x3A5C742E
|
||||
|
||||
/*
|
||||
* KeeLoq learning types
|
||||
* https://phreakerclub.com/forum/showthread.php?t=67
|
||||
*/
|
||||
#define KEELOQ_LEARNING_UNKNOWN 0u
|
||||
#define KEELOQ_LEARNING_SIMPLE 1u
|
||||
#define KEELOQ_LEARNING_NORMAL 2u
|
||||
#define KEELOQ_LEARNING_SECURE 3u
|
||||
#define KEELOQ_LEARNING_MAGIC_XOR_TYPE_1 4u
|
||||
#define KEELOQ_LEARNING_FAAC 5u
|
||||
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_1 6u
|
||||
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_2 7u
|
||||
#define KEELOQ_LEARNING_MAGIC_SERIAL_TYPE_3 8u
|
||||
|
||||
/**
|
||||
* Simple Learning Encrypt
|
||||
* @param data - 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
|
||||
* @param key - manufacture (64bit)
|
||||
* @return keeloq encrypt data
|
||||
*/
|
||||
uint32_t subghz_protocol_keeloq_common_encrypt(const uint32_t data, const uint64_t key);
|
||||
|
||||
/**
|
||||
* Simple Learning Decrypt
|
||||
* @param data - keeloq encrypt data
|
||||
* @param key - manufacture (64bit)
|
||||
* @return 0xBSSSCCCC, B(4bit) key, S(10bit) serial&0x3FF, C(16bit) counter
|
||||
*/
|
||||
uint32_t subghz_protocol_keeloq_common_decrypt(const uint32_t data, const uint64_t key);
|
||||
|
||||
/**
|
||||
* Normal Learning
|
||||
* @param data - serial number (28bit)
|
||||
* @param key - manufacture (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
uint64_t subghz_protocol_keeloq_common_normal_learning(uint32_t data, const uint64_t key);
|
||||
|
||||
/**
|
||||
* Secure Learning
|
||||
* @param data - serial number (28bit)
|
||||
* @param seed - seed number (32bit)
|
||||
* @param key - manufacture (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
uint64_t
|
||||
subghz_protocol_keeloq_common_secure_learning(uint32_t data, uint32_t seed, const uint64_t key);
|
||||
|
||||
/**
|
||||
* Magic_xor_type1 Learning
|
||||
* @param data - serial number (28bit)
|
||||
* @param xor - magic xor (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
uint64_t subghz_protocol_keeloq_common_magic_xor_type1_learning(uint32_t data, uint64_t xor);
|
||||
|
||||
/** Faac SLH (Spa) Learning
|
||||
* @param seed - seed number (32bit)
|
||||
* @param key - mfkey (64bit)
|
||||
* @return man_learning for this fix number (64bit)
|
||||
*/
|
||||
uint64_t subghz_protocol_keeloq_common_faac_learning(const uint32_t seed, const uint64_t key);
|
||||
|
||||
/** Magic_serial_type1 Learning
|
||||
* @param data - serial number (28bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
uint64_t subghz_protocol_keeloq_common_magic_serial_type1_learning(uint32_t data, uint64_t man);
|
||||
|
||||
/** Magic_serial_type2 Learning
|
||||
* @param data - btn+serial number (32bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
uint64_t subghz_protocol_keeloq_common_magic_serial_type2_learning(uint32_t data, uint64_t man);
|
||||
|
||||
/** Magic_serial_type3 Learning
|
||||
* @param data - btn+serial number (32bit)
|
||||
* @param man - magic man (64bit)
|
||||
* @return manufacture for this serial number (64bit)
|
||||
*/
|
||||
|
||||
uint64_t subghz_protocol_keeloq_common_magic_serial_type3_learning(uint32_t data, uint64_t man);
|
||||
279
applications/main/subghz/protocols/kia.c
Normal file
279
applications/main/subghz/protocols/kia.c
Normal file
@@ -0,0 +1,279 @@
|
||||
#include "kia.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocoKIA"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_kia_const = {
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 61,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderKIA {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint16_t header_count;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderKIA {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
KIADecoderStepReset = 0,
|
||||
KIADecoderStepCheckPreambula,
|
||||
KIADecoderStepSaveDuration,
|
||||
KIADecoderStepCheckDuration,
|
||||
} KIADecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_kia_decoder = {
|
||||
.alloc = subghz_protocol_decoder_kia_alloc,
|
||||
.free = subghz_protocol_decoder_kia_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_kia_feed,
|
||||
.reset = subghz_protocol_decoder_kia_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_kia_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_kia_serialize,
|
||||
.deserialize = subghz_protocol_decoder_kia_deserialize,
|
||||
.get_string = subghz_protocol_decoder_kia_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_kia_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_kia = {
|
||||
.name = SUBGHZ_PROTOCOL_KIA_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable,
|
||||
|
||||
.decoder = &subghz_protocol_kia_decoder,
|
||||
.encoder = &subghz_protocol_kia_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderKIA* instance = malloc(sizeof(SubGhzProtocolDecoderKIA));
|
||||
instance->base.protocol = &subghz_protocol_kia;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_kia_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKIA* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_kia_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKIA* instance = context;
|
||||
instance->decoder.parser_step = KIADecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKIA* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case KIADecoderStepReset:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) <
|
||||
subghz_protocol_kia_const.te_delta)) {
|
||||
instance->decoder.parser_step = KIADecoderStepCheckPreambula;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
case KIADecoderStepCheckPreambula:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) <
|
||||
subghz_protocol_kia_const.te_delta) ||
|
||||
(DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) <
|
||||
subghz_protocol_kia_const.te_delta)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = KIADecoderStepReset;
|
||||
}
|
||||
} else if(
|
||||
(DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) <
|
||||
subghz_protocol_kia_const.te_delta) &&
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) <
|
||||
subghz_protocol_kia_const.te_delta)) {
|
||||
// Found header
|
||||
instance->header_count++;
|
||||
break;
|
||||
} else if(
|
||||
(DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) <
|
||||
subghz_protocol_kia_const.te_delta) &&
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) <
|
||||
subghz_protocol_kia_const.te_delta)) {
|
||||
// Found start bit
|
||||
if(instance->header_count > 15) {
|
||||
instance->decoder.parser_step = KIADecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 1;
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
} else {
|
||||
instance->decoder.parser_step = KIADecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = KIADecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case KIADecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(duration >=
|
||||
(subghz_protocol_kia_const.te_long + subghz_protocol_kia_const.te_delta * 2UL)) {
|
||||
//Found stop bit
|
||||
instance->decoder.parser_step = KIADecoderStepReset;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_kia_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;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = KIADecoderStepCheckDuration;
|
||||
}
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = KIADecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case KIADecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_short) <
|
||||
subghz_protocol_kia_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_kia_const.te_short) <
|
||||
subghz_protocol_kia_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = KIADecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_kia_const.te_long) <
|
||||
subghz_protocol_kia_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_kia_const.te_long) <
|
||||
subghz_protocol_kia_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = KIADecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = KIADecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = KIADecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
*/
|
||||
static void subghz_protocol_kia_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/*
|
||||
* 0x0F 0112 43B04EC 1 7D
|
||||
* 0x0F 0113 43B04EC 1 DF
|
||||
* 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)
|
||||
*/
|
||||
|
||||
instance->serial = (uint32_t)((instance->data >> 12) & 0x0FFFFFFF);
|
||||
instance->btn = (instance->data >> 8) & 0x0F;
|
||||
instance->cnt = (instance->data >> 40) & 0xFFFF;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKIA* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_kia_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKIA* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKIA* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit != subghz_protocol_kia_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKIA* instance = context;
|
||||
|
||||
subghz_protocol_kia_check_remote_controller(&instance->generic);
|
||||
uint32_t code_found_hi = instance->generic.data >> 32;
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%08lX%08lX\r\n"
|
||||
"Sn:%07lX Btn:%X Cnt:%04lX\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);
|
||||
}
|
||||
73
applications/main/subghz/protocols/kia.h
Normal file
73
applications/main/subghz/protocols/kia.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_KIA_NAME "KIA Seed"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderKIA SubGhzProtocolDecoderKIA;
|
||||
typedef struct SubGhzProtocolEncoderKIA SubGhzProtocolEncoderKIA;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_kia_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_kia_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_kia;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderKIA.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderKIA* pointer to a SubGhzProtocolDecoderKIA instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_kia_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderKIA.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKIA instance
|
||||
*/
|
||||
void subghz_protocol_decoder_kia_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderKIA.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKIA instance
|
||||
*/
|
||||
void subghz_protocol_decoder_kia_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKIA instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_kia_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKIA instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_kia_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderKIA.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKIA instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_kia_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderKIA.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKIA instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_kia_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKIA instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_kia_get_string(void* context, FuriString* output);
|
||||
581
applications/main/subghz/protocols/kinggates_stylo_4k.c
Normal file
581
applications/main/subghz/protocols/kinggates_stylo_4k.c
Normal file
@@ -0,0 +1,581 @@
|
||||
#include "kinggates_stylo_4k.h"
|
||||
#include "keeloq_common.h"
|
||||
|
||||
#include "../subghz_keystore.h"
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocoKingGates_stylo_4k"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_kinggates_stylo_4k_const = {
|
||||
.te_short = 400,
|
||||
.te_long = 1100,
|
||||
.te_delta = 140,
|
||||
.min_count_bit_for_found = 89,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderKingGates_stylo_4k {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint16_t header_count;
|
||||
SubGhzKeystore* keystore;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderKingGates_stylo_4k {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
SubGhzKeystore* keystore;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
KingGates_stylo_4kDecoderStepReset = 0,
|
||||
KingGates_stylo_4kDecoderStepCheckPreambula,
|
||||
KingGates_stylo_4kDecoderStepCheckStartBit,
|
||||
KingGates_stylo_4kDecoderStepSaveDuration,
|
||||
KingGates_stylo_4kDecoderStepCheckDuration,
|
||||
} KingGates_stylo_4kDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder = {
|
||||
.alloc = subghz_protocol_decoder_kinggates_stylo_4k_alloc,
|
||||
.free = subghz_protocol_decoder_kinggates_stylo_4k_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_kinggates_stylo_4k_feed,
|
||||
.reset = subghz_protocol_decoder_kinggates_stylo_4k_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_kinggates_stylo_4k_serialize,
|
||||
.deserialize = subghz_protocol_decoder_kinggates_stylo_4k_deserialize,
|
||||
.get_string = subghz_protocol_decoder_kinggates_stylo_4k_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder = {
|
||||
.alloc = subghz_protocol_encoder_kinggates_stylo_4k_alloc,
|
||||
.free = subghz_protocol_encoder_kinggates_stylo_4k_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_kinggates_stylo_4k_deserialize,
|
||||
.stop = subghz_protocol_encoder_kinggates_stylo_4k_stop,
|
||||
.yield = subghz_protocol_encoder_kinggates_stylo_4k_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_kinggates_stylo_4k = {
|
||||
.name = SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_kinggates_stylo_4k_decoder,
|
||||
.encoder = &subghz_protocol_kinggates_stylo_4k_encoder,
|
||||
};
|
||||
|
||||
//
|
||||
// Encoder
|
||||
//
|
||||
|
||||
// Pre define function
|
||||
static void subghz_protocol_kinggates_stylo_4k_remote_controller(
|
||||
SubGhzBlockGeneric* instance,
|
||||
SubGhzKeystore* keystore);
|
||||
|
||||
void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolEncoderKingGates_stylo_4k* instance =
|
||||
malloc(sizeof(SubGhzProtocolEncoderKingGates_stylo_4k));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_kinggates_stylo_4k;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->keystore = subghz_environment_get_keystore(environment);
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 512;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderKingGates_stylo_4k* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context) {
|
||||
SubGhzProtocolEncoderKingGates_stylo_4k* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context) {
|
||||
SubGhzProtocolEncoderKingGates_stylo_4k* 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;
|
||||
}
|
||||
|
||||
/**
|
||||
* Key generation from simple data
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k* instance
|
||||
* @param btn Button number, 4 bit
|
||||
*/
|
||||
static bool subghz_protocol_kinggates_stylo_4k_gen_data(
|
||||
SubGhzProtocolEncoderKingGates_stylo_4k* instance,
|
||||
uint8_t btn) {
|
||||
UNUSED(btn);
|
||||
uint32_t hop = subghz_protocol_blocks_reverse_key(instance->generic.data_2 >> 4, 32);
|
||||
uint64_t fix = subghz_protocol_blocks_reverse_key(instance->generic.data, 53);
|
||||
int res = 0;
|
||||
uint32_t decrypt = 0;
|
||||
|
||||
for
|
||||
M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
|
||||
res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k");
|
||||
if(res == 0) {
|
||||
//Simple Learning
|
||||
decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);
|
||||
break;
|
||||
}
|
||||
}
|
||||
instance->generic.cnt = decrypt & 0xFFFF;
|
||||
|
||||
if(instance->generic.cnt < 0xFFFF) {
|
||||
instance->generic.cnt++;
|
||||
} else if(instance->generic.cnt >= 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
}
|
||||
|
||||
instance->generic.btn = (fix >> 17) & 0x0F;
|
||||
instance->generic.serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF);
|
||||
|
||||
uint32_t data = (decrypt & 0xFFFF0000) | instance->generic.cnt;
|
||||
|
||||
uint64_t encrypt = 0;
|
||||
for
|
||||
M_EACH(manufacture_code, *subghz_keystore_get_data(instance->keystore), SubGhzKeyArray_t) {
|
||||
res = strcmp(furi_string_get_cstr(manufacture_code->name), "Kingates_Stylo4k");
|
||||
if(res == 0) {
|
||||
//Simple Learning
|
||||
encrypt = subghz_protocol_keeloq_common_encrypt(data, manufacture_code->key);
|
||||
encrypt = subghz_protocol_blocks_reverse_key(encrypt, 32);
|
||||
instance->generic.data_2 = encrypt << 4;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_kinggates_stylo_4k_get_upload(
|
||||
SubGhzProtocolEncoderKingGates_stylo_4k* instance,
|
||||
uint8_t btn) {
|
||||
furi_assert(instance);
|
||||
|
||||
// Gen new key
|
||||
if(subghz_protocol_kinggates_stylo_4k_gen_data(instance, btn)) {
|
||||
//ToDo if you need to add a callback to automatically update the data on the display
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
||||
size_t index = 0;
|
||||
|
||||
// Start
|
||||
instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)9500);
|
||||
|
||||
// Send header
|
||||
for(uint8_t i = 12; i > 0; i--) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short);
|
||||
}
|
||||
|
||||
// After header
|
||||
instance->encoder.upload[index - 1].duration =
|
||||
(uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 2;
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short * 2);
|
||||
|
||||
// Send key fix
|
||||
for(uint8_t i = 53; i > 0; i--) {
|
||||
if(bit_read(instance->generic.data, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short);
|
||||
}
|
||||
}
|
||||
|
||||
// Send key hop
|
||||
for(uint8_t i = 36; i > 0; i--) {
|
||||
if(bit_read(instance->generic.data_2, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
true, (uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_short);
|
||||
}
|
||||
}
|
||||
|
||||
// Set upload size after generating upload, fix it later
|
||||
|
||||
instance->encoder.size_upload = index;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderKingGates_stylo_4k* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
|
||||
subghz_protocol_kinggates_stylo_4k_remote_controller(
|
||||
&instance->generic, instance->keystore);
|
||||
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Missing Data");
|
||||
break;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i];
|
||||
}
|
||||
|
||||
subghz_protocol_encoder_kinggates_stylo_4k_get_upload(instance, instance->generic.btn);
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data_2 >> i * 8) & 0xFF;
|
||||
}
|
||||
if(!flipper_format_update_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Key");
|
||||
break;
|
||||
}
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
//
|
||||
// Decoder
|
||||
//
|
||||
void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolDecoderKingGates_stylo_4k* instance =
|
||||
malloc(sizeof(SubGhzProtocolDecoderKingGates_stylo_4k));
|
||||
instance->base.protocol = &subghz_protocol_kinggates_stylo_4k;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->keystore = subghz_environment_get_keystore(environment);
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case KingGates_stylo_4kDecoderStepReset:
|
||||
if((level) && DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) <
|
||||
subghz_protocol_kinggates_stylo_4k_const.te_delta) {
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckPreambula;
|
||||
instance->header_count++;
|
||||
}
|
||||
break;
|
||||
case KingGates_stylo_4kDecoderStepCheckPreambula:
|
||||
if((!level) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) <
|
||||
subghz_protocol_kinggates_stylo_4k_const.te_delta)) {
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
if((instance->header_count > 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long * 2) <
|
||||
subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) {
|
||||
// Found header
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckStartBit;
|
||||
} else {
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
case KingGates_stylo_4kDecoderStepCheckStartBit:
|
||||
if((level) &&
|
||||
DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short * 2) <
|
||||
subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) {
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->generic.data_2 = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
case KingGates_stylo_4kDecoderStepSaveDuration:
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_kinggates_stylo_4k_const.te_long * 3)) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) {
|
||||
instance->generic.data = instance->generic.data_2;
|
||||
instance->generic.data_2 = 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.parser_step = KingGates_stylo_4kDecoderStepReset;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->generic.data_2 = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->header_count = 0;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepCheckDuration;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
case KingGates_stylo_4kDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_short) <
|
||||
subghz_protocol_kinggates_stylo_4k_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_long) <
|
||||
subghz_protocol_kinggates_stylo_4k_const.te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_kinggates_stylo_4k_const.te_long) <
|
||||
subghz_protocol_kinggates_stylo_4k_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_kinggates_stylo_4k_const.te_short) <
|
||||
subghz_protocol_kinggates_stylo_4k_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
if(instance->decoder.decode_count_bit == 53) {
|
||||
instance->generic.data_2 = instance->decoder.decode_data;
|
||||
instance->decoder.decode_data = 0;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = KingGates_stylo_4kDecoderStepReset;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
* @param data Input encrypted data
|
||||
* @param keystore Pointer to a SubGhzKeystore* instance
|
||||
*/
|
||||
static void subghz_protocol_kinggates_stylo_4k_remote_controller(
|
||||
SubGhzBlockGeneric* instance,
|
||||
SubGhzKeystore* keystore) {
|
||||
/**
|
||||
* 9500us 12*(400/400) 2200/800|1-bit|0-bit|
|
||||
* _ _ _ __ ___ _
|
||||
* ________| |_| |_..._| |_____| |_| |___| |.....
|
||||
*
|
||||
* 1-bit 400/1100 us
|
||||
* 0-bit 1100/400 us
|
||||
*
|
||||
* The package consists of 89 bits of data, LSB first
|
||||
* Data - 1C9037F0C80000 CE280BA00
|
||||
* S[3] S[2] 1 key S[1] S[0] 2 byte always 0 Hop[3] Hop[2] Hop[1] Hop[0] 0
|
||||
* 11100100 10000001 1 0111 11110000 11001000 00000000 00000000 11001110 00101000 00001011 10100000 0000
|
||||
*
|
||||
* Encryption - keeloq Simple Learning
|
||||
* key C S[3] CNT
|
||||
* Decrypt - 0xEC270B9C => 0x E C 27 0B9C
|
||||
*
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
uint32_t hop = subghz_protocol_blocks_reverse_key(instance->data_2 >> 4, 32);
|
||||
uint64_t fix = subghz_protocol_blocks_reverse_key(instance->data, 53);
|
||||
bool ret = false;
|
||||
uint32_t decrypt = 0;
|
||||
instance->btn = (fix >> 17) & 0x0F;
|
||||
instance->serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF);
|
||||
|
||||
for
|
||||
M_EACH(manufacture_code, *subghz_keystore_get_data(keystore), SubGhzKeyArray_t) {
|
||||
if(manufacture_code->type == KEELOQ_LEARNING_SIMPLE) {
|
||||
decrypt = subghz_protocol_keeloq_common_decrypt(hop, manufacture_code->key);
|
||||
if(((decrypt >> 28) == instance->btn) && (((decrypt >> 24) & 0x0F) == 0x0C) &&
|
||||
(((decrypt >> 16) & 0xFF) == (instance->serial & 0xFF))) {
|
||||
ret = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(ret) {
|
||||
instance->cnt = decrypt & 0xFFFF;
|
||||
} else {
|
||||
instance->btn = 0;
|
||||
instance->serial = 0;
|
||||
instance->cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_kinggates_stylo_4k_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
|
||||
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_2 >> (i * 8)) & 0xFF;
|
||||
}
|
||||
|
||||
if(res && !flipper_format_write_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Data");
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_kinggates_stylo_4k_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
if(!flipper_format_read_hex(flipper_format, "Data", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Missing Data");
|
||||
break;
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
instance->generic.data_2 = instance->generic.data_2 << 8 | key_data[i];
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderKingGates_stylo_4k* instance = context;
|
||||
subghz_protocol_kinggates_stylo_4k_remote_controller(&instance->generic, instance->keystore);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s\r\n"
|
||||
"Key:0x%llX%07llX %dbit\r\n"
|
||||
"Sn:0x%08lX Btn:0x%01X\r\n"
|
||||
"Cnt:0x%04lX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data,
|
||||
instance->generic.data_2,
|
||||
instance->generic.data_count_bit,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->generic.cnt);
|
||||
}
|
||||
110
applications/main/subghz/protocols/kinggates_stylo_4k.h
Normal file
110
applications/main/subghz/protocols/kinggates_stylo_4k.h
Normal file
@@ -0,0 +1,110 @@
|
||||
#pragma once
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_KINGGATES_STYLO_4K_NAME "KingGates Stylo4k"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderKingGates_stylo_4k SubGhzProtocolDecoderKingGates_stylo_4k;
|
||||
typedef struct SubGhzProtocolEncoderKingGates_stylo_4k SubGhzProtocolEncoderKingGates_stylo_4k;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_kinggates_stylo_4k_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_kinggates_stylo_4k_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_kinggates_stylo_4k;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderKingGates_stylo_4k.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderKingGates_stylo_4k* pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderKingGates_stylo_4k.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance
|
||||
*/
|
||||
void subghz_protocol_encoder_kinggates_stylo_4k_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_kinggates_stylo_4k_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance
|
||||
*/
|
||||
void subghz_protocol_encoder_kinggates_stylo_4k_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderKingGates_stylo_4k instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_kinggates_stylo_4k_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderKingGates_stylo_4k.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderKingGates_stylo_4k* pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_kinggates_stylo_4k_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderKingGates_stylo_4k.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance
|
||||
*/
|
||||
void subghz_protocol_decoder_kinggates_stylo_4k_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderKingGates_stylo_4k.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance
|
||||
*/
|
||||
void subghz_protocol_decoder_kinggates_stylo_4k_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_kinggates_stylo_4k_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_kinggates_stylo_4k_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderKingGates_stylo_4k.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_kinggates_stylo_4k_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderKingGates_stylo_4k.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_kinggates_stylo_4k_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderKingGates_stylo_4k instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_kinggates_stylo_4k_get_string(void* context, FuriString* output);
|
||||
352
applications/main/subghz/protocols/linear.c
Normal file
352
applications/main/subghz/protocols/linear.c
Normal file
@@ -0,0 +1,352 @@
|
||||
#include "linear.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolLinear"
|
||||
|
||||
#define DIP_PATTERN "%c%c%c%c%c%c%c%c%c%c"
|
||||
#define DATA_TO_DIP(dip) \
|
||||
(dip & 0x0200 ? '1' : '0'), (dip & 0x0100 ? '1' : '0'), (dip & 0x0080 ? '1' : '0'), \
|
||||
(dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), (dip & 0x0010 ? '1' : '0'), \
|
||||
(dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), (dip & 0x0002 ? '1' : '0'), \
|
||||
(dip & 0x0001 ? '1' : '0')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_linear_const = {
|
||||
.te_short = 500,
|
||||
.te_long = 1500,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 10,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderLinear {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderLinear {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LinearDecoderStepReset = 0,
|
||||
LinearDecoderStepSaveDuration,
|
||||
LinearDecoderStepCheckDuration,
|
||||
} LinearDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_linear_decoder = {
|
||||
.alloc = subghz_protocol_decoder_linear_alloc,
|
||||
.free = subghz_protocol_decoder_linear_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_linear_feed,
|
||||
.reset = subghz_protocol_decoder_linear_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_linear_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_linear_serialize,
|
||||
.deserialize = subghz_protocol_decoder_linear_deserialize,
|
||||
.get_string = subghz_protocol_decoder_linear_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_linear_encoder = {
|
||||
.alloc = subghz_protocol_encoder_linear_alloc,
|
||||
.free = subghz_protocol_encoder_linear_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_linear_deserialize,
|
||||
.stop = subghz_protocol_encoder_linear_stop,
|
||||
.yield = subghz_protocol_encoder_linear_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_linear = {
|
||||
.name = SUBGHZ_PROTOCOL_LINEAR_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_linear_decoder,
|
||||
.encoder = &subghz_protocol_linear_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderLinear* instance = malloc(sizeof(SubGhzProtocolEncoderLinear));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_linear;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 28; //max 10bit*2 + 2 (start, stop)
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_linear_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderLinear* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderLinear instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_linear_get_upload(SubGhzProtocolEncoderLinear* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2);
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send key data
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
|
||||
if(bit_read(instance->generic.data, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short * 3);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 3);
|
||||
}
|
||||
}
|
||||
//Send end bit
|
||||
if(bit_read(instance->generic.data, 0)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short * 3);
|
||||
//Send PT_GUARD
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 42);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_linear_const.te_short);
|
||||
//Send PT_GUARD
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_linear_const.te_short * 44);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderLinear* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_linear_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_linear_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_linear_stop(void* context) {
|
||||
SubGhzProtocolEncoderLinear* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_linear_yield(void* context) {
|
||||
SubGhzProtocolEncoderLinear* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_linear_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderLinear* instance = malloc(sizeof(SubGhzProtocolDecoderLinear));
|
||||
instance->base.protocol = &subghz_protocol_linear;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_linear_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinear* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_linear_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinear* instance = context;
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinear* instance = context;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case LinearDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) <
|
||||
subghz_protocol_linear_const.te_delta * 20)) {
|
||||
//Found header Linear
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->decoder.parser_step = LinearDecoderStepSaveDuration;
|
||||
}
|
||||
break;
|
||||
case LinearDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = LinearDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case LinearDecoderStepCheckDuration:
|
||||
if(!level) { //save interval
|
||||
if(duration >= (subghz_protocol_linear_const.te_short * 5)) {
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
//checking that the duration matches the guardtime
|
||||
if((DURATION_DIFF(duration, subghz_protocol_linear_const.te_short * 42) >
|
||||
subghz_protocol_linear_const.te_delta * 20)) {
|
||||
break;
|
||||
}
|
||||
if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) <
|
||||
subghz_protocol_linear_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
} else if(
|
||||
DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) <
|
||||
subghz_protocol_linear_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
}
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_linear_const.min_count_bit_for_found) {
|
||||
instance->generic.serial = 0x0;
|
||||
instance->generic.btn = 0x0;
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_short) <
|
||||
subghz_protocol_linear_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_linear_const.te_long) <
|
||||
subghz_protocol_linear_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = LinearDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_linear_const.te_long) <
|
||||
subghz_protocol_linear_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_linear_const.te_short) <
|
||||
subghz_protocol_linear_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = LinearDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
}
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinear* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_linear_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinear* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinear* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_linear_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_linear_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinear* instance = context;
|
||||
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
|
||||
uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
|
||||
uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%08lX\r\n"
|
||||
"Yek:0x%08lX\r\n"
|
||||
"DIP:" DIP_PATTERN "\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
code_found_lo,
|
||||
code_found_reverse_lo,
|
||||
DATA_TO_DIP(code_found_lo));
|
||||
}
|
||||
107
applications/main/subghz/protocols/linear.h
Normal file
107
applications/main/subghz/protocols/linear.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_LINEAR_NAME "Linear"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderLinear SubGhzProtocolDecoderLinear;
|
||||
typedef struct SubGhzProtocolEncoderLinear SubGhzProtocolEncoderLinear;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_linear_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_linear_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_linear;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderLinear.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderLinear* pointer to a SubGhzProtocolEncoderLinear instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_linear_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderLinear.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderLinear instance
|
||||
*/
|
||||
void subghz_protocol_encoder_linear_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderLinear instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_linear_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderLinear instance
|
||||
*/
|
||||
void subghz_protocol_encoder_linear_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderLinear instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_linear_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderLinear.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderLinear* pointer to a SubGhzProtocolDecoderLinear instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_linear_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderLinear.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinear instance
|
||||
*/
|
||||
void subghz_protocol_decoder_linear_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderLinear.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinear instance
|
||||
*/
|
||||
void subghz_protocol_decoder_linear_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinear instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_linear_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinear instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_linear_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderLinear.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinear instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_linear_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderLinear.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinear instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_linear_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinear instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_linear_get_string(void* context, FuriString* output);
|
||||
359
applications/main/subghz/protocols/linear_delta3.c
Normal file
359
applications/main/subghz/protocols/linear_delta3.c
Normal file
@@ -0,0 +1,359 @@
|
||||
#include "linear_delta3.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolLinearDelta3"
|
||||
|
||||
#define DIP_PATTERN "%c%c%c%c%c%c%c%c"
|
||||
#define DATA_TO_DIP(dip) \
|
||||
(dip & 0x0080 ? '1' : '0'), (dip & 0x0040 ? '1' : '0'), (dip & 0x0020 ? '1' : '0'), \
|
||||
(dip & 0x0010 ? '1' : '0'), (dip & 0x0008 ? '1' : '0'), (dip & 0x0004 ? '1' : '0'), \
|
||||
(dip & 0x0002 ? '1' : '0'), (dip & 0x0001 ? '1' : '0')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_linear_delta3_const = {
|
||||
.te_short = 500,
|
||||
.te_long = 2000,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 8,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderLinearDelta3 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint32_t last_data;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderLinearDelta3 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
LinearDecoderStepReset = 0,
|
||||
LinearDecoderStepSaveDuration,
|
||||
LinearDecoderStepCheckDuration,
|
||||
} LinearDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder = {
|
||||
.alloc = subghz_protocol_decoder_linear_delta3_alloc,
|
||||
.free = subghz_protocol_decoder_linear_delta3_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_linear_delta3_feed,
|
||||
.reset = subghz_protocol_decoder_linear_delta3_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_linear_delta3_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_linear_delta3_serialize,
|
||||
.deserialize = subghz_protocol_decoder_linear_delta3_deserialize,
|
||||
.get_string = subghz_protocol_decoder_linear_delta3_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder = {
|
||||
.alloc = subghz_protocol_encoder_linear_delta3_alloc,
|
||||
.free = subghz_protocol_encoder_linear_delta3_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_linear_delta3_deserialize,
|
||||
.stop = subghz_protocol_encoder_linear_delta3_stop,
|
||||
.yield = subghz_protocol_encoder_linear_delta3_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_linear_delta3 = {
|
||||
.name = SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_linear_delta3_decoder,
|
||||
.encoder = &subghz_protocol_linear_delta3_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderLinearDelta3* instance =
|
||||
malloc(sizeof(SubGhzProtocolEncoderLinearDelta3));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_linear_delta3;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 16;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_linear_delta3_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderLinearDelta3* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderLinearDelta3 instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_linear_delta3_get_upload(SubGhzProtocolEncoderLinearDelta3* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2);
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send key data
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
|
||||
if(bit_read(instance->generic.data, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 7);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_linear_delta3_const.te_long);
|
||||
}
|
||||
}
|
||||
//Send end bit
|
||||
if(bit_read(instance->generic.data, 0)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_short);
|
||||
//Send PT_GUARD
|
||||
instance->encoder.upload[index] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 73);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_linear_delta3_const.te_long);
|
||||
//Send PT_GUARD
|
||||
instance->encoder.upload[index] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_linear_delta3_const.te_short * 70);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_linear_delta3_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderLinearDelta3* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_linear_delta3_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_linear_delta3_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_linear_delta3_stop(void* context) {
|
||||
SubGhzProtocolEncoderLinearDelta3* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context) {
|
||||
SubGhzProtocolEncoderLinearDelta3* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderLinearDelta3* instance =
|
||||
malloc(sizeof(SubGhzProtocolDecoderLinearDelta3));
|
||||
instance->base.protocol = &subghz_protocol_linear_delta3;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_linear_delta3_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinearDelta3* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_linear_delta3_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinearDelta3* instance = context;
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
instance->last_data = 0;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinearDelta3* instance = context;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case LinearDecoderStepReset:
|
||||
if((!level) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 70) <
|
||||
subghz_protocol_linear_delta3_const.te_delta * 24)) {
|
||||
//Found header Linear
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->decoder.parser_step = LinearDecoderStepSaveDuration;
|
||||
}
|
||||
break;
|
||||
case LinearDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = LinearDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case LinearDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if(duration >= (subghz_protocol_linear_delta3_const.te_short * 10)) {
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
if(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) <
|
||||
subghz_protocol_linear_delta3_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
} else if(
|
||||
DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) <
|
||||
subghz_protocol_linear_delta3_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
}
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_linear_delta3_const.min_count_bit_for_found) {
|
||||
if((instance->last_data == instance->decoder.decode_data) &&
|
||||
instance->last_data) {
|
||||
instance->generic.serial = 0x0;
|
||||
instance->generic.btn = 0x0;
|
||||
|
||||
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.parser_step = LinearDecoderStepSaveDuration;
|
||||
instance->last_data = instance->decoder.decode_data;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_short) <
|
||||
subghz_protocol_linear_delta3_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_short * 7) <
|
||||
subghz_protocol_linear_delta3_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = LinearDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_linear_delta3_const.te_long) <
|
||||
subghz_protocol_linear_delta3_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_linear_delta3_const.te_long) <
|
||||
subghz_protocol_linear_delta3_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = LinearDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
}
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = LinearDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinearDelta3* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8));
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_linear_delta3_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinearDelta3* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_linear_delta3_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinearDelta3* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_linear_delta3_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderLinearDelta3* instance = context;
|
||||
|
||||
uint32_t data = instance->generic.data & 0xFF;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX\r\n"
|
||||
"DIP:" DIP_PATTERN "\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
data,
|
||||
DATA_TO_DIP(data));
|
||||
}
|
||||
111
applications/main/subghz/protocols/linear_delta3.h
Normal file
111
applications/main/subghz/protocols/linear_delta3.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_LINEAR_DELTA3_NAME "LinearDelta3"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderLinearDelta3 SubGhzProtocolDecoderLinearDelta3;
|
||||
typedef struct SubGhzProtocolEncoderLinearDelta3 SubGhzProtocolEncoderLinearDelta3;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_linear_delta3_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_linear_delta3_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_linear_delta3;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderLinearDelta3.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderLinearDelta3* pointer to a SubGhzProtocolEncoderLinearDelta3 instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_linear_delta3_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderLinearDelta3.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_linear_delta3_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_linear_delta3_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_linear_delta3_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderLinearDelta3 instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_linear_delta3_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderLinearDelta3.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderLinearDelta3* pointer to a SubGhzProtocolDecoderLinearDelta3 instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_linear_delta3_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderLinearDelta3.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_linear_delta3_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderLinearDelta3.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_linear_delta3_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_linear_delta3_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_linear_delta3_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderLinearDelta3.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_linear_delta3_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderLinearDelta3.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_linear_delta3_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderLinearDelta3 instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_linear_delta3_get_string(void* context, FuriString* output);
|
||||
445
applications/main/subghz/protocols/magellan.c
Normal file
445
applications/main/subghz/protocols/magellan.c
Normal file
@@ -0,0 +1,445 @@
|
||||
#include "magellan.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolMagellan"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_magellan_const = {
|
||||
.te_short = 200,
|
||||
.te_long = 400,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 32,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderMagellan {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint16_t header_count;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderMagellan {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MagellanDecoderStepReset = 0,
|
||||
MagellanDecoderStepCheckPreambula,
|
||||
MagellanDecoderStepFoundPreambula,
|
||||
MagellanDecoderStepSaveDuration,
|
||||
MagellanDecoderStepCheckDuration,
|
||||
} MagellanDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_magellan_decoder = {
|
||||
.alloc = subghz_protocol_decoder_magellan_alloc,
|
||||
.free = subghz_protocol_decoder_magellan_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_magellan_feed,
|
||||
.reset = subghz_protocol_decoder_magellan_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_magellan_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_magellan_serialize,
|
||||
.deserialize = subghz_protocol_decoder_magellan_deserialize,
|
||||
.get_string = subghz_protocol_decoder_magellan_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_magellan_encoder = {
|
||||
.alloc = subghz_protocol_encoder_magellan_alloc,
|
||||
.free = subghz_protocol_encoder_magellan_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_magellan_deserialize,
|
||||
.stop = subghz_protocol_encoder_magellan_stop,
|
||||
.yield = subghz_protocol_encoder_magellan_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_magellan = {
|
||||
.name = SUBGHZ_PROTOCOL_MAGELLAN_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_magellan_decoder,
|
||||
.encoder = &subghz_protocol_magellan_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderMagellan* instance = malloc(sizeof(SubGhzProtocolEncoderMagellan));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_magellan;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 256;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_magellan_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderMagellan* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_magellan_get_upload(SubGhzProtocolEncoderMagellan* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
size_t index = 0;
|
||||
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short * 4);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||
for(uint8_t i = 0; i < 12; i++) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long);
|
||||
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long * 3);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long);
|
||||
|
||||
//Send key data
|
||||
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_magellan_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||
}
|
||||
}
|
||||
|
||||
//Send stop bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_magellan_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_magellan_const.te_long * 100);
|
||||
|
||||
instance->encoder.size_upload = index;
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderMagellan* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_magellan_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_magellan_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_magellan_stop(void* context) {
|
||||
SubGhzProtocolEncoderMagellan* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_magellan_yield(void* context) {
|
||||
SubGhzProtocolEncoderMagellan* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderMagellan* instance = malloc(sizeof(SubGhzProtocolDecoderMagellan));
|
||||
instance->base.protocol = &subghz_protocol_magellan;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_magellan_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMagellan* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_magellan_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMagellan* instance = context;
|
||||
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) {
|
||||
uint8_t crc = 0x00;
|
||||
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) ^ 0x31);
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
static bool subghz_protocol_magellan_check_crc(SubGhzProtocolDecoderMagellan* instance) {
|
||||
uint8_t data[3] = {
|
||||
instance->decoder.decode_data >> 24,
|
||||
instance->decoder.decode_data >> 16,
|
||||
instance->decoder.decode_data >> 8};
|
||||
return (instance->decoder.decode_data & 0xFF) ==
|
||||
subghz_protocol_magellan_crc8(data, sizeof(data));
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMagellan* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case MagellanDecoderStepReset:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <
|
||||
subghz_protocol_magellan_const.te_delta)) {
|
||||
instance->decoder.parser_step = MagellanDecoderStepCheckPreambula;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case MagellanDecoderStepCheckPreambula:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <
|
||||
subghz_protocol_magellan_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <
|
||||
subghz_protocol_magellan_const.te_delta)) {
|
||||
// Found header
|
||||
instance->header_count++;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <
|
||||
subghz_protocol_magellan_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <
|
||||
subghz_protocol_magellan_const.te_delta * 2) &&
|
||||
(instance->header_count > 10)) {
|
||||
instance->decoder.parser_step = MagellanDecoderStepFoundPreambula;
|
||||
} else {
|
||||
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MagellanDecoderStepFoundPreambula:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_magellan_const.te_short * 6) <
|
||||
subghz_protocol_magellan_const.te_delta * 3) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <
|
||||
subghz_protocol_magellan_const.te_delta * 2)) {
|
||||
instance->decoder.parser_step = MagellanDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case MagellanDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = MagellanDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case MagellanDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_short) <
|
||||
subghz_protocol_magellan_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_long) <
|
||||
subghz_protocol_magellan_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = MagellanDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_magellan_const.te_long) <
|
||||
subghz_protocol_magellan_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_magellan_const.te_short) <
|
||||
subghz_protocol_magellan_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = MagellanDecoderStepSaveDuration;
|
||||
} else if(duration >= (subghz_protocol_magellan_const.te_long * 3)) {
|
||||
//Found stop bit
|
||||
if((instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_magellan_const.min_count_bit_for_found) &&
|
||||
subghz_protocol_magellan_check_crc(instance)) {
|
||||
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->decoder.parser_step = MagellanDecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = MagellanDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_magellan_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/*
|
||||
* package 32b data 24b CRC8
|
||||
* 0x037AE4828 => 001101111010111001001000 00101000
|
||||
*
|
||||
* 0x037AE48 (flipped in reverse bit sequence) => 0x1275EC
|
||||
*
|
||||
* 0x1275EC => 0x12-event codes, 0x75EC-serial (dec 117236)
|
||||
*
|
||||
* event codes
|
||||
* bit_0: 1-Open/Motion, 0-close/ok
|
||||
* bit_1: 1-Tamper On (alarm), 0-Tamper Off (ok)
|
||||
* bit_2: ?
|
||||
* bit_3: 1-power on
|
||||
* bit_4: model type - wireless reed
|
||||
* bit_5: model type - motion sensor
|
||||
* bit_6: ?
|
||||
* bit_7: ?
|
||||
*
|
||||
*/
|
||||
uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data >> 8, 24);
|
||||
instance->serial = data_rev & 0xFFFF;
|
||||
instance->btn = (data_rev >> 16) & 0xFF;
|
||||
}
|
||||
|
||||
static void subghz_protocol_magellan_get_event_serialize(uint8_t event, FuriString* output) {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s%s%s%s%s%s%s%s",
|
||||
((event >> 4) & 0x1 ? (event & 0x1 ? " Open" : " Close") :
|
||||
(event & 0x1 ? " Motion" : " Ok")),
|
||||
((event >> 1) & 0x1 ? ", Tamper On\n(Alarm)" : ""),
|
||||
((event >> 2) & 0x1 ? ", ?" : ""),
|
||||
((event >> 3) & 0x1 ? ", Power On" : ""),
|
||||
((event >> 4) & 0x1 ? ", MT:Wireless_Reed" : ""),
|
||||
((event >> 5) & 0x1 ? ", MT:Motion_\nSensor" : ""),
|
||||
((event >> 6) & 0x1 ? ", ?" : ""),
|
||||
((event >> 7) & 0x1 ? ", ?" : ""));
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMagellan* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_magellan_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMagellan* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMagellan* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_magellan_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMagellan* instance = context;
|
||||
subghz_protocol_magellan_check_remote_controller(&instance->generic);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%08lX\r\n"
|
||||
"Sn:%03ld%03ld, Event:0x%02X\r\n"
|
||||
"Stat:",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
|
||||
(instance->generic.serial >> 8) & 0xFF,
|
||||
instance->generic.serial & 0xFF,
|
||||
instance->generic.btn);
|
||||
|
||||
subghz_protocol_magellan_get_event_serialize(instance->generic.btn, output);
|
||||
}
|
||||
107
applications/main/subghz/protocols/magellan.h
Normal file
107
applications/main/subghz/protocols/magellan.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_MAGELLAN_NAME "Magellan"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderMagellan SubGhzProtocolDecoderMagellan;
|
||||
typedef struct SubGhzProtocolEncoderMagellan SubGhzProtocolEncoderMagellan;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_magellan_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_magellan_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_magellan;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderMagellan.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderMagellan* pointer to a SubGhzProtocolEncoderMagellan instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_magellan_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderMagellan.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||
*/
|
||||
void subghz_protocol_encoder_magellan_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_magellan_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||
*/
|
||||
void subghz_protocol_encoder_magellan_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMagellan instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_magellan_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderMagellan.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderMagellan* pointer to a SubGhzProtocolDecoderMagellan instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_magellan_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderMagellan.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||
*/
|
||||
void subghz_protocol_decoder_magellan_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderMagellan.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||
*/
|
||||
void subghz_protocol_decoder_magellan_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_magellan_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_magellan_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderMagellan.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_magellan_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderMagellan.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_magellan_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMagellan instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_magellan_get_string(void* context, FuriString* output);
|
||||
393
applications/main/subghz/protocols/marantec.c
Normal file
393
applications/main/subghz/protocols/marantec.c
Normal file
@@ -0,0 +1,393 @@
|
||||
#include "marantec.h"
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include <lib/toolbox/manchester_encoder.h>
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolMarantec"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_marantec_const = {
|
||||
.te_short = 1000,
|
||||
.te_long = 2000,
|
||||
.te_delta = 200,
|
||||
.min_count_bit_for_found = 49,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderMarantec {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
ManchesterState manchester_saved_state;
|
||||
uint16_t header_count;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderMarantec {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MarantecDecoderStepReset = 0,
|
||||
MarantecDecoderFoundHeader,
|
||||
MarantecDecoderStepDecoderData,
|
||||
} MarantecDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_marantec_decoder = {
|
||||
.alloc = subghz_protocol_decoder_marantec_alloc,
|
||||
.free = subghz_protocol_decoder_marantec_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_marantec_feed,
|
||||
.reset = subghz_protocol_decoder_marantec_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_marantec_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_marantec_serialize,
|
||||
.deserialize = subghz_protocol_decoder_marantec_deserialize,
|
||||
.get_string = subghz_protocol_decoder_marantec_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_marantec_encoder = {
|
||||
.alloc = subghz_protocol_encoder_marantec_alloc,
|
||||
.free = subghz_protocol_encoder_marantec_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_marantec_deserialize,
|
||||
.stop = subghz_protocol_encoder_marantec_stop,
|
||||
.yield = subghz_protocol_encoder_marantec_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_marantec = {
|
||||
.name = SUBGHZ_PROTOCOL_MARANTEC_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_marantec_decoder,
|
||||
.encoder = &subghz_protocol_marantec_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderMarantec* instance = malloc(sizeof(SubGhzProtocolEncoderMarantec));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_marantec;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 256;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_marantec_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderMarantec* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static LevelDuration
|
||||
subghz_protocol_encoder_marantec_add_duration_to_upload(ManchesterEncoderResult result) {
|
||||
LevelDuration data = {.duration = 0, .level = 0};
|
||||
switch(result) {
|
||||
case ManchesterEncoderResultShortLow:
|
||||
data.duration = subghz_protocol_marantec_const.te_short;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongLow:
|
||||
data.duration = subghz_protocol_marantec_const.te_long;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongHigh:
|
||||
data.duration = subghz_protocol_marantec_const.te_long;
|
||||
data.level = true;
|
||||
break;
|
||||
case ManchesterEncoderResultShortHigh:
|
||||
data.duration = subghz_protocol_marantec_const.te_short;
|
||||
data.level = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
furi_crash("SubGhz: ManchesterEncoderResult is incorrect.");
|
||||
break;
|
||||
}
|
||||
return level_duration_make(data.level, data.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderMarantec instance
|
||||
*/
|
||||
static void subghz_protocol_encoder_marantec_get_upload(SubGhzProtocolEncoderMarantec* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
|
||||
ManchesterEncoderState enc_state;
|
||||
manchester_encoder_reset(&enc_state);
|
||||
ManchesterEncoderResult result;
|
||||
|
||||
if(!manchester_encoder_advance(
|
||||
&enc_state,
|
||||
bit_read(instance->generic.data, instance->generic.data_count_bit - 1),
|
||||
&result)) {
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_marantec_add_duration_to_upload(result);
|
||||
manchester_encoder_advance(
|
||||
&enc_state,
|
||||
bit_read(instance->generic.data, instance->generic.data_count_bit - 1),
|
||||
&result);
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_marantec_const.te_long * 5);
|
||||
|
||||
for(uint8_t i = instance->generic.data_count_bit - 1; i > 0; i--) {
|
||||
if(!manchester_encoder_advance(
|
||||
&enc_state, bit_read(instance->generic.data, i - 1), &result)) {
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_marantec_add_duration_to_upload(result);
|
||||
manchester_encoder_advance(
|
||||
&enc_state, bit_read(instance->generic.data, i - 1), &result);
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_marantec_add_duration_to_upload(result);
|
||||
}
|
||||
instance->encoder.upload[index] = subghz_protocol_encoder_marantec_add_duration_to_upload(
|
||||
manchester_encoder_finish(&enc_state));
|
||||
if(level_duration_get_level(instance->encoder.upload[index])) {
|
||||
index++;
|
||||
}
|
||||
instance->encoder.size_upload = index;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_marantec_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) ^ 0x1D);
|
||||
else
|
||||
crc <<= 1;
|
||||
}
|
||||
}
|
||||
return crc;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
instance->btn = (instance->data >> 16) & 0xF;
|
||||
instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF);
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderMarantec* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_marantec_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
subghz_protocol_marantec_remote_controller(&instance->generic);
|
||||
subghz_protocol_encoder_marantec_get_upload(instance);
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_marantec_stop(void* context) {
|
||||
SubGhzProtocolEncoderMarantec* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_marantec_yield(void* context) {
|
||||
SubGhzProtocolEncoderMarantec* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderMarantec* instance = malloc(sizeof(SubGhzProtocolDecoderMarantec));
|
||||
instance->base.protocol = &subghz_protocol_marantec;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_marantec_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMarantec* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_marantec_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMarantec* instance = context;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_marantec_feed(void* context, bool level, volatile uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMarantec* instance = context;
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case MarantecDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long * 5) <
|
||||
subghz_protocol_marantec_const.te_delta * 8)) {
|
||||
//Found header marantec
|
||||
instance->decoder.parser_step = MarantecDecoderStepDecoderData;
|
||||
instance->decoder.decode_data = 1;
|
||||
instance->decoder.decode_count_bit = 1;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
case MarantecDecoderStepDecoderData:
|
||||
if(!level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) <
|
||||
subghz_protocol_marantec_const.te_delta) {
|
||||
event = ManchesterEventShortLow;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) <
|
||||
subghz_protocol_marantec_const.te_delta) {
|
||||
event = ManchesterEventLongLow;
|
||||
} else if(
|
||||
duration >= ((uint32_t)subghz_protocol_marantec_const.te_long * 2 +
|
||||
subghz_protocol_marantec_const.te_delta)) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_marantec_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 = 1;
|
||||
instance->decoder.decode_count_bit = 1;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
} else {
|
||||
instance->decoder.parser_step = MarantecDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_marantec_const.te_short) <
|
||||
subghz_protocol_marantec_const.te_delta) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_marantec_const.te_long) <
|
||||
subghz_protocol_marantec_const.te_delta) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
instance->decoder.parser_step = MarantecDecoderStepReset;
|
||||
}
|
||||
}
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = manchester_advance(
|
||||
instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;
|
||||
instance->decoder.decode_count_bit++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMarantec* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_marantec_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMarantec* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMarantec* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_marantec_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMarantec* instance = context;
|
||||
subghz_protocol_marantec_remote_controller(&instance->generic);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"Sn:0x%07lX \r\n"
|
||||
"Btn:%X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data >> 32),
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
|
||||
instance->generic.serial,
|
||||
instance->generic.btn);
|
||||
}
|
||||
107
applications/main/subghz/protocols/marantec.h
Normal file
107
applications/main/subghz/protocols/marantec.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_MARANTEC_NAME "Marantec"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderMarantec SubGhzProtocolDecoderMarantec;
|
||||
typedef struct SubGhzProtocolEncoderMarantec SubGhzProtocolEncoderMarantec;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_marantec_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_marantec_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_marantec;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderMarantec.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderMarantec* pointer to a SubGhzProtocolEncoderMarantec instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_marantec_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderMarantec.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMarantec instance
|
||||
*/
|
||||
void subghz_protocol_encoder_marantec_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMarantec instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_marantec_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMarantec instance
|
||||
*/
|
||||
void subghz_protocol_encoder_marantec_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMarantec instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_marantec_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderMarantec.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderMarantec* pointer to a SubGhzProtocolDecoderMarantec instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_marantec_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderMarantec.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMarantec instance
|
||||
*/
|
||||
void subghz_protocol_decoder_marantec_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderMarantec.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMarantec instance
|
||||
*/
|
||||
void subghz_protocol_decoder_marantec_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMarantec instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_marantec_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMarantec instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_marantec_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderMarantec.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMarantec instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_marantec_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderMarantec.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMarantec instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_marantec_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMarantec instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output);
|
||||
429
applications/main/subghz/protocols/megacode.c
Normal file
429
applications/main/subghz/protocols/megacode.c
Normal file
@@ -0,0 +1,429 @@
|
||||
#include "megacode.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://wiki.cuvoodoo.info/doku.php?id=megacode
|
||||
* https://wiki.cuvoodoo.info/lib/exe/fetch.php?media=megacode:megacode_1.pdf
|
||||
* https://fccid.io/EF4ACP00872/Test-Report/Megacode-2-112615.pdf
|
||||
* https://github.com/aaronsp777/megadecoder
|
||||
* https://github.com/rjmendez/Linear_keyfob
|
||||
* https://github.com/j07rdi/Linear_MegaCode_Garage_Remote
|
||||
*
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocolMegaCode"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_megacode_const = {
|
||||
.te_short = 1000,
|
||||
.te_long = 1000,
|
||||
.te_delta = 200,
|
||||
.min_count_bit_for_found = 24,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderMegaCode {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint8_t last_bit;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderMegaCode {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
MegaCodeDecoderStepReset = 0,
|
||||
MegaCodeDecoderStepFoundStartBit,
|
||||
MegaCodeDecoderStepSaveDuration,
|
||||
MegaCodeDecoderStepCheckDuration,
|
||||
} MegaCodeDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_megacode_decoder = {
|
||||
.alloc = subghz_protocol_decoder_megacode_alloc,
|
||||
.free = subghz_protocol_decoder_megacode_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_megacode_feed,
|
||||
.reset = subghz_protocol_decoder_megacode_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_megacode_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_megacode_serialize,
|
||||
.deserialize = subghz_protocol_decoder_megacode_deserialize,
|
||||
.get_string = subghz_protocol_decoder_megacode_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_megacode_encoder = {
|
||||
.alloc = subghz_protocol_encoder_megacode_alloc,
|
||||
.free = subghz_protocol_encoder_megacode_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_megacode_deserialize,
|
||||
.stop = subghz_protocol_encoder_megacode_stop,
|
||||
.yield = subghz_protocol_encoder_megacode_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_megacode = {
|
||||
.name = SUBGHZ_PROTOCOL_MEGACODE_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_megacode_decoder,
|
||||
.encoder = &subghz_protocol_megacode_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderMegaCode* instance = malloc(sizeof(SubGhzProtocolEncoderMegaCode));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_megacode;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 52;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_megacode_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderMegaCode* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderMegaCode instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_megacode_get_upload(SubGhzProtocolEncoderMegaCode* instance) {
|
||||
furi_assert(instance);
|
||||
uint8_t last_bit = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2);
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
/*
|
||||
* Due to the nature of the protocol
|
||||
*
|
||||
* 00000 1
|
||||
* _____|-| = 1 becomes
|
||||
*
|
||||
* 00 1 000
|
||||
* __|-|___ = 0 becomes
|
||||
*
|
||||
* it's easier for us to generate an upload backwards
|
||||
*/
|
||||
|
||||
size_t index = size_upload - 1;
|
||||
|
||||
// Send end level
|
||||
instance->encoder.upload[index--] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short);
|
||||
if(bit_read(instance->generic.data, 0)) {
|
||||
last_bit = 1;
|
||||
} else {
|
||||
last_bit = 0;
|
||||
}
|
||||
|
||||
//Send key data
|
||||
for(uint8_t i = 1; i < instance->generic.data_count_bit; i++) {
|
||||
if(bit_read(instance->generic.data, i)) {
|
||||
//if bit 1
|
||||
instance->encoder.upload[index--] = level_duration_make(
|
||||
false,
|
||||
last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 5 :
|
||||
(uint32_t)subghz_protocol_megacode_const.te_short * 2);
|
||||
last_bit = 1;
|
||||
} else {
|
||||
//if bit 0
|
||||
instance->encoder.upload[index--] = level_duration_make(
|
||||
false,
|
||||
last_bit ? (uint32_t)subghz_protocol_megacode_const.te_short * 8 :
|
||||
(uint32_t)subghz_protocol_megacode_const.te_short * 5);
|
||||
last_bit = 0;
|
||||
}
|
||||
instance->encoder.upload[index--] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_megacode_const.te_short);
|
||||
}
|
||||
|
||||
//Send PT_GUARD
|
||||
if(bit_read(instance->generic.data, 0)) {
|
||||
//if end bit 1
|
||||
instance->encoder.upload[index] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 11);
|
||||
} else {
|
||||
//if end bit 1
|
||||
instance->encoder.upload[index] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_megacode_const.te_short * 14);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderMegaCode* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_megacode_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_megacode_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_megacode_stop(void* context) {
|
||||
SubGhzProtocolEncoderMegaCode* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_megacode_yield(void* context) {
|
||||
SubGhzProtocolEncoderMegaCode* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderMegaCode* instance = malloc(sizeof(SubGhzProtocolDecoderMegaCode));
|
||||
instance->base.protocol = &subghz_protocol_megacode;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_megacode_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMegaCode* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_megacode_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMegaCode* instance = context;
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMegaCode* instance = context;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case MegaCodeDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short * 13) <
|
||||
subghz_protocol_megacode_const.te_delta * 17)) { //10..16ms
|
||||
//Found header MegaCode
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case MegaCodeDecoderStepFoundStartBit:
|
||||
if(level && (DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) <
|
||||
subghz_protocol_megacode_const.te_delta)) {
|
||||
//Found start bit MegaCode
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->last_bit = 1;
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case MegaCodeDecoderStepSaveDuration:
|
||||
if(!level) { //save interval
|
||||
if(duration >= (subghz_protocol_megacode_const.te_short * 10)) {
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepReset;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_megacode_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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
if(!instance->last_bit) {
|
||||
instance->decoder.te_last = duration - subghz_protocol_megacode_const.te_short * 3;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
}
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case MegaCodeDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 5) <
|
||||
subghz_protocol_megacode_const.te_delta * 5) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) <
|
||||
subghz_protocol_megacode_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->last_bit = 1;
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_megacode_const.te_short * 2) <
|
||||
subghz_protocol_megacode_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_megacode_const.te_short) <
|
||||
subghz_protocol_megacode_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->last_bit = 0;
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepSaveDuration;
|
||||
} else
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.parser_step = MegaCodeDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_megacode_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/*
|
||||
* Short: 1000 µs
|
||||
* Long: 1000 µs
|
||||
* Gap: 11000 .. 14000 µs
|
||||
* A Linear Megacode transmission consists of 24 bit frames starting with
|
||||
* the most significant bit and ending with the least. Each of the 24 bit
|
||||
* frames is 6 milliseconds wide and always contains a single 1 millisecond
|
||||
* pulse. A frame with more than 1 pulse or a frame with no pulse is invalid
|
||||
* and a receiver should reset and begin watching for another start bit.
|
||||
* Start bit is always 1.
|
||||
*
|
||||
*
|
||||
* Example (I created with my own remote):
|
||||
* Remote “A” has the code “17316”, a Facility Code of “3”, and a single button.
|
||||
* Start bit (S) = 1
|
||||
* Facility Code 3 (F) = 0011
|
||||
* Remote Code (Key) 17316 = 43A4 = 0100001110100100
|
||||
* Button (Btn) 1 = 001
|
||||
* S F Key Btn
|
||||
* Result = 1|0011|0100001110100100|001
|
||||
*
|
||||
* 00000 1
|
||||
* _____|-| = 1 becomes
|
||||
*
|
||||
* 00 1 000
|
||||
* __|-|___ = 0 becomes
|
||||
*
|
||||
* The device needs to transmit with a 9000 µs gap between retransmissions:
|
||||
* 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001
|
||||
* 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001
|
||||
* wait 9000 µs
|
||||
* 000001 001000 001000 000001 000001 001000 000001 001000 001000 001000 001000 000001
|
||||
* 000001 000001 001000 000001 001000 001000 000001 001000 001000 001000 001000 000001
|
||||
*
|
||||
*/
|
||||
if((instance->data >> 23) == 1) {
|
||||
instance->serial = (instance->data >> 3) & 0xFFFF;
|
||||
instance->btn = instance->data & 0b111;
|
||||
instance->cnt = (instance->data >> 19) & 0b1111;
|
||||
} else {
|
||||
instance->serial = 0;
|
||||
instance->btn = 0;
|
||||
instance->cnt = 0;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMegaCode* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_megacode_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMegaCode* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMegaCode* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_megacode_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderMegaCode* instance = context;
|
||||
subghz_protocol_megacode_check_remote_controller(&instance->generic);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%06lX\r\n"
|
||||
"Sn:0x%04lX - %lu\r\n"
|
||||
"Facility:%lX Btn:%X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)instance->generic.data,
|
||||
instance->generic.serial,
|
||||
instance->generic.serial,
|
||||
instance->generic.cnt,
|
||||
instance->generic.btn);
|
||||
}
|
||||
107
applications/main/subghz/protocols/megacode.h
Normal file
107
applications/main/subghz/protocols/megacode.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_MEGACODE_NAME "MegaCode"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderMegaCode SubGhzProtocolDecoderMegaCode;
|
||||
typedef struct SubGhzProtocolEncoderMegaCode SubGhzProtocolEncoderMegaCode;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_megacode_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_megacode_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_megacode;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderMegaCode.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderMegaCode* pointer to a SubGhzProtocolEncoderMegaCode instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_megacode_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderMegaCode.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMegaCode instance
|
||||
*/
|
||||
void subghz_protocol_encoder_megacode_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMegaCode instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_megacode_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMegaCode instance
|
||||
*/
|
||||
void subghz_protocol_encoder_megacode_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderMegaCode instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_megacode_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderMegaCode.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderMegaCode* pointer to a SubGhzProtocolDecoderMegaCode instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_megacode_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderMegaCode.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
|
||||
*/
|
||||
void subghz_protocol_decoder_megacode_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderMegaCode.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
|
||||
*/
|
||||
void subghz_protocol_decoder_megacode_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_megacode_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_megacode_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderMegaCode.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_megacode_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderMegaCode.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderMegaCode instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_megacode_get_string(void* context, FuriString* output);
|
||||
397
applications/main/subghz/protocols/nero_radio.c
Normal file
397
applications/main/subghz/protocols/nero_radio.c
Normal file
@@ -0,0 +1,397 @@
|
||||
#include "nero_radio.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolNeroRadio"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_nero_radio_const = {
|
||||
.te_short = 200,
|
||||
.te_long = 400,
|
||||
.te_delta = 80,
|
||||
.min_count_bit_for_found = 56,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderNeroRadio {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint16_t header_count;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderNeroRadio {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NeroRadioDecoderStepReset = 0,
|
||||
NeroRadioDecoderStepCheckPreambula,
|
||||
NeroRadioDecoderStepSaveDuration,
|
||||
NeroRadioDecoderStepCheckDuration,
|
||||
} NeroRadioDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder = {
|
||||
.alloc = subghz_protocol_decoder_nero_radio_alloc,
|
||||
.free = subghz_protocol_decoder_nero_radio_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_nero_radio_feed,
|
||||
.reset = subghz_protocol_decoder_nero_radio_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_nero_radio_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_nero_radio_serialize,
|
||||
.deserialize = subghz_protocol_decoder_nero_radio_deserialize,
|
||||
.get_string = subghz_protocol_decoder_nero_radio_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder = {
|
||||
.alloc = subghz_protocol_encoder_nero_radio_alloc,
|
||||
.free = subghz_protocol_encoder_nero_radio_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_nero_radio_deserialize,
|
||||
.stop = subghz_protocol_encoder_nero_radio_stop,
|
||||
.yield = subghz_protocol_encoder_nero_radio_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_nero_radio = {
|
||||
.name = SUBGHZ_PROTOCOL_NERO_RADIO_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_nero_radio_decoder,
|
||||
.encoder = &subghz_protocol_nero_radio_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolEncoderNeroRadio));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_nero_radio;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 256;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_nero_radio_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderNeroRadio* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderNeroRadio instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_nero_radio_get_upload(SubGhzProtocolEncoderNeroRadio* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = 49 * 2 + 2 + (instance->generic.data_count_bit * 2);
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send header
|
||||
for(uint8_t i = 0; i < 49; i++) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short);
|
||||
}
|
||||
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short * 4);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short);
|
||||
|
||||
//Send key data
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 1; i--) {
|
||||
if(bit_read(instance->generic.data, i - 1)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_long);
|
||||
}
|
||||
}
|
||||
if(bit_read(instance->generic.data, 0)) {
|
||||
//send bit 1
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_radio_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_radio_const.te_short * 37);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderNeroRadio* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_nero_radio_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_nero_radio_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_nero_radio_stop(void* context) {
|
||||
SubGhzProtocolEncoderNeroRadio* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_nero_radio_yield(void* context) {
|
||||
SubGhzProtocolEncoderNeroRadio* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_nero_radio_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderNeroRadio* instance = malloc(sizeof(SubGhzProtocolDecoderNeroRadio));
|
||||
instance->base.protocol = &subghz_protocol_nero_radio;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nero_radio_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroRadio* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nero_radio_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroRadio* instance = context;
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroRadio* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case NeroRadioDecoderStepReset:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) <
|
||||
subghz_protocol_nero_radio_const.te_delta)) {
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepCheckPreambula;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
case NeroRadioDecoderStepCheckPreambula:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) <
|
||||
subghz_protocol_nero_radio_const.te_delta) ||
|
||||
(DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short * 4) <
|
||||
subghz_protocol_nero_radio_const.te_delta)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepReset;
|
||||
}
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) <
|
||||
subghz_protocol_nero_radio_const.te_delta) {
|
||||
if(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) <
|
||||
subghz_protocol_nero_radio_const.te_delta) {
|
||||
// Found header
|
||||
instance->header_count++;
|
||||
break;
|
||||
} else if(
|
||||
DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short * 4) <
|
||||
subghz_protocol_nero_radio_const.te_delta) {
|
||||
// Found start bit
|
||||
if(instance->header_count > 40) {
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case NeroRadioDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case NeroRadioDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_nero_radio_const.te_short * 10 +
|
||||
subghz_protocol_nero_radio_const.te_delta * 2)) {
|
||||
//Found stop bit
|
||||
if(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) <
|
||||
subghz_protocol_nero_radio_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
} else if(
|
||||
DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) <
|
||||
subghz_protocol_nero_radio_const.te_delta) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
}
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepReset;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_nero_radio_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->decoder.parser_step = NeroRadioDecoderStepReset; //-V1048
|
||||
break;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nero_radio_const.te_short) <
|
||||
subghz_protocol_nero_radio_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_long) <
|
||||
subghz_protocol_nero_radio_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nero_radio_const.te_long) <
|
||||
subghz_protocol_nero_radio_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_nero_radio_const.te_short) <
|
||||
subghz_protocol_nero_radio_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroRadioDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroRadio* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_nero_radio_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroRadio* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroRadio* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_nero_radio_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroRadio* instance = context;
|
||||
|
||||
uint32_t code_found_hi = instance->generic.data >> 32;
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
|
||||
uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
|
||||
uint32_t code_found_reverse_hi = code_found_reverse >> 32;
|
||||
uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"Yek:0x%lX%08lX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
code_found_hi,
|
||||
code_found_lo,
|
||||
code_found_reverse_hi,
|
||||
code_found_reverse_lo);
|
||||
}
|
||||
107
applications/main/subghz/protocols/nero_radio.h
Normal file
107
applications/main/subghz/protocols/nero_radio.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_NERO_RADIO_NAME "Nero Radio"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderNeroRadio SubGhzProtocolDecoderNeroRadio;
|
||||
typedef struct SubGhzProtocolEncoderNeroRadio SubGhzProtocolEncoderNeroRadio;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_nero_radio_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_nero_radio_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_nero_radio;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderNeroRadio.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderNeroRadio* pointer to a SubGhzProtocolEncoderNeroRadio instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_nero_radio_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderNeroRadio.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance
|
||||
*/
|
||||
void subghz_protocol_encoder_nero_radio_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance
|
||||
*/
|
||||
void subghz_protocol_encoder_nero_radio_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNeroRadio instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_nero_radio_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderNeroRadio.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderNeroRadio* pointer to a SubGhzProtocolDecoderNeroRadio instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_nero_radio_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderNeroRadio.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance
|
||||
*/
|
||||
void subghz_protocol_decoder_nero_radio_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderNeroRadio.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance
|
||||
*/
|
||||
void subghz_protocol_decoder_nero_radio_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_nero_radio_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_nero_radio_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderNeroRadio.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_nero_radio_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderNeroRadio.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_nero_radio_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroRadio instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output);
|
||||
382
applications/main/subghz/protocols/nero_sketch.c
Normal file
382
applications/main/subghz/protocols/nero_sketch.c
Normal file
@@ -0,0 +1,382 @@
|
||||
#include "nero_sketch.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolNeroSketch"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_nero_sketch_const = {
|
||||
.te_short = 330,
|
||||
.te_long = 660,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 40,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderNeroSketch {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint16_t header_count;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderNeroSketch {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NeroSketchDecoderStepReset = 0,
|
||||
NeroSketchDecoderStepCheckPreambula,
|
||||
NeroSketchDecoderStepSaveDuration,
|
||||
NeroSketchDecoderStepCheckDuration,
|
||||
} NeroSketchDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder = {
|
||||
.alloc = subghz_protocol_decoder_nero_sketch_alloc,
|
||||
.free = subghz_protocol_decoder_nero_sketch_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_nero_sketch_feed,
|
||||
.reset = subghz_protocol_decoder_nero_sketch_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_nero_sketch_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_nero_sketch_serialize,
|
||||
.deserialize = subghz_protocol_decoder_nero_sketch_deserialize,
|
||||
.get_string = subghz_protocol_decoder_nero_sketch_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder = {
|
||||
.alloc = subghz_protocol_encoder_nero_sketch_alloc,
|
||||
.free = subghz_protocol_encoder_nero_sketch_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_nero_sketch_deserialize,
|
||||
.stop = subghz_protocol_encoder_nero_sketch_stop,
|
||||
.yield = subghz_protocol_encoder_nero_sketch_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_nero_sketch = {
|
||||
.name = SUBGHZ_PROTOCOL_NERO_SKETCH_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_nero_sketch_decoder,
|
||||
.encoder = &subghz_protocol_nero_sketch_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolEncoderNeroSketch));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_nero_sketch;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 256;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_nero_sketch_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderNeroSketch* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderNeroSketch instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_nero_sketch_get_upload(SubGhzProtocolEncoderNeroSketch* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
size_t index = 0;
|
||||
size_t size_upload = 47 * 2 + 2 + (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send header
|
||||
for(uint8_t i = 0; i < 47; i++) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short);
|
||||
}
|
||||
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 4);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short);
|
||||
|
||||
//Send key data
|
||||
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_nero_sketch_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_long);
|
||||
}
|
||||
}
|
||||
|
||||
//Send stop bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nero_sketch_const.te_short * 3);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nero_sketch_const.te_short);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderNeroSketch* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_nero_sketch_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_nero_sketch_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_nero_sketch_stop(void* context) {
|
||||
SubGhzProtocolEncoderNeroSketch* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context) {
|
||||
SubGhzProtocolEncoderNeroSketch* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_nero_sketch_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderNeroSketch* instance = malloc(sizeof(SubGhzProtocolDecoderNeroSketch));
|
||||
instance->base.protocol = &subghz_protocol_nero_sketch;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nero_sketch_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroSketch* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nero_sketch_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroSketch* instance = context;
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroSketch* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case NeroSketchDecoderStepReset:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) <
|
||||
subghz_protocol_nero_sketch_const.te_delta)) {
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepCheckPreambula;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
case NeroSketchDecoderStepCheckPreambula:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) <
|
||||
subghz_protocol_nero_sketch_const.te_delta) ||
|
||||
(DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short * 4) <
|
||||
subghz_protocol_nero_sketch_const.te_delta)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepReset;
|
||||
}
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) <
|
||||
subghz_protocol_nero_sketch_const.te_delta) {
|
||||
if(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) <
|
||||
subghz_protocol_nero_sketch_const.te_delta) {
|
||||
// Found header
|
||||
instance->header_count++;
|
||||
break;
|
||||
} else if(
|
||||
DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short * 4) <
|
||||
subghz_protocol_nero_sketch_const.te_delta) {
|
||||
// Found start bit
|
||||
if(instance->header_count > 40) {
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case NeroSketchDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(duration >= (subghz_protocol_nero_sketch_const.te_short * 2 +
|
||||
subghz_protocol_nero_sketch_const.te_delta * 2)) {
|
||||
//Found stop bit
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepReset;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_nero_sketch_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;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepCheckDuration;
|
||||
}
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case NeroSketchDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_short) <
|
||||
subghz_protocol_nero_sketch_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_long) <
|
||||
subghz_protocol_nero_sketch_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nero_sketch_const.te_long) <
|
||||
subghz_protocol_nero_sketch_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_nero_sketch_const.te_short) <
|
||||
subghz_protocol_nero_sketch_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = NeroSketchDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroSketch* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_nero_sketch_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroSketch* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroSketch* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_nero_sketch_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNeroSketch* instance = context;
|
||||
|
||||
uint32_t code_found_hi = instance->generic.data >> 32;
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
|
||||
uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
|
||||
uint32_t code_found_reverse_hi = code_found_reverse >> 32;
|
||||
uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"Yek:0x%lX%08lX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
code_found_hi,
|
||||
code_found_lo,
|
||||
code_found_reverse_hi,
|
||||
code_found_reverse_lo);
|
||||
}
|
||||
107
applications/main/subghz/protocols/nero_sketch.h
Normal file
107
applications/main/subghz/protocols/nero_sketch.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_NERO_SKETCH_NAME "Nero Sketch"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderNeroSketch SubGhzProtocolDecoderNeroSketch;
|
||||
typedef struct SubGhzProtocolEncoderNeroSketch SubGhzProtocolEncoderNeroSketch;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_nero_sketch_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_nero_sketch_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_nero_sketch;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderNeroSketch.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderNeroSketch* pointer to a SubGhzProtocolEncoderNeroSketch instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_nero_sketch_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderNeroSketch.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance
|
||||
*/
|
||||
void subghz_protocol_encoder_nero_sketch_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance
|
||||
*/
|
||||
void subghz_protocol_encoder_nero_sketch_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNeroSketch instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_nero_sketch_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderNeroSketch.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderNeroSketch* pointer to a SubGhzProtocolDecoderNeroSketch instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_nero_sketch_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderNeroSketch.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance
|
||||
*/
|
||||
void subghz_protocol_decoder_nero_sketch_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderNeroSketch.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance
|
||||
*/
|
||||
void subghz_protocol_decoder_nero_sketch_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_nero_sketch_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_nero_sketch_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderNeroSketch.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_nero_sketch_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderNeroSketch.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_nero_sketch_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNeroSketch instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_nero_sketch_get_string(void* context, FuriString* output);
|
||||
330
applications/main/subghz/protocols/nice_flo.c
Normal file
330
applications/main/subghz/protocols/nice_flo.c
Normal file
@@ -0,0 +1,330 @@
|
||||
#include "nice_flo.h"
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolNiceFLO"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_nice_flo_const = {
|
||||
.te_short = 700,
|
||||
.te_long = 1400,
|
||||
.te_delta = 200,
|
||||
.min_count_bit_for_found = 12,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderNiceFlo {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderNiceFlo {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NiceFloDecoderStepReset = 0,
|
||||
NiceFloDecoderStepFoundStartBit,
|
||||
NiceFloDecoderStepSaveDuration,
|
||||
NiceFloDecoderStepCheckDuration,
|
||||
} NiceFloDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder = {
|
||||
.alloc = subghz_protocol_decoder_nice_flo_alloc,
|
||||
.free = subghz_protocol_decoder_nice_flo_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_nice_flo_feed,
|
||||
.reset = subghz_protocol_decoder_nice_flo_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_nice_flo_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_nice_flo_serialize,
|
||||
.deserialize = subghz_protocol_decoder_nice_flo_deserialize,
|
||||
.get_string = subghz_protocol_decoder_nice_flo_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder = {
|
||||
.alloc = subghz_protocol_encoder_nice_flo_alloc,
|
||||
.free = subghz_protocol_encoder_nice_flo_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_nice_flo_deserialize,
|
||||
.stop = subghz_protocol_encoder_nice_flo_stop,
|
||||
.yield = subghz_protocol_encoder_nice_flo_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_nice_flo = {
|
||||
.name = SUBGHZ_PROTOCOL_NICE_FLO_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_nice_flo_decoder,
|
||||
.encoder = &subghz_protocol_nice_flo_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolEncoderNiceFlo));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_nice_flo;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_nice_flo_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderNiceFlo* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderNiceFlo instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_nice_flo_get_upload(SubGhzProtocolEncoderNiceFlo* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short * 36);
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short);
|
||||
//Send key data
|
||||
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(false, (uint32_t)subghz_protocol_nice_flo_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nice_flo_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nice_flo_const.te_long);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderNiceFlo* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if((instance->generic.data_count_bit <
|
||||
subghz_protocol_nice_flo_const.min_count_bit_for_found) ||
|
||||
(instance->generic.data_count_bit >
|
||||
2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_nice_flo_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_nice_flo_stop(void* context) {
|
||||
SubGhzProtocolEncoderNiceFlo* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_nice_flo_yield(void* context) {
|
||||
SubGhzProtocolEncoderNiceFlo* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_nice_flo_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderNiceFlo* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlo));
|
||||
instance->base.protocol = &subghz_protocol_nice_flo;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nice_flo_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlo* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nice_flo_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlo* instance = context;
|
||||
instance->decoder.parser_step = NiceFloDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlo* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case NiceFloDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short * 36) <
|
||||
subghz_protocol_nice_flo_const.te_delta * 36)) {
|
||||
//Found header Nice Flo
|
||||
instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case NiceFloDecoderStepFoundStartBit:
|
||||
if(!level) {
|
||||
break;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) <
|
||||
subghz_protocol_nice_flo_const.te_delta) {
|
||||
//Found start bit Nice Flo
|
||||
instance->decoder.parser_step = NiceFloDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = NiceFloDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case NiceFloDecoderStepSaveDuration:
|
||||
if(!level) { //save interval
|
||||
if(duration >= (subghz_protocol_nice_flo_const.te_short * 4)) {
|
||||
instance->decoder.parser_step = NiceFloDecoderStepFoundStartBit;
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_nice_flo_const.min_count_bit_for_found) {
|
||||
instance->generic.serial = 0x0;
|
||||
instance->generic.btn = 0x0;
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
}
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = NiceFloDecoderStepCheckDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = NiceFloDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case NiceFloDecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_short) <
|
||||
subghz_protocol_nice_flo_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_long) <
|
||||
subghz_protocol_nice_flo_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = NiceFloDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_nice_flo_const.te_long) <
|
||||
subghz_protocol_nice_flo_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_nice_flo_const.te_short) <
|
||||
subghz_protocol_nice_flo_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = NiceFloDecoderStepSaveDuration;
|
||||
} else
|
||||
instance->decoder.parser_step = NiceFloDecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.parser_step = NiceFloDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlo* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_nice_flo_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlo* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlo* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if((instance->generic.data_count_bit <
|
||||
subghz_protocol_nice_flo_const.min_count_bit_for_found) ||
|
||||
(instance->generic.data_count_bit >
|
||||
2 * subghz_protocol_nice_flo_const.min_count_bit_for_found)) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlo* instance = context;
|
||||
|
||||
uint32_t code_found_lo = instance->generic.data & 0x00000000ffffffff;
|
||||
uint64_t code_found_reverse = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%08lX\r\n"
|
||||
"Yek:0x%08lX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
code_found_lo,
|
||||
code_found_reverse_lo);
|
||||
}
|
||||
107
applications/main/subghz/protocols/nice_flo.h
Normal file
107
applications/main/subghz/protocols/nice_flo.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_NICE_FLO_NAME "Nice FLO"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderNiceFlo SubGhzProtocolDecoderNiceFlo;
|
||||
typedef struct SubGhzProtocolEncoderNiceFlo SubGhzProtocolEncoderNiceFlo;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_nice_flo_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_nice_flo_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_nice_flo;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderNiceFlo.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderNiceFlo* pointer to a SubGhzProtocolEncoderNiceFlo instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_nice_flo_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderNiceFlo.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance
|
||||
*/
|
||||
void subghz_protocol_encoder_nice_flo_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance
|
||||
*/
|
||||
void subghz_protocol_encoder_nice_flo_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNiceFlo instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_nice_flo_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderNiceFlo.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderNiceFlo* pointer to a SubGhzProtocolDecoderNiceFlo instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_nice_flo_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderNiceFlo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance
|
||||
*/
|
||||
void subghz_protocol_decoder_nice_flo_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderNiceFlo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance
|
||||
*/
|
||||
void subghz_protocol_decoder_nice_flo_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_nice_flo_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_nice_flo_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderNiceFlo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_nice_flo_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderNiceFlo.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_nice_flo_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlo instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_nice_flo_get_string(void* context, FuriString* output);
|
||||
694
applications/main/subghz/protocols/nice_flor_s.c
Normal file
694
applications/main/subghz/protocols/nice_flor_s.c
Normal file
@@ -0,0 +1,694 @@
|
||||
#include "nice_flor_s.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* https://phreakerclub.com/1615
|
||||
* https://phreakerclub.com/forum/showthread.php?t=2360
|
||||
* https://vrtp.ru/index.php?showtopic=27867
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocolNiceFlorS"
|
||||
|
||||
#define NICE_ONE_COUNT_BIT 72
|
||||
#define NICE_ONE_NAME "Nice One"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_nice_flor_s_const = {
|
||||
.te_short = 500,
|
||||
.te_long = 1000,
|
||||
.te_delta = 300,
|
||||
.min_count_bit_for_found = 52,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderNiceFlorS {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
const char* nice_flor_s_rainbow_table_file_name;
|
||||
uint64_t data;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderNiceFlorS {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
const char* nice_flor_s_rainbow_table_file_name;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
NiceFlorSDecoderStepReset = 0,
|
||||
NiceFlorSDecoderStepCheckHeader,
|
||||
NiceFlorSDecoderStepFoundHeader,
|
||||
NiceFlorSDecoderStepSaveDuration,
|
||||
NiceFlorSDecoderStepCheckDuration,
|
||||
} NiceFlorSDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder = {
|
||||
.alloc = subghz_protocol_decoder_nice_flor_s_alloc,
|
||||
.free = subghz_protocol_decoder_nice_flor_s_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_nice_flor_s_feed,
|
||||
.reset = subghz_protocol_decoder_nice_flor_s_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_nice_flor_s_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_nice_flor_s_serialize,
|
||||
.deserialize = subghz_protocol_decoder_nice_flor_s_deserialize,
|
||||
.get_string = subghz_protocol_decoder_nice_flor_s_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder = {
|
||||
.alloc = subghz_protocol_encoder_nice_flor_s_alloc,
|
||||
.free = subghz_protocol_encoder_nice_flor_s_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_nice_flor_s_deserialize,
|
||||
.stop = subghz_protocol_encoder_nice_flor_s_stop,
|
||||
.yield = subghz_protocol_encoder_nice_flor_s_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_nice_flor_s = {
|
||||
.name = SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM |
|
||||
SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save |
|
||||
SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_nice_flor_s_decoder,
|
||||
.encoder = &subghz_protocol_nice_flor_s_encoder,
|
||||
};
|
||||
|
||||
static void subghz_protocol_nice_flor_s_remote_controller(
|
||||
SubGhzBlockGeneric* instance,
|
||||
const char* file_name);
|
||||
|
||||
void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolEncoderNiceFlorS* instance = malloc(sizeof(SubGhzProtocolEncoderNiceFlorS));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_nice_flor_s;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->nice_flor_s_rainbow_table_file_name =
|
||||
subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment);
|
||||
if(instance->nice_flor_s_rainbow_table_file_name) {
|
||||
FURI_LOG_D(
|
||||
TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name);
|
||||
}
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 1728; //wrong!! upload 186*16 = 2976 - actual size about 1728
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_nice_flor_s_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderNiceFlorS* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderNiceFlorS instance
|
||||
* @return true On success
|
||||
*/
|
||||
static void subghz_protocol_encoder_nice_flor_s_get_upload(
|
||||
SubGhzProtocolEncoderNiceFlorS* instance,
|
||||
uint8_t btn,
|
||||
const char* file_name) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
btn = instance->generic.btn;
|
||||
|
||||
size_t size_upload = ((instance->generic.data_count_bit * 2) + ((37 + 2 + 2) * 2) * 16);
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
if(instance->generic.cnt < 0xFFFF) {
|
||||
instance->generic.cnt++;
|
||||
} else if(instance->generic.cnt >= 0xFFFF) {
|
||||
instance->generic.cnt = 0;
|
||||
}
|
||||
uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt;
|
||||
uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt(decrypt, file_name);
|
||||
|
||||
for(int i = 0; i < 16; i++) {
|
||||
static const uint64_t loops[16] = {
|
||||
0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7, 0x8, 0x9, 0xA, 0xB, 0xC, 0xD, 0xE, 0xF};
|
||||
|
||||
uint8_t byte;
|
||||
|
||||
byte = btn << 4 | (0xF ^ btn ^ loops[i]);
|
||||
instance->generic.data = (uint64_t)byte << 44 | enc_part;
|
||||
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 37);
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3);
|
||||
|
||||
//Send key data
|
||||
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_nice_flor_s_const.te_long);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short);
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_nice_flor_s_const.te_long);
|
||||
}
|
||||
}
|
||||
//Send stop bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3);
|
||||
//instance->encoder.upload[index++] =
|
||||
//level_duration_make(false, (uint32_t)subghz_protocol_nice_flor_s_const.te_short * 3);
|
||||
}
|
||||
instance->encoder.size_upload = index;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderNiceFlorS* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
subghz_protocol_nice_flor_s_remote_controller(
|
||||
&instance->generic, instance->nice_flor_s_rainbow_table_file_name);
|
||||
subghz_protocol_encoder_nice_flor_s_get_upload(
|
||||
instance, instance->generic.btn, instance->nice_flor_s_rainbow_table_file_name);
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
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");
|
||||
break;
|
||||
}
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_nice_flor_s_stop(void* context) {
|
||||
SubGhzProtocolEncoderNiceFlorS* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context) {
|
||||
SubGhzProtocolEncoderNiceFlorS* 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;
|
||||
}
|
||||
|
||||
// /**
|
||||
// * Read bytes from rainbow table
|
||||
// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-P8-P9-P10
|
||||
// * @return crc
|
||||
// */
|
||||
// static uint32_t subghz_protocol_nice_one_crc(uint8_t* p) {
|
||||
// uint8_t crc = 0;
|
||||
// uint8_t crc_data = 0xff;
|
||||
// for(uint8_t i = 4; i < 68; i++) {
|
||||
// if(subghz_protocol_blocks_get_bit_array(p, i)) {
|
||||
// crc = crc_data ^ 1;
|
||||
// } else {
|
||||
// crc = crc_data;
|
||||
// }
|
||||
// crc_data >>= 1;
|
||||
// if((crc & 0x01)) {
|
||||
// crc_data ^= 0x97;
|
||||
// }
|
||||
// }
|
||||
// crc = 0;
|
||||
// for(uint8_t i = 0; i < 8; i++) {
|
||||
// crc <<= 1;
|
||||
// if((crc_data >> i) & 0x01) crc = crc | 1;
|
||||
// }
|
||||
// return crc;
|
||||
// }
|
||||
|
||||
// /**
|
||||
// * Read bytes from rainbow table
|
||||
// * @param p array[10] P0-P1|P2-P3-P4-P5-P6-P7-XX-XX-XX
|
||||
// * @param num_parcel parcel number 0..15
|
||||
// * @param hold_bit 0 - the button was only pressed, 1 - the button was held down
|
||||
// */
|
||||
// static void subghz_protocol_nice_one_get_data(uint8_t* p, uint8_t num_parcel, uint8_t hold_bit) {
|
||||
// uint8_t k = 0;
|
||||
// uint8_t crc = 0;
|
||||
// p[1] = (p[1] & 0x0f) | ((0x0f ^ (p[0] & 0x0F) ^ num_parcel) << 4);
|
||||
// if(num_parcel < 4) {
|
||||
// k = 0x8f;
|
||||
// } else {
|
||||
// k = 0x80;
|
||||
// }
|
||||
|
||||
// if(!hold_bit) {
|
||||
// hold_bit = 0;
|
||||
// } else {
|
||||
// hold_bit = 0x10;
|
||||
// }
|
||||
// k = num_parcel ^ k;
|
||||
// p[7] = k;
|
||||
// p[8] = hold_bit ^ (k << 4);
|
||||
|
||||
// crc = subghz_protocol_nice_one_crc(p);
|
||||
|
||||
// p[8] |= crc >> 4;
|
||||
// p[9] = crc << 4;
|
||||
// }
|
||||
|
||||
/**
|
||||
* Read bytes from rainbow table
|
||||
* @param file_name Full path to rainbow table the file
|
||||
* @param address Byte address in file
|
||||
* @return data
|
||||
*/
|
||||
static uint8_t
|
||||
subghz_protocol_nice_flor_s_get_byte_in_file(const char* file_name, uint32_t address) {
|
||||
if(!file_name) return 0;
|
||||
|
||||
uint8_t buffer[1] = {0};
|
||||
if(subghz_keystore_raw_get_data(file_name, address, buffer, sizeof(uint8_t))) {
|
||||
return buffer[0];
|
||||
} else {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
static inline void subghz_protocol_decoder_nice_flor_s_magic_xor(uint8_t* p, uint8_t k) {
|
||||
for(uint8_t i = 1; i < 6; i++) {
|
||||
p[i] ^= k;
|
||||
}
|
||||
}
|
||||
|
||||
uint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_name) {
|
||||
uint8_t* p = (uint8_t*)&data;
|
||||
|
||||
uint8_t k = 0;
|
||||
for(uint8_t y = 0; y < 2; y++) {
|
||||
k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f);
|
||||
subghz_protocol_decoder_nice_flor_s_magic_xor(p, k);
|
||||
|
||||
p[5] &= 0x0f;
|
||||
p[0] ^= k & 0xe0;
|
||||
k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25;
|
||||
subghz_protocol_decoder_nice_flor_s_magic_xor(p, k);
|
||||
|
||||
p[5] &= 0x0f;
|
||||
p[0] ^= k & 0x7;
|
||||
if(y == 0) {
|
||||
k = p[0];
|
||||
p[0] = p[1];
|
||||
p[1] = k;
|
||||
}
|
||||
}
|
||||
|
||||
p[5] = ~p[5] & 0x0f;
|
||||
k = ~p[4];
|
||||
p[4] = ~p[0];
|
||||
p[0] = ~p[2];
|
||||
p[2] = k;
|
||||
k = ~p[3];
|
||||
p[3] = ~p[1];
|
||||
p[1] = k;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static uint64_t
|
||||
subghz_protocol_nice_flor_s_decrypt(SubGhzBlockGeneric* instance, const char* file_name) {
|
||||
furi_assert(instance);
|
||||
uint64_t data = instance->data;
|
||||
uint8_t* p = (uint8_t*)&data;
|
||||
|
||||
uint8_t k = 0;
|
||||
|
||||
k = ~p[4];
|
||||
p[5] = ~p[5];
|
||||
p[4] = ~p[2];
|
||||
p[2] = ~p[0];
|
||||
p[0] = k;
|
||||
k = ~p[3];
|
||||
p[3] = ~p[1];
|
||||
p[1] = k;
|
||||
|
||||
for(uint8_t y = 0; y < 2; y++) {
|
||||
k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] >> 3) + 0x25;
|
||||
subghz_protocol_decoder_nice_flor_s_magic_xor(p, k);
|
||||
|
||||
p[5] &= 0x0f;
|
||||
p[0] ^= k & 0x7;
|
||||
k = subghz_protocol_nice_flor_s_get_byte_in_file(file_name, p[0] & 0x1f);
|
||||
subghz_protocol_decoder_nice_flor_s_magic_xor(p, k);
|
||||
|
||||
p[5] &= 0x0f;
|
||||
p[0] ^= k & 0xe0;
|
||||
|
||||
if(y == 0) {
|
||||
k = p[0];
|
||||
p[0] = p[1];
|
||||
p[1] = k;
|
||||
}
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
bool subghz_protocol_nice_flor_s_create_data(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
uint32_t serial,
|
||||
uint8_t btn,
|
||||
uint16_t cnt,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderNiceFlorS* instance = context;
|
||||
instance->generic.serial = serial;
|
||||
instance->generic.cnt = cnt;
|
||||
instance->generic.data_count_bit = 52;
|
||||
uint64_t decrypt = ((uint64_t)instance->generic.serial << 16) | instance->generic.cnt;
|
||||
uint64_t enc_part = subghz_protocol_nice_flor_s_encrypt(
|
||||
decrypt, instance->nice_flor_s_rainbow_table_file_name);
|
||||
uint8_t byte = btn << 4 | (0xF ^ btn ^ 0x3);
|
||||
instance->generic.data = (uint64_t)byte << 44 | enc_part;
|
||||
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment) {
|
||||
SubGhzProtocolDecoderNiceFlorS* instance = malloc(sizeof(SubGhzProtocolDecoderNiceFlorS));
|
||||
instance->base.protocol = &subghz_protocol_nice_flor_s;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->nice_flor_s_rainbow_table_file_name =
|
||||
subghz_environment_get_nice_flor_s_rainbow_table_file_name(environment);
|
||||
if(instance->nice_flor_s_rainbow_table_file_name) {
|
||||
FURI_LOG_D(
|
||||
TAG, "Loading rainbow table from %s", instance->nice_flor_s_rainbow_table_file_name);
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nice_flor_s_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlorS* instance = context;
|
||||
instance->nice_flor_s_rainbow_table_file_name = NULL;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nice_flor_s_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlorS* instance = context;
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlorS* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case NiceFlorSDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 38) <
|
||||
subghz_protocol_nice_flor_s_const.te_delta * 38)) {
|
||||
//Found start header Nice Flor-S
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepCheckHeader;
|
||||
}
|
||||
break;
|
||||
case NiceFlorSDecoderStepCheckHeader:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) <
|
||||
subghz_protocol_nice_flor_s_const.te_delta * 3)) {
|
||||
//Found next header Nice Flor-S
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepFoundHeader;
|
||||
} else {
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case NiceFlorSDecoderStepFoundHeader:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) <
|
||||
subghz_protocol_nice_flor_s_const.te_delta * 3)) {
|
||||
//Found header Nice Flor-S
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case NiceFlorSDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short * 3) <
|
||||
subghz_protocol_nice_flor_s_const.te_delta) {
|
||||
//Found STOP bit
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepReset;
|
||||
if((instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_nice_flor_s_const.min_count_bit_for_found) ||
|
||||
(instance->decoder.decode_count_bit == NICE_ONE_COUNT_BIT)) {
|
||||
instance->generic.data = instance->data;
|
||||
instance->data = instance->decoder.decode_data;
|
||||
instance->decoder.decode_data = instance->generic.data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
break;
|
||||
} else {
|
||||
//save interval
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepCheckDuration;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case NiceFlorSDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_short) <
|
||||
subghz_protocol_nice_flor_s_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_long) <
|
||||
subghz_protocol_nice_flor_s_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_nice_flor_s_const.te_long) <
|
||||
subghz_protocol_nice_flor_s_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_nice_flor_s_const.te_short) <
|
||||
subghz_protocol_nice_flor_s_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepSaveDuration;
|
||||
} else
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepReset;
|
||||
} else {
|
||||
instance->decoder.parser_step = NiceFlorSDecoderStepReset;
|
||||
}
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_nice_flor_s_const.min_count_bit_for_found) {
|
||||
instance->data = instance->decoder.decode_data;
|
||||
instance->decoder.decode_data = 0;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
* @param file_name Full path to rainbow table the file
|
||||
*/
|
||||
static void subghz_protocol_nice_flor_s_remote_controller(
|
||||
SubGhzBlockGeneric* instance,
|
||||
const char* file_name) {
|
||||
/*
|
||||
* Protocol Nice Flor-S
|
||||
* Packet format Nice Flor-s: START-P0-P1-P2-P3-P4-P5-P6-P7-STOP
|
||||
* P0 (4-bit) - button positional code - 1:0x1, 2:0x2, 3:0x4, 4:0x8;
|
||||
* P1 (4-bit) - batch repetition number, calculated by the formula:
|
||||
* P1 = 0xF ^ P0 ^ n; where n changes from 1 to 15, then 0, and then in a circle
|
||||
* key 1: {0xE,0xF,0xC,0xD,0xA,0xB,0x8,0x9,0x6,0x7,0x4,0x5,0x2,0x3,0x0,0x1};
|
||||
* key 2: {0xD,0xC,0xF,0xE,0x9,0x8,0xB,0xA,0x5,0x4,0x7,0x6,0x1,0x0,0x3,0x2};
|
||||
* key 3: {0xB,0xA,0x9,0x8,0xF,0xE,0xD,0xC,0x3,0x2,0x1,0x0,0x7,0x6,0x5,0x4};
|
||||
* key 4: {0x7,0x6,0x5,0x4,0x3,0x2,0x1,0x0,0xF,0xE,0xD,0xC,0xB,0xA,0x9,0x8};
|
||||
* P2 (4-bit) - part of the serial number, P2 = (K ^ S3) & 0xF;
|
||||
* P3 (byte) - the major part of the encrypted index
|
||||
* P4 (byte) - the low-order part of the encrypted index
|
||||
* P5 (byte) - part of the serial number, P5 = K ^ S2;
|
||||
* P6 (byte) - part of the serial number, P6 = K ^ S1;
|
||||
* P7 (byte) - part of the serial number, P7 = K ^ S0;
|
||||
* K (byte) - depends on P3 and P4, K = Fk(P3, P4);
|
||||
* S3,S2,S1,S0 - serial number of the console 28 bit.
|
||||
*
|
||||
* data => 0x1c5783607f7b3 key serial cnt
|
||||
* decrypt => 0x10436c6820444 => 0x1 0436c682 0444
|
||||
*
|
||||
* Protocol Nice One
|
||||
* Generally repeats the Nice Flor-S protocol, but there are a few changes
|
||||
* Packet format first 52 bytes repeat Nice Flor-S protocol
|
||||
* The additional 20 bytes contain the code of the pressed button,
|
||||
* the button hold bit and the CRC of the entire message.
|
||||
* START-P0-P1-P2-P3-P4-P5-P6-P7-P8-P9-P10-STOP
|
||||
* P7 (byte) - if (n<4) k=0x8f : k=0x80; P7= k^n;
|
||||
* P8 (byte) - if (hold bit) b=0x00 : b=0x10; P8= b^(k<<4) | 4 hi bit crc
|
||||
* P10 (4-bit) - 4 lo bit crc
|
||||
* key+b crc
|
||||
* data => 0x1724A7D9A522F 899 D6 hold bit = 0 - just pressed the button
|
||||
* data => 0x1424A7D9A522F 8AB 03 hold bit = 1 - button hold
|
||||
*
|
||||
* A small button hold counter (0..15) is stored between each press,
|
||||
* i.e. if 1 press of the button stops counter 6, then the next press
|
||||
* of the button will start from the value 7 (hold bit = 0), 8 (hold bit = 1)...
|
||||
* further up to 15 with overflow
|
||||
*
|
||||
*/
|
||||
if(!file_name) {
|
||||
instance->cnt = 0;
|
||||
instance->serial = 0;
|
||||
instance->btn = 0;
|
||||
} else {
|
||||
uint64_t decrypt = subghz_protocol_nice_flor_s_decrypt(instance, file_name);
|
||||
instance->cnt = decrypt & 0xFFFF;
|
||||
instance->serial = (decrypt >> 16) & 0xFFFFFFF;
|
||||
instance->btn = (decrypt >> 48) & 0xF;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlorS* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_nice_flor_s_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlorS* instance = context;
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) {
|
||||
if(res &&
|
||||
!flipper_format_write_uint32(flipper_format, "Data", (uint32_t*)&instance->data, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Data");
|
||||
res = false;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlorS* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if((instance->generic.data_count_bit !=
|
||||
subghz_protocol_nice_flor_s_const.min_count_bit_for_found) &&
|
||||
(instance->generic.data_count_bit != NICE_ONE_COUNT_BIT)) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) {
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
uint32_t temp = 0;
|
||||
if(!flipper_format_read_uint32(flipper_format, "Data", (uint32_t*)&temp, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing Data");
|
||||
break;
|
||||
}
|
||||
instance->data = (uint64_t)temp;
|
||||
}
|
||||
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderNiceFlorS* instance = context;
|
||||
|
||||
subghz_protocol_nice_flor_s_remote_controller(
|
||||
&instance->generic, instance->nice_flor_s_rainbow_table_file_name);
|
||||
|
||||
if(instance->generic.data_count_bit == NICE_ONE_COUNT_BIT) {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%013llX%llX\r\n"
|
||||
"Sn:%05lX\r\n"
|
||||
"Cnt:%04lX Btn:%02X\r\n",
|
||||
NICE_ONE_NAME,
|
||||
instance->generic.data_count_bit,
|
||||
instance->generic.data,
|
||||
instance->data,
|
||||
instance->generic.serial,
|
||||
instance->generic.cnt,
|
||||
instance->generic.btn);
|
||||
} else {
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%013llX\r\n"
|
||||
"Sn:%05lX\r\n"
|
||||
"Cnt:%04lX Btn:%02X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
instance->generic.data,
|
||||
instance->generic.serial,
|
||||
instance->generic.cnt,
|
||||
instance->generic.btn);
|
||||
}
|
||||
}
|
||||
127
applications/main/subghz/protocols/nice_flor_s.h
Normal file
127
applications/main/subghz/protocols/nice_flor_s.h
Normal file
@@ -0,0 +1,127 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_NICE_FLOR_S_NAME "Nice FloR-S"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderNiceFlorS SubGhzProtocolDecoderNiceFlorS;
|
||||
typedef struct SubGhzProtocolEncoderNiceFlorS SubGhzProtocolEncoderNiceFlorS;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_nice_flor_s_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_nice_flor_s_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_nice_flor_s;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderNiceFlorS.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderNiceFlorS* pointer to a SubGhzProtocolEncoderNiceFlorS instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_nice_flor_s_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderNiceFlorS.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance
|
||||
*/
|
||||
void subghz_protocol_encoder_nice_flor_s_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance
|
||||
*/
|
||||
void subghz_protocol_encoder_nice_flor_s_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_nice_flor_s_yield(void* context);
|
||||
|
||||
uint64_t subghz_protocol_nice_flor_s_encrypt(uint64_t data, const char* file_name);
|
||||
|
||||
/**
|
||||
* New remote generation.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param serial Serial number
|
||||
* @param btn Button number, 4 bit
|
||||
* @param cnt Counter value, 16 bit
|
||||
* @param preset Modulation, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_nice_flor_s_create_data(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
uint32_t serial,
|
||||
uint8_t btn,
|
||||
uint16_t cnt,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderNiceFlorS.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderNiceFlorS* pointer to a SubGhzProtocolDecoderNiceFlorS instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_nice_flor_s_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderNiceFlorS.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance
|
||||
*/
|
||||
void subghz_protocol_decoder_nice_flor_s_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderNiceFlorS.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance
|
||||
*/
|
||||
void subghz_protocol_decoder_nice_flor_s_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_nice_flor_s_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_nice_flor_s_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderNiceFlorS.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_nice_flor_s_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderNiceFlorS.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_nice_flor_s_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderNiceFlorS instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_nice_flor_s_get_string(void* context, FuriString* output);
|
||||
339
applications/main/subghz/protocols/phoenix_v2.c
Normal file
339
applications/main/subghz/protocols/phoenix_v2.c
Normal file
@@ -0,0 +1,339 @@
|
||||
#include "phoenix_v2.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolPhoenix_V2"
|
||||
|
||||
//transmission only static mode
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_phoenix_v2_const = {
|
||||
.te_short = 427,
|
||||
.te_long = 853,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 52,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderPhoenix_V2 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderPhoenix_V2 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
Phoenix_V2DecoderStepReset = 0,
|
||||
Phoenix_V2DecoderStepFoundStartBit,
|
||||
Phoenix_V2DecoderStepSaveDuration,
|
||||
Phoenix_V2DecoderStepCheckDuration,
|
||||
} Phoenix_V2DecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder = {
|
||||
.alloc = subghz_protocol_decoder_phoenix_v2_alloc,
|
||||
.free = subghz_protocol_decoder_phoenix_v2_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_phoenix_v2_feed,
|
||||
.reset = subghz_protocol_decoder_phoenix_v2_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_phoenix_v2_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_phoenix_v2_serialize,
|
||||
.deserialize = subghz_protocol_decoder_phoenix_v2_deserialize,
|
||||
.get_string = subghz_protocol_decoder_phoenix_v2_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder = {
|
||||
.alloc = subghz_protocol_encoder_phoenix_v2_alloc,
|
||||
.free = subghz_protocol_encoder_phoenix_v2_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_phoenix_v2_deserialize,
|
||||
.stop = subghz_protocol_encoder_phoenix_v2_stop,
|
||||
.yield = subghz_protocol_encoder_phoenix_v2_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_phoenix_v2 = {
|
||||
.name = SUBGHZ_PROTOCOL_PHOENIX_V2_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_phoenix_v2_decoder,
|
||||
.encoder = &subghz_protocol_phoenix_v2_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolEncoderPhoenix_V2));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_phoenix_v2;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_phoenix_v2_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderPhoenix_V2* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_phoenix_v2_get_upload(SubGhzProtocolEncoderPhoenix_V2* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
//Send header
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 60);
|
||||
//Send start bit
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 6);
|
||||
//Send key data
|
||||
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(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_long);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_short);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_phoenix_v2_const.te_long);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderPhoenix_V2* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_phoenix_v2_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_phoenix_v2_stop(void* context) {
|
||||
SubGhzProtocolEncoderPhoenix_V2* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context) {
|
||||
SubGhzProtocolEncoderPhoenix_V2* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderPhoenix_V2* instance = malloc(sizeof(SubGhzProtocolDecoderPhoenix_V2));
|
||||
instance->base.protocol = &subghz_protocol_phoenix_v2;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_phoenix_v2_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPhoenix_V2* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_phoenix_v2_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPhoenix_V2* instance = context;
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPhoenix_V2* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case Phoenix_V2DecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short * 60) <
|
||||
subghz_protocol_phoenix_v2_const.te_delta * 30)) {
|
||||
//Found Preambula
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit;
|
||||
}
|
||||
break;
|
||||
case Phoenix_V2DecoderStepFoundStartBit:
|
||||
if(level && ((DURATION_DIFF(duration, (subghz_protocol_phoenix_v2_const.te_short * 6)) <
|
||||
subghz_protocol_phoenix_v2_const.te_delta * 4))) {
|
||||
//Found start bit
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case Phoenix_V2DecoderStepSaveDuration:
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_phoenix_v2_const.te_short * 10 +
|
||||
subghz_protocol_phoenix_v2_const.te_delta)) {
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepFoundStartBit;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_phoenix_v2_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;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepCheckDuration;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Phoenix_V2DecoderStepCheckDuration:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_short) <
|
||||
subghz_protocol_phoenix_v2_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_long) <
|
||||
subghz_protocol_phoenix_v2_const.te_delta * 3)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_phoenix_v2_const.te_long) <
|
||||
subghz_protocol_phoenix_v2_const.te_delta * 3) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_phoenix_v2_const.te_short) <
|
||||
subghz_protocol_phoenix_v2_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = Phoenix_V2DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
uint64_t data_rev =
|
||||
subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit + 4);
|
||||
instance->serial = data_rev & 0xFFFFFFFF;
|
||||
instance->cnt = (data_rev >> 40) & 0xFFFF;
|
||||
instance->btn = (data_rev >> 32) & 0xF;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPhoenix_V2* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_phoenix_v2_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPhoenix_V2* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPhoenix_V2* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_phoenix_v2_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPhoenix_V2* instance = context;
|
||||
subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic);
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%02lX%08lX\r\n"
|
||||
"Sn:0x%07lX \r\n"
|
||||
"Btn:%X\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF,
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
|
||||
instance->generic.serial,
|
||||
instance->generic.btn);
|
||||
}
|
||||
107
applications/main/subghz/protocols/phoenix_v2.h
Normal file
107
applications/main/subghz/protocols/phoenix_v2.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_PHOENIX_V2_NAME "Phoenix_V2"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderPhoenix_V2 SubGhzProtocolDecoderPhoenix_V2;
|
||||
typedef struct SubGhzProtocolEncoderPhoenix_V2 SubGhzProtocolEncoderPhoenix_V2;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_phoenix_v2_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_phoenix_v2;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderPhoenix_V2.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderPhoenix_V2* pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_phoenix_v2_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderPhoenix_V2.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_phoenix_v2_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_phoenix_v2_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_phoenix_v2_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderPhoenix_V2.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderPhoenix_V2* pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_phoenix_v2_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderPhoenix_V2.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_phoenix_v2_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderPhoenix_V2.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_phoenix_v2_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderPhoenix_V2.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_phoenix_v2_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderPhoenix_V2.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_phoenix_v2_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPhoenix_V2 instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* output);
|
||||
394
applications/main/subghz/protocols/power_smart.c
Normal file
394
applications/main/subghz/protocols/power_smart.c
Normal file
@@ -0,0 +1,394 @@
|
||||
#include "power_smart.h"
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include <lib/toolbox/manchester_encoder.h>
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolPowerSmart"
|
||||
#define POWER_SMART_PACKET_HEADER 0xFD000000AA000000
|
||||
#define POWER_SMART_PACKET_HEADER_MASK 0xFF000000FF000000
|
||||
|
||||
#define CHANNEL_PATTERN "%c%c%c%c%c%c"
|
||||
#define CNT_TO_CHANNEL(dip) \
|
||||
(dip & 0x0001 ? '*' : '-'), (dip & 0x0002 ? '*' : '-'), (dip & 0x0004 ? '*' : '-'), \
|
||||
(dip & 0x0008 ? '*' : '-'), (dip & 0x0010 ? '*' : '-'), (dip & 0x0020 ? '*' : '-')
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_power_smart_const = {
|
||||
.te_short = 225,
|
||||
.te_long = 450,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 64,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderPowerSmart {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
ManchesterState manchester_saved_state;
|
||||
uint16_t header_count;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderPowerSmart {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
PowerSmartDecoderStepReset = 0,
|
||||
PowerSmartDecoderFoundHeader,
|
||||
PowerSmartDecoderStepDecoderData,
|
||||
} PowerSmartDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_power_smart_decoder = {
|
||||
.alloc = subghz_protocol_decoder_power_smart_alloc,
|
||||
.free = subghz_protocol_decoder_power_smart_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_power_smart_feed,
|
||||
.reset = subghz_protocol_decoder_power_smart_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_power_smart_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_power_smart_serialize,
|
||||
.deserialize = subghz_protocol_decoder_power_smart_deserialize,
|
||||
.get_string = subghz_protocol_decoder_power_smart_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_power_smart_encoder = {
|
||||
.alloc = subghz_protocol_encoder_power_smart_alloc,
|
||||
.free = subghz_protocol_encoder_power_smart_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_power_smart_deserialize,
|
||||
.stop = subghz_protocol_encoder_power_smart_stop,
|
||||
.yield = subghz_protocol_encoder_power_smart_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_power_smart = {
|
||||
.name = SUBGHZ_PROTOCOL_POWER_SMART_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_power_smart_decoder,
|
||||
.encoder = &subghz_protocol_power_smart_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolEncoderPowerSmart));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_power_smart;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 1024;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_power_smart_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderPowerSmart* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static LevelDuration
|
||||
subghz_protocol_encoder_power_smart_add_duration_to_upload(ManchesterEncoderResult result) {
|
||||
LevelDuration data = {.duration = 0, .level = 0};
|
||||
switch(result) {
|
||||
case ManchesterEncoderResultShortLow:
|
||||
data.duration = subghz_protocol_power_smart_const.te_short;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongLow:
|
||||
data.duration = subghz_protocol_power_smart_const.te_long;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongHigh:
|
||||
data.duration = subghz_protocol_power_smart_const.te_long;
|
||||
data.level = true;
|
||||
break;
|
||||
case ManchesterEncoderResultShortHigh:
|
||||
data.duration = subghz_protocol_power_smart_const.te_short;
|
||||
data.level = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
furi_crash("SubGhz: ManchesterEncoderResult is incorrect.");
|
||||
break;
|
||||
}
|
||||
return level_duration_make(data.level, data.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderPowerSmart instance
|
||||
*/
|
||||
static void
|
||||
subghz_protocol_encoder_power_smart_get_upload(SubGhzProtocolEncoderPowerSmart* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
|
||||
ManchesterEncoderState enc_state;
|
||||
manchester_encoder_reset(&enc_state);
|
||||
ManchesterEncoderResult result;
|
||||
|
||||
for(int i = 8; i > 0; i--) {
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
||||
if(!manchester_encoder_advance(
|
||||
&enc_state, !bit_read(instance->generic.data, i - 1), &result)) {
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_power_smart_add_duration_to_upload(result);
|
||||
manchester_encoder_advance(
|
||||
&enc_state, !bit_read(instance->generic.data, i - 1), &result);
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_power_smart_add_duration_to_upload(result);
|
||||
}
|
||||
}
|
||||
instance->encoder.upload[index] = subghz_protocol_encoder_power_smart_add_duration_to_upload(
|
||||
manchester_encoder_finish(&enc_state));
|
||||
if(level_duration_get_level(instance->encoder.upload[index])) {
|
||||
index++;
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_power_smart_const.te_long * 1111);
|
||||
instance->encoder.size_upload = index;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_power_smart_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
/*
|
||||
* Protocol: Manchester encoding, symbol rate ~2222.
|
||||
* Packet Format:
|
||||
* 0xFDXXXXYYAAZZZZWW where 0xFD and 0xAA sync word
|
||||
* XXXX = ~ZZZZ, YY=(~WW)-1
|
||||
* Example:
|
||||
* SYNC1 K1 CHANNEL DATA1 K2 DATA2 SYNC2 ~K1 ~CHANNEL ~DATA2 ~K2 (~DATA2)-1
|
||||
* 0xFD2137ACAADEC852 => 11111101 0 010000 10011011 1 10101100 10101010 1 1011110 1100100 0 01010010
|
||||
* 0xFDA137ACAA5EC852 => 11111101 1 010000 10011011 1 10101100 10101010 0 1011110 1100100 0 01010010
|
||||
* 0xFDA136ACAA5EC952 => 11111101 1 010000 10011011 0 10101100 10101010 0 1011110 1100100 1 01010010
|
||||
*
|
||||
* Key:
|
||||
* K1K2
|
||||
* 0 0 - key_unknown
|
||||
* 0 1 - key_down
|
||||
* 1 0 - key_up
|
||||
* 1 1 - key_stop
|
||||
*
|
||||
*/
|
||||
|
||||
instance->btn = ((instance->data >> 54) & 0x02) | ((instance->data >> 40) & 0x1);
|
||||
instance->serial = ((instance->data >> 33) & 0x3FFF00) | ((instance->data >> 32) & 0xFF);
|
||||
instance->cnt = ((instance->data >> 49) & 0x3F);
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderPowerSmart* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_power_smart_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
subghz_protocol_power_smart_remote_controller(&instance->generic);
|
||||
subghz_protocol_encoder_power_smart_get_upload(instance);
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_power_smart_stop(void* context) {
|
||||
SubGhzProtocolEncoderPowerSmart* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_power_smart_yield(void* context) {
|
||||
SubGhzProtocolEncoderPowerSmart* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_power_smart_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderPowerSmart* instance = malloc(sizeof(SubGhzProtocolDecoderPowerSmart));
|
||||
instance->base.protocol = &subghz_protocol_power_smart;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_power_smart_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPowerSmart* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_power_smart_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPowerSmart* instance = context;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
}
|
||||
|
||||
bool subghz_protocol_power_smart_chek_valid(uint64_t packet) {
|
||||
uint32_t data_1 = (uint32_t)((packet >> 40) & 0xFFFF);
|
||||
uint32_t data_2 = (uint32_t)((~packet >> 8) & 0xFFFF);
|
||||
uint8_t data_3 = (uint8_t)(packet >> 32) & 0xFF;
|
||||
uint8_t data_4 = (uint8_t)(((~packet) & 0xFF) - 1);
|
||||
return (data_1 == data_2) && (data_3 == data_4);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_power_smart_feed(
|
||||
void* context,
|
||||
bool level,
|
||||
volatile uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPowerSmart* instance = context;
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
if(!level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) <
|
||||
subghz_protocol_power_smart_const.te_delta) {
|
||||
event = ManchesterEventShortLow;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) <
|
||||
subghz_protocol_power_smart_const.te_delta * 2) {
|
||||
event = ManchesterEventLongLow;
|
||||
}
|
||||
} else {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_short) <
|
||||
subghz_protocol_power_smart_const.te_delta) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_power_smart_const.te_long) <
|
||||
subghz_protocol_power_smart_const.te_delta * 2) {
|
||||
event = ManchesterEventLongHigh;
|
||||
}
|
||||
}
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = manchester_advance(
|
||||
instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | !data;
|
||||
}
|
||||
if((instance->decoder.decode_data & POWER_SMART_PACKET_HEADER_MASK) ==
|
||||
POWER_SMART_PACKET_HEADER) {
|
||||
if(subghz_protocol_power_smart_chek_valid(instance->decoder.decode_data)) {
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit =
|
||||
subghz_protocol_power_smart_const.min_count_bit_for_found;
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
static const char* subghz_protocol_power_smart_get_name_button(uint8_t btn) {
|
||||
btn &= 0x3;
|
||||
const char* name_btn[0x4] = {"Unknown", "Down", "Up", "Stop"};
|
||||
return name_btn[btn];
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPowerSmart* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_power_smart_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPowerSmart* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPowerSmart* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_power_smart_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPowerSmart* instance = context;
|
||||
subghz_protocol_power_smart_remote_controller(&instance->generic);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"Sn:0x%07lX \r\n"
|
||||
"Btn:%s\r\n"
|
||||
"Channel:" CHANNEL_PATTERN "\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data >> 32),
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFFFF),
|
||||
instance->generic.serial,
|
||||
subghz_protocol_power_smart_get_name_button(instance->generic.btn),
|
||||
CNT_TO_CHANNEL(instance->generic.cnt));
|
||||
}
|
||||
107
applications/main/subghz/protocols/power_smart.h
Normal file
107
applications/main/subghz/protocols/power_smart.h
Normal file
@@ -0,0 +1,107 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_POWER_SMART_NAME "Power Smart"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderPowerSmart SubGhzProtocolDecoderPowerSmart;
|
||||
typedef struct SubGhzProtocolEncoderPowerSmart SubGhzProtocolEncoderPowerSmart;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_power_smart_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_power_smart_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_power_smart;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderPowerSmart.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderPowerSmart* pointer to a SubGhzProtocolEncoderPowerSmart instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_power_smart_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderPowerSmart.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance
|
||||
*/
|
||||
void subghz_protocol_encoder_power_smart_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance
|
||||
*/
|
||||
void subghz_protocol_encoder_power_smart_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPowerSmart instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_power_smart_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderPowerSmart.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderPowerSmart* pointer to a SubGhzProtocolDecoderPowerSmart instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_power_smart_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderPowerSmart.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance
|
||||
*/
|
||||
void subghz_protocol_decoder_power_smart_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderPowerSmart.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance
|
||||
*/
|
||||
void subghz_protocol_decoder_power_smart_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_power_smart_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_power_smart_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderPowerSmart.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_power_smart_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderPowerSmart.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_power_smart_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPowerSmart instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_power_smart_get_string(void* context, FuriString* output);
|
||||
374
applications/main/subghz/protocols/princeton.c
Normal file
374
applications/main/subghz/protocols/princeton.c
Normal file
@@ -0,0 +1,374 @@
|
||||
#include "princeton.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://phreakerclub.com/447
|
||||
*
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocolPrinceton"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_princeton_const = {
|
||||
.te_short = 390,
|
||||
.te_long = 1170,
|
||||
.te_delta = 300,
|
||||
.min_count_bit_for_found = 24,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderPrinceton {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint32_t te;
|
||||
uint32_t last_data;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderPrinceton {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint32_t te;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
PrincetonDecoderStepReset = 0,
|
||||
PrincetonDecoderStepSaveDuration,
|
||||
PrincetonDecoderStepCheckDuration,
|
||||
} PrincetonDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_princeton_decoder = {
|
||||
.alloc = subghz_protocol_decoder_princeton_alloc,
|
||||
.free = subghz_protocol_decoder_princeton_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_princeton_feed,
|
||||
.reset = subghz_protocol_decoder_princeton_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_princeton_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_princeton_serialize,
|
||||
.deserialize = subghz_protocol_decoder_princeton_deserialize,
|
||||
.get_string = subghz_protocol_decoder_princeton_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_princeton_encoder = {
|
||||
.alloc = subghz_protocol_encoder_princeton_alloc,
|
||||
.free = subghz_protocol_encoder_princeton_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_princeton_deserialize,
|
||||
.stop = subghz_protocol_encoder_princeton_stop,
|
||||
.yield = subghz_protocol_encoder_princeton_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_princeton = {
|
||||
.name = SUBGHZ_PROTOCOL_PRINCETON_NAME,
|
||||
.type = SubGhzProtocolTypeStatic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load |
|
||||
SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_princeton_decoder,
|
||||
.encoder = &subghz_protocol_princeton_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderPrinceton* instance = malloc(sizeof(SubGhzProtocolEncoderPrinceton));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_princeton;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 52; //max 24bit*2 + 2 (start, stop)
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_princeton_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderPrinceton* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderPrinceton instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_princeton_get_upload(SubGhzProtocolEncoderPrinceton* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2) + 2;
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send key data
|
||||
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)instance->te * 3);
|
||||
instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te);
|
||||
} else {
|
||||
//send bit 0
|
||||
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)instance->te * 3);
|
||||
}
|
||||
}
|
||||
|
||||
//Send Stop bit
|
||||
instance->encoder.upload[index++] = level_duration_make(true, (uint32_t)instance->te);
|
||||
//Send PT_GUARD
|
||||
instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)instance->te * 30);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderPrinceton* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing TE");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_princeton_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_princeton_get_upload(instance)) break;
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_princeton_stop(void* context) {
|
||||
SubGhzProtocolEncoderPrinceton* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_princeton_yield(void* context) {
|
||||
SubGhzProtocolEncoderPrinceton* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_princeton_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderPrinceton* instance = malloc(sizeof(SubGhzProtocolDecoderPrinceton));
|
||||
instance->base.protocol = &subghz_protocol_princeton;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_princeton_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPrinceton* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_princeton_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPrinceton* instance = context;
|
||||
instance->decoder.parser_step = PrincetonDecoderStepReset;
|
||||
instance->last_data = 0;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPrinceton* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case PrincetonDecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short * 36) <
|
||||
subghz_protocol_princeton_const.te_delta * 36)) {
|
||||
//Found Preambula
|
||||
instance->decoder.parser_step = PrincetonDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->te = 0;
|
||||
}
|
||||
break;
|
||||
case PrincetonDecoderStepSaveDuration:
|
||||
//save duration
|
||||
if(level) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->te += duration;
|
||||
instance->decoder.parser_step = PrincetonDecoderStepCheckDuration;
|
||||
}
|
||||
break;
|
||||
case PrincetonDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)subghz_protocol_princeton_const.te_long * 2)) {
|
||||
instance->decoder.parser_step = PrincetonDecoderStepSaveDuration;
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_princeton_const.min_count_bit_for_found) {
|
||||
if((instance->last_data == instance->decoder.decode_data) &&
|
||||
instance->last_data) {
|
||||
instance->te /= (instance->decoder.decode_count_bit * 4 + 1);
|
||||
|
||||
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->last_data = instance->decoder.decode_data;
|
||||
}
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->te = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->te += duration;
|
||||
|
||||
if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_short) <
|
||||
subghz_protocol_princeton_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_princeton_const.te_long) <
|
||||
subghz_protocol_princeton_const.te_delta * 3)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = PrincetonDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->decoder.te_last, subghz_protocol_princeton_const.te_long) <
|
||||
subghz_protocol_princeton_const.te_delta * 3) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_princeton_const.te_short) <
|
||||
subghz_protocol_princeton_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = PrincetonDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = PrincetonDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = PrincetonDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
*/
|
||||
static void subghz_protocol_princeton_check_remote_controller(SubGhzBlockGeneric* instance) {
|
||||
instance->serial = instance->data >> 4;
|
||||
instance->btn = instance->data & 0xF;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPrinceton* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_princeton_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPrinceton* instance = context;
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(res && !flipper_format_write_uint32(flipper_format, "TE", &instance->te, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add TE");
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPrinceton* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_princeton_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_read_uint32(flipper_format, "TE", (uint32_t*)&instance->te, 1)) {
|
||||
FURI_LOG_E(TAG, "Missing TE");
|
||||
break;
|
||||
}
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderPrinceton* instance = context;
|
||||
subghz_protocol_princeton_check_remote_controller(&instance->generic);
|
||||
uint32_t data_rev = subghz_protocol_blocks_reverse_key(
|
||||
instance->generic.data, instance->generic.data_count_bit);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%08lX\r\n"
|
||||
"Yek:0x%08lX\r\n"
|
||||
"Sn:0x%05lX Btn:%01X\r\n"
|
||||
"Te:%luus\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data & 0xFFFFFF),
|
||||
data_rev,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->te);
|
||||
}
|
||||
115
applications/main/subghz/protocols/princeton.h
Normal file
115
applications/main/subghz/protocols/princeton.h
Normal file
@@ -0,0 +1,115 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_PRINCETON_NAME "Princeton"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct SubGhzProtocolDecoderPrinceton SubGhzProtocolDecoderPrinceton;
|
||||
typedef struct SubGhzProtocolEncoderPrinceton SubGhzProtocolEncoderPrinceton;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_princeton_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_princeton_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_princeton;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderPrinceton.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderPrinceton* pointer to a SubGhzProtocolEncoderPrinceton instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_princeton_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderPrinceton.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPrinceton instance
|
||||
*/
|
||||
void subghz_protocol_encoder_princeton_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPrinceton instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_princeton_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPrinceton instance
|
||||
*/
|
||||
void subghz_protocol_encoder_princeton_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderPrinceton instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_princeton_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderPrinceton.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderPrinceton* pointer to a SubGhzProtocolDecoderPrinceton instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_princeton_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderPrinceton.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPrinceton instance
|
||||
*/
|
||||
void subghz_protocol_decoder_princeton_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderPrinceton.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPrinceton instance
|
||||
*/
|
||||
void subghz_protocol_decoder_princeton_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPrinceton instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_princeton_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPrinceton instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_princeton_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderPrinceton.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPrinceton instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_princeton_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderPrinceton.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPrinceton instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_princeton_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderPrinceton instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_princeton_get_string(void* context, FuriString* output);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
288
applications/main/subghz/protocols/princeton_for_testing.c
Normal file
288
applications/main/subghz/protocols/princeton_for_testing.c
Normal file
@@ -0,0 +1,288 @@
|
||||
#include "princeton_for_testing.h"
|
||||
|
||||
#include <furi_hal.h>
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://phreakerclub.com/447
|
||||
*
|
||||
*/
|
||||
|
||||
#define SUBGHZ_PT_SHORT 300
|
||||
#define SUBGHZ_PT_LONG (SUBGHZ_PT_SHORT * 3)
|
||||
#define SUBGHZ_PT_GUARD (SUBGHZ_PT_SHORT * 30)
|
||||
#define SUBGHZ_PT_COUNT_KEY_433 9
|
||||
#define SUBGHZ_PT_TIMEOUT_433 900
|
||||
#define SUBGHZ_PT_COUNT_KEY_868 9
|
||||
#define SUBGHZ_PT_TIMEOUT_868 14000
|
||||
|
||||
#define TAG "SubGhzProtocolPrinceton"
|
||||
|
||||
struct SubGhzEncoderPrinceton {
|
||||
uint32_t key;
|
||||
uint16_t te;
|
||||
size_t repeat;
|
||||
size_t front;
|
||||
size_t count_key;
|
||||
size_t count_key_package;
|
||||
uint32_t time_high;
|
||||
uint32_t time_low;
|
||||
uint32_t timeout;
|
||||
uint32_t time_stop;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
PrincetonDecoderStepReset = 0,
|
||||
PrincetonDecoderStepSaveDuration,
|
||||
PrincetonDecoderStepCheckDuration,
|
||||
} PrincetonDecoderStep;
|
||||
|
||||
SubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc() {
|
||||
SubGhzEncoderPrinceton* instance = malloc(sizeof(SubGhzEncoderPrinceton));
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance) {
|
||||
furi_assert(instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_encoder_princeton_for_testing_stop(
|
||||
SubGhzEncoderPrinceton* instance,
|
||||
uint32_t time_stop) {
|
||||
instance->time_stop = time_stop;
|
||||
}
|
||||
|
||||
void subghz_encoder_princeton_for_testing_set(
|
||||
SubGhzEncoderPrinceton* instance,
|
||||
uint32_t key,
|
||||
size_t repeat,
|
||||
uint32_t frequency) {
|
||||
furi_assert(instance);
|
||||
instance->te = SUBGHZ_PT_SHORT;
|
||||
instance->key = key;
|
||||
instance->repeat = repeat + 1;
|
||||
instance->front = 48;
|
||||
instance->time_high = 0;
|
||||
instance->time_low = 0;
|
||||
if(frequency < 700000000) {
|
||||
instance->count_key_package = SUBGHZ_PT_COUNT_KEY_433;
|
||||
instance->timeout = SUBGHZ_PT_TIMEOUT_433;
|
||||
} else {
|
||||
instance->count_key_package = SUBGHZ_PT_COUNT_KEY_868;
|
||||
instance->timeout = SUBGHZ_PT_TIMEOUT_868;
|
||||
}
|
||||
|
||||
instance->count_key = instance->count_key_package + 3;
|
||||
|
||||
if((furi_get_tick() - instance->time_stop) < instance->timeout) {
|
||||
instance->time_stop = (instance->timeout - (furi_get_tick() - instance->time_stop)) * 1000;
|
||||
} else {
|
||||
instance->time_stop = 0;
|
||||
}
|
||||
}
|
||||
|
||||
size_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance) {
|
||||
furi_assert(instance);
|
||||
return instance->repeat;
|
||||
}
|
||||
|
||||
void subghz_encoder_princeton_for_testing_print_log(void* context) {
|
||||
SubGhzEncoderPrinceton* instance = context;
|
||||
float duty_cycle =
|
||||
((float)instance->time_high / (instance->time_high + instance->time_low)) * 100;
|
||||
FURI_LOG_I(
|
||||
TAG "Encoder",
|
||||
"Radio tx_time=%luus ON=%luus, OFF=%luus, DutyCycle=%lu,%lu%%",
|
||||
instance->time_high + instance->time_low,
|
||||
instance->time_high,
|
||||
instance->time_low,
|
||||
(uint32_t)duty_cycle,
|
||||
(uint32_t)((duty_cycle - (uint32_t)duty_cycle) * 100UL));
|
||||
}
|
||||
|
||||
LevelDuration subghz_encoder_princeton_for_testing_yield(void* context) {
|
||||
SubGhzEncoderPrinceton* instance = context;
|
||||
if(instance->repeat == 0) {
|
||||
subghz_encoder_princeton_for_testing_print_log(instance);
|
||||
return level_duration_reset();
|
||||
}
|
||||
|
||||
size_t bit = instance->front / 2;
|
||||
bool level = !(instance->front % 2);
|
||||
|
||||
LevelDuration ret;
|
||||
if(bit < 24) {
|
||||
uint8_t byte = bit / 8;
|
||||
uint8_t bit_in_byte = bit % 8;
|
||||
bool value = (((uint8_t*)&instance->key)[2 - byte] >> (7 - bit_in_byte)) & 1;
|
||||
if(value) {
|
||||
ret = level_duration_make(level, level ? instance->te * 3 : instance->te);
|
||||
if(level)
|
||||
instance->time_high += instance->te * 3;
|
||||
else
|
||||
instance->time_low += instance->te;
|
||||
} else {
|
||||
ret = level_duration_make(level, level ? instance->te : instance->te * 3);
|
||||
if(level)
|
||||
instance->time_high += instance->te;
|
||||
else
|
||||
instance->time_low += instance->te * 3;
|
||||
}
|
||||
} else {
|
||||
if(instance->time_stop) {
|
||||
ret = level_duration_make(level, level ? instance->te : instance->time_stop);
|
||||
if(level)
|
||||
instance->time_high += instance->te;
|
||||
else {
|
||||
instance->time_low += instance->time_stop;
|
||||
instance->time_stop = 0;
|
||||
instance->front = 47;
|
||||
}
|
||||
} else {
|
||||
if(--instance->count_key != 0) {
|
||||
ret = level_duration_make(level, level ? instance->te : instance->te * 30);
|
||||
if(level)
|
||||
instance->time_high += instance->te;
|
||||
else
|
||||
instance->time_low += instance->te * 30;
|
||||
} else {
|
||||
instance->count_key = instance->count_key_package + 2;
|
||||
instance->front = 48;
|
||||
ret = level_duration_make(level, level ? instance->te : instance->timeout * 1000);
|
||||
if(level)
|
||||
instance->time_high += instance->te;
|
||||
else
|
||||
instance->time_low += instance->timeout * 1000;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
instance->front++;
|
||||
if(instance->front == 50) {
|
||||
instance->repeat--;
|
||||
instance->front = 0;
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
struct SubGhzDecoderPrinceton {
|
||||
const char* name;
|
||||
uint16_t te_long;
|
||||
uint16_t te_short;
|
||||
uint16_t te_delta;
|
||||
uint8_t code_count_bit;
|
||||
uint8_t code_last_count_bit;
|
||||
uint64_t code_found;
|
||||
uint64_t code_last_found;
|
||||
uint8_t code_min_count_bit_for_found;
|
||||
uint8_t btn;
|
||||
uint32_t te_last;
|
||||
uint32_t serial;
|
||||
uint32_t parser_step;
|
||||
uint16_t cnt;
|
||||
uint32_t te;
|
||||
|
||||
SubGhzDecoderPrincetonCallback callback;
|
||||
void* context;
|
||||
};
|
||||
|
||||
SubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc(void) {
|
||||
SubGhzDecoderPrinceton* instance = malloc(sizeof(SubGhzDecoderPrinceton));
|
||||
|
||||
instance->te = SUBGHZ_PT_SHORT;
|
||||
instance->name = "Princeton";
|
||||
instance->code_min_count_bit_for_found = 24;
|
||||
instance->te_short = 400;
|
||||
instance->te_long = 1200;
|
||||
instance->te_delta = 250;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance) {
|
||||
furi_assert(instance);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_decoder_princeton_for_testing_set_callback(
|
||||
SubGhzDecoderPrinceton* instance,
|
||||
SubGhzDecoderPrincetonCallback callback,
|
||||
void* context) {
|
||||
instance->callback = callback;
|
||||
instance->context = context;
|
||||
}
|
||||
|
||||
void subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance) {
|
||||
instance->parser_step = PrincetonDecoderStepReset;
|
||||
}
|
||||
|
||||
static void
|
||||
subghz_decoder_princeton_for_testing_add_bit(SubGhzDecoderPrinceton* instance, uint8_t bit) {
|
||||
instance->code_found = instance->code_found << 1 | bit;
|
||||
instance->code_count_bit++;
|
||||
}
|
||||
|
||||
void subghz_decoder_princeton_for_testing_parse(
|
||||
SubGhzDecoderPrinceton* instance,
|
||||
bool level,
|
||||
uint32_t duration) {
|
||||
switch(instance->parser_step) {
|
||||
case PrincetonDecoderStepReset:
|
||||
if((!level) &&
|
||||
(DURATION_DIFF(duration, instance->te_short * 36) < instance->te_delta * 36)) {
|
||||
//Found Preambula
|
||||
instance->parser_step = PrincetonDecoderStepSaveDuration;
|
||||
instance->code_found = 0;
|
||||
instance->code_count_bit = 0;
|
||||
instance->te = 0;
|
||||
}
|
||||
break;
|
||||
case PrincetonDecoderStepSaveDuration:
|
||||
//save duration
|
||||
if(level) {
|
||||
instance->te_last = duration;
|
||||
instance->te += duration;
|
||||
instance->parser_step = PrincetonDecoderStepCheckDuration;
|
||||
}
|
||||
break;
|
||||
case PrincetonDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if(duration >= ((uint32_t)instance->te_short * 10 + instance->te_delta)) {
|
||||
instance->parser_step = PrincetonDecoderStepSaveDuration;
|
||||
if(instance->code_count_bit == instance->code_min_count_bit_for_found) {
|
||||
instance->te /= (instance->code_count_bit * 4 + 1);
|
||||
|
||||
instance->code_last_found = instance->code_found;
|
||||
instance->code_last_count_bit = instance->code_count_bit;
|
||||
instance->serial = instance->code_found >> 4;
|
||||
instance->btn = (uint8_t)instance->code_found & 0x00000F;
|
||||
|
||||
if(instance->callback) instance->callback(instance, instance->context);
|
||||
}
|
||||
instance->code_found = 0;
|
||||
instance->code_count_bit = 0;
|
||||
instance->te = 0;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->te += duration;
|
||||
|
||||
if((DURATION_DIFF(instance->te_last, instance->te_short) < instance->te_delta) &&
|
||||
(DURATION_DIFF(duration, instance->te_long) < instance->te_delta * 3)) {
|
||||
subghz_decoder_princeton_for_testing_add_bit(instance, 0);
|
||||
instance->parser_step = PrincetonDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(instance->te_last, instance->te_long) < instance->te_delta * 3) &&
|
||||
(DURATION_DIFF(duration, instance->te_short) < instance->te_delta)) {
|
||||
subghz_decoder_princeton_for_testing_add_bit(instance, 1);
|
||||
instance->parser_step = PrincetonDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->parser_step = PrincetonDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->parser_step = PrincetonDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
97
applications/main/subghz/protocols/princeton_for_testing.h
Normal file
97
applications/main/subghz/protocols/princeton_for_testing.h
Normal file
@@ -0,0 +1,97 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
/** SubGhzDecoderPrinceton anonymous type */
|
||||
typedef struct SubGhzDecoderPrinceton SubGhzDecoderPrinceton;
|
||||
/** SubGhzEncoderPrinceton anonymous type */
|
||||
typedef struct SubGhzEncoderPrinceton SubGhzEncoderPrinceton;
|
||||
|
||||
typedef void (*SubGhzDecoderPrincetonCallback)(SubGhzDecoderPrinceton* parser, void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzEncoderPrinceton
|
||||
* @return pointer to SubGhzEncoderPrinceton instance
|
||||
*/
|
||||
SubGhzEncoderPrinceton* subghz_encoder_princeton_for_testing_alloc();
|
||||
|
||||
/**
|
||||
* Free SubGhzEncoderPrinceton instance
|
||||
* @param instance - SubGhzEncoderPrinceton instance
|
||||
*/
|
||||
void subghz_encoder_princeton_for_testing_free(SubGhzEncoderPrinceton* instance);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param instance Pointer to a SubGhzEncoderPrinceton instance
|
||||
* @param time_stop Transmission stop time, ms
|
||||
*/
|
||||
void subghz_encoder_princeton_for_testing_stop(
|
||||
SubGhzEncoderPrinceton* instance,
|
||||
uint32_t time_stop);
|
||||
|
||||
/**
|
||||
* Set new encoder params
|
||||
* @param instance - SubGhzEncoderPrinceton instance
|
||||
* @param key - 24bit key
|
||||
* @param repeat - how many times to repeat
|
||||
* @param frequency - frequency
|
||||
*/
|
||||
void subghz_encoder_princeton_for_testing_set(
|
||||
SubGhzEncoderPrinceton* instance,
|
||||
uint32_t key,
|
||||
size_t repeat,
|
||||
uint32_t frequency);
|
||||
|
||||
/**
|
||||
* Get repeat count left
|
||||
* @param instance - SubGhzEncoderPrinceton instance
|
||||
* @return repeat count left
|
||||
*/
|
||||
size_t subghz_encoder_princeton_for_testing_get_repeat_left(SubGhzEncoderPrinceton* instance);
|
||||
|
||||
/**
|
||||
* Print encoder log
|
||||
* @param instance - SubGhzEncoderPrinceton instance
|
||||
*/
|
||||
void subghz_encoder_princeton_for_testing_print_log(void* context);
|
||||
|
||||
/**
|
||||
* Get level duration
|
||||
* @param instance - SubGhzEncoderPrinceton instance
|
||||
* @return level duration
|
||||
*/
|
||||
LevelDuration subghz_encoder_princeton_for_testing_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzDecoderPrinceton
|
||||
* @return SubGhzDecoderPrinceton*
|
||||
*/
|
||||
SubGhzDecoderPrinceton* subghz_decoder_princeton_for_testing_alloc();
|
||||
|
||||
/**
|
||||
* Free SubGhzDecoderPrinceton
|
||||
* @param instance
|
||||
*/
|
||||
void subghz_decoder_princeton_for_testing_free(SubGhzDecoderPrinceton* instance);
|
||||
|
||||
void subghz_decoder_princeton_for_testing_set_callback(
|
||||
SubGhzDecoderPrinceton* instance,
|
||||
SubGhzDecoderPrincetonCallback callback,
|
||||
void* context);
|
||||
|
||||
/**
|
||||
* Reset internal state
|
||||
* @param instance - SubGhzDecoderPrinceton instance
|
||||
*/
|
||||
void subghz_decoder_princeton_for_testing_reset(SubGhzDecoderPrinceton* instance);
|
||||
|
||||
/**
|
||||
* Parse accepted duration
|
||||
* @param instance - SubGhzDecoderPrinceton instance
|
||||
* @param data - LevelDuration level_duration
|
||||
*/
|
||||
void subghz_decoder_princeton_for_testing_parse(
|
||||
SubGhzDecoderPrinceton* instance,
|
||||
bool level,
|
||||
uint32_t duration);
|
||||
50
applications/main/subghz/protocols/protocol_items.c
Normal file
50
applications/main/subghz/protocols/protocol_items.c
Normal file
@@ -0,0 +1,50 @@
|
||||
#include "protocol_items.h"
|
||||
|
||||
const SubGhzProtocol* subghz_protocol_registry_items[] = {
|
||||
&subghz_protocol_gate_tx,
|
||||
&subghz_protocol_keeloq,
|
||||
&subghz_protocol_star_line,
|
||||
&subghz_protocol_nice_flo,
|
||||
&subghz_protocol_came,
|
||||
&subghz_protocol_faac_slh,
|
||||
&subghz_protocol_nice_flor_s,
|
||||
&subghz_protocol_came_twee,
|
||||
&subghz_protocol_came_atomo,
|
||||
&subghz_protocol_nero_sketch,
|
||||
&subghz_protocol_ido,
|
||||
&subghz_protocol_kia,
|
||||
&subghz_protocol_hormann,
|
||||
&subghz_protocol_nero_radio,
|
||||
&subghz_protocol_somfy_telis,
|
||||
&subghz_protocol_somfy_keytis,
|
||||
&subghz_protocol_scher_khan,
|
||||
&subghz_protocol_princeton,
|
||||
&subghz_protocol_raw,
|
||||
&subghz_protocol_linear,
|
||||
&subghz_protocol_secplus_v2,
|
||||
&subghz_protocol_secplus_v1,
|
||||
&subghz_protocol_megacode,
|
||||
&subghz_protocol_holtek,
|
||||
&subghz_protocol_chamb_code,
|
||||
&subghz_protocol_power_smart,
|
||||
&subghz_protocol_marantec,
|
||||
&subghz_protocol_bett,
|
||||
&subghz_protocol_doitrand,
|
||||
&subghz_protocol_phoenix_v2,
|
||||
&subghz_protocol_honeywell_wdb,
|
||||
&subghz_protocol_magellan,
|
||||
&subghz_protocol_intertechno_v3,
|
||||
&subghz_protocol_clemsa,
|
||||
&subghz_protocol_ansonic,
|
||||
&subghz_protocol_smc5326,
|
||||
&subghz_protocol_holtek_th12x,
|
||||
&subghz_protocol_linear_delta3,
|
||||
&subghz_protocol_dooya,
|
||||
&subghz_protocol_alutech_at_4n,
|
||||
&subghz_protocol_kinggates_stylo_4k,
|
||||
&subghz_protocol_bin_raw,
|
||||
};
|
||||
|
||||
const SubGhzProtocolRegistry subghz_protocol_registry = {
|
||||
.items = subghz_protocol_registry_items,
|
||||
.size = COUNT_OF(subghz_protocol_registry_items)};
|
||||
55
applications/main/subghz/protocols/protocol_items.h
Normal file
55
applications/main/subghz/protocols/protocol_items.h
Normal file
@@ -0,0 +1,55 @@
|
||||
#pragma once
|
||||
#include "../registry.h"
|
||||
|
||||
#include "princeton.h"
|
||||
#include "keeloq.h"
|
||||
#include "star_line.h"
|
||||
#include "nice_flo.h"
|
||||
#include "came.h"
|
||||
#include "faac_slh.h"
|
||||
#include "nice_flor_s.h"
|
||||
#include "came_twee.h"
|
||||
#include "came_atomo.h"
|
||||
#include "nero_sketch.h"
|
||||
#include "ido.h"
|
||||
#include "kia.h"
|
||||
#include "hormann.h"
|
||||
#include "nero_radio.h"
|
||||
#include "somfy_telis.h"
|
||||
#include "somfy_keytis.h"
|
||||
#include "scher_khan.h"
|
||||
#include "gate_tx.h"
|
||||
#include "raw.h"
|
||||
#include "linear.h"
|
||||
#include "linear_delta3.h"
|
||||
#include "secplus_v2.h"
|
||||
#include "secplus_v1.h"
|
||||
#include "megacode.h"
|
||||
#include "holtek.h"
|
||||
#include "chamberlain_code.h"
|
||||
#include "power_smart.h"
|
||||
#include "marantec.h"
|
||||
#include "bett.h"
|
||||
#include "doitrand.h"
|
||||
#include "phoenix_v2.h"
|
||||
#include "honeywell_wdb.h"
|
||||
#include "magellan.h"
|
||||
#include "intertechno_v3.h"
|
||||
#include "clemsa.h"
|
||||
#include "ansonic.h"
|
||||
#include "smc5326.h"
|
||||
#include "holtek_ht12x.h"
|
||||
#include "dooya.h"
|
||||
#include "alutech_at_4n.h"
|
||||
#include "kinggates_stylo_4k.h"
|
||||
#include "bin_raw.h"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
extern const SubGhzProtocolRegistry subghz_protocol_registry;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
374
applications/main/subghz/protocols/raw.c
Normal file
374
applications/main/subghz/protocols/raw.c
Normal file
@@ -0,0 +1,374 @@
|
||||
#include "raw.h"
|
||||
#include <lib/flipper_format/flipper_format.h>
|
||||
#include "../subghz_file_encoder_worker.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#include <flipper_format/flipper_format_i.h>
|
||||
#include <lib/toolbox/stream/stream.h>
|
||||
|
||||
#define TAG "SubGhzProtocolRAW"
|
||||
#define SUBGHZ_DOWNLOAD_MAX_SIZE 512
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_raw_const = {
|
||||
.te_short = 50,
|
||||
.te_long = 32700,
|
||||
.te_delta = 0,
|
||||
.min_count_bit_for_found = 0,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderRAW {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
int32_t* upload_raw;
|
||||
uint16_t ind_write;
|
||||
Storage* storage;
|
||||
FlipperFormat* flipper_file;
|
||||
uint32_t file_is_open;
|
||||
FuriString* file_name;
|
||||
size_t sample_write;
|
||||
bool last_level;
|
||||
bool pause;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderRAW {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
bool is_running;
|
||||
FuriString* file_name;
|
||||
SubGhzFileEncoderWorker* file_worker_encoder;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
RAWFileIsOpenClose = 0,
|
||||
RAWFileIsOpenWrite,
|
||||
RAWFileIsOpenRead,
|
||||
} RAWFilIsOpen;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_raw_decoder = {
|
||||
.alloc = subghz_protocol_decoder_raw_alloc,
|
||||
.free = subghz_protocol_decoder_raw_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_raw_feed,
|
||||
.reset = subghz_protocol_decoder_raw_reset,
|
||||
|
||||
.get_hash_data = NULL,
|
||||
.serialize = NULL,
|
||||
.deserialize = subghz_protocol_decoder_raw_deserialize,
|
||||
.get_string = subghz_protocol_decoder_raw_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_raw_encoder = {
|
||||
.alloc = subghz_protocol_encoder_raw_alloc,
|
||||
.free = subghz_protocol_encoder_raw_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_raw_deserialize,
|
||||
.stop = subghz_protocol_encoder_raw_stop,
|
||||
.yield = subghz_protocol_encoder_raw_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_raw = {
|
||||
.name = SUBGHZ_PROTOCOL_RAW_NAME,
|
||||
.type = SubGhzProtocolTypeRAW,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_315 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_RAW |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_raw_decoder,
|
||||
.encoder = &subghz_protocol_raw_encoder,
|
||||
};
|
||||
|
||||
bool subghz_protocol_raw_save_to_file_init(
|
||||
SubGhzProtocolDecoderRAW* instance,
|
||||
const char* dev_name,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->storage = furi_record_open(RECORD_STORAGE);
|
||||
instance->flipper_file = flipper_format_file_alloc(instance->storage);
|
||||
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
bool init = false;
|
||||
|
||||
do {
|
||||
// Create subghz folder directory if necessary
|
||||
if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) {
|
||||
break;
|
||||
}
|
||||
// Create saved directory if necessary
|
||||
if(!storage_simply_mkdir(instance->storage, SUBGHZ_RAW_FOLDER)) {
|
||||
break;
|
||||
}
|
||||
|
||||
furi_string_set(instance->file_name, dev_name);
|
||||
// First remove subghz device file if it was saved
|
||||
furi_string_printf(temp_str, "%s/%s%s", SUBGHZ_RAW_FOLDER, dev_name, SUBGHZ_APP_EXTENSION);
|
||||
|
||||
if(!storage_simply_remove(instance->storage, furi_string_get_cstr(temp_str))) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Open file
|
||||
if(!flipper_format_file_open_always(
|
||||
instance->flipper_file, furi_string_get_cstr(temp_str))) {
|
||||
FURI_LOG_E(TAG, "Unable to open file for write: %s", furi_string_get_cstr(temp_str));
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_header_cstr(
|
||||
instance->flipper_file, SUBGHZ_RAW_FILE_TYPE, SUBGHZ_RAW_FILE_VERSION)) {
|
||||
FURI_LOG_E(TAG, "Unable to add header");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_uint32(
|
||||
instance->flipper_file, "Frequency", &preset->frequency, 1)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Frequency");
|
||||
break;
|
||||
}
|
||||
|
||||
subghz_block_generic_get_preset_name(furi_string_get_cstr(preset->name), temp_str);
|
||||
if(!flipper_format_write_string_cstr(
|
||||
instance->flipper_file, "Preset", furi_string_get_cstr(temp_str))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Preset");
|
||||
break;
|
||||
}
|
||||
if(!strcmp(furi_string_get_cstr(temp_str), "FuriHalSubGhzPresetCustom")) {
|
||||
if(!flipper_format_write_string_cstr(
|
||||
instance->flipper_file, "Custom_preset_module", "CC1101")) {
|
||||
FURI_LOG_E(TAG, "Unable to add Custom_preset_module");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_write_hex(
|
||||
instance->flipper_file, "Custom_preset_data", preset->data, preset->data_size)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Custom_preset_data");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if(!flipper_format_write_string_cstr(
|
||||
instance->flipper_file, "Protocol", instance->base.protocol->name)) {
|
||||
FURI_LOG_E(TAG, "Unable to add Protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
instance->upload_raw = malloc(SUBGHZ_DOWNLOAD_MAX_SIZE * sizeof(int32_t));
|
||||
instance->file_is_open = RAWFileIsOpenWrite;
|
||||
instance->sample_write = 0;
|
||||
instance->last_level = false;
|
||||
instance->pause = false;
|
||||
init = true;
|
||||
} while(0);
|
||||
|
||||
furi_string_free(temp_str);
|
||||
|
||||
return init;
|
||||
}
|
||||
|
||||
static bool subghz_protocol_raw_save_to_file_write(SubGhzProtocolDecoderRAW* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
bool is_write = false;
|
||||
if(instance->file_is_open == RAWFileIsOpenWrite) {
|
||||
if(!flipper_format_write_int32(
|
||||
instance->flipper_file, "RAW_Data", instance->upload_raw, instance->ind_write)) {
|
||||
FURI_LOG_E(TAG, "Unable to add RAW_Data");
|
||||
} else {
|
||||
instance->sample_write += instance->ind_write;
|
||||
instance->ind_write = 0;
|
||||
is_write = true;
|
||||
}
|
||||
}
|
||||
return is_write;
|
||||
}
|
||||
|
||||
void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->file_is_open == RAWFileIsOpenWrite && instance->ind_write)
|
||||
subghz_protocol_raw_save_to_file_write(instance);
|
||||
if(instance->file_is_open != RAWFileIsOpenClose) {
|
||||
free(instance->upload_raw);
|
||||
instance->upload_raw = NULL;
|
||||
flipper_format_file_close(instance->flipper_file);
|
||||
flipper_format_free(instance->flipper_file);
|
||||
furi_record_close(RECORD_STORAGE);
|
||||
}
|
||||
|
||||
instance->file_is_open = RAWFileIsOpenClose;
|
||||
}
|
||||
|
||||
void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->pause != pause) {
|
||||
instance->pause = pause;
|
||||
}
|
||||
}
|
||||
|
||||
size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance) {
|
||||
return instance->sample_write + instance->ind_write;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderRAW* instance = malloc(sizeof(SubGhzProtocolDecoderRAW));
|
||||
instance->base.protocol = &subghz_protocol_raw;
|
||||
instance->upload_raw = NULL;
|
||||
instance->ind_write = 0;
|
||||
instance->last_level = false;
|
||||
instance->file_is_open = RAWFileIsOpenClose;
|
||||
instance->file_name = furi_string_alloc();
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_raw_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderRAW* instance = context;
|
||||
furi_string_free(instance->file_name);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_raw_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderRAW* instance = context;
|
||||
instance->ind_write = 0;
|
||||
instance->last_level = false;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderRAW* instance = context;
|
||||
|
||||
if(!instance->pause && (instance->upload_raw != NULL)) {
|
||||
if(duration > subghz_protocol_raw_const.te_short) {
|
||||
if(instance->last_level != level) {
|
||||
instance->last_level = (level ? true : false);
|
||||
instance->upload_raw[instance->ind_write++] = (level ? duration : -duration);
|
||||
}
|
||||
}
|
||||
|
||||
if(instance->ind_write == SUBGHZ_DOWNLOAD_MAX_SIZE) {
|
||||
subghz_protocol_raw_save_to_file_write(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
UNUSED(context);
|
||||
UNUSED(flipper_format);
|
||||
//ToDo stub, for backwards compatibility
|
||||
return true;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
//SubGhzProtocolDecoderRAW* instance = context;
|
||||
UNUSED(context);
|
||||
//ToDo no use
|
||||
furi_string_cat_printf(output, "RAW Data");
|
||||
}
|
||||
|
||||
void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderRAW* instance = malloc(sizeof(SubGhzProtocolEncoderRAW));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_raw;
|
||||
instance->file_name = furi_string_alloc();
|
||||
instance->is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_raw_stop(void* context) {
|
||||
SubGhzProtocolEncoderRAW* instance = context;
|
||||
instance->is_running = false;
|
||||
if(subghz_file_encoder_worker_is_running(instance->file_worker_encoder)) {
|
||||
subghz_file_encoder_worker_stop(instance->file_worker_encoder);
|
||||
subghz_file_encoder_worker_free(instance->file_worker_encoder);
|
||||
}
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_raw_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderRAW* instance = context;
|
||||
subghz_protocol_encoder_raw_stop(instance);
|
||||
furi_string_free(instance->file_name);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_raw_file_encoder_worker_set_callback_end(
|
||||
SubGhzProtocolEncoderRAW* instance,
|
||||
SubGhzProtocolEncoderRAWCallbackEnd callback_end,
|
||||
void* context_end) {
|
||||
furi_assert(instance);
|
||||
furi_assert(callback_end);
|
||||
subghz_file_encoder_worker_callback_end(
|
||||
instance->file_worker_encoder, callback_end, context_end);
|
||||
}
|
||||
|
||||
static bool subghz_protocol_encoder_raw_worker_init(SubGhzProtocolEncoderRAW* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->file_worker_encoder = subghz_file_encoder_worker_alloc();
|
||||
if(subghz_file_encoder_worker_start(
|
||||
instance->file_worker_encoder, furi_string_get_cstr(instance->file_name))) {
|
||||
//the worker needs a file in order to open and read part of the file
|
||||
furi_delay_ms(100);
|
||||
instance->is_running = true;
|
||||
} else {
|
||||
subghz_protocol_encoder_raw_stop(instance);
|
||||
}
|
||||
return instance->is_running;
|
||||
}
|
||||
|
||||
void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path) {
|
||||
do {
|
||||
stream_clean(flipper_format_get_raw_stream(flipper_format));
|
||||
if(!flipper_format_write_string_cstr(flipper_format, "Protocol", "RAW")) {
|
||||
FURI_LOG_E(TAG, "Unable to add Protocol");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_write_string_cstr(flipper_format, "File_name", file_path)) {
|
||||
FURI_LOG_E(TAG, "Unable to add File_name");
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderRAW* instance = context;
|
||||
bool res = false;
|
||||
FuriString* temp_str;
|
||||
temp_str = furi_string_alloc();
|
||||
do {
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
|
||||
if(!flipper_format_read_string(flipper_format, "File_name", temp_str)) {
|
||||
FURI_LOG_E(TAG, "Missing File_name");
|
||||
break;
|
||||
}
|
||||
furi_string_set(instance->file_name, temp_str);
|
||||
|
||||
res = subghz_protocol_encoder_raw_worker_init(instance);
|
||||
} while(false);
|
||||
furi_string_free(temp_str);
|
||||
return res;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_raw_yield(void* context) {
|
||||
SubGhzProtocolEncoderRAW* instance = context;
|
||||
|
||||
if(!instance->is_running) return level_duration_reset();
|
||||
return subghz_file_encoder_worker_get_level_duration(instance->file_worker_encoder);
|
||||
}
|
||||
148
applications/main/subghz/protocols/raw.h
Normal file
148
applications/main/subghz/protocols/raw.h
Normal file
@@ -0,0 +1,148 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_RAW_NAME "RAW"
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef void (*SubGhzProtocolEncoderRAWCallbackEnd)(void* context);
|
||||
|
||||
typedef struct SubGhzProtocolDecoderRAW SubGhzProtocolDecoderRAW;
|
||||
typedef struct SubGhzProtocolEncoderRAW SubGhzProtocolEncoderRAW;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_raw_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_raw_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_raw;
|
||||
|
||||
/**
|
||||
* Open file for writing
|
||||
* @param instance Pointer to a SubGhzProtocolDecoderRAW instance
|
||||
* @param dev_name File name
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_raw_save_to_file_init(
|
||||
SubGhzProtocolDecoderRAW* instance,
|
||||
const char* dev_name,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Stop writing file to flash
|
||||
* @param instance Pointer to a SubGhzProtocolDecoderRAW instance
|
||||
*/
|
||||
void subghz_protocol_raw_save_to_file_stop(SubGhzProtocolDecoderRAW* instance);
|
||||
|
||||
/**
|
||||
* Get the number of samples received SubGhzProtocolDecoderRAW.
|
||||
* @param instance Pointer to a SubGhzProtocolDecoderRAW instance
|
||||
* @return count of samples
|
||||
*/
|
||||
size_t subghz_protocol_raw_get_sample_write(SubGhzProtocolDecoderRAW* instance);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderRAW.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderRAW* pointer to a SubGhzProtocolDecoderRAW instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_raw_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderRAW.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderRAW instance
|
||||
*/
|
||||
void subghz_protocol_decoder_raw_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderRAW.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderRAW instance
|
||||
*/
|
||||
void subghz_protocol_decoder_raw_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderRAW instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_raw_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderRAW.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderRAW instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_raw_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderRAW instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_raw_get_string(void* context, FuriString* output);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderRAW.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderRAW* pointer to a SubGhzProtocolEncoderRAW instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_raw_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderRAW.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderRAW instance
|
||||
*/
|
||||
void subghz_protocol_encoder_raw_free(void* context);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderRAW instance
|
||||
*/
|
||||
void subghz_protocol_encoder_raw_stop(void* context);
|
||||
|
||||
/**
|
||||
* pause writing to flash.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderRAW instance
|
||||
* @param pause pause writing
|
||||
*/
|
||||
void subghz_protocol_raw_save_to_file_pause(SubGhzProtocolDecoderRAW* instance, bool pause);
|
||||
|
||||
/**
|
||||
* Set callback on completion of file transfer.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderRAW instance
|
||||
* @param callback_end Callback, SubGhzProtocolEncoderRAWCallbackEnd
|
||||
* @param context_end Context
|
||||
*/
|
||||
void subghz_protocol_raw_file_encoder_worker_set_callback_end(
|
||||
SubGhzProtocolEncoderRAW* instance,
|
||||
SubGhzProtocolEncoderRAWCallbackEnd callback_end,
|
||||
void* context_end);
|
||||
|
||||
/**
|
||||
* File generation for RAW work.
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param file_path File path
|
||||
*/
|
||||
void subghz_protocol_raw_gen_fff_data(FlipperFormat* flipper_format, const char* file_path);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderRAW instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_raw_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderRAW instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_raw_yield(void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
288
applications/main/subghz/protocols/scher_khan.c
Normal file
288
applications/main/subghz/protocols/scher_khan.c
Normal file
@@ -0,0 +1,288 @@
|
||||
#include "scher_khan.h"
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
//https://phreakerclub.com/72
|
||||
//https://phreakerclub.com/forum/showthread.php?t=7&page=2
|
||||
//https://phreakerclub.com/forum/showthread.php?t=274&highlight=magicar
|
||||
//!!! https://phreakerclub.com/forum/showthread.php?t=489&highlight=magicar&page=5
|
||||
|
||||
#define TAG "SubGhzProtocolScherKhan"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_scher_khan_const = {
|
||||
.te_short = 750,
|
||||
.te_long = 1100,
|
||||
.te_delta = 150,
|
||||
.min_count_bit_for_found = 35,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderScherKhan {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint16_t header_count;
|
||||
const char* protocol_name;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderScherKhan {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
ScherKhanDecoderStepReset = 0,
|
||||
ScherKhanDecoderStepCheckPreambula,
|
||||
ScherKhanDecoderStepSaveDuration,
|
||||
ScherKhanDecoderStepCheckDuration,
|
||||
} ScherKhanDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder = {
|
||||
.alloc = subghz_protocol_decoder_scher_khan_alloc,
|
||||
.free = subghz_protocol_decoder_scher_khan_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_scher_khan_feed,
|
||||
.reset = subghz_protocol_decoder_scher_khan_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_scher_khan_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_scher_khan_serialize,
|
||||
.deserialize = subghz_protocol_decoder_scher_khan_deserialize,
|
||||
.get_string = subghz_protocol_decoder_scher_khan_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder = {
|
||||
.alloc = NULL,
|
||||
.free = NULL,
|
||||
|
||||
.deserialize = NULL,
|
||||
.stop = NULL,
|
||||
.yield = NULL,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_scher_khan = {
|
||||
.name = SUBGHZ_PROTOCOL_SCHER_KHAN_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Save,
|
||||
|
||||
.decoder = &subghz_protocol_scher_khan_decoder,
|
||||
.encoder = &subghz_protocol_scher_khan_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderScherKhan* instance = malloc(sizeof(SubGhzProtocolDecoderScherKhan));
|
||||
instance->base.protocol = &subghz_protocol_scher_khan;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_scher_khan_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderScherKhan* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_scher_khan_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderScherKhan* instance = context;
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepReset;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderScherKhan* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case ScherKhanDecoderStepReset:
|
||||
if((level) && (DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) <
|
||||
subghz_protocol_scher_khan_const.te_delta)) {
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepCheckPreambula;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->header_count = 0;
|
||||
}
|
||||
break;
|
||||
case ScherKhanDecoderStepCheckPreambula:
|
||||
if(level) {
|
||||
if((DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) <
|
||||
subghz_protocol_scher_khan_const.te_delta) ||
|
||||
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) <
|
||||
subghz_protocol_scher_khan_const.te_delta)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepReset;
|
||||
}
|
||||
} else if(
|
||||
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short * 2) <
|
||||
subghz_protocol_scher_khan_const.te_delta) ||
|
||||
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) <
|
||||
subghz_protocol_scher_khan_const.te_delta)) {
|
||||
if(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short * 2) <
|
||||
subghz_protocol_scher_khan_const.te_delta) {
|
||||
// Found header
|
||||
instance->header_count++;
|
||||
break;
|
||||
} else if(
|
||||
DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) <
|
||||
subghz_protocol_scher_khan_const.te_delta) {
|
||||
// Found start bit
|
||||
if(instance->header_count >= 2) {
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 1;
|
||||
} else {
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case ScherKhanDecoderStepSaveDuration:
|
||||
if(level) {
|
||||
if(duration >= (subghz_protocol_scher_khan_const.te_delta * 2UL +
|
||||
subghz_protocol_scher_khan_const.te_long)) {
|
||||
//Found stop bit
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepReset;
|
||||
if(instance->decoder.decode_count_bit >=
|
||||
subghz_protocol_scher_khan_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;
|
||||
break;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepCheckDuration;
|
||||
}
|
||||
|
||||
} else {
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case ScherKhanDecoderStepCheckDuration:
|
||||
if(!level) {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_scher_khan_const.te_short) <
|
||||
subghz_protocol_scher_khan_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_short) <
|
||||
subghz_protocol_scher_khan_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0);
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_scher_khan_const.te_long) <
|
||||
subghz_protocol_scher_khan_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_scher_khan_const.te_long) <
|
||||
subghz_protocol_scher_khan_const.te_delta)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1);
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = ScherKhanDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
* @param protocol_name
|
||||
*/
|
||||
static void subghz_protocol_scher_khan_check_remote_controller(
|
||||
SubGhzBlockGeneric* instance,
|
||||
const char** protocol_name) {
|
||||
/*
|
||||
* MAGICAR 51 bit 00000001A99121DE83C3 MAGIC CODE, Dynamic
|
||||
* 0E8C1619E830C -> 000011101000110000010110 0001 1001 1110 1000001100001100
|
||||
* 0E8C1629D830D -> 000011101000110000010110 0010 1001 1101 1000001100001101
|
||||
* 0E8C1649B830E -> 000011101000110000010110 0100 1001 1011 1000001100001110
|
||||
* 0E8C16897830F -> 000011101000110000010110 1000 1001 0111 1000001100001111
|
||||
* Serial Key Ser ~Key CNT
|
||||
*/
|
||||
|
||||
switch(instance->data_count_bit) {
|
||||
// case 35: //MAGIC CODE, Static
|
||||
// instance->protocol_name = "MAGIC CODE, Static";
|
||||
// break;
|
||||
case 51: //MAGIC CODE, Dynamic
|
||||
*protocol_name = "MAGIC CODE, Dynamic";
|
||||
instance->serial = ((instance->data >> 24) & 0xFFFFFF0) | ((instance->data >> 20) & 0x0F);
|
||||
instance->btn = (instance->data >> 24) & 0x0F;
|
||||
instance->cnt = instance->data & 0xFFFF;
|
||||
break;
|
||||
// case 57: //MAGIC CODE PRO / PRO2
|
||||
// instance->protocol_name = "MAGIC CODE PRO / PRO2";
|
||||
// break;
|
||||
|
||||
default:
|
||||
*protocol_name = "Unknown";
|
||||
instance->serial = 0;
|
||||
instance->btn = 0;
|
||||
instance->cnt = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderScherKhan* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_scher_khan_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderScherKhan* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderScherKhan* instance = context;
|
||||
return subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderScherKhan* instance = context;
|
||||
|
||||
subghz_protocol_scher_khan_check_remote_controller(
|
||||
&instance->generic, &instance->protocol_name);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"Sn:%07lX Btn:%X Cnt:%04lX\r\n"
|
||||
"Pt: %s\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data >> 32),
|
||||
(uint32_t)instance->generic.data,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->generic.cnt,
|
||||
instance->protocol_name);
|
||||
}
|
||||
73
applications/main/subghz/protocols/scher_khan.h
Normal file
73
applications/main/subghz/protocols/scher_khan.h
Normal file
@@ -0,0 +1,73 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_SCHER_KHAN_NAME "Scher-Khan"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderScherKhan SubGhzProtocolDecoderScherKhan;
|
||||
typedef struct SubGhzProtocolEncoderScherKhan SubGhzProtocolEncoderScherKhan;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_scher_khan_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_scher_khan_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_scher_khan;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderScherKhan.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderScherKhan* pointer to a SubGhzProtocolDecoderScherKhan instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_scher_khan_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderScherKhan.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
|
||||
*/
|
||||
void subghz_protocol_decoder_scher_khan_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderScherKhan.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
|
||||
*/
|
||||
void subghz_protocol_decoder_scher_khan_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_scher_khan_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_scher_khan_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderScherKhan.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_scher_khan_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderScherKhan.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_scher_khan_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderScherKhan instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_scher_khan_get_string(void* context, FuriString* output);
|
||||
634
applications/main/subghz/protocols/secplus_v1.c
Normal file
634
applications/main/subghz/protocols/secplus_v1.c
Normal file
@@ -0,0 +1,634 @@
|
||||
#include "secplus_v1.h"
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://github.com/argilo/secplus
|
||||
* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v1.c
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocoSecPlus_v1"
|
||||
|
||||
#define SECPLUS_V1_BIT_ERR -1 //0b0000
|
||||
#define SECPLUS_V1_BIT_0 0 //0b0001
|
||||
#define SECPLUS_V1_BIT_1 1 //0b0011
|
||||
#define SECPLUS_V1_BIT_2 2 //0b0111
|
||||
|
||||
#define SECPLUS_V1_PACKET_1_HEADER 0x00
|
||||
#define SECPLUS_V1_PACKET_2_HEADER 0x02
|
||||
#define SECPLUS_V1_PACKET_1_INDEX_BASE 0
|
||||
#define SECPLUS_V1_PACKET_2_INDEX_BASE 21
|
||||
#define SECPLUS_V1_PACKET_1_ACCEPTED (1 << 0)
|
||||
#define SECPLUS_V1_PACKET_2_ACCEPTED (1 << 1)
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_secplus_v1_const = {
|
||||
.te_short = 500,
|
||||
.te_long = 1500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = 21,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderSecPlus_v1 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint8_t packet_accepted;
|
||||
uint8_t base_packet_index;
|
||||
uint8_t data_array[44];
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderSecPlus_v1 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint8_t data_array[44];
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SecPlus_v1DecoderStepReset = 0,
|
||||
SecPlus_v1DecoderStepSearchStartBit,
|
||||
SecPlus_v1DecoderStepSaveDuration,
|
||||
SecPlus_v1DecoderStepDecoderData,
|
||||
} SecPlus_v1DecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder = {
|
||||
.alloc = subghz_protocol_decoder_secplus_v1_alloc,
|
||||
.free = subghz_protocol_decoder_secplus_v1_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_secplus_v1_feed,
|
||||
.reset = subghz_protocol_decoder_secplus_v1_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_secplus_v1_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_secplus_v1_serialize,
|
||||
.deserialize = subghz_protocol_decoder_secplus_v1_deserialize,
|
||||
.get_string = subghz_protocol_decoder_secplus_v1_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder = {
|
||||
.alloc = subghz_protocol_encoder_secplus_v1_alloc,
|
||||
.free = subghz_protocol_encoder_secplus_v1_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_secplus_v1_deserialize,
|
||||
.stop = subghz_protocol_encoder_secplus_v1_stop,
|
||||
.yield = subghz_protocol_encoder_secplus_v1_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_secplus_v1 = {
|
||||
.name = SUBGHZ_PROTOCOL_SECPLUS_V1_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Send | SubGhzProtocolFlag_Save,
|
||||
|
||||
.decoder = &subghz_protocol_secplus_v1_decoder,
|
||||
.encoder = &subghz_protocol_secplus_v1_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v1));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_secplus_v1;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 128;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_secplus_v1_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderSecPlus_v1* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool
|
||||
subghz_protocol_encoder_secplus_v1_get_upload(SubGhzProtocolEncoderSecPlus_v1* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
size_t size_upload = (instance->generic.data_count_bit * 2);
|
||||
if(size_upload > instance->encoder.size_upload) {
|
||||
FURI_LOG_E(TAG, "Encoder size upload exceeds allocated encoder buffer.");
|
||||
return false;
|
||||
} else {
|
||||
instance->encoder.size_upload = size_upload;
|
||||
}
|
||||
|
||||
//Send header packet 1
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116 + 3));
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short);
|
||||
|
||||
//Send data packet 1
|
||||
for(uint8_t i = SECPLUS_V1_PACKET_1_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_1_INDEX_BASE + 21;
|
||||
i++) {
|
||||
switch(instance->data_array[i]) {
|
||||
case SECPLUS_V1_BIT_0:
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short);
|
||||
break;
|
||||
case SECPLUS_V1_BIT_1:
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2);
|
||||
break;
|
||||
case SECPLUS_V1_BIT_2:
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Encoder error, wrong bit type");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
//Send header packet 2
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * (116));
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);
|
||||
|
||||
//Send data packet 2
|
||||
for(uint8_t i = SECPLUS_V1_PACKET_2_INDEX_BASE + 1; i < SECPLUS_V1_PACKET_2_INDEX_BASE + 21;
|
||||
i++) {
|
||||
switch(instance->data_array[i]) {
|
||||
case SECPLUS_V1_BIT_0:
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short);
|
||||
break;
|
||||
case SECPLUS_V1_BIT_1:
|
||||
instance->encoder.upload[index++] = level_duration_make(
|
||||
false, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 2);
|
||||
break;
|
||||
case SECPLUS_V1_BIT_2:
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_secplus_v1_const.te_short);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(true, (uint32_t)subghz_protocol_secplus_v1_const.te_short * 3);
|
||||
break;
|
||||
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Encoder error, wrong bit type.");
|
||||
return false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Security+ 1.0 message encoding
|
||||
* @param instance SubGhzProtocolEncoderSecPlus_v1*
|
||||
*/
|
||||
|
||||
static bool subghz_protocol_secplus_v1_encode(SubGhzProtocolEncoderSecPlus_v1* instance) {
|
||||
uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF;
|
||||
uint32_t rolling = instance->generic.data & 0xFFFFFFFF;
|
||||
|
||||
uint8_t rolling_array[20] = {0};
|
||||
uint8_t fixed_array[20] = {0};
|
||||
uint32_t acc = 0;
|
||||
|
||||
//increment the counter
|
||||
rolling += 2;
|
||||
|
||||
//update data
|
||||
instance->generic.data &= 0xFFFFFFFF00000000;
|
||||
instance->generic.data |= rolling;
|
||||
|
||||
if(rolling == 0xFFFFFFFF) {
|
||||
rolling = 0xE6000000;
|
||||
}
|
||||
if(fixed > 0xCFD41B90) {
|
||||
FURI_LOG_E("TAG", "Encode wrong fixed data");
|
||||
return false;
|
||||
}
|
||||
|
||||
rolling = subghz_protocol_blocks_reverse_key(rolling, 32);
|
||||
|
||||
for(int i = 19; i > -1; i--) {
|
||||
rolling_array[i] = rolling % 3;
|
||||
rolling /= 3;
|
||||
fixed_array[i] = fixed % 3;
|
||||
fixed /= 3;
|
||||
}
|
||||
|
||||
instance->data_array[SECPLUS_V1_PACKET_1_INDEX_BASE] = SECPLUS_V1_PACKET_1_HEADER;
|
||||
instance->data_array[SECPLUS_V1_PACKET_2_INDEX_BASE] = SECPLUS_V1_PACKET_2_HEADER;
|
||||
|
||||
//encode packet 1
|
||||
for(uint8_t i = 1; i < 11; i++) {
|
||||
acc += rolling_array[i - 1];
|
||||
instance->data_array[i * 2 - 1] = rolling_array[i - 1];
|
||||
acc += fixed_array[i - 1];
|
||||
instance->data_array[i * 2] = acc % 3;
|
||||
}
|
||||
|
||||
acc = 0;
|
||||
//encode packet 2
|
||||
for(uint8_t i = 11; i < 21; i++) {
|
||||
acc += rolling_array[i - 1];
|
||||
instance->data_array[i * 2] = rolling_array[i - 1];
|
||||
acc += fixed_array[i - 1];
|
||||
instance->data_array[i * 2 + 1] = acc % 3;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderSecPlus_v1* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_secplus_v1_encode(instance)) {
|
||||
break;
|
||||
}
|
||||
if(!subghz_protocol_encoder_secplus_v1_get_upload(instance)) {
|
||||
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");
|
||||
break;
|
||||
}
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_secplus_v1_stop(void* context) {
|
||||
SubGhzProtocolEncoderSecPlus_v1* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context) {
|
||||
SubGhzProtocolEncoderSecPlus_v1* 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;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_secplus_v1_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderSecPlus_v1* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v1));
|
||||
instance->base.protocol = &subghz_protocol_secplus_v1;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_secplus_v1_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v1* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_secplus_v1_reset(void* context) {
|
||||
furi_assert(context);
|
||||
// SubGhzProtocolDecoderSecPlus_v1* instance = context;
|
||||
// does not reset the decoder because you need to get 2 parts of the package
|
||||
}
|
||||
|
||||
/**
|
||||
* Security+ 1.0 message decoding
|
||||
* @param instance SubGhzProtocolDecoderSecPlus_v1*
|
||||
*/
|
||||
|
||||
static void subghz_protocol_secplus_v1_decode(SubGhzProtocolDecoderSecPlus_v1* instance) {
|
||||
uint32_t rolling = 0;
|
||||
uint32_t fixed = 0;
|
||||
uint32_t acc = 0;
|
||||
uint8_t digit = 0;
|
||||
|
||||
//decode packet 1
|
||||
for(uint8_t i = 1; i < 21; i += 2) {
|
||||
digit = instance->data_array[i];
|
||||
rolling = (rolling * 3) + digit;
|
||||
acc += digit;
|
||||
|
||||
digit = (60 + instance->data_array[i + 1] - acc) % 3;
|
||||
fixed = (fixed * 3) + digit;
|
||||
acc += digit;
|
||||
}
|
||||
|
||||
acc = 0;
|
||||
//decode packet 2
|
||||
for(uint8_t i = 22; i < 42; i += 2) {
|
||||
digit = instance->data_array[i];
|
||||
rolling = (rolling * 3) + digit;
|
||||
acc += digit;
|
||||
|
||||
digit = (60 + instance->data_array[i + 1] - acc) % 3;
|
||||
fixed = (fixed * 3) + digit;
|
||||
acc += digit;
|
||||
}
|
||||
|
||||
rolling = subghz_protocol_blocks_reverse_key(rolling, 32);
|
||||
instance->generic.data = (uint64_t)fixed << 32 | rolling;
|
||||
|
||||
instance->generic.data_count_bit =
|
||||
subghz_protocol_secplus_v1_const.min_count_bit_for_found * 2;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v1* instance = context;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case SecPlus_v1DecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) <
|
||||
subghz_protocol_secplus_v1_const.te_delta * 120)) {
|
||||
//Found header Security+ 1.0
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->packet_accepted = 0;
|
||||
memset(instance->data_array, 0, sizeof(instance->data_array));
|
||||
}
|
||||
break;
|
||||
case SecPlus_v1DecoderStepSearchStartBit:
|
||||
if(level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) <
|
||||
subghz_protocol_secplus_v1_const.te_delta) {
|
||||
instance->base_packet_index = SECPLUS_V1_PACKET_1_INDEX_BASE;
|
||||
instance
|
||||
->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =
|
||||
SECPLUS_V1_BIT_0;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_long) <
|
||||
subghz_protocol_secplus_v1_const.te_delta) {
|
||||
instance->base_packet_index = SECPLUS_V1_PACKET_2_INDEX_BASE;
|
||||
instance
|
||||
->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =
|
||||
SECPLUS_V1_BIT_2;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case SecPlus_v1DecoderStepSaveDuration:
|
||||
if(!level) { //save interval
|
||||
if(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 120) <
|
||||
subghz_protocol_secplus_v1_const.te_delta * 120) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_secplus_v1_const.min_count_bit_for_found) {
|
||||
if(instance->base_packet_index == SECPLUS_V1_PACKET_1_INDEX_BASE)
|
||||
instance->packet_accepted |= SECPLUS_V1_PACKET_1_ACCEPTED;
|
||||
if(instance->base_packet_index == SECPLUS_V1_PACKET_2_INDEX_BASE)
|
||||
instance->packet_accepted |= SECPLUS_V1_PACKET_2_ACCEPTED;
|
||||
|
||||
if(instance->packet_accepted ==
|
||||
(SECPLUS_V1_PACKET_1_ACCEPTED | SECPLUS_V1_PACKET_2_ACCEPTED)) {
|
||||
subghz_protocol_secplus_v1_decode(instance);
|
||||
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepReset;
|
||||
}
|
||||
}
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepSearchStartBit;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
} else {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepDecoderData;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
case SecPlus_v1DecoderStepDecoderData:
|
||||
if(level && (instance->decoder.decode_count_bit <=
|
||||
subghz_protocol_secplus_v1_const.min_count_bit_for_found)) {
|
||||
if((DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 3) <
|
||||
subghz_protocol_secplus_v1_const.te_delta * 3) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short) <
|
||||
subghz_protocol_secplus_v1_const.te_delta)) {
|
||||
instance
|
||||
->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =
|
||||
SECPLUS_V1_BIT_0;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short * 2) <
|
||||
subghz_protocol_secplus_v1_const.te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 2) <
|
||||
subghz_protocol_secplus_v1_const.te_delta * 2)) {
|
||||
instance
|
||||
->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =
|
||||
SECPLUS_V1_BIT_1;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;
|
||||
} else if(
|
||||
(DURATION_DIFF(
|
||||
instance->decoder.te_last, subghz_protocol_secplus_v1_const.te_short) <
|
||||
subghz_protocol_secplus_v1_const.te_delta) &&
|
||||
(DURATION_DIFF(duration, subghz_protocol_secplus_v1_const.te_short * 3) <
|
||||
subghz_protocol_secplus_v1_const.te_delta * 3)) {
|
||||
instance
|
||||
->data_array[instance->decoder.decode_count_bit + instance->base_packet_index] =
|
||||
SECPLUS_V1_BIT_2;
|
||||
instance->decoder.decode_count_bit++;
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepSaveDuration;
|
||||
} else {
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
instance->decoder.parser_step = SecPlus_v1DecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v1* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_secplus_v1_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v1* instance = context;
|
||||
return subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v1* instance = context;
|
||||
bool ret = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
2 * subghz_protocol_secplus_v1_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
ret = true;
|
||||
} while(false);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed) {
|
||||
//uint8_t id0 = (fixed / 3) % 3;
|
||||
uint8_t id1 = (fixed / 9) % 3;
|
||||
uint8_t btn = fixed % 3;
|
||||
|
||||
do {
|
||||
if(id1 == 0) return false;
|
||||
if(!(btn == 0 || btn == 1 || btn == 2)) return false; //-V560
|
||||
} while(false);
|
||||
return true;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v1* instance = context;
|
||||
|
||||
uint32_t fixed = (instance->generic.data >> 32) & 0xFFFFFFFF;
|
||||
instance->generic.cnt = instance->generic.data & 0xFFFFFFFF;
|
||||
|
||||
instance->generic.btn = fixed % 3;
|
||||
uint8_t id0 = (fixed / 3) % 3;
|
||||
uint8_t id1 = (fixed / 9) % 3;
|
||||
uint16_t pin = 0;
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Key:0x%lX%08lX\r\n"
|
||||
"id1:%d id0:%d",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->generic.data >> 32),
|
||||
(uint32_t)instance->generic.data,
|
||||
id1,
|
||||
id0);
|
||||
|
||||
if(id1 == 0) {
|
||||
// (fixed // 3**3) % (3**7) 3^3=27 3^73=72187
|
||||
|
||||
instance->generic.serial = (fixed / 27) % 2187;
|
||||
// pin = (fixed // 3**10) % (3**9) 3^10=59049 3^9=19683
|
||||
pin = (fixed / 59049) % 19683;
|
||||
|
||||
if(pin <= 9999) {
|
||||
furi_string_cat_printf(output, " pin:%d", pin);
|
||||
} else if(pin <= 11029) {
|
||||
furi_string_cat_printf(output, " pin:enter");
|
||||
}
|
||||
|
||||
int pin_suffix = 0;
|
||||
// pin_suffix = (fixed // 3**19) % 3 3^19=1162261467
|
||||
pin_suffix = (fixed / 1162261467) % 3;
|
||||
|
||||
if(pin_suffix == 1) {
|
||||
furi_string_cat_printf(output, " #\r\n");
|
||||
} else if(pin_suffix == 2) {
|
||||
furi_string_cat_printf(output, " *\r\n");
|
||||
} else {
|
||||
furi_string_cat_printf(output, "\r\n");
|
||||
}
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"Sn:0x%08lX\r\n"
|
||||
"Cnt:0x%03lX "
|
||||
"Sw_id:0x%X\r\n",
|
||||
instance->generic.serial,
|
||||
instance->generic.cnt,
|
||||
instance->generic.btn);
|
||||
} else {
|
||||
//id = fixed / 27;
|
||||
instance->generic.serial = fixed / 27;
|
||||
if(instance->generic.btn == 1) {
|
||||
furi_string_cat_printf(output, " Btn:left\r\n");
|
||||
} else if(instance->generic.btn == 0) {
|
||||
furi_string_cat_printf(output, " Btn:middle\r\n");
|
||||
} else if(instance->generic.btn == 2) { //-V547
|
||||
furi_string_cat_printf(output, " Btn:right\r\n");
|
||||
}
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"Sn:0x%08lX\r\n"
|
||||
"Cnt:0x%03lX "
|
||||
"Sw_id:0x%X\r\n",
|
||||
instance->generic.serial,
|
||||
instance->generic.cnt,
|
||||
instance->generic.btn);
|
||||
}
|
||||
}
|
||||
113
applications/main/subghz/protocols/secplus_v1.h
Normal file
113
applications/main/subghz/protocols/secplus_v1.h
Normal file
@@ -0,0 +1,113 @@
|
||||
#pragma once
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_SECPLUS_V1_NAME "Security+ 1.0"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderSecPlus_v1 SubGhzProtocolDecoderSecPlus_v1;
|
||||
typedef struct SubGhzProtocolEncoderSecPlus_v1 SubGhzProtocolEncoderSecPlus_v1;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_secplus_v1_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_secplus_v1_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_secplus_v1;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderSecPlus_v1.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderSecPlus_v1* pointer to a SubGhzProtocolEncoderSecPlus_v1 instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_secplus_v1_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderSecPlus_v1.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_secplus_v1_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_secplus_v1_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderSecPlus_v1 instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_secplus_v1_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderSecPlus_v1.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderSecPlus_v1* pointer to a SubGhzProtocolDecoderSecPlus_v1 instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_secplus_v1_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderSecPlus_v1.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_secplus_v1_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderSecPlus_v1.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_secplus_v1_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_secplus_v1_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_secplus_v1_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderSecPlus_v1.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_secplus_v1_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderSecPlus_v1.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_secplus_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Validation of fixed parts SubGhzProtocolDecoderSecPlus_v1.
|
||||
* @param fixed fixed parts
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_secplus_v1_check_fixed(uint32_t fixed);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v1 instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_secplus_v1_get_string(void* context, FuriString* output);
|
||||
833
applications/main/subghz/protocols/secplus_v2.c
Normal file
833
applications/main/subghz/protocols/secplus_v2.c
Normal file
@@ -0,0 +1,833 @@
|
||||
#include "secplus_v2.h"
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include <lib/toolbox/manchester_encoder.h>
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
/*
|
||||
* Help
|
||||
* https://github.com/argilo/secplus
|
||||
* https://github.com/merbanan/rtl_433/blob/master/src/devices/secplus_v2.c
|
||||
*/
|
||||
|
||||
#define TAG "SubGhzProtocoSecPlus_v2"
|
||||
|
||||
#define SECPLUS_V2_HEADER 0x3C0000000000
|
||||
#define SECPLUS_V2_HEADER_MASK 0xFFFF3C0000000000
|
||||
#define SECPLUS_V2_PACKET_1 0x000000000000
|
||||
#define SECPLUS_V2_PACKET_2 0x010000000000
|
||||
#define SECPLUS_V2_PACKET_MASK 0x30000000000
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_secplus_v2_const = {
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 110,
|
||||
.min_count_bit_for_found = 62,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderSecPlus_v2 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
ManchesterState manchester_saved_state;
|
||||
uint64_t secplus_packet_1;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderSecPlus_v2 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
uint64_t secplus_packet_1;
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
SecPlus_v2DecoderStepReset = 0,
|
||||
SecPlus_v2DecoderStepDecoderData,
|
||||
} SecPlus_v2DecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder = {
|
||||
.alloc = subghz_protocol_decoder_secplus_v2_alloc,
|
||||
.free = subghz_protocol_decoder_secplus_v2_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_secplus_v2_feed,
|
||||
.reset = subghz_protocol_decoder_secplus_v2_reset,
|
||||
|
||||
.get_hash_data = subghz_protocol_decoder_secplus_v2_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_secplus_v2_serialize,
|
||||
.deserialize = subghz_protocol_decoder_secplus_v2_deserialize,
|
||||
.get_string = subghz_protocol_decoder_secplus_v2_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder = {
|
||||
.alloc = subghz_protocol_encoder_secplus_v2_alloc,
|
||||
.free = subghz_protocol_encoder_secplus_v2_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_secplus_v2_deserialize,
|
||||
.stop = subghz_protocol_encoder_secplus_v2_stop,
|
||||
.yield = subghz_protocol_encoder_secplus_v2_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_secplus_v2 = {
|
||||
.name = SUBGHZ_PROTOCOL_SECPLUS_V2_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_315 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_secplus_v2_decoder,
|
||||
.encoder = &subghz_protocol_secplus_v2_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolEncoderSecPlus_v2));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_secplus_v2;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
instance->encoder.repeat = 10;
|
||||
instance->encoder.size_upload = 256;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_secplus_v2_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderSecPlus_v2* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
static bool subghz_protocol_secplus_v2_mix_invet(uint8_t invert, uint16_t p[]) {
|
||||
// selectively invert buffers
|
||||
switch(invert) {
|
||||
case 0x00: // 0b0000 (True, True, False),
|
||||
p[0] = ~p[0] & 0x03FF;
|
||||
p[1] = ~p[1] & 0x03FF;
|
||||
break;
|
||||
case 0x01: // 0b0001 (False, True, False),
|
||||
p[1] = ~p[1] & 0x03FF;
|
||||
break;
|
||||
case 0x02: // 0b0010 (False, False, True),
|
||||
p[2] = ~p[2] & 0x03FF;
|
||||
break;
|
||||
case 0x04: // 0b0100 (True, True, True),
|
||||
p[0] = ~p[0] & 0x03FF;
|
||||
p[1] = ~p[1] & 0x03FF;
|
||||
p[2] = ~p[2] & 0x03FF;
|
||||
break;
|
||||
case 0x05: // 0b0101 (True, False, True),
|
||||
case 0x0a: // 0b1010 (True, False, True),
|
||||
p[0] = ~p[0] & 0x03FF;
|
||||
p[2] = ~p[2] & 0x03FF;
|
||||
break;
|
||||
case 0x06: // 0b0110 (False, True, True),
|
||||
p[1] = ~p[1] & 0x03FF;
|
||||
p[2] = ~p[2] & 0x03FF;
|
||||
break;
|
||||
case 0x08: // 0b1000 (True, False, False),
|
||||
p[0] = ~p[0] & 0x03FF;
|
||||
break;
|
||||
case 0x09: // 0b1001 (False, False, False),
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Invert FAIL");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool subghz_protocol_secplus_v2_mix_order_decode(uint8_t order, uint16_t p[]) {
|
||||
uint16_t a = p[0], b = p[1], c = p[2];
|
||||
|
||||
// selectively reorder buffers
|
||||
switch(order) {
|
||||
case 0x06: // 0b0110 2, 1, 0],
|
||||
case 0x09: // 0b1001 2, 1, 0],
|
||||
p[2] = a;
|
||||
// p[1]: no change
|
||||
p[0] = c;
|
||||
break;
|
||||
case 0x08: // 0b1000 1, 2, 0],
|
||||
case 0x04: // 0b0100 1, 2, 0],
|
||||
p[1] = a;
|
||||
p[2] = b;
|
||||
p[0] = c;
|
||||
break;
|
||||
case 0x01: // 0b0001 2, 0, 1],
|
||||
p[2] = a;
|
||||
p[0] = b;
|
||||
p[1] = c;
|
||||
break;
|
||||
case 0x00: // 0b0000 0, 2, 1],
|
||||
// p[0]: no change
|
||||
p[2] = b;
|
||||
p[1] = c;
|
||||
break;
|
||||
case 0x05: // 0b0101 1, 0, 2],
|
||||
p[1] = a;
|
||||
p[0] = b;
|
||||
// p[2]: no change
|
||||
break;
|
||||
case 0x02: // 0b0010 0, 1, 2],
|
||||
case 0x0A: // 0b1010 0, 1, 2],
|
||||
// no reordering
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Order FAIL");
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool subghz_protocol_secplus_v2_mix_order_encode(uint8_t order, uint16_t p[]) {
|
||||
uint16_t a, b, c;
|
||||
|
||||
// selectively reorder buffers
|
||||
switch(order) {
|
||||
case 0x06: // 0b0110 2, 1, 0],
|
||||
case 0x09: // 0b1001 2, 1, 0],
|
||||
a = p[2];
|
||||
b = p[1];
|
||||
c = p[0];
|
||||
break;
|
||||
case 0x08: // 0b1000 1, 2, 0],
|
||||
case 0x04: // 0b0100 1, 2, 0],
|
||||
a = p[1];
|
||||
b = p[2];
|
||||
c = p[0];
|
||||
break;
|
||||
case 0x01: // 0b0001 2, 0, 1],
|
||||
a = p[2];
|
||||
b = p[0];
|
||||
c = p[1];
|
||||
break;
|
||||
case 0x00: // 0b0000 0, 2, 1],
|
||||
a = p[0];
|
||||
b = p[2];
|
||||
c = p[1];
|
||||
break;
|
||||
case 0x05: // 0b0101 1, 0, 2],
|
||||
a = p[1];
|
||||
b = p[0];
|
||||
c = p[2];
|
||||
break;
|
||||
case 0x02: // 0b0010 0, 1, 2],
|
||||
case 0x0A: // 0b1010 0, 1, 2],
|
||||
a = p[0];
|
||||
b = p[1];
|
||||
c = p[2];
|
||||
break;
|
||||
default:
|
||||
FURI_LOG_E(TAG, "Order FAIL");
|
||||
return false;
|
||||
}
|
||||
|
||||
p[0] = a;
|
||||
p[1] = b;
|
||||
p[2] = c;
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Security+ 2.0 half-message decoding
|
||||
* @param data data
|
||||
* @param roll_array[] return roll_array part
|
||||
* @param fixed[] return fixed part
|
||||
* @return true On success
|
||||
*/
|
||||
|
||||
static bool
|
||||
subghz_protocol_secplus_v2_decode_half(uint64_t data, uint8_t roll_array[], uint32_t* fixed) {
|
||||
uint8_t order = (data >> 34) & 0x0f;
|
||||
uint8_t invert = (data >> 30) & 0x0f;
|
||||
uint16_t p[3] = {0};
|
||||
|
||||
for(int i = 29; i >= 0; i -= 3) {
|
||||
p[0] = p[0] << 1 | bit_read(data, i);
|
||||
p[1] = p[1] << 1 | bit_read(data, i - 1);
|
||||
p[2] = p[2] << 1 | bit_read(data, i - 2);
|
||||
}
|
||||
|
||||
if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return false;
|
||||
if(!subghz_protocol_secplus_v2_mix_order_decode(order, p)) return false;
|
||||
|
||||
data = order << 4 | invert;
|
||||
int k = 0;
|
||||
for(int i = 6; i >= 0; i -= 2) {
|
||||
roll_array[k] = (data >> i) & 0x03;
|
||||
if(roll_array[k++] == 3) {
|
||||
FURI_LOG_E(TAG, "Roll_Array FAIL");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
for(int i = 8; i >= 0; i -= 2) {
|
||||
roll_array[k] = (p[2] >> i) & 0x03;
|
||||
if(roll_array[k++] == 3) {
|
||||
FURI_LOG_E(TAG, "Roll_Array FAIL");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
fixed[0] = p[0] << 10 | p[1];
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Analysis of received data
|
||||
* @param instance Pointer to a SubGhzBlockGeneric* instance
|
||||
* @param packet_1 first part of the message
|
||||
*/
|
||||
static void
|
||||
subghz_protocol_secplus_v2_remote_controller(SubGhzBlockGeneric* instance, uint64_t packet_1) {
|
||||
uint32_t fixed_1[1];
|
||||
uint8_t roll_1[9] = {0};
|
||||
uint32_t fixed_2[1];
|
||||
uint8_t roll_2[9] = {0};
|
||||
uint8_t rolling_digits[18] = {0};
|
||||
|
||||
if(subghz_protocol_secplus_v2_decode_half(packet_1, roll_1, fixed_1) &&
|
||||
subghz_protocol_secplus_v2_decode_half(instance->data, roll_2, fixed_2)) {
|
||||
rolling_digits[0] = roll_2[8];
|
||||
rolling_digits[1] = roll_1[8];
|
||||
|
||||
rolling_digits[2] = roll_2[4];
|
||||
rolling_digits[3] = roll_2[5];
|
||||
rolling_digits[4] = roll_2[6];
|
||||
rolling_digits[5] = roll_2[7];
|
||||
|
||||
rolling_digits[6] = roll_1[4];
|
||||
rolling_digits[7] = roll_1[5];
|
||||
rolling_digits[8] = roll_1[6];
|
||||
rolling_digits[9] = roll_1[7];
|
||||
|
||||
rolling_digits[10] = roll_2[0];
|
||||
rolling_digits[11] = roll_2[1];
|
||||
rolling_digits[12] = roll_2[2];
|
||||
rolling_digits[13] = roll_2[3];
|
||||
|
||||
rolling_digits[14] = roll_1[0];
|
||||
rolling_digits[15] = roll_1[1];
|
||||
rolling_digits[16] = roll_1[2];
|
||||
rolling_digits[17] = roll_1[3];
|
||||
|
||||
uint32_t rolling = 0;
|
||||
for(int i = 0; i < 18; i++) {
|
||||
rolling = (rolling * 3) + rolling_digits[i];
|
||||
}
|
||||
// Max value = 2^28 (268435456)
|
||||
if(rolling >= 0x10000000) {
|
||||
FURI_LOG_E(TAG, "Rolling FAIL");
|
||||
instance->cnt = 0;
|
||||
instance->btn = 0;
|
||||
instance->serial = 0;
|
||||
} else {
|
||||
instance->cnt = subghz_protocol_blocks_reverse_key(rolling, 28);
|
||||
instance->btn = fixed_1[0] >> 12;
|
||||
instance->serial = fixed_1[0] << 20 | fixed_2[0];
|
||||
}
|
||||
} else {
|
||||
instance->cnt = 0;
|
||||
instance->btn = 0;
|
||||
instance->serial = 0;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Security+ 2.0 half-message encoding
|
||||
* @param roll_array[] roll_array part
|
||||
* @param fixed[] fixed part
|
||||
* @return return data
|
||||
*/
|
||||
|
||||
static uint64_t subghz_protocol_secplus_v2_encode_half(uint8_t roll_array[], uint32_t fixed) {
|
||||
uint64_t data = 0;
|
||||
uint16_t p[3] = {(fixed >> 10) & 0x3FF, fixed & 0x3FF, 0};
|
||||
uint8_t order = roll_array[0] << 2 | roll_array[1];
|
||||
uint8_t invert = roll_array[2] << 2 | roll_array[3];
|
||||
p[2] = (uint16_t)roll_array[4] << 8 | roll_array[5] << 6 | roll_array[6] << 4 |
|
||||
roll_array[7] << 2 | roll_array[8];
|
||||
|
||||
if(!subghz_protocol_secplus_v2_mix_order_encode(order, p)) return 0;
|
||||
if(!subghz_protocol_secplus_v2_mix_invet(invert, p)) return 0;
|
||||
|
||||
for(int i = 0; i < 10; i++) {
|
||||
data <<= 3;
|
||||
data |= bit_read(p[0], 9 - i) << 2 | bit_read(p[1], 9 - i) << 1 | bit_read(p[2], 9 - i);
|
||||
}
|
||||
data |= ((uint64_t)order) << 34 | ((uint64_t)invert) << 30;
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
/**
|
||||
* Security+ 2.0 message encoding
|
||||
* @param instance SubGhzProtocolEncoderSecPlus_v2*
|
||||
*/
|
||||
|
||||
static void subghz_protocol_secplus_v2_encode(SubGhzProtocolEncoderSecPlus_v2* instance) {
|
||||
uint32_t fixed_1[1] = {instance->generic.btn << 12 | instance->generic.serial >> 20};
|
||||
uint32_t fixed_2[1] = {instance->generic.serial & 0xFFFFF};
|
||||
uint8_t rolling_digits[18] = {0};
|
||||
uint8_t roll_1[9] = {0};
|
||||
uint8_t roll_2[9] = {0};
|
||||
|
||||
instance->generic.cnt++;
|
||||
//ToDo it is not known what value the counter starts
|
||||
if(instance->generic.cnt > 0xFFFFFFF) instance->generic.cnt = 0xE500000;
|
||||
uint32_t rolling = subghz_protocol_blocks_reverse_key(instance->generic.cnt, 28);
|
||||
|
||||
for(int8_t i = 17; i > -1; i--) {
|
||||
rolling_digits[i] = rolling % 3;
|
||||
rolling /= 3;
|
||||
}
|
||||
|
||||
roll_2[8] = rolling_digits[0];
|
||||
roll_1[8] = rolling_digits[1];
|
||||
|
||||
roll_2[4] = rolling_digits[2];
|
||||
roll_2[5] = rolling_digits[3];
|
||||
roll_2[6] = rolling_digits[4];
|
||||
roll_2[7] = rolling_digits[5];
|
||||
|
||||
roll_1[4] = rolling_digits[6];
|
||||
roll_1[5] = rolling_digits[7];
|
||||
roll_1[6] = rolling_digits[8];
|
||||
roll_1[7] = rolling_digits[9];
|
||||
|
||||
roll_2[0] = rolling_digits[10];
|
||||
roll_2[1] = rolling_digits[11];
|
||||
roll_2[2] = rolling_digits[12];
|
||||
roll_2[3] = rolling_digits[13];
|
||||
|
||||
roll_1[0] = rolling_digits[14];
|
||||
roll_1[1] = rolling_digits[15];
|
||||
roll_1[2] = rolling_digits[16];
|
||||
roll_1[3] = rolling_digits[17];
|
||||
|
||||
instance->secplus_packet_1 = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_1 |
|
||||
subghz_protocol_secplus_v2_encode_half(roll_1, fixed_1[0]);
|
||||
instance->generic.data = SECPLUS_V2_HEADER | SECPLUS_V2_PACKET_2 |
|
||||
subghz_protocol_secplus_v2_encode_half(roll_2, fixed_2[0]);
|
||||
}
|
||||
|
||||
static LevelDuration
|
||||
subghz_protocol_encoder_secplus_v2_add_duration_to_upload(ManchesterEncoderResult result) {
|
||||
LevelDuration data = {.duration = 0, .level = 0};
|
||||
switch(result) {
|
||||
case ManchesterEncoderResultShortLow:
|
||||
data.duration = subghz_protocol_secplus_v2_const.te_short;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongLow:
|
||||
data.duration = subghz_protocol_secplus_v2_const.te_long;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongHigh:
|
||||
data.duration = subghz_protocol_secplus_v2_const.te_long;
|
||||
data.level = true;
|
||||
break;
|
||||
case ManchesterEncoderResultShortHigh:
|
||||
data.duration = subghz_protocol_secplus_v2_const.te_short;
|
||||
data.level = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
furi_crash("SubGhz: ManchesterEncoderResult is incorrect.");
|
||||
break;
|
||||
}
|
||||
return level_duration_make(data.level, data.duration);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
|
||||
*/
|
||||
static void
|
||||
subghz_protocol_encoder_secplus_v2_get_upload(SubGhzProtocolEncoderSecPlus_v2* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
|
||||
ManchesterEncoderState enc_state;
|
||||
manchester_encoder_reset(&enc_state);
|
||||
ManchesterEncoderResult result;
|
||||
|
||||
//Send data packet 1
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
||||
if(!manchester_encoder_advance(
|
||||
&enc_state, bit_read(instance->secplus_packet_1, i - 1), &result)) {
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);
|
||||
manchester_encoder_advance(
|
||||
&enc_state, bit_read(instance->secplus_packet_1, i - 1), &result);
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);
|
||||
}
|
||||
instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload(
|
||||
manchester_encoder_finish(&enc_state));
|
||||
if(level_duration_get_level(instance->encoder.upload[index])) {
|
||||
index++;
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136);
|
||||
|
||||
//Send data packet 2
|
||||
manchester_encoder_reset(&enc_state);
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
||||
if(!manchester_encoder_advance(
|
||||
&enc_state, bit_read(instance->generic.data, i - 1), &result)) {
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);
|
||||
manchester_encoder_advance(
|
||||
&enc_state, bit_read(instance->generic.data, i - 1), &result);
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_secplus_v2_add_duration_to_upload(result);
|
||||
}
|
||||
instance->encoder.upload[index] = subghz_protocol_encoder_secplus_v2_add_duration_to_upload(
|
||||
manchester_encoder_finish(&enc_state));
|
||||
if(level_duration_get_level(instance->encoder.upload[index])) {
|
||||
index++;
|
||||
}
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(false, (uint32_t)subghz_protocol_secplus_v2_const.te_long * 136);
|
||||
|
||||
instance->encoder.size_upload = index;
|
||||
}
|
||||
|
||||
bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderSecPlus_v2* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_secplus_v2_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
if(!flipper_format_read_hex(
|
||||
flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Secplus_packet_1");
|
||||
break;
|
||||
}
|
||||
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i];
|
||||
}
|
||||
|
||||
subghz_protocol_secplus_v2_remote_controller(
|
||||
&instance->generic, instance->secplus_packet_1);
|
||||
subghz_protocol_secplus_v2_encode(instance);
|
||||
//optional parameter parameter
|
||||
flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
subghz_protocol_encoder_secplus_v2_get_upload(instance);
|
||||
|
||||
//update data
|
||||
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");
|
||||
break;
|
||||
}
|
||||
|
||||
for(size_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
key_data[sizeof(uint64_t) - i - 1] = (instance->secplus_packet_1 >> (i * 8)) & 0xFF;
|
||||
}
|
||||
if(!flipper_format_update_hex(
|
||||
flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Secplus_packet_1");
|
||||
break;
|
||||
}
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_secplus_v2_stop(void* context) {
|
||||
SubGhzProtocolEncoderSecPlus_v2* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context) {
|
||||
SubGhzProtocolEncoderSecPlus_v2* 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;
|
||||
}
|
||||
|
||||
bool subghz_protocol_secplus_v2_create_data(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
uint32_t serial,
|
||||
uint8_t btn,
|
||||
uint32_t cnt,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderSecPlus_v2* instance = context;
|
||||
instance->generic.serial = serial;
|
||||
instance->generic.cnt = cnt;
|
||||
instance->generic.btn = btn;
|
||||
instance->generic.data_count_bit =
|
||||
(uint8_t)subghz_protocol_secplus_v2_const.min_count_bit_for_found;
|
||||
subghz_protocol_secplus_v2_encode(instance);
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
|
||||
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->secplus_packet_1 >> (i * 8)) & 0xFF;
|
||||
}
|
||||
|
||||
if(res &&
|
||||
!flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Secplus_packet_1");
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderSecPlus_v2* instance = malloc(sizeof(SubGhzProtocolDecoderSecPlus_v2));
|
||||
instance->base.protocol = &subghz_protocol_secplus_v2;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_secplus_v2_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v2* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_secplus_v2_reset(void* context) {
|
||||
furi_assert(context);
|
||||
// SubGhzProtocolDecoderSecPlus_v2* instance = context;
|
||||
// does not reset the decoder because you need to get 2 parts of the package
|
||||
}
|
||||
|
||||
static bool subghz_protocol_secplus_v2_check_packet(SubGhzProtocolDecoderSecPlus_v2* instance) {
|
||||
if((instance->decoder.decode_data & SECPLUS_V2_HEADER_MASK) == SECPLUS_V2_HEADER) {
|
||||
if((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_1) {
|
||||
instance->secplus_packet_1 = instance->decoder.decode_data;
|
||||
} else if(
|
||||
((instance->decoder.decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_2) &&
|
||||
(instance->secplus_packet_1)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v2* instance = context;
|
||||
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
switch(instance->decoder.parser_step) {
|
||||
case SecPlus_v2DecoderStepReset:
|
||||
if((!level) && (DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long * 130) <
|
||||
subghz_protocol_secplus_v2_const.te_delta * 100)) {
|
||||
//Found header Security+ 2.0
|
||||
instance->decoder.parser_step = SecPlus_v2DecoderStepDecoderData;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->secplus_packet_1 = 0;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventLongHigh,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventShortLow,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
case SecPlus_v2DecoderStepDecoderData:
|
||||
if(!level) {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) <
|
||||
subghz_protocol_secplus_v2_const.te_delta) {
|
||||
event = ManchesterEventShortLow;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) <
|
||||
subghz_protocol_secplus_v2_const.te_delta) {
|
||||
event = ManchesterEventLongLow;
|
||||
} else if(
|
||||
duration >= (subghz_protocol_secplus_v2_const.te_long * 2UL +
|
||||
subghz_protocol_secplus_v2_const.te_delta)) {
|
||||
if(instance->decoder.decode_count_bit ==
|
||||
subghz_protocol_secplus_v2_const.min_count_bit_for_found) {
|
||||
instance->generic.data = instance->decoder.decode_data;
|
||||
instance->generic.data_count_bit = instance->decoder.decode_count_bit;
|
||||
if(subghz_protocol_secplus_v2_check_packet(instance)) {
|
||||
if(instance->base.callback)
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
instance->decoder.parser_step = SecPlus_v2DecoderStepReset;
|
||||
}
|
||||
}
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventLongHigh,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
manchester_advance(
|
||||
instance->manchester_saved_state,
|
||||
ManchesterEventShortLow,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
} else {
|
||||
instance->decoder.parser_step = SecPlus_v2DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if(DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_short) <
|
||||
subghz_protocol_secplus_v2_const.te_delta) {
|
||||
event = ManchesterEventShortHigh;
|
||||
} else if(
|
||||
DURATION_DIFF(duration, subghz_protocol_secplus_v2_const.te_long) <
|
||||
subghz_protocol_secplus_v2_const.te_delta) {
|
||||
event = ManchesterEventLongHigh;
|
||||
} else {
|
||||
instance->decoder.parser_step = SecPlus_v2DecoderStepReset;
|
||||
}
|
||||
}
|
||||
if(event != ManchesterEventReset) {
|
||||
bool data;
|
||||
bool data_ok = manchester_advance(
|
||||
instance->manchester_saved_state, event, &instance->manchester_saved_state, &data);
|
||||
|
||||
if(data_ok) {
|
||||
instance->decoder.decode_data = (instance->decoder.decode_data << 1) | data;
|
||||
instance->decoder.decode_count_bit++;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v2* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit / 8) + 1);
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_secplus_v2_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v2* instance = context;
|
||||
bool res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
|
||||
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->secplus_packet_1 >> (i * 8)) & 0xFF;
|
||||
}
|
||||
|
||||
if(res &&
|
||||
!flipper_format_write_hex(flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Unable to add Secplus_packet_1");
|
||||
res = false;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v2* instance = context;
|
||||
bool res = false;
|
||||
do {
|
||||
if(!subghz_block_generic_deserialize(&instance->generic, flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Deserialize error");
|
||||
break;
|
||||
}
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_secplus_v2_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
break;
|
||||
}
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
break;
|
||||
}
|
||||
uint8_t key_data[sizeof(uint64_t)] = {0};
|
||||
if(!flipper_format_read_hex(
|
||||
flipper_format, "Secplus_packet_1", key_data, sizeof(uint64_t))) {
|
||||
FURI_LOG_E(TAG, "Missing Secplus_packet_1");
|
||||
break;
|
||||
}
|
||||
for(uint8_t i = 0; i < sizeof(uint64_t); i++) {
|
||||
instance->secplus_packet_1 = instance->secplus_packet_1 << 8 | key_data[i];
|
||||
}
|
||||
res = true;
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderSecPlus_v2* instance = context;
|
||||
subghz_protocol_secplus_v2_remote_controller(&instance->generic, instance->secplus_packet_1);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %db\r\n"
|
||||
"Pk1:0x%lX%08lX\r\n"
|
||||
"Pk2:0x%lX%08lX\r\n"
|
||||
"Sn:0x%08lX Btn:0x%01X\r\n"
|
||||
"Cnt:0x%03lX\r\n",
|
||||
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
(uint32_t)(instance->secplus_packet_1 >> 32),
|
||||
(uint32_t)instance->secplus_packet_1,
|
||||
(uint32_t)(instance->generic.data >> 32),
|
||||
(uint32_t)instance->generic.data,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
instance->generic.cnt);
|
||||
}
|
||||
125
applications/main/subghz/protocols/secplus_v2.h
Normal file
125
applications/main/subghz/protocols/secplus_v2.h
Normal file
@@ -0,0 +1,125 @@
|
||||
#pragma once
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_SECPLUS_V2_NAME "Security+ 2.0"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderSecPlus_v2 SubGhzProtocolDecoderSecPlus_v2;
|
||||
typedef struct SubGhzProtocolEncoderSecPlus_v2 SubGhzProtocolEncoderSecPlus_v2;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_secplus_v2_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_secplus_v2_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_secplus_v2;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderSecPlus_v2.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderSecPlus_v2* pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_secplus_v2_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderSecPlus_v2.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_secplus_v2_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_encoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
|
||||
*/
|
||||
void subghz_protocol_encoder_secplus_v2_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_secplus_v2_yield(void* context);
|
||||
|
||||
/**
|
||||
* Key generation from simple data.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderSecPlus_v2 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param serial Serial number, 32 bit
|
||||
* @param btn Button number, 8 bit
|
||||
* @param cnt Container value, 28 bit
|
||||
* @param manufacture_name Name of manufacturer's key
|
||||
* @param preset Modulation, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_secplus_v2_create_data(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
uint32_t serial,
|
||||
uint8_t btn,
|
||||
uint32_t cnt,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderSecPlus_v2.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderSecPlus_v2* pointer to a SubGhzProtocolDecoderSecPlus_v2 instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_secplus_v2_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderSecPlus_v2.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_secplus_v2_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderSecPlus_v2.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance
|
||||
*/
|
||||
void subghz_protocol_decoder_secplus_v2_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_secplus_v2_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_secplus_v2_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderSecPlus_v2.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_secplus_v2_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderSecPlus_v2.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return true On success
|
||||
*/
|
||||
bool subghz_protocol_decoder_secplus_v2_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderSecPlus_v2 instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_secplus_v2_get_string(void* context, FuriString* output);
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user