mirror of
https://github.com/Next-Flip/Momentum-Firmware.git
synced 2026-04-24 03:29:57 -07:00
Add new SubGHz protocol: Hormann BiSecur (#118)
This commit is contained in:
committed by
GitHub
parent
e982c31d06
commit
45999b4416
687
lib/subghz/protocols/hormann_bisecur.c
Normal file
687
lib/subghz/protocols/hormann_bisecur.c
Normal file
@@ -0,0 +1,687 @@
|
||||
#include "hormann_bisecur.h"
|
||||
|
||||
#include <lib/flipper_format/flipper_format_i.h>
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include <lib/toolbox/manchester_encoder.h>
|
||||
#include <lib/toolbox/stream/stream.h>
|
||||
|
||||
#include "../blocks/const.h"
|
||||
#include "../blocks/decoder.h"
|
||||
#include "../blocks/encoder.h"
|
||||
#include "../blocks/generic.h"
|
||||
#include "../blocks/math.h"
|
||||
|
||||
#define TAG "SubGhzProtocolHormannBiSecur"
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_hormann_bisecur_const = {
|
||||
.te_short = 208,
|
||||
.te_long = 416,
|
||||
.te_delta = 104,
|
||||
.min_count_bit_for_found = 176,
|
||||
};
|
||||
|
||||
struct SubGhzProtocolDecoderHormannBiSecur {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint8_t sync_cnt;
|
||||
ManchesterState manchester_saved_state;
|
||||
|
||||
uint8_t type;
|
||||
uint8_t data[22];
|
||||
uint8_t crc;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderHormannBiSecur {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint8_t data[22];
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
HormannBiSecurDecoderStepReset = 0,
|
||||
HormannBiSecurDecoderStepFoundPreambleAlternatingShort,
|
||||
HormannBiSecurDecoderStepFoundPreambleHighVeryLong,
|
||||
HormannBiSecurDecoderStepFoundPreambleAlternatingLong,
|
||||
HormannBiSecurDecoderStepFoundData,
|
||||
} HormannBiSecurDecoderStep;
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_hormann_bisecur_decoder = {
|
||||
.alloc = subghz_protocol_decoder_hormann_bisecur_alloc,
|
||||
.free = subghz_protocol_decoder_hormann_bisecur_free,
|
||||
|
||||
.feed = subghz_protocol_decoder_hormann_bisecur_feed,
|
||||
.reset = subghz_protocol_decoder_hormann_bisecur_reset,
|
||||
|
||||
.get_hash_data = NULL,
|
||||
.get_hash_data_long = subghz_protocol_decoder_hormann_bisecur_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_hormann_bisecur_serialize,
|
||||
.deserialize = subghz_protocol_decoder_hormann_bisecur_deserialize,
|
||||
.get_string = subghz_protocol_decoder_hormann_bisecur_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_hormann_bisecur_encoder = {
|
||||
.alloc = subghz_protocol_encoder_hormann_bisecur_alloc,
|
||||
.free = subghz_protocol_encoder_hormann_bisecur_free,
|
||||
|
||||
.deserialize = subghz_protocol_encoder_hormann_bisecur_deserialize,
|
||||
.stop = subghz_protocol_encoder_hormann_bisecur_stop,
|
||||
.yield = subghz_protocol_encoder_hormann_bisecur_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_hormann_bisecur = {
|
||||
.name = SUBGHZ_PROTOCOL_HORMANN_BISECUR_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_868 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
|
||||
.decoder = &subghz_protocol_hormann_bisecur_decoder,
|
||||
.encoder = &subghz_protocol_hormann_bisecur_encoder,
|
||||
};
|
||||
|
||||
// there is a problem with the function in lib/toolbox/manchester_decoder, so it is reimplemented
|
||||
// thanks to CodeAllNight (https://github.com/jamisonderek) for sharing his fixed version
|
||||
static const ManchesterState manchester_reset_state = ManchesterStateMid1;
|
||||
|
||||
bool manchester_advance_alt(
|
||||
ManchesterState state,
|
||||
ManchesterEvent event,
|
||||
ManchesterState* next_state,
|
||||
bool* data) {
|
||||
bool result = false;
|
||||
ManchesterState new_state = manchester_reset_state;
|
||||
|
||||
if(event == ManchesterEventReset) {
|
||||
new_state = manchester_reset_state;
|
||||
} else if(state == ManchesterStateStart1) {
|
||||
if(event == ManchesterEventShortLow) {
|
||||
new_state = ManchesterStateMid1;
|
||||
result = true;
|
||||
if(data) *data = false;
|
||||
} else if(event == ManchesterEventLongLow) {
|
||||
new_state = ManchesterStateStart0;
|
||||
result = true;
|
||||
if(data) *data = false;
|
||||
} else {
|
||||
new_state = manchester_reset_state;
|
||||
}
|
||||
} else if(state == ManchesterStateMid1) {
|
||||
if(event == ManchesterEventShortHigh) {
|
||||
new_state = ManchesterStateStart1;
|
||||
} else if(event == ManchesterEventShortLow) {
|
||||
new_state = ManchesterStateStart0;
|
||||
} else {
|
||||
new_state = manchester_reset_state;
|
||||
}
|
||||
} else if(state == ManchesterStateStart0) {
|
||||
if(event == ManchesterEventShortHigh) {
|
||||
new_state = ManchesterStateMid0;
|
||||
result = true;
|
||||
if(data) *data = true;
|
||||
} else if(event == ManchesterEventLongHigh) {
|
||||
new_state = ManchesterStateStart1;
|
||||
result = true;
|
||||
if(data) *data = true;
|
||||
} else {
|
||||
new_state = manchester_reset_state;
|
||||
}
|
||||
} else if(state == ManchesterStateMid0) {
|
||||
if(event == ManchesterEventShortLow) {
|
||||
new_state = ManchesterStateStart0;
|
||||
} else if(event == ManchesterEventShortHigh) {
|
||||
new_state = ManchesterStateStart1;
|
||||
} else {
|
||||
new_state = manchester_reset_state;
|
||||
}
|
||||
}
|
||||
|
||||
*next_state = new_state;
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Calculates the next LevelDuration in an upload
|
||||
* @param result ManchesterEncoderResult
|
||||
* @return LevelDuration
|
||||
*/
|
||||
static LevelDuration
|
||||
subghz_protocol_encoder_hormann_bisecur_add_duration_to_upload(ManchesterEncoderResult result);
|
||||
|
||||
/**
|
||||
* Calculates CRC from the raw demodulated bytes
|
||||
* @param instance Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
*/
|
||||
static uint8_t
|
||||
subghz_protocol_decoder_hormann_bisecur_crc(SubGhzProtocolDecoderHormannBiSecur* instance);
|
||||
|
||||
/**
|
||||
* Checks if the raw demodulated data has correct CRC
|
||||
* @param instance Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
* @return if CRC is valid or not
|
||||
*/
|
||||
static bool subghz_protocol_decoder_hormann_bisecur_check_crc(
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance);
|
||||
|
||||
/**
|
||||
* Add the next bit to the decoding result
|
||||
* @param instance Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
* @param level Level of the next bit
|
||||
*/
|
||||
static void subghz_protocol_decoder_hormann_bisecur_add_bit(
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance,
|
||||
bool level);
|
||||
|
||||
/**
|
||||
* Parses the raw data into separate fields
|
||||
* @param instance Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
*/
|
||||
static void
|
||||
subghz_protocol_hormann_bisecur_parse_data(SubGhzProtocolDecoderHormannBiSecur* instance);
|
||||
|
||||
void* subghz_protocol_encoder_hormann_bisecur_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolEncoderHormannBiSecur* instance =
|
||||
malloc(sizeof(SubGhzProtocolEncoderHormannBiSecur));
|
||||
|
||||
instance->base.protocol = &subghz_protocol_hormann_bisecur;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
// TODO insert 504.3ms carrier off delay between repeats
|
||||
// subghz framework does not support this for FM encoders
|
||||
// sending either frequency constantly also seems to work
|
||||
|
||||
// instance->encoder.repeat = 3; //original remote does 3 repeats
|
||||
instance->encoder.repeat = 1;
|
||||
instance->encoder.size_upload =
|
||||
21 * 2 + 2 * 2 + subghz_protocol_hormann_bisecur_const.min_count_bit_for_found * 2 + 1;
|
||||
instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration));
|
||||
instance->encoder.is_running = false;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_hormann_bisecur_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHormannBiSecur* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
/**
|
||||
* Generating an upload from data.
|
||||
* @param instance Pointer to a SubGhzProtocolEncoderHormannBiSecur instance
|
||||
* @return true On success
|
||||
*/
|
||||
static bool subghz_protocol_encoder_hormann_bisecur_get_upload(
|
||||
SubGhzProtocolEncoderHormannBiSecur* instance) {
|
||||
furi_assert(instance);
|
||||
size_t index = 0;
|
||||
ManchesterEncoderState enc_state;
|
||||
manchester_encoder_reset(&enc_state);
|
||||
ManchesterEncoderResult result;
|
||||
|
||||
uint32_t duration_short = (uint32_t)subghz_protocol_hormann_bisecur_const.te_short;
|
||||
uint32_t duration_long = (uint32_t)subghz_protocol_hormann_bisecur_const.te_long;
|
||||
uint32_t duration_half_short = duration_short / 2;
|
||||
|
||||
// Send preamble
|
||||
for(uint8_t i = 0; i < 21; i++) {
|
||||
uint32_t duration_low = duration_short;
|
||||
uint32_t duration_high = duration_short;
|
||||
|
||||
if(i == 0) {
|
||||
duration_low += duration_half_short;
|
||||
}
|
||||
|
||||
if(i == 20) {
|
||||
duration_high = duration_long * 4;
|
||||
}
|
||||
|
||||
instance->encoder.upload[index++] = level_duration_make(false, duration_low);
|
||||
instance->encoder.upload[index++] = level_duration_make(true, duration_high);
|
||||
}
|
||||
|
||||
for(uint8_t i = 0; i < 2; i++) {
|
||||
instance->encoder.upload[index++] = level_duration_make(false, duration_long);
|
||||
instance->encoder.upload[index++] = level_duration_make(true, duration_long);
|
||||
}
|
||||
|
||||
// Send key data
|
||||
uint8_t max_byte_index = instance->generic.data_count_bit / 8 - 1;
|
||||
|
||||
for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) {
|
||||
uint8_t bit_index = i - 1;
|
||||
uint8_t byte_index = max_byte_index - bit_index / 8;
|
||||
bool bit_is_set = !bit_read(instance->data[byte_index], bit_index & 0x07);
|
||||
|
||||
if(!manchester_encoder_advance(&enc_state, bit_is_set, &result)) {
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_hormann_bisecur_add_duration_to_upload(result);
|
||||
manchester_encoder_advance(&enc_state, bit_is_set, &result);
|
||||
}
|
||||
|
||||
instance->encoder.upload[index++] =
|
||||
subghz_protocol_encoder_hormann_bisecur_add_duration_to_upload(result);
|
||||
}
|
||||
|
||||
LevelDuration last_level_duration =
|
||||
subghz_protocol_encoder_hormann_bisecur_add_duration_to_upload(
|
||||
manchester_encoder_finish(&enc_state));
|
||||
|
||||
last_level_duration.duration += duration_short + duration_half_short;
|
||||
instance->encoder.upload[index++] = last_level_duration;
|
||||
|
||||
instance->encoder.size_upload = index;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_encoder_hormann_bisecur_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolEncoderHormannBiSecur* instance = context;
|
||||
|
||||
SubGhzProtocolStatus ret =
|
||||
subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Generic key is too small, so we reset it and rewind to get real, longer, data
|
||||
instance->generic.data = 0;
|
||||
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_hormann_bisecur_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
return SubGhzProtocolStatusErrorValueBitCount;
|
||||
}
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
size_t key_length = instance->generic.data_count_bit / 8;
|
||||
|
||||
if(!flipper_format_read_hex(flipper_format, "Key", instance->data, key_length)) {
|
||||
FURI_LOG_E(TAG, "Unable to read Key in encoder");
|
||||
return SubGhzProtocolStatusErrorParserKey;
|
||||
}
|
||||
|
||||
// optional parameter
|
||||
flipper_format_read_uint32(flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1);
|
||||
|
||||
if(!subghz_protocol_encoder_hormann_bisecur_get_upload(instance)) {
|
||||
return SubGhzProtocolStatusErrorEncoderGetUpload;
|
||||
}
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
|
||||
return SubGhzProtocolStatusOk;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_hormann_bisecur_stop(void* context) {
|
||||
SubGhzProtocolEncoderHormannBiSecur* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_hormann_bisecur_yield(void* context) {
|
||||
SubGhzProtocolEncoderHormannBiSecur* 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_bisecur_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance =
|
||||
malloc(sizeof(SubGhzProtocolDecoderHormannBiSecur));
|
||||
instance->base.protocol = &subghz_protocol_hormann_bisecur;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_bisecur_free(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_bisecur_reset(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance = context;
|
||||
instance->decoder.parser_step = HormannBiSecurDecoderStepReset;
|
||||
memset(instance->data, 0, 22);
|
||||
instance->generic.data_count_bit = 0;
|
||||
instance->manchester_saved_state = 0;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_bisecur_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance = context;
|
||||
|
||||
uint32_t duration_short = (uint32_t)subghz_protocol_hormann_bisecur_const.te_short;
|
||||
uint32_t duration_long = (uint32_t)subghz_protocol_hormann_bisecur_const.te_long;
|
||||
uint32_t duration_delta = (uint32_t)subghz_protocol_hormann_bisecur_const.te_delta;
|
||||
uint32_t duration_half_short = duration_short / 2;
|
||||
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case HormannBiSecurDecoderStepReset:
|
||||
if(!level &&
|
||||
DURATION_DIFF(duration, duration_short + duration_half_short) < duration_delta) {
|
||||
instance->decoder.parser_step = HormannBiSecurDecoderStepFoundPreambleAlternatingShort;
|
||||
}
|
||||
break;
|
||||
case HormannBiSecurDecoderStepFoundPreambleAlternatingShort:
|
||||
if(DURATION_DIFF(duration, duration_short) < duration_delta) {
|
||||
// stay on the same step, the pattern repeats around 21 times
|
||||
break;
|
||||
}
|
||||
|
||||
if(level && DURATION_DIFF(duration, duration_long * 4) < duration_delta) {
|
||||
instance->decoder.parser_step = HormannBiSecurDecoderStepFoundPreambleHighVeryLong;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->decoder.parser_step = HormannBiSecurDecoderStepReset;
|
||||
break;
|
||||
case HormannBiSecurDecoderStepFoundPreambleHighVeryLong:
|
||||
if(!level && DURATION_DIFF(duration, duration_long) < duration_delta) {
|
||||
instance->sync_cnt = 3;
|
||||
instance->decoder.parser_step = HormannBiSecurDecoderStepFoundPreambleAlternatingLong;
|
||||
break;
|
||||
}
|
||||
|
||||
instance->decoder.parser_step = HormannBiSecurDecoderStepReset;
|
||||
break;
|
||||
case HormannBiSecurDecoderStepFoundPreambleAlternatingLong:
|
||||
if(level == (instance->sync_cnt-- & 1) &&
|
||||
DURATION_DIFF(duration, duration_long) < duration_delta) {
|
||||
if(!instance->sync_cnt) {
|
||||
manchester_advance_alt(
|
||||
instance->manchester_saved_state,
|
||||
event,
|
||||
&instance->manchester_saved_state,
|
||||
NULL);
|
||||
instance->decoder.parser_step = HormannBiSecurDecoderStepFoundData;
|
||||
}
|
||||
|
||||
// stay on the same step, or advance to the next if enough transitions are found
|
||||
break;
|
||||
}
|
||||
|
||||
instance->decoder.parser_step = HormannBiSecurDecoderStepReset;
|
||||
break;
|
||||
case HormannBiSecurDecoderStepFoundData:
|
||||
if(DURATION_DIFF(duration, duration_short) < duration_delta ||
|
||||
(
|
||||
// the last bit can be arbitrary long, but it is parsed as a short
|
||||
instance->generic.data_count_bit ==
|
||||
subghz_protocol_hormann_bisecur_const.min_count_bit_for_found - 1 &&
|
||||
duration > duration_short)) {
|
||||
event = !level ? ManchesterEventShortHigh : ManchesterEventShortLow;
|
||||
}
|
||||
|
||||
if(DURATION_DIFF(duration, duration_long) < duration_delta) {
|
||||
event = !level ? ManchesterEventLongHigh : ManchesterEventLongLow;
|
||||
}
|
||||
|
||||
if(event == ManchesterEventReset) {
|
||||
subghz_protocol_decoder_hormann_bisecur_reset(instance);
|
||||
} else {
|
||||
bool new_level;
|
||||
|
||||
if(manchester_advance_alt(
|
||||
instance->manchester_saved_state,
|
||||
event,
|
||||
&instance->manchester_saved_state,
|
||||
&new_level)) {
|
||||
subghz_protocol_decoder_hormann_bisecur_add_bit(instance, new_level);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t subghz_protocol_decoder_hormann_bisecur_get_hash_data(void* context) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance = context;
|
||||
|
||||
union {
|
||||
uint32_t full;
|
||||
uint8_t split[4];
|
||||
} hash = {0};
|
||||
size_t key_length = instance->generic.data_count_bit / 8;
|
||||
|
||||
for(size_t i = 0; i < key_length; i++) {
|
||||
hash.split[i % sizeof(hash)] ^= instance->data[i];
|
||||
}
|
||||
|
||||
return hash.full;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_hormann_bisecur_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_assert(context);
|
||||
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance = context;
|
||||
SubGhzProtocolStatus res = SubGhzProtocolStatusError;
|
||||
|
||||
do {
|
||||
res = subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(res != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
|
||||
// Generic key is too small, so it writes empty and we update here with real, longer, data
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
res = SubGhzProtocolStatusErrorParserOthers;
|
||||
break;
|
||||
}
|
||||
|
||||
uint16_t key_length = instance->generic.data_count_bit / 8;
|
||||
|
||||
if(!flipper_format_update_hex(flipper_format, "Key", instance->data, key_length)) {
|
||||
FURI_LOG_E(TAG, "Unable to update Key");
|
||||
res = SubGhzProtocolStatusErrorParserKey;
|
||||
break;
|
||||
}
|
||||
} while(false);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_hormann_bisecur_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance = context;
|
||||
|
||||
SubGhzProtocolStatus ret =
|
||||
subghz_block_generic_deserialize(&instance->generic, flipper_format);
|
||||
if(ret != SubGhzProtocolStatusOk) {
|
||||
return ret;
|
||||
}
|
||||
|
||||
// Generic key is too small, so we reset it and rewind to get real, longer, data
|
||||
instance->generic.data = 0;
|
||||
|
||||
if(instance->generic.data_count_bit !=
|
||||
subghz_protocol_hormann_bisecur_const.min_count_bit_for_found) {
|
||||
FURI_LOG_E(TAG, "Wrong number of bits in key");
|
||||
return SubGhzProtocolStatusErrorValueBitCount;
|
||||
}
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
FURI_LOG_E(TAG, "Rewind error");
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
size_t key_length = instance->generic.data_count_bit / 8;
|
||||
|
||||
if(!flipper_format_read_hex(flipper_format, "Key", instance->data, key_length)) {
|
||||
FURI_LOG_E(TAG, "Unable to read Key in decoder");
|
||||
return SubGhzProtocolStatusErrorParserKey;
|
||||
}
|
||||
|
||||
subghz_protocol_hormann_bisecur_parse_data(instance);
|
||||
|
||||
return SubGhzProtocolStatusOk;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_hormann_bisecur_get_string(void* context, FuriString* output) {
|
||||
furi_assert(context);
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance = context;
|
||||
bool valid_crc = subghz_protocol_decoder_hormann_bisecur_check_crc(instance);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s\r\n"
|
||||
"%dbit CRC:0x%02X %s\r\n"
|
||||
"Type:0x%02X Sn:0x%08lX\r\n"
|
||||
"Key:%016llX\r\n"
|
||||
"Key:%016llX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
instance->crc,
|
||||
valid_crc ? "OK" : "WRONG",
|
||||
instance->type,
|
||||
instance->generic.serial,
|
||||
instance->generic.data,
|
||||
instance->generic.data_2);
|
||||
}
|
||||
|
||||
static LevelDuration subghz_protocol_encoder_hormann_bisecur_add_duration_to_upload(
|
||||
ManchesterEncoderResult result) {
|
||||
LevelDuration data = {.duration = 0, .level = 0};
|
||||
|
||||
switch(result) {
|
||||
case ManchesterEncoderResultShortLow:
|
||||
data.duration = subghz_protocol_hormann_bisecur_const.te_short;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongLow:
|
||||
data.duration = subghz_protocol_hormann_bisecur_const.te_long;
|
||||
data.level = false;
|
||||
break;
|
||||
case ManchesterEncoderResultLongHigh:
|
||||
data.duration = subghz_protocol_hormann_bisecur_const.te_long;
|
||||
data.level = true;
|
||||
break;
|
||||
case ManchesterEncoderResultShortHigh:
|
||||
data.duration = subghz_protocol_hormann_bisecur_const.te_short;
|
||||
data.level = true;
|
||||
break;
|
||||
|
||||
default:
|
||||
furi_crash("SubGhz: ManchesterEncoderResult is incorrect.");
|
||||
break;
|
||||
}
|
||||
|
||||
return level_duration_make(data.level, data.duration);
|
||||
}
|
||||
|
||||
static uint8_t
|
||||
subghz_protocol_decoder_hormann_bisecur_crc(SubGhzProtocolDecoderHormannBiSecur* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
switch(instance->type) {
|
||||
case 0x50:
|
||||
return ~(subghz_protocol_blocks_crc8(instance->data + 1, 20, 0x07, 0x00) ^ 0x55);
|
||||
case 0x70:
|
||||
return subghz_protocol_blocks_crc8le(instance->data, 21, 0x07, 0xFF);
|
||||
}
|
||||
|
||||
FURI_LOG_E(TAG, "Unknown type 0x%02X", instance->type);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static bool subghz_protocol_decoder_hormann_bisecur_check_crc(
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->type != 0x50 && instance->type != 0x70) {
|
||||
FURI_LOG_W(TAG, "Unknown type 0x%02X", instance->type);
|
||||
return false;
|
||||
}
|
||||
|
||||
return subghz_protocol_decoder_hormann_bisecur_crc(instance) == instance->crc;
|
||||
}
|
||||
|
||||
static void
|
||||
subghz_protocol_hormann_bisecur_parse_data(SubGhzProtocolDecoderHormannBiSecur* instance) {
|
||||
furi_assert(instance);
|
||||
|
||||
instance->type = instance->data[0];
|
||||
|
||||
instance->generic.serial = 0;
|
||||
|
||||
for(uint8_t i = 1; i < 5; i++) {
|
||||
instance->generic.serial = instance->generic.serial << 8 | instance->data[i];
|
||||
}
|
||||
|
||||
instance->generic.data = 0;
|
||||
|
||||
for(uint8_t i = 5; i < 13; i++) {
|
||||
instance->generic.data = instance->generic.data << 8 | instance->data[i];
|
||||
}
|
||||
|
||||
instance->generic.data_2 = 0;
|
||||
|
||||
for(uint8_t i = 13; i < 21; i++) {
|
||||
instance->generic.data_2 = instance->generic.data_2 << 8 | instance->data[i];
|
||||
}
|
||||
|
||||
instance->crc = instance->data[21];
|
||||
}
|
||||
|
||||
static void subghz_protocol_decoder_hormann_bisecur_add_bit(
|
||||
SubGhzProtocolDecoderHormannBiSecur* instance,
|
||||
bool level) {
|
||||
furi_assert(instance);
|
||||
|
||||
if(instance->generic.data_count_bit >=
|
||||
subghz_protocol_hormann_bisecur_const.min_count_bit_for_found) {
|
||||
return;
|
||||
}
|
||||
|
||||
if(level) {
|
||||
uint8_t byte_index = instance->generic.data_count_bit / 8;
|
||||
uint8_t bit_index = instance->generic.data_count_bit % 8;
|
||||
|
||||
instance->data[byte_index] |= 1 << (7 - bit_index);
|
||||
}
|
||||
|
||||
instance->generic.data_count_bit++;
|
||||
|
||||
if(instance->generic.data_count_bit >=
|
||||
subghz_protocol_hormann_bisecur_const.min_count_bit_for_found) {
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
} else {
|
||||
subghz_protocol_decoder_hormann_bisecur_reset(instance);
|
||||
}
|
||||
}
|
||||
}
|
||||
111
lib/subghz/protocols/hormann_bisecur.h
Normal file
111
lib/subghz/protocols/hormann_bisecur.h
Normal file
@@ -0,0 +1,111 @@
|
||||
#pragma once
|
||||
|
||||
#include "base.h"
|
||||
|
||||
#define SUBGHZ_PROTOCOL_HORMANN_BISECUR_NAME "Hormann BiSecur"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderHormannBiSecur SubGhzProtocolDecoderHormannBiSecur;
|
||||
typedef struct SubGhzProtocolEncoderHormannBiSecur SubGhzProtocolEncoderHormannBiSecur;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_hormann_bisecur_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_hormann_bisecur_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_hormann_bisecur;
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolEncoderHormannBiSecur.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolEncoderHormannBiSecur* pointer to a SubGhzProtocolEncoderHormannBiSecur instance
|
||||
*/
|
||||
void* subghz_protocol_encoder_hormann_bisecur_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolEncoderHormannBiSecur.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHormannBiSecur instance
|
||||
*/
|
||||
void subghz_protocol_encoder_hormann_bisecur_free(void* context);
|
||||
|
||||
/**
|
||||
* Deserialize and generating an upload to send.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHormannBiSecur instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_encoder_hormann_bisecur_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Forced transmission stop.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHormannBiSecur instance
|
||||
*/
|
||||
void subghz_protocol_encoder_hormann_bisecur_stop(void* context);
|
||||
|
||||
/**
|
||||
* Getting the level and duration of the upload to be loaded into DMA.
|
||||
* @param context Pointer to a SubGhzProtocolEncoderHormannBiSecur instance
|
||||
* @return LevelDuration
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_hormann_bisecur_yield(void* context);
|
||||
|
||||
/**
|
||||
* Allocate SubGhzProtocolDecoderHormannBiSecur.
|
||||
* @param environment Pointer to a SubGhzEnvironment instance
|
||||
* @return SubGhzProtocolDecoderHormannBiSecur* pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
*/
|
||||
void* subghz_protocol_decoder_hormann_bisecur_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free SubGhzProtocolDecoderHormannBiSecur.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
*/
|
||||
void subghz_protocol_decoder_hormann_bisecur_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset decoder SubGhzProtocolDecoderHormannBiSecur.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
*/
|
||||
void subghz_protocol_decoder_hormann_bisecur_reset(void* context);
|
||||
|
||||
/**
|
||||
* Parse a raw sequence of levels and durations received from the air.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
* @param level Signal level true-high false-low
|
||||
* @param duration Duration of this level in, us
|
||||
*/
|
||||
void subghz_protocol_decoder_hormann_bisecur_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Getting the hash sum of the last randomly received parcel.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
* @return hash Hash sum
|
||||
*/
|
||||
uint32_t subghz_protocol_decoder_hormann_bisecur_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize data SubGhzProtocolDecoderHormannBiSecur.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @param preset The modulation on which the signal was received, SubGhzRadioPreset
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_hormann_bisecur_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize data SubGhzProtocolDecoderHormannBiSecur.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
* @param flipper_format Pointer to a FlipperFormat instance
|
||||
* @return status
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_hormann_bisecur_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format);
|
||||
|
||||
/**
|
||||
* Getting a textual representation of the received data.
|
||||
* @param context Pointer to a SubGhzProtocolDecoderHormannBiSecur instance
|
||||
* @param output Resulting text
|
||||
*/
|
||||
void subghz_protocol_decoder_hormann_bisecur_get_string(void* context, FuriString* output);
|
||||
@@ -68,8 +68,9 @@ const SubGhzProtocol* subghz_protocol_registry_items[] = {
|
||||
&subghz_protocol_bin_raw,
|
||||
&subghz_protocol_mastercode,
|
||||
&subghz_protocol_x10,
|
||||
&subghz_protocol_hormann_bisecur,
|
||||
};
|
||||
|
||||
const SubGhzProtocolRegistry subghz_protocol_registry = {
|
||||
.items = subghz_protocol_registry_items,
|
||||
.size = COUNT_OF(subghz_protocol_registry_items)};
|
||||
.size = COUNT_OF(subghz_protocol_registry_items)};
|
||||
|
||||
@@ -69,3 +69,4 @@
|
||||
#include "bin_raw.h"
|
||||
#include "mastercode.h"
|
||||
#include "x10.h"
|
||||
#include "hormann_bisecur.h"
|
||||
|
||||
Reference in New Issue
Block a user