SubGhz: add protocol MegaCode (#1204)
* SubGhz: add protocol MegaCode * SubGhz: check for guard time injection at the end of buffer * SubGhz: rollback samples counting in trasmitter * SubGhz: fix subghz_file_encoder_worker incorrect pulse sequence * Input: tune debounce interval * SubGhz: fix spelling in subghz_file_encoder_worker_add_level_duration Co-authored-by: Aleksandr Kutuzov <alleteam@gmail.com>
This commit is contained in:
		
							parent
							
								
									23cff2a7d2
								
							
						
					
					
						commit
						f04d0eea96
					
				| @ -202,7 +202,8 @@ bool subghz_scene_read_raw_on_event(void* context, SceneManagerEvent event) { | ||||
|                         DOLPHIN_DEED(DolphinDeedSubGhzSend); | ||||
|                         // set callback end tx
 | ||||
|                         subghz_protocol_raw_file_encoder_worker_set_callback_end( | ||||
|                             (SubGhzProtocolEncoderRAW*)subghz->txrx->transmitter->protocol_instance, | ||||
|                             (SubGhzProtocolEncoderRAW*)subghz_transmitter_get_protocol_instance( | ||||
|                                 subghz->txrx->transmitter), | ||||
|                             subghz_scene_read_raw_callback_end_tx, | ||||
|                             subghz); | ||||
|                         subghz->state_notifications = SubGhzNotificationStateTx; | ||||
|  | ||||
| @ -268,7 +268,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | ||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); | ||||
|             if(subghz->txrx->transmitter) { | ||||
|                 subghz_protocol_keeloq_create_data( | ||||
|                     subghz->txrx->transmitter->protocol_instance, | ||||
|                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), | ||||
|                     subghz->txrx->fff_data, | ||||
|                     key & 0x0FFFFFFF, | ||||
|                     0x2, | ||||
| @ -292,7 +292,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | ||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); | ||||
|             if(subghz->txrx->transmitter) { | ||||
|                 subghz_protocol_keeloq_create_data( | ||||
|                     subghz->txrx->transmitter->protocol_instance, | ||||
|                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), | ||||
|                     subghz->txrx->fff_data, | ||||
|                     key & 0x0FFFFFFF, | ||||
|                     0x2, | ||||
|  | ||||
| @ -10,7 +10,7 @@ extern "C" { | ||||
| #endif | ||||
| 
 | ||||
| /* Input Related Constants */ | ||||
| #define INPUT_DEBOUNCE_TICKS 20 | ||||
| #define INPUT_DEBOUNCE_TICKS 30 | ||||
| 
 | ||||
| /* Input Keys */ | ||||
| typedef enum { | ||||
|  | ||||
| @ -799,6 +799,9 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { | ||||
|                 } else { | ||||
|                     furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; | ||||
|                 } | ||||
|                 // This code must be invoked only once: when encoder starts with low level.
 | ||||
|                 // Otherwise whole thing will crash.
 | ||||
|                 furi_check(samples > 0); | ||||
|             } | ||||
| 
 | ||||
|             uint32_t duration = level_duration_get_duration(ld); | ||||
|  | ||||
							
								
								
									
										413
									
								
								lib/subghz/protocols/megacode.c
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										413
									
								
								lib/subghz/protocols/megacode.c
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,413 @@ | ||||
| #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_runing = 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; | ||||
|         } | ||||
| 
 | ||||
|         //optional parameter parameter
 | ||||
|         flipper_format_read_uint32( | ||||
|             flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); | ||||
| 
 | ||||
|         subghz_protocol_encoder_megacode_get_upload(instance); | ||||
|         instance->encoder.is_runing = true; | ||||
| 
 | ||||
|         res = true; | ||||
|     } while(false); | ||||
| 
 | ||||
|     return res; | ||||
| } | ||||
| 
 | ||||
| void subghz_protocol_encoder_megacode_stop(void* context) { | ||||
|     SubGhzProtocolEncoderMegaCode* instance = context; | ||||
|     instance->encoder.is_runing = false; | ||||
| } | ||||
| 
 | ||||
| LevelDuration subghz_protocol_encoder_megacode_yield(void* context) { | ||||
|     SubGhzProtocolEncoderMegaCode* instance = context; | ||||
| 
 | ||||
|     if(instance->encoder.repeat == 0 || !instance->encoder.is_runing) { | ||||
|         instance->encoder.is_runing = 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 * 15)) { //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, | ||||
|     uint32_t frequency, | ||||
|     FuriHalSubGhzPreset preset) { | ||||
|     furi_assert(context); | ||||
|     SubGhzProtocolDecoderMegaCode* instance = context; | ||||
|     return subghz_block_generic_serialize(&instance->generic, flipper_format, frequency, preset); | ||||
| } | ||||
| 
 | ||||
| bool subghz_protocol_decoder_megacode_deserialize(void* context, FlipperFormat* flipper_format) { | ||||
|     furi_assert(context); | ||||
|     SubGhzProtocolDecoderMegaCode* instance = context; | ||||
|     return subghz_block_generic_deserialize(&instance->generic, flipper_format); | ||||
| } | ||||
| 
 | ||||
| void subghz_protocol_decoder_megacode_get_string(void* context, string_t output) { | ||||
|     furi_assert(context); | ||||
|     SubGhzProtocolDecoderMegaCode* instance = context; | ||||
|     subghz_protocol_megacode_check_remote_controller(&instance->generic); | ||||
| 
 | ||||
|     string_cat_printf( | ||||
|         output, | ||||
|         "%s %dbit\r\n" | ||||
|         "Key:%06lX\r\n" | ||||
|         "Sn:%04lX Btn:%X\r\n" | ||||
|         "Facility:%X\r\n", | ||||
|         instance->generic.protocol_name, | ||||
|         instance->generic.data_count_bit, | ||||
|         (uint32_t)instance->generic.data, | ||||
|         instance->generic.serial, | ||||
|         instance->generic.btn, | ||||
|         instance->generic.cnt); | ||||
| } | ||||
							
								
								
									
										109
									
								
								lib/subghz/protocols/megacode.h
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										109
									
								
								lib/subghz/protocols/megacode.h
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,109 @@ | ||||
| #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 frequency The frequency at which the signal was received, Hz | ||||
|  * @param preset The modulation on which the signal was received, FuriHalSubGhzPreset | ||||
|  * @return true On success | ||||
|  */ | ||||
| bool subghz_protocol_decoder_megacode_serialize( | ||||
|     void* context, | ||||
|     FlipperFormat* flipper_format, | ||||
|     uint32_t frequency, | ||||
|     FuriHalSubGhzPreset 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, string_t output); | ||||
| @ -8,7 +8,7 @@ const SubGhzProtocol* subghz_protocol_registry[] = { | ||||
|     &subghz_protocol_hormann,      &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, | ||||
|     &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_gate_tx, | ||||
|     &subghz_protocol_raw,          &subghz_protocol_firefly,    &subghz_protocol_secplus_v2, | ||||
|     &subghz_protocol_secplus_v1, | ||||
|     &subghz_protocol_secplus_v1,   &subghz_protocol_megacode, | ||||
| 
 | ||||
| }; | ||||
| 
 | ||||
|  | ||||
| @ -24,6 +24,7 @@ | ||||
| #include "firefly.h" | ||||
| #include "secplus_v2.h" | ||||
| #include "secplus_v1.h" | ||||
| #include "megacode.h" | ||||
| 
 | ||||
| /**
 | ||||
|  * Registration by name SubGhzProtocol. | ||||
|  | ||||
| @ -19,7 +19,6 @@ struct SubGhzFileEncoderWorker { | ||||
|     volatile bool worker_running; | ||||
|     volatile bool worker_stoping; | ||||
|     bool level; | ||||
|     int32_t duration; | ||||
|     string_t str_data; | ||||
|     string_t file_path; | ||||
| 
 | ||||
| @ -37,25 +36,21 @@ void subghz_file_encoder_worker_callback_end( | ||||
|     instance->context_end = context_end; | ||||
| } | ||||
| 
 | ||||
| void subghz_file_encoder_worker_add_livel_duration( | ||||
| void subghz_file_encoder_worker_add_level_duration( | ||||
|     SubGhzFileEncoderWorker* instance, | ||||
|     int32_t duration) { | ||||
|     bool res = true; | ||||
|     if(duration < 0 && !instance->level) { | ||||
|         instance->duration += duration; | ||||
|         res = false; | ||||
|     } else if(duration > 0 && instance->level) { | ||||
|         instance->duration += duration; | ||||
|         res = false; | ||||
|     } else if(duration == 0) { | ||||
|         instance->duration = 0; | ||||
|     } | ||||
| 
 | ||||
|     if(res) { | ||||
|         instance->level = !instance->level; | ||||
|         instance->duration += duration; | ||||
|         xStreamBufferSend(instance->stream, &instance->duration, sizeof(int32_t), 10); | ||||
|         instance->duration = 0; | ||||
|         xStreamBufferSend(instance->stream, &duration, sizeof(int32_t), 100); | ||||
|     } else { | ||||
|         FURI_LOG_E(TAG, "Invalid level in the stream"); | ||||
|     } | ||||
| } | ||||
| 
 | ||||
| @ -80,7 +75,7 @@ bool subghz_file_encoder_worker_data_parse( | ||||
|               ind_start))) { //check that there is still an element in the line and that it has not gone beyond the line
 | ||||
|             str1 = strchr(str1, ' '); | ||||
|             str1 += 1; //if found, shift the pointer by next element per line
 | ||||
|             subghz_file_encoder_worker_add_livel_duration(instance, atoi(str1)); | ||||
|             subghz_file_encoder_worker_add_level_duration(instance, atoi(str1)); | ||||
|         } | ||||
|         res = true; | ||||
|     } | ||||
| @ -152,14 +147,14 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { | ||||
|                        string_get_cstr(instance->str_data), | ||||
|                        strlen(string_get_cstr(instance->str_data)))) { | ||||
|                     //to stop DMA correctly
 | ||||
|                     subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET); | ||||
|                     subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET); | ||||
|                     subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); | ||||
|                     subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); | ||||
| 
 | ||||
|                     break; | ||||
|                 } | ||||
|             } else { | ||||
|                 subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET); | ||||
|                 subghz_file_encoder_worker_add_livel_duration(instance, LEVEL_DURATION_RESET); | ||||
|                 subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); | ||||
|                 subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); | ||||
|                 break; | ||||
|             } | ||||
|         } | ||||
|  | ||||
| @ -3,6 +3,11 @@ | ||||
| #include "protocols/base.h" | ||||
| #include "protocols/registry.h" | ||||
| 
 | ||||
| struct SubGhzTransmitter { | ||||
|     const SubGhzProtocol* protocol; | ||||
|     SubGhzProtocolEncoderBase* protocol_instance; | ||||
| }; | ||||
| 
 | ||||
| SubGhzTransmitter* | ||||
|     subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) { | ||||
|     SubGhzTransmitter* instance = NULL; | ||||
| @ -23,6 +28,11 @@ void subghz_transmitter_free(SubGhzTransmitter* instance) { | ||||
|     free(instance); | ||||
| } | ||||
| 
 | ||||
| SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance) { | ||||
|     furi_assert(instance); | ||||
|     return instance->protocol_instance; | ||||
| } | ||||
| 
 | ||||
| bool subghz_transmitter_stop(SubGhzTransmitter* instance) { | ||||
|     furi_assert(instance); | ||||
|     bool ret = false; | ||||
| @ -46,6 +56,5 @@ bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* | ||||
| 
 | ||||
| LevelDuration subghz_transmitter_yield(void* context) { | ||||
|     SubGhzTransmitter* instance = context; | ||||
| 
 | ||||
|     return instance->protocol->encoder->yield(instance->protocol_instance); | ||||
| } | ||||
|  | ||||
| @ -6,11 +6,6 @@ | ||||
| 
 | ||||
| typedef struct SubGhzTransmitter SubGhzTransmitter; | ||||
| 
 | ||||
| struct SubGhzTransmitter { | ||||
|     const SubGhzProtocol* protocol; | ||||
|     SubGhzProtocolEncoderBase* protocol_instance; | ||||
| }; | ||||
| 
 | ||||
| /**
 | ||||
|  * Allocate and init SubGhzTransmitter. | ||||
|  * @param environment Pointer to a SubGhzEnvironment instance | ||||
| @ -25,6 +20,11 @@ SubGhzTransmitter* | ||||
|  */ | ||||
| void subghz_transmitter_free(SubGhzTransmitter* instance); | ||||
| 
 | ||||
| /** Get protocol instance.
 | ||||
|  * @param instance Pointer to a SubGhzTransmitter instance | ||||
|  */ | ||||
| SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance); | ||||
| 
 | ||||
| /**
 | ||||
|  * Forced transmission stop. | ||||
|  * @param instance Pointer to a SubGhzTransmitter instance | ||||
|  | ||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Skorpionm
						Skorpionm