From cb0369a7f369243098cd598a92a5f5a6faa0834d Mon Sep 17 00:00:00 2001 From: Skorpionm <85568270+Skorpionm@users.noreply.github.com> Date: Thu, 27 Jan 2022 16:28:21 +0400 Subject: [PATCH] SubGhz: add protocol Somfy Telis RTS, Somfy Keytis RTS (#964) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * SubGhz: add 433.42 MHz for Somfy * SupGhz: add protocol Somfy Telis RTS, Somfy Keytis RTS Co-authored-by: あく --- applications/subghz/subghz.c | 4 +- applications/subghz/subghz_history.c | 13 +- .../protocols/subghz_protocol_somfy_keytis.c | 345 ++++++++++++++++++ .../protocols/subghz_protocol_somfy_keytis.h | 50 +++ .../protocols/subghz_protocol_somfy_telis.c | 293 +++++++++++++++ .../protocols/subghz_protocol_somfy_telis.h | 46 +++ lib/subghz/subghz_parser.c | 24 ++ 7 files changed, 770 insertions(+), 5 deletions(-) create mode 100644 lib/subghz/protocols/subghz_protocol_somfy_keytis.c create mode 100644 lib/subghz/protocols/subghz_protocol_somfy_keytis.h create mode 100644 lib/subghz/protocols/subghz_protocol_somfy_telis.c create mode 100644 lib/subghz/protocols/subghz_protocol_somfy_telis.h diff --git a/applications/subghz/subghz.c b/applications/subghz/subghz.c index 884bd4a3..f13c86d6 100644 --- a/applications/subghz/subghz.c +++ b/applications/subghz/subghz.c @@ -9,6 +9,7 @@ const char* const subghz_frequencies_text[] = { "348.00", "387.00", "433.08", + "433.42", "433.92", "434.42", "434.78", @@ -29,6 +30,7 @@ const uint32_t subghz_frequencies[] = { /* 387 - 464 */ 387000000, 433075000, /* LPD433 first */ + 433420000, 433920000, /* LPD433 mid */ 434420000, 434775000, /* LPD433 last channels */ @@ -51,7 +53,7 @@ const uint32_t subghz_hopper_frequencies[] = { const uint32_t subghz_frequencies_count = sizeof(subghz_frequencies) / sizeof(uint32_t); const uint32_t subghz_hopper_frequencies_count = sizeof(subghz_hopper_frequencies) / sizeof(uint32_t); -const uint32_t subghz_frequencies_433_92 = 5; +const uint32_t subghz_frequencies_433_92 = 6; bool subghz_custom_event_callback(void* context, uint32_t event) { furi_assert(context); diff --git a/applications/subghz/subghz_history.c b/applications/subghz/subghz_history.c index 470bcea0..ad6c3d4c 100644 --- a/applications/subghz/subghz_history.c +++ b/applications/subghz/subghz_history.c @@ -2,6 +2,7 @@ #include #include #include +#include #include #include @@ -16,7 +17,7 @@ struct SubGhzHistoryStruct { uint8_t type_protocol; uint8_t code_count_bit; uint64_t code_found; - uint16_t te; + uint32_t data1; FuriHalSubGhzPreset preset; uint32_t real_frequency; }; @@ -85,7 +86,7 @@ SubGhzProtocolCommonLoad* subghz_history_get_raw_data(SubGhzHistory* instance, u furi_assert(instance); instance->data.code_found = instance->history[idx].code_found; instance->data.code_count_bit = instance->history[idx].code_count_bit; - instance->data.param1 = instance->history[idx].te; + instance->data.param1 = instance->history[idx].data1; return &instance->data; } bool subghz_history_get_text_space_left(SubGhzHistory* instance, string_t output) { @@ -149,7 +150,7 @@ bool subghz_history_add_to_history( instance->history[instance->last_index_write].real_frequency = frequency; instance->history[instance->last_index_write].preset = preset; - instance->history[instance->last_index_write].te = 0; + instance->history[instance->last_index_write].data1 = 0; instance->history[instance->last_index_write].manufacture_name = NULL; instance->history[instance->last_index_write].name = protocol->name; instance->history[instance->last_index_write].code_count_bit = protocol->code_last_count_bit; @@ -161,9 +162,13 @@ bool subghz_history_add_to_history( instance->history[instance->last_index_write].manufacture_name = subghz_protocol_star_line_find_and_get_manufacture_name(protocol); } else if(strcmp(protocol->name, "Princeton") == 0) { - instance->history[instance->last_index_write].te = + instance->history[instance->last_index_write].data1 = subghz_protocol_princeton_get_te(protocol); + } else if(strcmp(protocol->name, "Somfy Keytis") == 0) { + instance->history[instance->last_index_write].data1 = + subghz_protocol_somfy_keytis_get_press_duration(protocol); } + instance->history[instance->last_index_write].type_protocol = protocol->type_protocol; instance->last_index_write++; diff --git a/lib/subghz/protocols/subghz_protocol_somfy_keytis.c b/lib/subghz/protocols/subghz_protocol_somfy_keytis.c new file mode 100644 index 00000000..f8e023d9 --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_somfy_keytis.c @@ -0,0 +1,345 @@ +#include "subghz_protocol_somfy_keytis.h" +#include "subghz_protocol_common.h" +#include + +#define TAG "SubGhzSomfyKeytis" + +struct SubGhzProtocolSomfyKeytis { + SubGhzProtocolCommon common; + ManchesterState manchester_saved_state; + uint32_t press_duration_counter; +}; + +typedef enum { + SomfyKeytisDecoderStepReset = 0, + SomfyKeytisDecoderStepCheckPreambula, + SomfyKeytisDecoderStepFoundPreambula, + SomfyKeytisDecoderStepStartDecode, + SomfyKeytisDecoderStepDecoderData, +} SomfyKeytisDecoderStep; + +SubGhzProtocolSomfyKeytis* subghz_protocol_somfy_keytis_alloc() { + SubGhzProtocolSomfyKeytis* instance = furi_alloc(sizeof(SubGhzProtocolSomfyKeytis)); + + instance->common.name = "Somfy Keytis"; + instance->common.code_min_count_bit_for_found = 80; + instance->common.te_short = 640; + instance->common.te_long = 1280; + instance->common.te_delta = 250; + instance->common.type_protocol = SubGhzProtocolCommonTypeDynamic; + instance->common.to_string = (SubGhzProtocolCommonToStr)subghz_protocol_somfy_keytis_to_str; + instance->common.to_load_protocol = + (SubGhzProtocolCommonLoadFromRAW)subghz_decoder_somfy_keytis_to_load_protocol; + + return instance; +} + +void subghz_protocol_somfy_keytis_free(SubGhzProtocolSomfyKeytis* instance) { + furi_assert(instance); + free(instance); +} + +/** Analysis of received data + * + * @param instance SubGhzProtocolSomfyKeytis instance + */ +void subghz_protocol_somfy_keytis_remote_controller(SubGhzProtocolSomfyKeytis* instance) { + //https://pushstack.wordpress.com/somfy-rts-protocol/ + /* + * 604 us + * / + * | 2416us | 2416us | 2416us | 2416us | 4550 us | | + * + * +--------+ +--------+ +---...---+ + * + +--------+ +--------+ +--+XXXX...XXX+ + * + * | hw. sync. | soft. | | + * | | sync. | data | + * + * + * encrypt | decrypt + * + * package 80 bit pdc key btn crc cnt serial + * + * 0xA453537C4B9855 C40019 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 C80026 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 CC0033 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 D00049 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 D4005C => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 D80063 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 DC0076 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 E00086 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 E40093 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 E800AC => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 EC00B9 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 F000C3 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 F400D6 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 F800E9 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC00FC => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC0102 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC0113 => 0xA 4 F 7 002F 37D3CD + * 0xA453537C4B9855 FC0120 => 0xA 4 F 7 002F 37D3CD + * .......... + * 0xA453537C4B9855 FC048F => 0xA 4 F 7 002F 37D3CD + * + * Pdc: "Press Duration Counter" the total delay of the button is sent 72 parcels, + * pdc cnt4b cnt8b pdc_crc + * C40019 => 11 0001 00 0000 00000001 1001 + * C80026 => 11 0010 00 0000 00000010 0110 + * CC0033 => 11 0011 00 0000 00000011 0011 + * D00049 => 11 0100 00 0000 00000100 1001 + * D4005C => 11 0101 00 0000 00000101 1100 + * D80063 => 11 0110 00 0000 00000110 0011 + * DC0076 => 11 0111 00 0000 00000111 0110 + * E00086 => 11 1000 00 0000 00001000 0110 + * E40093 => 11 1001 00 0000 00001001 0011 + * E800AC => 11 1010 00 0000 00001010 1100 + * EC00B9 => 11 1011 00 0000 00001011 1001 + * F000C3 => 11 1100 00 0000 00001100 0011 + * F400D6 => 11 1101 00 0000 00001101 0110 + * F800E9 => 11 1110 00 0000 00001110 1001 + * FC00FC => 11 1111 00 0000 00001111 1100 + * FC0102 => 11 1111 00 0000 00010000 0010 + * FC0113 => 11 1111 00 0000 00010001 0011 + * FC0120 => 11 1111 00 0000 00010010 0000 + * + * Cnt4b: 4-bit counter changes from 1 to 15 then always equals 15 + * Cnt8b: 8-bit counter changes from 1 to 72 (0x48) + * Ppdc_crc: + * uint8_t crc=0; + * for(i=4; i<24; i+=4){ + * crc ^=(pdc>>i); + * } + * return crc; + * example: crc = 1^0^0^4^C = 9 + * 11, 00, 0000: const + * + * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is + * a linear counter. In the Smoove Origin this counter is increased together with the + * rolling code. But leaving this on a constant value also works. Gerardwr notes that + * for some other types of remotes the MSB is not constant. + * Btn: 4-bit Control codes, this indicates the button that is pressed + * CRC: 4-bit Checksum. + * Ctn: 16-bit rolling code (big-endian) increased with every button press. + * Serial: 24-bit identifier of sending device (little-endian) + * + * + * Decrypt + * + * uint8_t frame[7]; + * for (i=1; i < 7; i++) { + * frame[i] = frame[i] ^ frame[i-1]; + * } + * or + * uint64 Decrypt = frame ^ (frame>>8); + * + * CRC + * + * uint8_t frame[7]; + * for (i=0; i < 7; i++) { + * crc = crc ^ frame[i] ^ (frame[i] >> 4); + * } + * crc = crc & 0xf; + * + */ + + uint64_t data = instance->common.code_last_found ^ (instance->common.code_last_found >> 8); + instance->common.btn = (data >> 48) & 0xF; + instance->common.cnt = (data >> 24) & 0xFFFF; + instance->common.serial = data & 0xFFFFFF; +} + +uint8_t subghz_protocol_somfy_keytis_crc(uint64_t data) { + uint8_t crc = 0; + data &= 0xFFF0FFFFFFFFFF; + for(uint8_t i = 0; i < 56; i += 8) { + crc = crc ^ data >> i ^ (data >> (i + 4)); + } + return crc & 0xf; +} +const char* subghz_protocol_somfy_keytis_get_name_button(uint8_t btn) { + const char* name_btn[0x10] = { + "Unknown", + "0x01", + "0x02", + "Prog", + "Key_1", + "0x05", + "0x06", + "0x07", + "0x08", + "0x09", + "0x0A", + "0x0B", + "0x0C", + "0x0D", + "0x0E", + "0x0F"}; + return btn <= 0xf ? name_btn[btn] : name_btn[0]; +} + +uint32_t subghz_protocol_somfy_keytis_get_press_duration(void* context) { + SubGhzProtocolSomfyKeytis* instance = context; + return instance->press_duration_counter; +} + +void subghz_protocol_somfy_keytis_reset(SubGhzProtocolSomfyKeytis* instance) { + instance->common.parser_step = SomfyKeytisDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +void subghz_protocol_somfy_keytis_parse( + SubGhzProtocolSomfyKeytis* instance, + bool level, + uint32_t duration) { + ManchesterEvent event = ManchesterEventReset; + switch(instance->common.parser_step) { + case SomfyKeytisDecoderStepReset: + if((level) && DURATION_DIFF(duration, instance->common.te_short * 4) < + instance->common.te_delta * 4) { + instance->common.parser_step = SomfyKeytisDecoderStepFoundPreambula; + instance->common.header_count++; + } + break; + case SomfyKeytisDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, instance->common.te_short * 4) < + instance->common.te_delta * 4)) { + instance->common.parser_step = SomfyKeytisDecoderStepCheckPreambula; + } else { + instance->common.header_count = 0; + instance->common.parser_step = SomfyKeytisDecoderStepReset; + } + break; + case SomfyKeytisDecoderStepCheckPreambula: + if(level) { + if(DURATION_DIFF(duration, instance->common.te_short * 4) < + instance->common.te_delta * 4) { + instance->common.parser_step = SomfyKeytisDecoderStepFoundPreambula; + instance->common.header_count++; + } else if( + (instance->common.header_count > 1) && + (DURATION_DIFF(duration, instance->common.te_short * 7) < + instance->common.te_delta * 4)) { + instance->common.parser_step = SomfyKeytisDecoderStepDecoderData; + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + instance->press_duration_counter = 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); + } + } + + break; + + case SomfyKeytisDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta) { + event = ManchesterEventShortLow; + } else if(DURATION_DIFF(duration, instance->common.te_long) < instance->common.te_delta) { + event = ManchesterEventLongLow; + } else if(duration >= (instance->common.te_long + instance->common.te_delta)) { + if(instance->common.code_count_bit == + instance->common.code_min_count_bit_for_found) { + instance->common.code_last_found = instance->common.code_found; + instance->common.code_last_count_bit = instance->common.code_count_bit; + + //check crc + uint64_t data_tmp = instance->common.code_last_found ^ + (instance->common.code_last_found >> 8); + if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_keytis_crc(data_tmp)) { + if(instance->common.callback) + instance->common.callback( + (SubGhzProtocolCommon*)instance, instance->common.context); + } + } + instance->common.code_found = 0; + instance->common.code_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); + instance->common.parser_step = SomfyKeytisDecoderStepReset; + } else { + instance->common.parser_step = SomfyKeytisDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta) { + event = ManchesterEventShortHigh; + } else if(DURATION_DIFF(duration, instance->common.te_long) < instance->common.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->common.parser_step = SomfyKeytisDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + if(instance->common.code_count_bit < 56) { + instance->common.code_found = (instance->common.code_found << 1) | data; + } else { + instance->press_duration_counter = (instance->press_duration_counter << 1) | + data; + } + + instance->common.code_count_bit++; + } + } + break; + } +} + +void subghz_protocol_somfy_keytis_to_str(SubGhzProtocolSomfyKeytis* instance, string_t output) { + subghz_protocol_somfy_keytis_remote_controller(instance); + uint32_t code_found_hi = instance->common.code_last_found >> 32; + uint32_t code_found_lo = instance->common.code_last_found & 0x00000000ffffffff; + + string_cat_printf( + output, + "%s %db\r\n" + "%lX%08lX%06lX\r\n" + "Sn:0x%06lX \r\n" + "Cnt:0x%04X\r\n" + "Btn:%s\r\n", + + instance->common.name, + instance->common.code_last_count_bit, + code_found_hi, + code_found_lo, + instance->press_duration_counter, + instance->common.serial, + instance->common.cnt, + subghz_protocol_somfy_keytis_get_name_button(instance->common.btn)); +} + +void subghz_decoder_somfy_keytis_to_load_protocol( + SubGhzProtocolSomfyKeytis* instance, + void* context) { + furi_assert(context); + furi_assert(instance); + SubGhzProtocolCommonLoad* data = context; + instance->common.code_last_found = data->code_found; + instance->common.code_last_count_bit = data->code_count_bit; + instance->press_duration_counter = data->param1; + subghz_protocol_somfy_keytis_remote_controller(instance); +} diff --git a/lib/subghz/protocols/subghz_protocol_somfy_keytis.h b/lib/subghz/protocols/subghz_protocol_somfy_keytis.h new file mode 100644 index 00000000..c5aa4244 --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_somfy_keytis.h @@ -0,0 +1,50 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolSomfyKeytis SubGhzProtocolSomfyKeytis; + +/** Allocate SubGhzProtocolSomfyKeytis + * + * @return SubGhzProtocolSomfyKeytis* + */ +SubGhzProtocolSomfyKeytis* subghz_protocol_somfy_keytis_alloc(); + +/** Free SubGhzProtocolSomfyKeytis + * + * @param instance + */ +void subghz_protocol_somfy_keytis_free(SubGhzProtocolSomfyKeytis* instance); + +uint32_t subghz_protocol_somfy_keytis_get_press_duration(void* context); + +/** Reset internal state + * @param instance - SubGhzProtocolSomfyKeytis instance + */ +void subghz_protocol_somfy_keytis_reset(SubGhzProtocolSomfyKeytis* instance); + +/** Parse accepted duration + * + * @param instance - SubGhzProtocolSomfyKeytis instance + * @param data - LevelDuration level_duration + */ +void subghz_protocol_somfy_keytis_parse( + SubGhzProtocolSomfyKeytis* instance, + bool level, + uint32_t duration); + +/** Outputting information from the parser + * + * @param instance - SubGhzProtocolSomfyKeytis* instance + * @param output - output string + */ +void subghz_protocol_somfy_keytis_to_str(SubGhzProtocolSomfyKeytis* instance, string_t output); + +/** Loading protocol from bin data + * + * @param instance - SubGhzProtocolSomfyKeytis instance + * @param context - SubGhzProtocolCommonLoad context + */ +void subghz_decoder_somfy_keytis_to_load_protocol( + SubGhzProtocolSomfyKeytis* instance, + void* context); \ No newline at end of file diff --git a/lib/subghz/protocols/subghz_protocol_somfy_telis.c b/lib/subghz/protocols/subghz_protocol_somfy_telis.c new file mode 100644 index 00000000..02d3c47b --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_somfy_telis.c @@ -0,0 +1,293 @@ +#include "subghz_protocol_somfy_telis.h" +#include "subghz_protocol_common.h" +#include + +#define TAG "SubGhzSomfyTelis" + +struct SubGhzProtocolSomfyTelis { + SubGhzProtocolCommon common; + ManchesterState manchester_saved_state; +}; + +typedef enum { + SomfyTelisDecoderStepReset = 0, + SomfyTelisDecoderStepCheckPreambula, + SomfyTelisDecoderStepFoundPreambula, + SomfyTelisDecoderStepStartDecode, + SomfyTelisDecoderStepDecoderData, +} SomfyTelisDecoderStep; + +SubGhzProtocolSomfyTelis* subghz_protocol_somfy_telis_alloc() { + SubGhzProtocolSomfyTelis* instance = furi_alloc(sizeof(SubGhzProtocolSomfyTelis)); + + instance->common.name = "Somfy Telis"; + instance->common.code_min_count_bit_for_found = 56; + instance->common.te_short = 640; + instance->common.te_long = 1280; + instance->common.te_delta = 250; + instance->common.type_protocol = SubGhzProtocolCommonTypeDynamic; + instance->common.to_string = (SubGhzProtocolCommonToStr)subghz_protocol_somfy_telis_to_str; + instance->common.to_load_protocol = + (SubGhzProtocolCommonLoadFromRAW)subghz_decoder_somfy_telis_to_load_protocol; + + return instance; +} + +void subghz_protocol_somfy_telis_free(SubGhzProtocolSomfyTelis* instance) { + furi_assert(instance); + free(instance); +} + +/** Analysis of received data + * + * @param instance SubGhzProtocolSomfyTelis instance + */ +void subghz_protocol_somfy_telis_remote_controller(SubGhzProtocolSomfyTelis* instance) { + //https://pushstack.wordpress.com/somfy-rts-protocol/ + /* + * 604 us + * / + * | 2416us | 2416us | 2416us | 2416us | 4550 us | | 67648 us | 30415 us | + * + * +--------+ +--------+ +---...---+ + * + +--------+ +--------+ +--+XXXX...XXX+-----...----- + * + * | hw. sync. | soft. | | Inter-frame + * | | sync. | data | gap + * + * + * encrypt | decrypt + * + * package 56 bit cnt key btn|crc cnt serial + * 0xA7232323312222 - 0 => A7 8 0 | 00 00 | 12 13 00 + * 0xA7222223312222 - 1 => A7 8 5 | 00 01 | 12 13 00 + * 0xA7212123312222 - 2 => A7 8 6 | 00 02 | 12 13 00 + * + * Key: “Encryption Key”, Most significant 4-bit are always 0xA, Least Significant bits is + * a linear counter. In the Smoove Origin this counter is increased together with the + * rolling code. But leaving this on a constant value also works. Gerardwr notes that + * for some other types of remotes the MSB is not constant. + * Btn: 4-bit Control codes, this indicates the button that is pressed + * CRC: 4-bit Checksum. + * Ctn: 16-bit rolling code (big-endian) increased with every button press. + * Serial: 24-bit identifier of sending device (little-endian) + * + * + * Decrypt + * + * uint8_t frame[7]; + * for (i=1; i < 7; i++) { + * frame[i] = frame[i] ^ frame[i-1]; + * } + * or + * uint64 Decrypt = frame ^ (frame>>8); + * + * Btn + * + * Value Button(s) Description + * 0x1 My Stop or move to favourite position + * 0x2 Up Move up + * 0x3 My + Up Set upper motor limit in initial programming mode + * 0x4 Down Move down + * 0x5 My + Down Set lower motor limit in initial programming mode + * 0x6 Up + Down Change motor limit and initial programming mode + * 0x8 Prog Used for (de-)registering remotes, see below + * 0x9 Sun + Flag Enable sun and wind detector (SUN and FLAG symbol on the Telis Soliris RC) + * 0xA Flag Disable sun detector (FLAG symbol on the Telis Soliris RC) + * + * CRC + * + * uint8_t frame[7]; + * for (i=0; i < 7; i++) { + * cksum = cksum ^ frame[i] ^ (frame[i] >> 4); + * } + * cksum = cksum & 0xf; + * + */ + + uint64_t data = instance->common.code_last_found ^ (instance->common.code_last_found >> 8); + instance->common.btn = (data >> 44) & 0xF; + instance->common.cnt = (data >> 24) & 0xFFFF; + instance->common.serial = data & 0xFFFFFF; +} + +uint8_t subghz_protocol_somfy_telis_crc(uint64_t data) { + uint8_t crc = 0; + data &= 0xFFF0FFFFFFFFFF; + for(uint8_t i = 0; i < 56; i += 8) { + crc = crc ^ data >> i ^ (data >> (i + 4)); + } + return crc & 0xf; +} + +const char* subghz_protocol_somfy_telis_get_name_button(uint8_t btn) { + const char* name_btn[0x10] = { + "Unknown", + "My", + "Up", + "My+Up", + "Down", + "My+Down", + "Up+Down", + "0x07", + "Prog", + "Sun+Flag", + "Flag", + "0x0B", + "0x0C", + "0x0D", + "0x0E", + "0x0F"}; + return btn <= 0xf ? name_btn[btn] : name_btn[0]; +} + +void subghz_protocol_somfy_telis_reset(SubGhzProtocolSomfyTelis* instance) { + instance->common.parser_step = SomfyTelisDecoderStepReset; + manchester_advance( + instance->manchester_saved_state, + ManchesterEventReset, + &instance->manchester_saved_state, + NULL); +} + +void subghz_protocol_somfy_telis_parse( + SubGhzProtocolSomfyTelis* instance, + bool level, + uint32_t duration) { + ManchesterEvent event = ManchesterEventReset; + switch(instance->common.parser_step) { + case SomfyTelisDecoderStepReset: + if((level) && DURATION_DIFF(duration, instance->common.te_short * 4) < + instance->common.te_delta * 4) { + instance->common.parser_step = SomfyTelisDecoderStepFoundPreambula; + instance->common.header_count++; + } + break; + case SomfyTelisDecoderStepFoundPreambula: + if((!level) && (DURATION_DIFF(duration, instance->common.te_short * 4) < + instance->common.te_delta * 4)) { + instance->common.parser_step = SomfyTelisDecoderStepCheckPreambula; + } else { + instance->common.header_count = 0; + instance->common.parser_step = SomfyTelisDecoderStepReset; + } + break; + case SomfyTelisDecoderStepCheckPreambula: + if(level) { + if(DURATION_DIFF(duration, instance->common.te_short * 4) < + instance->common.te_delta * 4) { + instance->common.parser_step = SomfyTelisDecoderStepFoundPreambula; + instance->common.header_count++; + } else if( + (instance->common.header_count > 1) && + (DURATION_DIFF(duration, instance->common.te_short * 7) < + instance->common.te_delta * 4)) { + instance->common.parser_step = SomfyTelisDecoderStepDecoderData; + instance->common.code_found = 0; + instance->common.code_count_bit = 0; + instance->common.header_count = 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); + } + } + + break; + + case SomfyTelisDecoderStepDecoderData: + if(!level) { + if(DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta) { + event = ManchesterEventShortLow; + } else if(DURATION_DIFF(duration, instance->common.te_long) < instance->common.te_delta) { + event = ManchesterEventLongLow; + } else if(duration >= (instance->common.te_long + instance->common.te_delta)) { + if(instance->common.code_count_bit == + instance->common.code_min_count_bit_for_found) { + instance->common.code_last_found = instance->common.code_found; + instance->common.code_last_count_bit = instance->common.code_count_bit; + + //check crc + uint64_t data_tmp = instance->common.code_last_found ^ + (instance->common.code_last_found >> 8); + if(((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_telis_crc(data_tmp)) { + if(instance->common.callback) + instance->common.callback( + (SubGhzProtocolCommon*)instance, instance->common.context); + } + } + instance->common.code_found = 0; + instance->common.code_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); + instance->common.parser_step = SomfyTelisDecoderStepReset; + } else { + instance->common.parser_step = SomfyTelisDecoderStepReset; + } + } else { + if(DURATION_DIFF(duration, instance->common.te_short) < instance->common.te_delta) { + event = ManchesterEventShortHigh; + } else if(DURATION_DIFF(duration, instance->common.te_long) < instance->common.te_delta) { + event = ManchesterEventLongHigh; + } else { + instance->common.parser_step = SomfyTelisDecoderStepReset; + } + } + if(event != ManchesterEventReset) { + bool data; + bool data_ok = manchester_advance( + instance->manchester_saved_state, event, &instance->manchester_saved_state, &data); + + if(data_ok) { + instance->common.code_found = (instance->common.code_found << 1) | data; + instance->common.code_count_bit++; + } + } + break; + } +} + +void subghz_protocol_somfy_telis_to_str(SubGhzProtocolSomfyTelis* instance, string_t output) { + subghz_protocol_somfy_telis_remote_controller(instance); + uint32_t code_found_hi = instance->common.code_last_found >> 32; + uint32_t code_found_lo = instance->common.code_last_found & 0x00000000ffffffff; + + string_cat_printf( + output, + "%s %db\r\n" + "Key:0x%lX%08lX\r\n" + "Sn:0x%06lX \r\n" + "Cnt:0x%04X\r\n" + "Btn:%s\r\n", + + instance->common.name, + instance->common.code_last_count_bit, + code_found_hi, + code_found_lo, + instance->common.serial, + instance->common.cnt, + subghz_protocol_somfy_telis_get_name_button(instance->common.btn)); +} + +void subghz_decoder_somfy_telis_to_load_protocol(SubGhzProtocolSomfyTelis* instance, void* context) { + furi_assert(context); + furi_assert(instance); + SubGhzProtocolCommonLoad* data = context; + instance->common.code_last_found = data->code_found; + instance->common.code_last_count_bit = data->code_count_bit; + subghz_protocol_somfy_telis_remote_controller(instance); +} diff --git a/lib/subghz/protocols/subghz_protocol_somfy_telis.h b/lib/subghz/protocols/subghz_protocol_somfy_telis.h new file mode 100644 index 00000000..007ae829 --- /dev/null +++ b/lib/subghz/protocols/subghz_protocol_somfy_telis.h @@ -0,0 +1,46 @@ +#pragma once + +#include "subghz_protocol_common.h" + +typedef struct SubGhzProtocolSomfyTelis SubGhzProtocolSomfyTelis; + +/** Allocate SubGhzProtocolSomfyTelis + * + * @return SubGhzProtocolSomfyTelis* + */ +SubGhzProtocolSomfyTelis* subghz_protocol_somfy_telis_alloc(); + +/** Free SubGhzProtocolSomfyTelis + * + * @param instance + */ +void subghz_protocol_somfy_telis_free(SubGhzProtocolSomfyTelis* instance); + +/** Reset internal state + * @param instance - SubGhzProtocolSomfyTelis instance + */ +void subghz_protocol_somfy_telis_reset(SubGhzProtocolSomfyTelis* instance); + +/** Parse accepted duration + * + * @param instance - SubGhzProtocolSomfyTelis instance + * @param data - LevelDuration level_duration + */ +void subghz_protocol_somfy_telis_parse( + SubGhzProtocolSomfyTelis* instance, + bool level, + uint32_t duration); + +/** Outputting information from the parser + * + * @param instance - SubGhzProtocolSomfyTelis* instance + * @param output - output string + */ +void subghz_protocol_somfy_telis_to_str(SubGhzProtocolSomfyTelis* instance, string_t output); + +/** Loading protocol from bin data + * + * @param instance - SubGhzProtocolSomfyTelis instance + * @param context - SubGhzProtocolCommonLoad context + */ +void subghz_decoder_somfy_telis_to_load_protocol(SubGhzProtocolSomfyTelis* instance, void* context); \ No newline at end of file diff --git a/lib/subghz/subghz_parser.c b/lib/subghz/subghz_parser.c index fb50bf3a..c4ad873f 100644 --- a/lib/subghz/subghz_parser.c +++ b/lib/subghz/subghz_parser.c @@ -18,6 +18,8 @@ #include "protocols/subghz_protocol_kia.h" #include "protocols/subghz_protocol_raw.h" #include "protocols/subghz_protocol_hormann.h" +#include "protocols/subghz_protocol_somfy_telis.h" +#include "protocols/subghz_protocol_somfy_keytis.h" #include "subghz_keystore.h" @@ -44,6 +46,8 @@ typedef enum { SubGhzProtocolTypeKIA, SubGhzProtocolTypeRAW, SubGhzProtocolTypeHormann, + SubGhzProtocolTypeSomfyTelis, + SubGhzProtocolTypeSomfyKeytis, SubGhzProtocolTypeMax, } SubGhzProtocolType; @@ -119,6 +123,10 @@ SubGhzParser* subghz_parser_alloc() { (SubGhzProtocolCommon*)subghz_protocol_raw_alloc(); instance->protocols[SubGhzProtocolTypeHormann] = (SubGhzProtocolCommon*)subghz_protocol_hormann_alloc(); + instance->protocols[SubGhzProtocolTypeSomfyTelis] = + (SubGhzProtocolCommon*)subghz_protocol_somfy_telis_alloc(); + instance->protocols[SubGhzProtocolTypeSomfyKeytis] = + (SubGhzProtocolCommon*)subghz_protocol_somfy_keytis_alloc(); return instance; } @@ -156,6 +164,10 @@ void subghz_parser_free(SubGhzParser* instance) { subghz_protocol_raw_free((SubGhzProtocolRAW*)instance->protocols[SubGhzProtocolTypeRAW]); subghz_protocol_hormann_free( (SubGhzProtocolHormann*)instance->protocols[SubGhzProtocolTypeHormann]); + subghz_protocol_somfy_telis_free( + (SubGhzProtocolSomfyTelis*)instance->protocols[SubGhzProtocolTypeSomfyTelis]); + subghz_protocol_somfy_keytis_free( + (SubGhzProtocolSomfyKeytis*)instance->protocols[SubGhzProtocolTypeSomfyKeytis]); subghz_keystore_free(instance->keystore); @@ -257,6 +269,10 @@ void subghz_parser_reset(SubGhzParser* instance) { subghz_protocol_raw_reset((SubGhzProtocolRAW*)instance->protocols[SubGhzProtocolTypeRAW]); subghz_protocol_hormann_reset( (SubGhzProtocolHormann*)instance->protocols[SubGhzProtocolTypeHormann]); + subghz_protocol_somfy_telis_reset( + (SubGhzProtocolSomfyTelis*)instance->protocols[SubGhzProtocolTypeSomfyTelis]); + subghz_protocol_somfy_keytis_reset( + (SubGhzProtocolSomfyKeytis*)instance->protocols[SubGhzProtocolTypeSomfyKeytis]); } void subghz_parser_raw_parse(SubGhzParser* instance, bool level, uint32_t duration) { @@ -309,4 +325,12 @@ void subghz_parser_parse(SubGhzParser* instance, bool level, uint32_t duration) (SubGhzProtocolKIA*)instance->protocols[SubGhzProtocolTypeKIA], level, duration); subghz_protocol_hormann_parse( (SubGhzProtocolHormann*)instance->protocols[SubGhzProtocolTypeHormann], level, duration); + subghz_protocol_somfy_telis_parse( + (SubGhzProtocolSomfyTelis*)instance->protocols[SubGhzProtocolTypeSomfyTelis], + level, + duration); + subghz_protocol_somfy_keytis_parse( + (SubGhzProtocolSomfyKeytis*)instance->protocols[SubGhzProtocolTypeSomfyKeytis], + level, + duration); }