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); |                         DOLPHIN_DEED(DolphinDeedSubGhzSend); | ||||||
|                         // set callback end tx
 |                         // set callback end tx
 | ||||||
|                         subghz_protocol_raw_file_encoder_worker_set_callback_end( |                         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_scene_read_raw_callback_end_tx, | ||||||
|                             subghz); |                             subghz); | ||||||
|                         subghz->state_notifications = SubGhzNotificationStateTx; |                         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); |                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); | ||||||
|             if(subghz->txrx->transmitter) { |             if(subghz->txrx->transmitter) { | ||||||
|                 subghz_protocol_keeloq_create_data( |                 subghz_protocol_keeloq_create_data( | ||||||
|                     subghz->txrx->transmitter->protocol_instance, |                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), | ||||||
|                     subghz->txrx->fff_data, |                     subghz->txrx->fff_data, | ||||||
|                     key & 0x0FFFFFFF, |                     key & 0x0FFFFFFF, | ||||||
|                     0x2, |                     0x2, | ||||||
| @ -292,7 +292,7 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { | |||||||
|                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); |                 subghz->txrx->environment, SUBGHZ_PROTOCOL_KEELOQ_NAME); | ||||||
|             if(subghz->txrx->transmitter) { |             if(subghz->txrx->transmitter) { | ||||||
|                 subghz_protocol_keeloq_create_data( |                 subghz_protocol_keeloq_create_data( | ||||||
|                     subghz->txrx->transmitter->protocol_instance, |                     subghz_transmitter_get_protocol_instance(subghz->txrx->transmitter), | ||||||
|                     subghz->txrx->fff_data, |                     subghz->txrx->fff_data, | ||||||
|                     key & 0x0FFFFFFF, |                     key & 0x0FFFFFFF, | ||||||
|                     0x2, |                     0x2, | ||||||
|  | |||||||
| @ -10,7 +10,7 @@ extern "C" { | |||||||
| #endif | #endif | ||||||
| 
 | 
 | ||||||
| /* Input Related Constants */ | /* Input Related Constants */ | ||||||
| #define INPUT_DEBOUNCE_TICKS 20 | #define INPUT_DEBOUNCE_TICKS 30 | ||||||
| 
 | 
 | ||||||
| /* Input Keys */ | /* Input Keys */ | ||||||
| typedef enum { | typedef enum { | ||||||
|  | |||||||
| @ -799,6 +799,9 @@ static void furi_hal_subghz_async_tx_refill(uint32_t* buffer, size_t samples) { | |||||||
|                 } else { |                 } else { | ||||||
|                     furi_hal_subghz_async_tx.duty_low += API_HAL_SUBGHZ_ASYNC_TX_GUARD_TIME; |                     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); |             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_hormann,      &subghz_protocol_nero_radio, &subghz_protocol_somfy_telis, | ||||||
|     &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_gate_tx, |     &subghz_protocol_somfy_keytis, &subghz_protocol_scher_khan, &subghz_protocol_gate_tx, | ||||||
|     &subghz_protocol_raw,          &subghz_protocol_firefly,    &subghz_protocol_secplus_v2, |     &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 "firefly.h" | ||||||
| #include "secplus_v2.h" | #include "secplus_v2.h" | ||||||
| #include "secplus_v1.h" | #include "secplus_v1.h" | ||||||
|  | #include "megacode.h" | ||||||
| 
 | 
 | ||||||
| /**
 | /**
 | ||||||
|  * Registration by name SubGhzProtocol. |  * Registration by name SubGhzProtocol. | ||||||
|  | |||||||
| @ -19,7 +19,6 @@ struct SubGhzFileEncoderWorker { | |||||||
|     volatile bool worker_running; |     volatile bool worker_running; | ||||||
|     volatile bool worker_stoping; |     volatile bool worker_stoping; | ||||||
|     bool level; |     bool level; | ||||||
|     int32_t duration; |  | ||||||
|     string_t str_data; |     string_t str_data; | ||||||
|     string_t file_path; |     string_t file_path; | ||||||
| 
 | 
 | ||||||
| @ -37,25 +36,21 @@ void subghz_file_encoder_worker_callback_end( | |||||||
|     instance->context_end = context_end; |     instance->context_end = context_end; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| void subghz_file_encoder_worker_add_livel_duration( | void subghz_file_encoder_worker_add_level_duration( | ||||||
|     SubGhzFileEncoderWorker* instance, |     SubGhzFileEncoderWorker* instance, | ||||||
|     int32_t duration) { |     int32_t duration) { | ||||||
|     bool res = true; |     bool res = true; | ||||||
|     if(duration < 0 && !instance->level) { |     if(duration < 0 && !instance->level) { | ||||||
|         instance->duration += duration; |  | ||||||
|         res = false; |         res = false; | ||||||
|     } else if(duration > 0 && instance->level) { |     } else if(duration > 0 && instance->level) { | ||||||
|         instance->duration += duration; |  | ||||||
|         res = false; |         res = false; | ||||||
|     } else if(duration == 0) { |  | ||||||
|         instance->duration = 0; |  | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if(res) { |     if(res) { | ||||||
|         instance->level = !instance->level; |         instance->level = !instance->level; | ||||||
|         instance->duration += duration; |         xStreamBufferSend(instance->stream, &duration, sizeof(int32_t), 100); | ||||||
|         xStreamBufferSend(instance->stream, &instance->duration, sizeof(int32_t), 10); |     } else { | ||||||
|         instance->duration = 0; |         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
 |               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 = strchr(str1, ' '); | ||||||
|             str1 += 1; //if found, shift the pointer by next element per line
 |             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; |         res = true; | ||||||
|     } |     } | ||||||
| @ -152,14 +147,14 @@ static int32_t subghz_file_encoder_worker_thread(void* context) { | |||||||
|                        string_get_cstr(instance->str_data), |                        string_get_cstr(instance->str_data), | ||||||
|                        strlen(string_get_cstr(instance->str_data)))) { |                        strlen(string_get_cstr(instance->str_data)))) { | ||||||
|                     //to stop DMA correctly
 |                     //to stop DMA correctly
 | ||||||
|                     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_livel_duration(instance, LEVEL_DURATION_RESET); |                     subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); | ||||||
| 
 | 
 | ||||||
|                     break; |                     break; | ||||||
|                 } |                 } | ||||||
|             } else { |             } else { | ||||||
|                 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_livel_duration(instance, LEVEL_DURATION_RESET); |                 subghz_file_encoder_worker_add_level_duration(instance, LEVEL_DURATION_RESET); | ||||||
|                 break; |                 break; | ||||||
|             } |             } | ||||||
|         } |         } | ||||||
|  | |||||||
| @ -3,6 +3,11 @@ | |||||||
| #include "protocols/base.h" | #include "protocols/base.h" | ||||||
| #include "protocols/registry.h" | #include "protocols/registry.h" | ||||||
| 
 | 
 | ||||||
|  | struct SubGhzTransmitter { | ||||||
|  |     const SubGhzProtocol* protocol; | ||||||
|  |     SubGhzProtocolEncoderBase* protocol_instance; | ||||||
|  | }; | ||||||
|  | 
 | ||||||
| SubGhzTransmitter* | SubGhzTransmitter* | ||||||
|     subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) { |     subghz_transmitter_alloc_init(SubGhzEnvironment* environment, const char* protocol_name) { | ||||||
|     SubGhzTransmitter* instance = NULL; |     SubGhzTransmitter* instance = NULL; | ||||||
| @ -23,6 +28,11 @@ void subghz_transmitter_free(SubGhzTransmitter* instance) { | |||||||
|     free(instance); |     free(instance); | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | SubGhzProtocolEncoderBase* subghz_transmitter_get_protocol_instance(SubGhzTransmitter* instance) { | ||||||
|  |     furi_assert(instance); | ||||||
|  |     return instance->protocol_instance; | ||||||
|  | } | ||||||
|  | 
 | ||||||
| bool subghz_transmitter_stop(SubGhzTransmitter* instance) { | bool subghz_transmitter_stop(SubGhzTransmitter* instance) { | ||||||
|     furi_assert(instance); |     furi_assert(instance); | ||||||
|     bool ret = false; |     bool ret = false; | ||||||
| @ -46,6 +56,5 @@ bool subghz_transmitter_deserialize(SubGhzTransmitter* instance, FlipperFormat* | |||||||
| 
 | 
 | ||||||
| LevelDuration subghz_transmitter_yield(void* context) { | LevelDuration subghz_transmitter_yield(void* context) { | ||||||
|     SubGhzTransmitter* instance = context; |     SubGhzTransmitter* instance = context; | ||||||
| 
 |  | ||||||
|     return instance->protocol->encoder->yield(instance->protocol_instance); |     return instance->protocol->encoder->yield(instance->protocol_instance); | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,11 +6,6 @@ | |||||||
| 
 | 
 | ||||||
| typedef struct SubGhzTransmitter SubGhzTransmitter; | typedef struct SubGhzTransmitter SubGhzTransmitter; | ||||||
| 
 | 
 | ||||||
| struct SubGhzTransmitter { |  | ||||||
|     const SubGhzProtocol* protocol; |  | ||||||
|     SubGhzProtocolEncoderBase* protocol_instance; |  | ||||||
| }; |  | ||||||
| 
 |  | ||||||
| /**
 | /**
 | ||||||
|  * Allocate and init SubGhzTransmitter. |  * Allocate and init SubGhzTransmitter. | ||||||
|  * @param environment Pointer to a SubGhzEnvironment instance |  * @param environment Pointer to a SubGhzEnvironment instance | ||||||
| @ -25,6 +20,11 @@ SubGhzTransmitter* | |||||||
|  */ |  */ | ||||||
| void subghz_transmitter_free(SubGhzTransmitter* instance); | 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. |  * Forced transmission stop. | ||||||
|  * @param instance Pointer to a SubGhzTransmitter instance |  * @param instance Pointer to a SubGhzTransmitter instance | ||||||
|  | |||||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user
	 Skorpionm
						Skorpionm