Update subghz

This commit is contained in:
VerstreuteSeele
2023-02-10 17:28:26 +01:00
parent 792d58c4cb
commit e7d01998c1
174 changed files with 31269 additions and 1885 deletions

View 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")

View File

@@ -12,7 +12,7 @@ App(
],
provides=["subghz_start"],
icon="A_Sub1ghz_14",
stack_size=2 * 1024,
stack_size=3 * 1024,
order=10,
)

View File

@@ -0,0 +1 @@
#include "const.h"

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}

View 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

View 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;
}
}

View 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

View File

@@ -50,7 +50,6 @@ typedef enum {
SubGhzCustomEventSceneAnalyzerLock,
SubGhzCustomEventSceneAnalyzerUnlock,
SubGhzCustomEventSceneSettingLock,
SubGhzCustomEventSceneSettingError,
SubGhzCustomEventSceneExit,
SubGhzCustomEventSceneStay,

View File

@@ -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);

View 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);
}

View 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);

View 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));
}

View 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);

View 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;
}

View 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

View 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));
}

View 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);

File diff suppressed because it is too large Load Diff

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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));
}

View 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);

View 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;
}
}

View 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);

View 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));
}

View 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);

View 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));
}

View 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);

View 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);
}
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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");
}
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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));
}
}

View 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);

File diff suppressed because it is too large Load Diff

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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));
}

View 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);

View 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));
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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);
}

View 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);

View 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);
}
}

View 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);

View 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);
}

View 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);

View 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));
}

View 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);

View 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);
}

View 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

View 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;
}
}

View 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);

View 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)};

View 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

View 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);
}

View 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

View 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);
}

View 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);

View 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);
}
}

View 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);

View 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);
}

View 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