From 8f203f47d9e1eea937bcade5eb126ee1fa31ae2c Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 5 Jul 2025 17:48:30 +0300 Subject: [PATCH 01/18] comunello add manually support --- .../main/subghz/helpers/subghz_custom_event.h | 2 ++ .../subghz/scenes/subghz_scene_set_type.c | 22 +++++++++++++++++++ 2 files changed, 24 insertions(+) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index bd8dee161..bdd5f849d 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -79,6 +79,8 @@ typedef enum { SetTypeDoorHan_433_92, SetTypeBeninca433, SetTypeBeninca868, + SetTypeComunello433, + SetTypeComunello868, SetTypeAllmatic433, SetTypeAllmatic868, SetTypeCenturion433, diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 3155f9f1e..f70aec803 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -25,6 +25,8 @@ static const char* submenu_names[SetTypeMAX] = { [SetTypeDoorHan_433_92] = "KL: DoorHan 433MHz", [SetTypeBeninca433] = "KL: Beninca 433MHz", [SetTypeBeninca868] = "KL: Beninca 868MHz", + [SetTypeComunello433] = "KL: Comunello 433MHz", + [SetTypeComunello868] = "KL: Comunello 868MHz", [SetTypeAllmatic433] = "KL: Allmatic 433MHz", [SetTypeAllmatic868] = "KL: Allmatic 868MHz", [SetTypeCenturion433] = "KL: Centurion 433MHz", @@ -400,6 +402,26 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { .keeloq.cnt = 0x05, .keeloq.manuf = "Beninca"}; break; + case SetTypeComunello433: + gen_info = (GenInfo){ + .type = GenKeeloq, + .mod = "AM650", + .freq = 433920000, + .keeloq.serial = key & 0x00FFFFFF, + .keeloq.btn = 0x08, + .keeloq.cnt = 0x05, + .keeloq.manuf = "Comunello"}; + break; + case SetTypeComunello868: + gen_info = (GenInfo){ + .type = GenKeeloq, + .mod = "AM650", + .freq = 868460000, + .keeloq.serial = key & 0x00FFFFFF, + .keeloq.btn = 0x08, + .keeloq.cnt = 0x05, + .keeloq.manuf = "Comunello"}; + break; case SetTypeAllmatic433: gen_info = (GenInfo){ .type = GenKeeloq, From aad07ed9431b4397073d16231b008a96ba1f0e07 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 5 Jul 2025 17:57:30 +0300 Subject: [PATCH 02/18] expansion and serial fixes and new api by HaxSam & WillyJL --- CHANGELOG.md | 2 + applications/services/expansion/expansion.c | 66 ++++++++++++++++-- applications/services/expansion/expansion.h | 9 +++ .../services/expansion/expansion_worker.c | 8 ++- .../services/expansion/expansion_worker.h | 8 ++- targets/f7/furi_hal/furi_hal_serial.c | 67 +++++++++++-------- 6 files changed, 122 insertions(+), 38 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 4a4deadc0..3e3969f86 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,8 @@ - SmartRider parser (by @jaylikesbunda) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes +* FuriHalSerial: Fix RXFNE interrupt hang, aka freezing with UART output when Expansion Modules are enabled (by @WillyJL) +* Expansion: add is_connected api (by @HaxSam & @WillyJL) * RFID 125khz: Fix strange bug with LCD backlight going off after doing "Write" * GUI: Added `submenu_remove_item()` to API, was needed for NFC Type 4 related changes (by @WillyJL) * SubGHz: Fix possible frequency analyzer deadlock when holding Ok (by @WillyJL) diff --git a/applications/services/expansion/expansion.c b/applications/services/expansion/expansion.c index 219bf0641..2b0c5b27a 100644 --- a/applications/services/expansion/expansion.c +++ b/applications/services/expansion/expansion.c @@ -18,6 +18,7 @@ typedef enum { ExpansionStateDisabled, ExpansionStateEnabled, ExpansionStateRunning, + ExpansionStateConnectionEstablished, } ExpansionState; typedef enum { @@ -27,10 +28,13 @@ typedef enum { ExpansionMessageTypeReloadSettings, ExpansionMessageTypeModuleConnected, ExpansionMessageTypeModuleDisconnected, + ExpansionMessageTypeConnectionEstablished, + ExpansionMessageTypeIsConnected, } ExpansionMessageType; typedef union { FuriHalSerialId serial_id; + bool* is_connected; } ExpansionMessageData; typedef struct { @@ -67,13 +71,21 @@ static void expansion_detect_callback(void* context) { UNUSED(status); } -static void expansion_worker_callback(void* context) { +static void expansion_worker_callback(void* context, ExpansionWorkerCallbackReason reason) { furi_assert(context); Expansion* instance = context; - ExpansionMessage message = { - .type = ExpansionMessageTypeModuleDisconnected, - .api_lock = NULL, // Not locking the API here to avoid a deadlock + ExpansionMessage message; + switch(reason) { + case ExpansionWorkerCallbackReasonExit: + message.type = ExpansionMessageTypeModuleDisconnected; + message.api_lock = NULL; // Not locking the API here to avoid a deadlock + break; + + case ExpansionWorkerCallbackReasonConnected: + message.type = ExpansionMessageTypeConnectionEstablished; + message.api_lock = api_lock_alloc_locked(); + break; }; const FuriStatus status = furi_message_queue_put(instance->queue, &message, FuriWaitForever); @@ -106,7 +118,9 @@ static void UNUSED(data); if(instance->state == ExpansionStateDisabled) { return; - } else if(instance->state == ExpansionStateRunning) { + } else if( + instance->state == ExpansionStateRunning || + instance->state == ExpansionStateConnectionEstablished) { expansion_worker_stop(instance->worker); expansion_worker_free(instance->worker); } else { @@ -124,7 +138,9 @@ static void expansion_control_handler_set_listen_serial( if(instance->state != ExpansionStateDisabled && instance->serial_id == data->serial_id) { return; - } else if(instance->state == ExpansionStateRunning) { + } else if( + instance->state == ExpansionStateRunning || + instance->state == ExpansionStateConnectionEstablished) { expansion_worker_stop(instance->worker); expansion_worker_free(instance->worker); @@ -182,7 +198,8 @@ static void expansion_control_handler_module_disconnected( Expansion* instance, const ExpansionMessageData* data) { UNUSED(data); - if(instance->state != ExpansionStateRunning) { + if(instance->state != ExpansionStateRunning && + instance->state != ExpansionStateConnectionEstablished) { return; } @@ -192,6 +209,23 @@ static void expansion_control_handler_module_disconnected( instance->serial_id, expansion_detect_callback, instance); } +static void expansion_control_handler_connection_established( + Expansion* instance, + const ExpansionMessageData* data) { + UNUSED(data); + if(instance->state != ExpansionStateRunning && + instance->state != ExpansionStateConnectionEstablished) { + return; + } + + instance->state = ExpansionStateConnectionEstablished; +} + +static void + expansion_control_handler_is_connected(Expansion* instance, const ExpansionMessageData* data) { + *data->is_connected = instance->state == ExpansionStateConnectionEstablished; +} + typedef void (*ExpansionControlHandler)(Expansion*, const ExpansionMessageData*); static const ExpansionControlHandler expansion_control_handlers[] = { @@ -201,6 +235,8 @@ static const ExpansionControlHandler expansion_control_handlers[] = { [ExpansionMessageTypeReloadSettings] = expansion_control_handler_reload_settings, [ExpansionMessageTypeModuleConnected] = expansion_control_handler_module_connected, [ExpansionMessageTypeModuleDisconnected] = expansion_control_handler_module_disconnected, + [ExpansionMessageTypeConnectionEstablished] = expansion_control_handler_connection_established, + [ExpansionMessageTypeIsConnected] = expansion_control_handler_is_connected, }; static int32_t expansion_control(void* context) { @@ -295,6 +331,22 @@ void expansion_disable(Expansion* instance) { api_lock_wait_unlock_and_free(message.api_lock); } +bool expansion_is_connected(Expansion* instance) { + furi_check(instance); + bool is_connected; + + ExpansionMessage message = { + .type = ExpansionMessageTypeIsConnected, + .data.is_connected = &is_connected, + .api_lock = api_lock_alloc_locked(), + }; + + furi_message_queue_put(instance->queue, &message, FuriWaitForever); + api_lock_wait_unlock_and_free(message.api_lock); + + return is_connected; +} + void expansion_set_listen_serial(Expansion* instance, FuriHalSerialId serial_id) { furi_check(instance); furi_check(serial_id < FuriHalSerialIdMax); diff --git a/applications/services/expansion/expansion.h b/applications/services/expansion/expansion.h index e169b3c15..1b0879b1e 100644 --- a/applications/services/expansion/expansion.h +++ b/applications/services/expansion/expansion.h @@ -50,6 +50,15 @@ void expansion_enable(Expansion* instance); */ void expansion_disable(Expansion* instance); +/** + * @brief Check if an expansion module is connected. + * + * @param[in,out] instance pointer to the Expansion instance. + * + * @returns true if the module is connected and initialized, false otherwise. + */ +bool expansion_is_connected(Expansion* instance); + /** * @brief Enable support for expansion modules on designated serial port. * diff --git a/applications/services/expansion/expansion_worker.c b/applications/services/expansion/expansion_worker.c index ac2a5935b..e6d17c152 100644 --- a/applications/services/expansion/expansion_worker.c +++ b/applications/services/expansion/expansion_worker.c @@ -35,7 +35,8 @@ typedef enum { ExpansionWorkerFlagError = 1 << 2, } ExpansionWorkerFlag; -#define EXPANSION_ALL_FLAGS (ExpansionWorkerFlagData | ExpansionWorkerFlagStop) +#define EXPANSION_ALL_FLAGS \ + (ExpansionWorkerFlagData | ExpansionWorkerFlagStop | ExpansionWorkerFlagError) struct ExpansionWorker { FuriThread* thread; @@ -225,6 +226,7 @@ static bool expansion_worker_handle_state_handshake( if(furi_hal_serial_is_baud_rate_supported(instance->serial_handle, baud_rate)) { instance->state = ExpansionWorkerStateConnected; + instance->callback(instance->cb_context, ExpansionWorkerCallbackReasonConnected); // Send response at previous baud rate if(!expansion_worker_send_status_response(instance, ExpansionFrameErrorNone)) break; furi_hal_serial_set_br(instance->serial_handle, baud_rate); @@ -360,6 +362,8 @@ static int32_t expansion_worker(void* context) { expansion_worker_state_machine(instance); } + furi_hal_serial_async_rx_stop(instance->serial_handle); + if(instance->state == ExpansionWorkerStateRpcActive) { expansion_worker_rpc_session_close(instance); } @@ -371,7 +375,7 @@ static int32_t expansion_worker(void* context) { // Do not invoke worker callback on user-requested exit if((instance->exit_reason != ExpansionWorkerExitReasonUser) && (instance->callback != NULL)) { - instance->callback(instance->cb_context); + instance->callback(instance->cb_context, ExpansionWorkerCallbackReasonExit); } return 0; diff --git a/applications/services/expansion/expansion_worker.h b/applications/services/expansion/expansion_worker.h index 761f79c1d..faab2887f 100644 --- a/applications/services/expansion/expansion_worker.h +++ b/applications/services/expansion/expansion_worker.h @@ -17,14 +17,20 @@ */ typedef struct ExpansionWorker ExpansionWorker; +typedef enum { + ExpansionWorkerCallbackReasonExit, + ExpansionWorkerCallbackReasonConnected, +} ExpansionWorkerCallbackReason; + /** * @brief Worker callback type. * * @see expansion_worker_set_callback() * * @param[in,out] context pointer to a user-defined object. + * @param[in] reason reason for the callback. */ -typedef void (*ExpansionWorkerCallback)(void* context); +typedef void (*ExpansionWorkerCallback)(void* context, ExpansionWorkerCallbackReason reason); /** * @brief Create an expansion worker instance. diff --git a/targets/f7/furi_hal/furi_hal_serial.c b/targets/f7/furi_hal/furi_hal_serial.c index 8ad9794a8..e5c30a278 100644 --- a/targets/f7/furi_hal/furi_hal_serial.c +++ b/targets/f7/furi_hal/furi_hal_serial.c @@ -817,6 +817,21 @@ static void furi_hal_serial_async_rx_configure( FuriHalSerialHandle* handle, FuriHalSerialAsyncRxCallback callback, void* context) { + // Disable RXFNE interrupts before unsetting the user callback that reads data + // Otherwise interrupt runs without reading data and without clearing RXFNE flag + // This would cause a system hang as the same interrupt runs in loop forever + if(!callback) { + if(handle->id == FuriHalSerialIdUsart) { + LL_USART_DisableIT_RXNE_RXFNE(USART1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); + furi_hal_serial_usart_deinit_dma_rx(); + } else if(handle->id == FuriHalSerialIdLpuart) { + LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); + furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); + furi_hal_serial_lpuart_deinit_dma_rx(); + } + } + // Handle must be configured before enabling RX interrupt // as it might be triggered right away on a misconfigured handle furi_hal_serial[handle->id].rx_byte_callback = callback; @@ -824,27 +839,17 @@ static void furi_hal_serial_async_rx_configure( furi_hal_serial[handle->id].rx_dma_callback = NULL; furi_hal_serial[handle->id].context = context; - if(handle->id == FuriHalSerialIdUsart) { - if(callback) { + if(callback) { + if(handle->id == FuriHalSerialIdUsart) { furi_hal_serial_usart_deinit_dma_rx(); furi_hal_interrupt_set_isr( FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); LL_USART_EnableIT_RXNE_RXFNE(USART1); - } else { - furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); - furi_hal_serial_usart_deinit_dma_rx(); - LL_USART_DisableIT_RXNE_RXFNE(USART1); - } - } else if(handle->id == FuriHalSerialIdLpuart) { - if(callback) { + } else if(handle->id == FuriHalSerialIdLpuart) { furi_hal_serial_lpuart_deinit_dma_rx(); furi_hal_interrupt_set_isr( FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); LL_LPUART_EnableIT_RXNE_RXFNE(LPUART1); - } else { - furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); - furi_hal_serial_lpuart_deinit_dma_rx(); - LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); } } } @@ -944,33 +949,39 @@ static void furi_hal_serial_dma_configure( FuriHalSerialHandle* handle, FuriHalSerialDmaRxCallback callback, void* context) { - furi_check(handle); - - if(handle->id == FuriHalSerialIdUsart) { - if(callback) { - furi_hal_serial_usart_init_dma_rx(); - furi_hal_interrupt_set_isr( - FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); - } else { + // Disable RXFNE interrupts before unsetting the user callback that reads data + // Otherwise interrupt runs without reading data and without clearing RXFNE flag + // This would cause a system hang as the same interrupt runs in loop forever + if(!callback) { + if(handle->id == FuriHalSerialIdUsart) { LL_USART_DisableIT_RXNE_RXFNE(USART1); furi_hal_interrupt_set_isr(FuriHalInterruptIdUart1, NULL, NULL); furi_hal_serial_usart_deinit_dma_rx(); - } - } else if(handle->id == FuriHalSerialIdLpuart) { - if(callback) { - furi_hal_serial_lpuart_init_dma_rx(); - furi_hal_interrupt_set_isr( - FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); - } else { + } else if(handle->id == FuriHalSerialIdLpuart) { LL_LPUART_DisableIT_RXNE_RXFNE(LPUART1); furi_hal_interrupt_set_isr(FuriHalInterruptIdLpUart1, NULL, NULL); furi_hal_serial_lpuart_deinit_dma_rx(); } } + + // Handle must be configured before enabling RX interrupt + // as it might be triggered right away on a misconfigured handle furi_hal_serial[handle->id].rx_byte_callback = NULL; furi_hal_serial[handle->id].handle = handle; furi_hal_serial[handle->id].rx_dma_callback = callback; furi_hal_serial[handle->id].context = context; + + if(callback) { + if(handle->id == FuriHalSerialIdUsart) { + furi_hal_serial_usart_init_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdUart1, furi_hal_serial_usart_irq_callback, NULL); + } else if(handle->id == FuriHalSerialIdLpuart) { + furi_hal_serial_lpuart_init_dma_rx(); + furi_hal_interrupt_set_isr( + FuriHalInterruptIdLpUart1, furi_hal_serial_lpuart_irq_callback, NULL); + } + } } void furi_hal_serial_dma_rx_start( From e003a19edcfc3201c449f7be76c782af3adf9d55 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 5 Jul 2025 17:59:27 +0300 Subject: [PATCH 03/18] add 868 46 mhz to default freq list --- CHANGELOG.md | 2 +- documentation/SubGHzSettings.md | 1 + lib/subghz/subghz_setting.c | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 3e3969f86..591829d8f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ * SubGHz: V2 Phoenix show counter value * SubGHz: Add keeloq ironlogic (aka il100) smart clone cloners support (thanks to Vitaly for RAWs) * SubGHz: Fix CAME 24bit decoder -* SubGHz: Add 462.750 MHz to default subghz freqs list +* SubGHz: Add 462.750 MHz & 868.46 MHz to default subghz freqs list * SubGHz: Tune holtek ht12x to decode holtek only and not conflict with came 12bit * SubGHz: Fix Rename scene bug, that was replacing file name with random name when Rename is opened then closed then opened again * Display: Backlight option "always on" and RGB bug removed (PR #900 | by @Dmitry422) diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md index 95e19bdbf..89f3eba7f 100644 --- a/documentation/SubGHzSettings.md +++ b/documentation/SubGHzSettings.md @@ -68,6 +68,7 @@ if you need your custom one, make sure it doesn't listed here 779000000, 868350000, 868400000, + 868460000, 868800000, 868950000, 906400000, diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 8f41f576c..867459d05 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -74,6 +74,7 @@ static const uint32_t subghz_frequency_list[] = { 779000000, 868350000, 868400000, + 868460000, 868800000, 868950000, 906400000, From 95171046268ee0f2037e221a6cd1a63609fdb409 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 5 Jul 2025 19:14:48 +0300 Subject: [PATCH 04/18] Update doorhan programming instructions by @li0ard --- CHANGELOG.md | 1 + documentation/SubGHzRemoteProg.md | 19 +++++++++++++------ 2 files changed, 14 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 591829d8f..5116f3154 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ - SmartRider parser (by @jaylikesbunda) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes +* Docs: Update doorhan programming instructions (by @li0ard) * FuriHalSerial: Fix RXFNE interrupt hang, aka freezing with UART output when Expansion Modules are enabled (by @WillyJL) * Expansion: add is_connected api (by @HaxSam & @WillyJL) * RFID 125khz: Fix strange bug with LCD backlight going off after doing "Write" diff --git a/documentation/SubGHzRemoteProg.md b/documentation/SubGHzRemoteProg.md index c02c6ee43..18f726941 100644 --- a/documentation/SubGHzRemoteProg.md +++ b/documentation/SubGHzRemoteProg.md @@ -74,13 +74,20 @@ Watch this video to learn more and see how different boards can be programmed (v ## Doorhan With access to the receiver box: -1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote) -2. Open your new remote file -3. Push `P` button for ~2 sec, led will start flashing -4. Press `Send` on your flipper for ~2 seconds -5. Led on the receiver board will flash and turn off -6. Done! +1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote or follow guide below) +- Finding frequency +There are 2 frequencies for DoorHan: 315.00 / 433.92. To determine them it is enough to create a DoorHan remote control with one of the frequencies via Sub-GHz -> Add manually, press the button and watch the receiver's reaction. If you have guessed the frequency, the light bulb will turn on when we press the button on the FZ and turn off when we release it. + +2. Binding the remote control + +Once you have access to the receiver (removed the protective cover), look at the buttons: +- If there are 4 buttons (Radio, Reverse, Auto, ...) then press and hold Radio until the LED lights up, then press the FZ button 2 times and the LED goes out; +- If there are 4 buttons (R, P, +, -) and display, press R, then press 2 times the button on FZ and wait +/- 10 seconds; +- If there are 4 buttons (+, -, F, TR) and display, press TR, then press 2 times the button on FZ and wait +/- 10 seconds; +- In other cases there is a “universal” instruction: Press and hold the button “P” +/- 2 seconds until the LED flashes, then press 2 times the button on the FZ and the LED goes out. + +In all cases it is recommended to wait until the receiver returns to normal mode. With existing remote: 1. Create new remote with randomly generated serial: Go to SubGHz -> Add Manually -> KL: Doorhan 433Mhz or 315Mhz depends on your receiver (find out by reading your existing remote) From 6ae1ce68610376ba5e198896d94588ccc9ca8df1 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 6 Jul 2025 01:36:26 +0300 Subject: [PATCH 05/18] upd api symbols --- targets/f7/api_symbols.csv | 1 + 1 file changed, 1 insertion(+) diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index 73ae7daae..d0cea83e2 100755 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -1071,6 +1071,7 @@ Function,-,exp2f,float,float Function,-,exp2l,long double,long double Function,+,expansion_disable,void,Expansion* Function,+,expansion_enable,void,Expansion* +Function,+,expansion_is_connected,_Bool,Expansion* Function,+,expansion_set_listen_serial,void,"Expansion*, FuriHalSerialId" Function,-,expf,float,float Function,-,expl,long double,long double From 43b35019ed168fd921313f51f1a84a2e939d0d32 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 6 Jul 2025 18:42:16 +0300 Subject: [PATCH 06/18] subghz marantec protocol implement crc verification and add manually fix crc function, add valid/invalid display (does not affect TX) and use new crc sum in add manually menu --- .../main/subghz/helpers/subghz_custom_event.h | 2 ++ .../helpers/subghz_txrx_create_protocol_key.c | 25 ++++++++++++++ .../helpers/subghz_txrx_create_protocol_key.h | 7 ++++ .../subghz/scenes/subghz_scene_set_type.c | 27 +++++++++++++++ lib/subghz/protocols/marantec.c | 34 ++++++++++++++++--- lib/subghz/protocols/marantec.h | 8 +++++ 6 files changed, 99 insertions(+), 4 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index bdd5f849d..a3dae60b8 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -126,6 +126,8 @@ typedef enum { SetTypeHollarm_433, SetTypeReversRB2_433, SetTypeMarantec24_868, + SetTypeMarantec_433, + SetTypeMarantec_868, SetTypeLinear_300_00, // SetTypeNeroSketch, //Deleted in OFW // SetTypeNeroRadio, //Deleted in OFW diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c index 63b892401..813b706b6 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -395,3 +396,27 @@ void subghz_txrx_gen_serial_gangqi(uint64_t* result_key) { // serial | const_and_button *result_key = (serial << 18) | (const_and_button << 10) | (bytesum << 2); } + +void subghz_txrx_gen_key_marantec(uint64_t* result_key) { + uint64_t randkey = (uint64_t)rand(); + uint32_t serial = (uint32_t)((randkey) & 0xFFFFF); + // 0x130 is the constant + // 0x4 is the button code + // 0x86 is the serial constant + // serial is random value that we pre generate above + // At the end we will put the crc sum + uint64_t full_key_no_crc = (uint64_t)((uint64_t)0x130 << 40 | (uint64_t)serial << 20 | + (uint64_t)0x4 << 16 | (uint64_t)0x86 << 8); + + uint8_t tdata[6] = { + full_key_no_crc >> 48, + full_key_no_crc >> 40, + full_key_no_crc >> 32, + full_key_no_crc >> 24, + full_key_no_crc >> 16, + full_key_no_crc >> 8}; + + uint8_t crc = subghz_protocol_marantec_crc8(tdata, sizeof(tdata)); + + *result_key = ((full_key_no_crc >> 8) << 8) | crc; +} diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h index 55932bd39..fba7acb6f 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h @@ -153,3 +153,10 @@ bool subghz_txrx_gen_secplus_v1_protocol( * @return uint64_t if success */ void subghz_txrx_gen_serial_gangqi(uint64_t* result_key); + +/** + * Generate key for Marantec protocol + * + * @param result_key Pointer to a uint64_t where the key will be stored + */ +void subghz_txrx_gen_key_marantec(uint64_t* result_key); diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index f70aec803..134d2e1d1 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -71,6 +71,8 @@ static const char* submenu_names[SetTypeMAX] = { [SetTypeHollarm_433] = "Hollarm 433MHz", [SetTypeReversRB2_433] = "Revers RB2 433MHz", [SetTypeMarantec24_868] = "Marantec24 868MHz", + [SetTypeMarantec_433] = "Marantec 433MHz", + [SetTypeMarantec_868] = "Marantec 868MHz", [SetTypeBETT_433] = "BETT 433MHz", [SetTypeLinear_300_00] = "Linear 300MHz", // [SetTypeNeroSketch] = "Nero Sketch", // Deleted in OFW @@ -192,6 +194,9 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { uint64_t gangqi_key; subghz_txrx_gen_serial_gangqi(&gangqi_key); + uint64_t marantec_key; + subghz_txrx_gen_key_marantec(&marantec_key); + GenInfo gen_info = {0}; switch(event.event) { case SetTypePricenton433: @@ -360,6 +365,28 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { .data.bits = 24, .data.te = 0}; break; + case SetTypeMarantec_433: + gen_info = (GenInfo){ + .type = GenData, + .mod = "AM650", + .freq = 433920000, + .data.name = + SUBGHZ_PROTOCOL_MARANTEC_NAME, // Button code is 0x4 and crc sum to the end + .data.key = marantec_key, + .data.bits = 49, + .data.te = 0}; + break; + case SetTypeMarantec_868: + gen_info = (GenInfo){ + .type = GenData, + .mod = "AM650", + .freq = 868350000, + .data.name = + SUBGHZ_PROTOCOL_MARANTEC_NAME, // Button code is 0x4 and crc sum to the end + .data.key = marantec_key, + .data.bits = 49, + .data.te = 0}; + break; case SetTypeFaacSLH_433: gen_info = (GenInfo){ .type = GenFaacSLH, diff --git a/lib/subghz/protocols/marantec.c b/lib/subghz/protocols/marantec.c index fc4aa0dca..edb176635 100644 --- a/lib/subghz/protocols/marantec.c +++ b/lib/subghz/protocols/marantec.c @@ -165,7 +165,7 @@ static void subghz_protocol_encoder_marantec_get_upload(SubGhzProtocolEncoderMar } uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) { - uint8_t crc = 0x08; + uint8_t crc = 0x01; size_t i, j; for(i = 0; i < len; i++) { crc ^= data[i]; @@ -184,6 +184,18 @@ uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len) { * @param instance Pointer to a SubGhzBlockGeneric* instance */ static void subghz_protocol_marantec_remote_controller(SubGhzBlockGeneric* instance) { + // Key samples + // 1307EDF6486C5 = 000 100110000 01111110110111110110 0100 10000110 11000101 + // 1303EFAFD8683 = 000 100110000 00111110111110101111 1101 10000110 10000011 + + // From unittests + // 1300710DF869F + + // const serial button serial crc + // 130 7EDF6 4 86 C5 + // 130 3EFAF D 86 83 + // 130 0710D F 86 9F + instance->btn = (instance->data >> 16) & 0xF; instance->serial = ((instance->data >> 12) & 0xFFFFFF00) | ((instance->data >> 8) & 0xFF); } @@ -367,16 +379,30 @@ void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* outp SubGhzProtocolDecoderMarantec* instance = context; subghz_protocol_marantec_remote_controller(&instance->generic); + uint8_t tdata[6] = { + instance->generic.data >> 48, + instance->generic.data >> 40, + instance->generic.data >> 32, + instance->generic.data >> 24, + instance->generic.data >> 16, + instance->generic.data >> 8}; + + uint8_t crc = subghz_protocol_marantec_crc8(tdata, sizeof(tdata)); + bool crc_ok = (crc == (instance->generic.data & 0xFF)); + furi_string_cat_printf( output, "%s %db\r\n" - "Key:0x%lX%08lX\r\n" - "Sn:0x%07lX \r\n" - "Btn:%X\r\n", + "Key: 0x%lX%08lX\r\n" + "Sn: 0x%07lX \r\n" + "CRC: 0x%02X - %s\r\n" + "Btn: %X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32), (uint32_t)(instance->generic.data & 0xFFFFFFFF), instance->generic.serial, + crc, + crc_ok ? "Valid" : "Invalid", instance->generic.btn); } diff --git a/lib/subghz/protocols/marantec.h b/lib/subghz/protocols/marantec.h index 485c563b2..9a7b55b7e 100644 --- a/lib/subghz/protocols/marantec.h +++ b/lib/subghz/protocols/marantec.h @@ -107,3 +107,11 @@ SubGhzProtocolStatus * @param output Resulting text */ void subghz_protocol_decoder_marantec_get_string(void* context, FuriString* output); + +/** + * Calculate CRC8 for Marantec protocol. + * @param data Pointer to the data buffer + * @param len Length of the data buffer + * @return CRC8 value + */ +uint8_t subghz_protocol_marantec_crc8(uint8_t* data, size_t len); From e025c5742550b2a6609a3ee3a536c235e67c0835 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sun, 6 Jul 2025 18:47:12 +0300 Subject: [PATCH 07/18] Update marantec24 protocol info --- lib/subghz/protocols/marantec24.c | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/subghz/protocols/marantec24.c b/lib/subghz/protocols/marantec24.c index 588aa1e5a..ac602fc96 100644 --- a/lib/subghz/protocols/marantec24.c +++ b/lib/subghz/protocols/marantec24.c @@ -217,6 +217,11 @@ void subghz_protocol_decoder_marantec24_feed(void* context, bool level, volatile // Marantec24 Decoder // 2024 - @xMasterX (MMX) + // 2025 update - The protocol is not real marantec, + // it comes from chinese remote that pretends to be replica of original marantec, actually it was a cloner + // which had some thing written on it, which is uknown, but since its pretentding to be marantec, + // it was decided to keep the name of the protocol as marantec24 (24 bits) + // Key samples // 101011000000010111001000 = AC05C8 // 101011000000010111000100 = AC05C4 @@ -266,16 +271,12 @@ void subghz_protocol_decoder_marantec24_feed(void* context, bool level, volatile //Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes) if((DURATION_DIFF( instance->decoder.te_last, subghz_protocol_marantec24_const.te_long) < - subghz_protocol_marantec24_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) < - subghz_protocol_marantec24_const.te_delta * 4)) { + subghz_protocol_marantec24_const.te_delta)) { subghz_protocol_blocks_add_bit(&instance->decoder, 0); } if((DURATION_DIFF( instance->decoder.te_last, subghz_protocol_marantec24_const.te_short) < - subghz_protocol_marantec24_const.te_delta) && - (DURATION_DIFF(duration, subghz_protocol_marantec24_const.te_long * 9) < - subghz_protocol_marantec24_const.te_delta * 4)) { + subghz_protocol_marantec24_const.te_delta)) { subghz_protocol_blocks_add_bit(&instance->decoder, 1); } // If got 24 bits key reading is finished From a547c946abbc3fcfae95c4aa2fe28d2d59d461a9 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 7 Jul 2025 04:16:58 +0300 Subject: [PATCH 08/18] reduce less popular freqs in default hopper preset, make it faster also add 303 mhz freq to default list --- documentation/SubGHzSettings.md | 5 ++--- lib/subghz/subghz_setting.c | 5 ++--- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/documentation/SubGHzSettings.md b/documentation/SubGHzSettings.md index 89f3eba7f..682111bcd 100644 --- a/documentation/SubGHzSettings.md +++ b/documentation/SubGHzSettings.md @@ -12,6 +12,7 @@ if you need your custom one, make sure it doesn't listed here /* 300 - 348 */ 300000000, 302757000, + 303000000, 303875000, 303900000, 304250000, @@ -100,10 +101,8 @@ Your frequencies will be added after default ones ### Default hopper list ``` - 310000000, 315000000, - 318000000, - 418000000, 433920000, + 434420000, 868350000, ``` diff --git a/lib/subghz/subghz_setting.c b/lib/subghz/subghz_setting.c index 867459d05..49a0af58d 100644 --- a/lib/subghz/subghz_setting.c +++ b/lib/subghz/subghz_setting.c @@ -18,6 +18,7 @@ static const uint32_t subghz_frequency_list[] = { /* 300 - 348 */ 300000000, 302757000, + 303000000, 303875000, 303900000, 304250000, @@ -85,11 +86,9 @@ static const uint32_t subghz_frequency_list[] = { }; static const uint32_t subghz_hopper_frequency_list[] = { - 310000000, 315000000, - 318000000, - 418000000, 433920000, + 434420000, 868350000, 0, }; From 74f6ee1e7ceac5d2405cb00c64928d4c55296c03 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Mon, 7 Jul 2025 17:55:14 +0300 Subject: [PATCH 09/18] badusb fix modifier keys with HOLD/RELEASE commands by WillyJL --- CHANGELOG.md | 1 + applications/main/bad_usb/helpers/ducky_script.c | 13 ++++++++++--- .../main/bad_usb/helpers/ducky_script_commands.c | 4 ++-- applications/main/bad_usb/helpers/ducky_script_i.h | 4 +++- .../main/bad_usb/helpers/ducky_script_keycodes.c | 12 ++++++++++++ 5 files changed, 28 insertions(+), 6 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5116f3154..beab8f799 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -36,6 +36,7 @@ - SmartRider parser (by @jaylikesbunda) * Apps: **Check out more Apps updates and fixes by following** [this link](https://github.com/xMasterX/all-the-plugins/commits/dev) ## Other changes +* BadUSB: Fix modifier keys with HOLD/RELEASE commands (by @WillyJL) * Docs: Update doorhan programming instructions (by @li0ard) * FuriHalSerial: Fix RXFNE interrupt hang, aka freezing with UART output when Expansion Modules are enabled (by @WillyJL) * Expansion: add is_connected api (by @HaxSam & @WillyJL) diff --git a/applications/main/bad_usb/helpers/ducky_script.c b/applications/main/bad_usb/helpers/ducky_script.c index 43621de78..4b427b759 100644 --- a/applications/main/bad_usb/helpers/ducky_script.c +++ b/applications/main/bad_usb/helpers/ducky_script.c @@ -50,13 +50,20 @@ bool ducky_is_line_end(const char chr) { return (chr == ' ') || (chr == '\0') || (chr == '\r') || (chr == '\n'); } -uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars) { +uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_modifiers) { uint16_t keycode = ducky_get_keycode_by_name(param); if(keycode != HID_KEYBOARD_NONE) { return keycode; } - if((accept_chars) && (strlen(param) > 0)) { + if(accept_modifiers) { + uint16_t keycode = ducky_get_modifier_keycode_by_name(param); + if(keycode != HID_KEYBOARD_NONE) { + return keycode; + } + } + + if(strlen(param) > 0) { return BADUSB_ASCII_TO_KEY(bad_usb, param[0]) & 0xFF; } return 0; @@ -213,7 +220,7 @@ static int32_t ducky_parse_line(BadUsbScript* bad_usb, FuriString* line) { // Main key char next_char = *line_cstr; - key = modifiers | ducky_get_keycode(bad_usb, line_cstr, true); + key = modifiers | ducky_get_keycode(bad_usb, line_cstr, false); if(key == 0 && next_char) ducky_error(bad_usb, "No keycode defined for %s", line_cstr); diff --git a/applications/main/bad_usb/helpers/ducky_script_commands.c b/applications/main/bad_usb/helpers/ducky_script_commands.c index 6c6fe36c7..dbf9c1aef 100644 --- a/applications/main/bad_usb/helpers/ducky_script_commands.c +++ b/applications/main/bad_usb/helpers/ducky_script_commands.c @@ -92,7 +92,7 @@ static int32_t ducky_fnc_sysrq(BadUsbScript* bad_usb, const char* line, int32_t UNUSED(param); line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_usb, line, true); + uint16_t key = ducky_get_keycode(bad_usb, line, false); bad_usb->hid->kb_press(bad_usb->hid_inst, KEY_MOD_LEFT_ALT | HID_KEYBOARD_PRINT_SCREEN); bad_usb->hid->kb_press(bad_usb->hid_inst, key); bad_usb->hid->release_all(bad_usb->hid_inst); @@ -196,7 +196,7 @@ static int32_t ducky_fnc_globe(BadUsbScript* bad_usb, const char* line, int32_t UNUSED(param); line = &line[ducky_get_command_len(line) + 1]; - uint16_t key = ducky_get_keycode(bad_usb, line, true); + uint16_t key = ducky_get_keycode(bad_usb, line, false); if(key == HID_KEYBOARD_NONE) { return ducky_error(bad_usb, "No keycode defined for %s", line); } diff --git a/applications/main/bad_usb/helpers/ducky_script_i.h b/applications/main/bad_usb/helpers/ducky_script_i.h index d735a8407..e5e0d645c 100644 --- a/applications/main/bad_usb/helpers/ducky_script_i.h +++ b/applications/main/bad_usb/helpers/ducky_script_i.h @@ -50,7 +50,7 @@ struct BadUsbScript { size_t string_print_pos; }; -uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_chars); +uint16_t ducky_get_keycode(BadUsbScript* bad_usb, const char* param, bool accept_modifiers); uint32_t ducky_get_command_len(const char* line); @@ -58,6 +58,8 @@ bool ducky_is_line_end(const char chr); uint16_t ducky_get_next_modifier_keycode_by_name(const char** param); +uint16_t ducky_get_modifier_keycode_by_name(const char* param); + uint16_t ducky_get_keycode_by_name(const char* param); uint16_t ducky_get_media_keycode_by_name(const char* param); diff --git a/applications/main/bad_usb/helpers/ducky_script_keycodes.c b/applications/main/bad_usb/helpers/ducky_script_keycodes.c index ce957bb4e..77cc324d3 100644 --- a/applications/main/bad_usb/helpers/ducky_script_keycodes.c +++ b/applications/main/bad_usb/helpers/ducky_script_keycodes.c @@ -131,6 +131,18 @@ uint16_t ducky_get_next_modifier_keycode_by_name(const char** param) { return HID_KEYBOARD_NONE; } +uint16_t ducky_get_modifier_keycode_by_name(const char* param) { + for(size_t i = 0; i < COUNT_OF(ducky_modifier_keys); i++) { + size_t key_cmd_len = strlen(ducky_modifier_keys[i].name); + if((strncmp(param, ducky_modifier_keys[i].name, key_cmd_len) == 0) && + (ducky_is_line_end(param[key_cmd_len]))) { + return ducky_modifier_keys[i].keycode; + } + } + + return HID_KEYBOARD_NONE; +} + uint16_t ducky_get_keycode_by_name(const char* param) { for(size_t i = 0; i < COUNT_OF(ducky_keys); i++) { size_t key_cmd_len = strlen(ducky_keys[i].name); From 8ef9a07608949513e4c30fb990da4b3195a8f89b Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 9 Jul 2025 04:54:34 +0300 Subject: [PATCH 10/18] Subghz V2 Phoenix fully supported now With big thanks to all contributors 2022.08 - @Skorpionm 2025.07 - @xMasterX & @RocketGod-git --- .../main/subghz/helpers/subghz_custom_event.h | 1 + .../helpers/subghz_txrx_create_protocol_key.c | 28 ++ .../helpers/subghz_txrx_create_protocol_key.h | 7 + .../subghz/scenes/subghz_scene_set_type.c | 30 +- lib/subghz/protocols/phoenix_v2.c | 280 +++++++++++++++++- lib/subghz/protocols/public_api.h | 16 + targets/f7/api_symbols.csv | 1 + 7 files changed, 352 insertions(+), 11 deletions(-) diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index a3dae60b8..e971bd056 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -74,6 +74,7 @@ typedef enum { SetTypeSomfyTelis, SetTypeANMotorsAT4, SetTypeAlutechAT4N, + SetTypePhoenix_V2_433, SetTypeHCS101_433_92, SetTypeDoorHan_315_00, SetTypeDoorHan_433_92, diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c index 813b706b6..5a7e07e0e 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.c @@ -384,6 +384,34 @@ bool subghz_txrx_gen_secplus_v1_protocol( return ret; } +bool subghz_txrx_gen_phoenix_v2_protocol( + void* context, + const char* preset_name, + uint32_t frequency, + uint32_t serial, + uint16_t cnt) { + SubGhzTxRx* txrx = context; + + bool res = false; + + txrx->transmitter = + subghz_transmitter_alloc_init(txrx->environment, SUBGHZ_PROTOCOL_PHOENIX_V2_NAME); + subghz_txrx_set_preset(txrx, preset_name, frequency, NULL, 0); + + if(txrx->transmitter && subghz_protocol_phoenix_v2_create_data( + subghz_transmitter_get_protocol_instance(txrx->transmitter), + txrx->fff_data, + serial, + cnt, + txrx->preset)) { + res = true; + } + + subghz_transmitter_free(txrx->transmitter); + + return res; +} + void subghz_txrx_gen_serial_gangqi(uint64_t* result_key) { uint64_t randkey = (uint64_t)rand(); uint16_t serial = (uint16_t)((randkey) & 0xFFFF); diff --git a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h index fba7acb6f..7daa61b31 100644 --- a/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h +++ b/applications/main/subghz/helpers/subghz_txrx_create_protocol_key.h @@ -115,6 +115,13 @@ bool subghz_txrx_gen_came_atomo_protocol( uint32_t serial, uint16_t cnt); +bool subghz_txrx_gen_phoenix_v2_protocol( + void* context, + const char* preset_name, + uint32_t frequency, + uint32_t serial, + uint16_t cnt); + /** * Generate data SecPlus v2 protocol * diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 134d2e1d1..3f2fd9eff 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -20,6 +20,7 @@ static const char* submenu_names[SetTypeMAX] = { [SetTypeSomfyTelis] = "Somfy Telis 433MHz", [SetTypeANMotorsAT4] = "AN-Motors AT4 433MHz", [SetTypeAlutechAT4N] = "Alutech AT4N 433MHz", + [SetTypePhoenix_V2_433] = "V2 Phoenix 433MHz", [SetTypeHCS101_433_92] = "KL: HCS101 433MHz", [SetTypeDoorHan_315_00] = "KL: DoorHan 315MHz", [SetTypeDoorHan_433_92] = "KL: DoorHan 433MHz", @@ -112,6 +113,7 @@ typedef enum { GenNiceFlorS, GenSecPlus1, GenSecPlus2, + GenPhoenixV2, } GenType; typedef struct { @@ -170,6 +172,10 @@ typedef struct { uint8_t btn; uint32_t cnt; } sec_plus_2; + struct { + uint32_t serial; + uint16_t cnt; + } phoenix_v2; }; } GenInfo; @@ -634,16 +640,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { .type = GenCameAtomo, .mod = "AM650", .freq = 433920000, - .keeloq.serial = (key & 0x0FFFFFFF) | 0x10000000, - .keeloq.cnt = 0x03}; + .came_atomo.serial = (key & 0x0FFFFFFF) | 0x10000000, + .came_atomo.cnt = 0x03}; break; case SetTypeCameAtomo868: gen_info = (GenInfo){ .type = GenCameAtomo, .mod = "AM650", .freq = 868350000, - .keeloq.serial = (key & 0x0FFFFFFF) | 0x10000000, - .keeloq.cnt = 0x03}; + .came_atomo.serial = (key & 0x0FFFFFFF) | 0x10000000, + .came_atomo.cnt = 0x03}; break; case SetTypeBFTMitto: gen_info = (GenInfo){ @@ -869,6 +875,14 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { .sec_plus_2.btn = 0x68, .sec_plus_2.cnt = 0xE500000}; break; + case SetTypePhoenix_V2_433: + gen_info = (GenInfo){ + .type = GenPhoenixV2, + .mod = "AM650", + .freq = 433920000, + .phoenix_v2.serial = (key & 0x0FFFFFFF) | 0xB0000000, + .phoenix_v2.cnt = 0x025D}; + break; default: furi_crash("Not implemented"); break; @@ -976,6 +990,14 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { gen_info.sec_plus_2.btn, gen_info.sec_plus_2.cnt); break; + case GenPhoenixV2: + generated_protocol = subghz_txrx_gen_phoenix_v2_protocol( + subghz->txrx, + gen_info.mod, + gen_info.freq, + gen_info.phoenix_v2.serial, + gen_info.phoenix_v2.cnt); + break; default: furi_crash("Not implemented"); break; diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c index 9e88324c4..0229aeaeb 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -6,9 +6,9 @@ #include "../blocks/generic.h" #include "../blocks/math.h" -#define TAG "SubGhzProtocolPhoenixV2" +#include "../blocks/custom_btn_i.h" -//transmission only static mode +#define TAG "SubGhzProtocolPhoenixV2" static const SubGhzBlockConst subghz_protocol_phoenix_v2_const = { .te_short = 427, @@ -62,7 +62,7 @@ const SubGhzProtocolEncoder subghz_protocol_phoenix_v2_encoder = { const SubGhzProtocol subghz_protocol_phoenix_v2 = { .name = SUBGHZ_PROTOCOL_PHOENIX_V2_NAME, - .type = SubGhzProtocolTypeStatic, + .type = SubGhzProtocolTypeDynamic, .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send, @@ -91,6 +91,138 @@ void subghz_protocol_encoder_phoenix_v2_free(void* context) { free(instance); } +// Pre define functions +static uint16_t subghz_protocol_phoenix_v2_encrypt_counter(uint64_t full_key, uint16_t counter); +static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance); + +bool subghz_protocol_phoenix_v2_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint16_t cnt, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolEncoderPhoenix_V2* instance = context; + instance->generic.btn = 0x1; + instance->generic.serial = serial; + instance->generic.cnt = cnt; + instance->generic.data_count_bit = 52; + + uint64_t local_data_rev = + (uint64_t)(((uint64_t)instance->generic.cnt << 40) | + ((uint64_t)instance->generic.btn << 32) | (uint64_t)instance->generic.serial); + + uint16_t encrypted_counter = (uint16_t)subghz_protocol_phoenix_v2_encrypt_counter( + local_data_rev, instance->generic.cnt); + + instance->generic.data = subghz_protocol_blocks_reverse_key( + (uint64_t)(((uint64_t)encrypted_counter << 40) | ((uint64_t)instance->generic.btn << 32) | + (uint64_t)instance->generic.serial), + instance->generic.data_count_bit + 4); + + return SubGhzProtocolStatusOk == + subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +// Get custom button code +static uint8_t subghz_protocol_phoenix_v2_get_btn_code(void) { + uint8_t custom_btn_id = subghz_custom_btn_get(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); + uint8_t btn = original_btn_code; + + // Set custom button + if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) { + // Restore original button code + btn = original_btn_code; + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) { + switch(original_btn_code) { + case 0x1: + btn = 0x2; + break; + case 0x2: + btn = 0x1; + break; + case 0x4: + btn = 0x1; + break; + case 0x8: + btn = 0x1; + break; + case 0x3: + btn = 0x1; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0x4: + btn = 0x2; + break; + case 0x8: + btn = 0x4; + break; + case 0x3: + btn = 0x4; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0x1: + btn = 0x8; + break; + case 0x2: + btn = 0x8; + break; + case 0x4: + btn = 0x8; + break; + case 0x8: + btn = 0x2; + break; + case 0x3: + btn = 0x8; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_RIGHT) { + switch(original_btn_code) { + case 0x1: + btn = 0x3; + break; + case 0x2: + btn = 0x3; + break; + case 0x4: + btn = 0x3; + break; + case 0x8: + btn = 0x3; + break; + case 0x3: + btn = 0x2; + break; + + default: + break; + } + } + + return btn; +} + /** * Generating an upload from data. * @param instance Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance @@ -107,6 +239,40 @@ static bool } else { instance->encoder.size_upload = size_upload; } + + uint8_t btn = instance->generic.btn; + + // Save original button for later use + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(btn); + } + + // Get custom button code + // This will override the btn variable if a custom button is set + btn = subghz_protocol_phoenix_v2_get_btn_code(); + + // Reconstruction of the data + if(instance->generic.cnt < 0xFFFF) { + if((instance->generic.cnt + furi_hal_subghz_get_rolling_counter_mult()) > 0xFFFF) { + instance->generic.cnt = 0; + } else { + instance->generic.cnt += furi_hal_subghz_get_rolling_counter_mult(); + } + } else if((instance->generic.cnt >= 0xFFFF) && (furi_hal_subghz_get_rolling_counter_mult() != 0)) { + instance->generic.cnt = 0; + } + + uint64_t local_data_rev = subghz_protocol_blocks_reverse_key( + instance->generic.data, instance->generic.data_count_bit + 4); + + uint16_t encrypted_counter = (uint16_t)subghz_protocol_phoenix_v2_encrypt_counter( + local_data_rev, instance->generic.cnt); + + instance->generic.data = subghz_protocol_blocks_reverse_key( + (uint64_t)(((uint64_t)encrypted_counter << 40) | ((uint64_t)btn << 32) | + (uint64_t)instance->generic.serial), + instance->generic.data_count_bit + 4); + //Send header instance->encoder.upload[index++] = level_duration_make(false, (uint32_t)subghz_protocol_phoenix_v2_const.te_short * 60); @@ -149,10 +315,22 @@ SubGhzProtocolStatus flipper_format_read_uint32( flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic); + if(!subghz_protocol_encoder_phoenix_v2_get_upload(instance)) { ret = SubGhzProtocolStatusErrorEncoderGetUpload; break; } + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + instance->encoder.is_running = true; } while(false); @@ -274,16 +452,103 @@ void subghz_protocol_decoder_phoenix_v2_feed(void* context, bool level, uint32_t } } +static uint16_t subghz_protocol_phoenix_v2_encrypt_counter(uint64_t full_key, uint16_t counter) { + uint8_t xor_key1 = (uint8_t)(full_key >> 24); // First byte of serial + uint8_t xor_key2 = (uint8_t)((full_key >> 16) & 0xFF); // Second byte of serial + + uint8_t byte2 = (uint8_t)(counter >> 8); // First counter byte + uint8_t byte1 = (uint8_t)(counter & 0xFF); // Second counter byte + + // See decrypt function before reading these comments + for(int i = 0; i < 16; i++) { + // The key to reversing the process is that the MSB of the *current* byte2 + // tells us what the MSB of the *previous* byte1 was. This allows us to + // determine if the conditional XOR was applied before?. + uint8_t msb_of_prev_byte1 = byte2 & 0x80; + + if(msb_of_prev_byte1 == 0) { + // reverse the XOR. + byte2 ^= xor_key2; + byte1 ^= xor_key1; + } + + // Perform the bit shuffle in reverse + // Store the least significant bit (LSB) of the current byte1. + uint8_t lsb_of_current_byte1 = byte1 & 1; + + byte2 = (byte2 << 1) | lsb_of_current_byte1; + byte1 = (byte1 >> 1) | msb_of_prev_byte1; + } + + return (uint16_t)byte1 << 8 | byte2; +} + +static uint16_t subghz_protocol_phoenix_v2_decrypt_counter(uint64_t full_key) { + uint16_t encrypted_value = (uint16_t)((full_key >> 40) & 0xFFFF); + + uint8_t byte1 = (uint8_t)(encrypted_value >> 8); // First encrypted counter byte + uint8_t byte2 = (uint8_t)(encrypted_value & 0xFF); // Second encrypted counter byte + + uint8_t xor_key1 = (uint8_t)(full_key >> 24); // First byte of serial + uint8_t xor_key2 = (uint8_t)((full_key >> 16) & 0xFF); // Second byte of serial + + for(int i = 0; i < 16; i++) { + // Store the most significant bit (MSB) of byte1. + // The check `(msb_of_byte1 == 0)` will determine if we apply the XOR keys. + uint8_t msb_of_byte1 = byte1 & 0x80; + + // Store the least significant bit (LSB) of byte2. + uint8_t lsb_of_byte2 = byte2 & 1; + + // Perform a bit shuffle between the two bytes + byte2 = (byte2 >> 1) | msb_of_byte1; + byte1 = (byte1 << 1) | lsb_of_byte2; + + // Conditionally apply the XOR keys based on the original MSB of byte1. + if(msb_of_byte1 == 0) { + byte1 ^= xor_key1; + // The mask `& 0x7F` clears the MSB of byte2 after the XOR. + byte2 = (byte2 ^ xor_key2) & 0x7F; + } + } + + return (uint16_t)byte2 << 8 | byte1; +} + /** * Analysis of received data * @param instance Pointer to a SubGhzBlockGeneric* instance */ static void subghz_protocol_phoenix_v2_check_remote_controller(SubGhzBlockGeneric* instance) { + // 2022.08 - @Skorpionm + // 2025.07 - @xMasterX & @RocketGod-git + // Fully supported now, with button switch and add manually + // + // Key samples + // Full key example: 0xC63E01B9615720 - after subghz_protocol_blocks_reverse_key was applied + // Serial - B9615720 + // Button - 01 + // Encrypted -> Decrypted counters + // C63E - 025C + // BCC1 - 025D + // 3341 - 025E + // 49BE - 025F + // 99D3 - 0260 + // E32C - 0261 + uint64_t data_rev = subghz_protocol_blocks_reverse_key(instance->data, instance->data_count_bit + 4); + instance->serial = data_rev & 0xFFFFFFFF; - instance->cnt = (data_rev >> 40) & 0xFFFF; + instance->cnt = subghz_protocol_phoenix_v2_decrypt_counter(data_rev); instance->btn = (data_rev >> 32) & 0xF; + // encrypted cnt is (data_rev >> 40) & 0xFFFF + + // Save original button for later use + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->btn); + } + subghz_custom_btn_set_max(4); } uint8_t subghz_protocol_decoder_phoenix_v2_get_hash_data(void* context) { @@ -321,12 +586,13 @@ void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* ou "%s %dbit\r\n" "Key:%05lX%08lX\r\n" "Sn:0x%07lX \r\n" - "Btn:%X Cnt: 0x%04lX\r\n", + "Cnt: 0x%04lX\r\n" + "Btn: %X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, (uint32_t)(instance->generic.data & 0xFFFFFFFF), instance->generic.serial, - instance->generic.btn, - instance->generic.cnt); + instance->generic.cnt, + instance->generic.btn); } diff --git a/lib/subghz/protocols/public_api.h b/lib/subghz/protocols/public_api.h index d7ae21c2a..39c08e6aa 100644 --- a/lib/subghz/protocols/public_api.h +++ b/lib/subghz/protocols/public_api.h @@ -123,6 +123,22 @@ bool subghz_protocol_came_atomo_create_data( uint16_t cnt, SubGhzRadioPreset* preset); +/** + * Key generation from simple data. + * @param context Pointer to a SubGhzProtocolEncoderPhoenix_V2 instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param serial Serial number + * @param cnt Counter value, 16 bit + * @param preset Modulation, SubGhzRadioPreset + * @return true On success + */ +bool subghz_protocol_phoenix_v2_create_data( + void* context, + FlipperFormat* flipper_format, + uint32_t serial, + uint16_t cnt, + SubGhzRadioPreset* preset); + /** * New remote generation. * @param context Pointer to a SubGhzProtocolEncoderNiceFlorS instance diff --git a/targets/f7/api_symbols.csv b/targets/f7/api_symbols.csv index d0cea83e2..7696a05bc 100755 --- a/targets/f7/api_symbols.csv +++ b/targets/f7/api_symbols.csv @@ -3621,6 +3621,7 @@ Function,+,subghz_protocol_faac_slh_create_data,_Bool,"void*, FlipperFormat*, ui Function,+,subghz_protocol_keeloq_bft_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, uint32_t, const char*, SubGhzRadioPreset*" Function,+,subghz_protocol_keeloq_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, const char*, SubGhzRadioPreset*" Function,+,subghz_protocol_nice_flor_s_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint8_t, uint16_t, SubGhzRadioPreset*, _Bool" +Function,+,subghz_protocol_phoenix_v2_create_data,_Bool,"void*, FlipperFormat*, uint32_t, uint16_t, SubGhzRadioPreset*" Function,+,subghz_protocol_raw_file_encoder_worker_set_callback_end,void,"SubGhzProtocolEncoderRAW*, SubGhzProtocolEncoderRAWCallbackEnd, void*" Function,+,subghz_protocol_raw_gen_fff_data,void,"FlipperFormat*, const char*, const char*" Function,+,subghz_protocol_raw_get_sample_write,size_t,SubGhzProtocolDecoderRAW* From 06b29ddc1bedae78ab8605c66838b3ca068691b9 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 9 Jul 2025 04:59:01 +0300 Subject: [PATCH 11/18] upd changelog --- CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index beab8f799..8e9eff410 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,9 @@ ## Main changes - Current API: 86.0 +* SubGHz: V2 Phoenix full support (button switch, add manually, counter decrypt/encrypt) (by @xMasterX & @RocketGod-git, original code by @Skorpionm) +* SubGHz: Reduce less popular freqs in default hopper preset, make it faster +* SubGHz: Marantec protocol implement crc verification display and add manually support (by @xMasterX & @li0ard, original code by @Skorpionm) +* SubGHz: Keeloq: Comunello - add manually support * iButton: TM01x Dallas write support (PR #899 | by @Leptopt1los) * SubGHz: Rename and extend Alarms, Sensors, Cars ignore options (Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights)) * SubGHz: V2 Phoenix show counter value From 30621b2fd7367115e868a31f7f9ed70448170d35 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 9 Jul 2025 05:47:38 +0300 Subject: [PATCH 12/18] Update keeloq keys, motorline add manually support, readme spoiler alert Add keeloq keys: by @xMasterX & @RocketGod-git --- CHANGELOG.md | 22 +-- ReadMe.md | 12 +- .../main/subghz/helpers/subghz_custom_event.h | 1 + .../resources/subghz/assets/keeloq_mfcodes | 126 +++++++++--------- .../subghz/scenes/subghz_scene_set_type.c | 11 ++ lib/subghz/protocols/keeloq.c | 13 +- 6 files changed, 109 insertions(+), 76 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 8e9eff410..feb903787 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,23 +1,25 @@ ## Main changes - Current API: 86.0 -* SubGHz: V2 Phoenix full support (button switch, add manually, counter decrypt/encrypt) (by @xMasterX & @RocketGod-git, original code by @Skorpionm) -* SubGHz: Reduce less popular freqs in default hopper preset, make it faster -* SubGHz: Marantec protocol implement crc verification display and add manually support (by @xMasterX & @li0ard, original code by @Skorpionm) -* SubGHz: Keeloq: Comunello - add manually support -* iButton: TM01x Dallas write support (PR #899 | by @Leptopt1los) -* SubGHz: Rename and extend Alarms, Sensors, Cars ignore options (Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights)) +* SubGHz: **Roger (static 28 bit) with add manually support** (by @xMasterX & @mishamyte) +* SubGHz: **V2 Phoenix full support** (button switch, add manually, counter decrypt/encrypt) (by @xMasterX & @RocketGod-git, original code by @Skorpionm) +* SubGHz: **Keeloq: Add support for - Motorline (with add manually support), Rosh, Pecinin, Rossi, Merlin, Steelmate** (by @xMasterX & @RocketGod-git) +* SubGHz: Reduce less popular freqs in default hopper preset, **make it faster** +* SubGHz: **Marantec protocol implement crc verification display and add manually support** (by @xMasterX & @li0ard, original code by @Skorpionm) +* SubGHz: **Keeloq: Comunello - add manually support** +* iButton: **TM01x Dallas write support** (PR #899 | by @Leptopt1los) +* SubGHz: Rename and **extend Alarms, Sensors, Cars ignore options** (Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights)) * SubGHz: V2 Phoenix show counter value -* SubGHz: Add keeloq ironlogic (aka il100) smart clone cloners support (thanks to Vitaly for RAWs) -* SubGHz: Fix CAME 24bit decoder +* SubGHz: **Add keeloq ironlogic (aka il100) smart clone cloners support** (thanks to Vitaly for RAWs) +* SubGHz: **Fix CAME 24bit decoder** * SubGHz: Add 462.750 MHz & 868.46 MHz to default subghz freqs list -* SubGHz: Tune holtek ht12x to decode holtek only and not conflict with came 12bit +* SubGHz: **Tune holtek ht12x to decode holtek only** and not conflict with came 12bit * SubGHz: Fix Rename scene bug, that was replacing file name with random name when Rename is opened then closed then opened again * Display: Backlight option "always on" and RGB bug removed (PR #900 | by @Dmitry422) * NFC: Ultralight C - Attempt of authentication with default key (PR #898 | by @mishamyte) * System: Loader - Fix misplaced ApplicationBeforeLoad events (PR #905 | by @WillyJL) * OFW PR 4210: Infrared: Add text scroll to remote buttons (by @956MB) * NFC: - - NFC Type 4 support + many other improvements (by @WillyJL) + - **NFC Type 4 support + many other improvements** (by @WillyJL) - New Type 4 Tag (NDEF on NTAG4xx / MIFARE DESFire) protocol, full support - New NTAG4xx (NTAG413 DNA / NTAG424 DNA) protocol, only detection and basic info support - NDEF parsing plugin supports Type 4 Tag protocol diff --git a/ReadMe.md b/ReadMe.md index 395222fff..eaa221ea7 100644 --- a/ReadMe.md +++ b/ReadMe.md @@ -127,7 +127,7 @@ Before getting started: > - Battery percentage display with different styles `Settings -> Desktop -> Battery View` > - More games in Dummy Mode → click or hold any of arrow buttons > - Lock device with pin (or regular lock if pin not set) by holding UP button on main screen [(by an4tur0r)](https://github.com/DarkFlippers/unleashed-firmware/pull/107) -> - **BadKB** plugin [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/Flipper-XFW/Xtreme-Firmware/tree/dev/applications/main/bad_kb) - (See in Applications → Tools) - (aka BadUSB via Bluetooth) +> - **BadKB** (BadUSB) [(by Willy-JL, ClaraCrazy, XFW contributors)](https://github.com/Flipper-XFW/Xtreme-Firmware/tree/dev/applications/main/bad_kb) - (Integrated into BadUSB app now!) - (aka BadUSB via Bluetooth) > - BadUSB → Keyboard layouts [(by rien > dummy-decoy)](https://github.com/dummy-decoy/flipperzero-firmware/tree/dummy_decoy/bad_usb_keyboard_layout) > - Custom community plugins and games added + all known working apps can be downloaded in extra pack in every release > - Other small fixes and changes throughout @@ -157,8 +157,9 @@ Thanks to Official team (to their SubGHz Developer, Skorp) for implementing supp > | Cenmax | FAAC_SLH | KGB/Subaru | Pantera_CLK | Tomahawk_Z,X_3-5 | > | Cenmax_St-5 | Faraon | Leopard | Pantera_XS/Jaguar | ZX-730-750-1055 | > | Cenmax_St-7 | Genius_Bravo | Magic_1 | Partisan_RX | IL-100(Smart) | -> | Centurion | Gibidi | Magic_2 | Reff | | -> | Monarch | Jolly Motors | Magic_3 | Sheriff | | +> | Centurion | Gibidi | Magic_2 | Reff | Merlin | +> | Monarch | Jolly Motors | Magic_3 | Sheriff | Steelmate | +> | Motorline | Rosh | Pecinin | Rossi | | >
@@ -166,6 +167,9 @@ Thanks to Official team (to their SubGHz Developer, Skorp) for implementing supp Decoders/Encoders or emulation (+ programming mode) support made by @xMasterX
+- Roger (static 28 bit) with add manually support (by @xMasterX & @mishamyte) +- V2 Phoenix (Phox) (dynamic 52 bit) (by @xMasterX & @RocketGod-git) +- Marantec (static 49 bit) (add manually support and CRC verify) (by @xMasterX & @li0ard) - Feron (static 32 bit) - ReversRB2 / RB2M (static 64 bit) with add manually support - Marantec24 (static 24 bit) with add manually support @@ -174,7 +178,7 @@ Thanks to Official team (to their SubGHz Developer, Skorp) for implementing supp - Hay21 (dynamic 21 bit) with button parsing - Nero Radio 57bit (+ 56bit support) - CAME 12bit/24bit encoder fixes (Fixes are now merged in OFW) -- Keeloq: Dea Mio, Genius Bravo, GSN, HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !), Hormann EcoStar, Novoferm, Sommer, Monarch (thanks @ashphx !), Jolly Motors (thanks @pkooiman !), IL-100(Smart) (thx Vitaly for RAWs) +- Keeloq: Dea Mio, Genius Bravo, GSN, HCS101, AN-Motors, JCM Tech, MHouse, Nice Smilo, DTM Neo, FAAC RC,XT, Mutancode, Normstahl, Beninca + Allmatic, Stilmatic, CAME Space, Aprimatic (model TR and similar), Centurion Nova (thanks Carlos !), Hormann EcoStar, Novoferm, Sommer, Monarch (thanks @ashphx !), Jolly Motors (thanks @pkooiman !), IL-100(Smart) (thx Vitaly for RAWs), Motorline (with add manually support), Rosh, Pecinin, Rossi, Merlin, Steelmate (thanks @RocketGod-git)
diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index e971bd056..cffad2929 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -75,6 +75,7 @@ typedef enum { SetTypeANMotorsAT4, SetTypeAlutechAT4N, SetTypePhoenix_V2_433, + SetTypeMotorline433, SetTypeHCS101_433_92, SetTypeDoorHan_315_00, SetTypeDoorHan_433_92, diff --git a/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes b/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes index 3f3e825af..62135c0b9 100644 --- a/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes +++ b/applications/main/subghz/resources/subghz/assets/keeloq_mfcodes @@ -1,63 +1,69 @@ Filetype: Flipper SubGhz Keystore File Version: 0 Encryption: 1 -IV: 4E 6F 20 66 75 72 69 20 63 68 65 63 6B 3F 21 3F -2F0767B5B190608EB032D12BFA937D760A77D08D37F851E940767F1915E97ACF -332F8DCCFDBF0485EC2EEED0C279F277E52A86A93BC5E4E96BE5F7276CC66713 -D9A02CC785FC0495063C424B0B1BAE7C120A2C24D4C0EE743F5D216718B16490 -4D9DD617090BDB100986B6987CAAC3652D2ADAB1AD9E368C5806D98562FF6B2F -28D21748FF3826FA13C785A6721CC5927C81EDAB0C5CF31C92EAFF12AA91608298485D8A3AB443640237372ADF0DDC49 -5058E12C0A41EDCB5C0812554F619DADFB6E895B94421952ECD9255A04EE5E1A -83A3EB8B22D94487A6B0F37856FB6AE9F42272BF25E1AE06DE03AA881A12D15F -D0E207DE64402B43ECD0C341216B6BCDC449508116E81D8ACDE7FA0BFBEA56F7 -6C4F723DE3B775D4C07E12ED3C369250B4D2089ADE2207816DED130D4B498CDF -B041911C56555E5F4676BF16819F61BF7A92402EB0427B8C2E7367B0AEA6B53C -1AD460260F20146A763BF6D4CD26DF5139EE29FFF8B53F6C5367EA779E1BEE56D5DFD872EA0268FE27204175925079AA -B1A9331AED36137CD078536A67775E2880D3CD7305373BC44A5649435E466AD2DC9FDE8AC1F572EF094D4B438C9509EA -105819300A9152F16E3478151799ECBBB7CCCE63DADA3F6C6D16D46830E1E035 -354186E04BC90D672F76A427FC1CD35C2EFAE8D4D1C36247FFB71ACB862A3601 -84B533148282D0D8121E5BBBBD39DE16F398365B015E02417ECC535C88EB9C57 -E899C9DC779F82E798EE923D257D5F051E1254DCDA2A6A955882727AA8E98ED8 -B8EC34F9B75E61D34E9C075A5316FAFC395E8FBA4F76B215620C5B5C76C53DB7BF316E53582161AD20F64CAB8E3347B2 -966C3B0873F48889B51D7D9DACC0674CBC95B06E231E30F235C826285D529A54 -370DED014764D7278E342D3AB631AB37D8F8A8FAE08987E03D7FC279DEEEB913 -2318A2DA42EEA2A35FFC9BDFBB244DF3FF6933010D74B3465336C0E37FFDC48A200568F8D6003AB215388734B8AC1F20 -475B35437FECEE9792F53A671252E78566AA9894DE7A4DEC9AED70834864E804E87478009F424CE1424C00F162BB03C5 -01CE6251ED9682BA6366075081167196CD740D346C4DAC4E0012C7951C475AE7 -CB225891F937CA491B711AA942B04C61C7CFA6A8E912D91D0906B401737E03B4 -F35D279815DEF19C9C5BC9ED55E7C1A0366E37DCD0A93E6E168B5A2602201C7B -3569D8DF2490797D40978F8325B94CC707383FEA1B46240BFDAECFEFB1F8176D -3D7BAF13573BBF3102757C68D52236638CC57126FF3795A604CFFA2D3F1B9C26 -B9102C87D7DBCF35463F38B6B80B70408968B6E01A47F6A7E8A3E87A0577B4ED -7673FAC14D94ABF72800A78E2DC4CAF2166FBB24719C22CFC1010492F4C87734 -1AF74DA07EA3A418EB86BB7ABAD6192B8E5A53F61B3E74CB898CB3EE4A7E244A -832D18C44062DDE856384E19D1417FA48D809C2CB2107CDEC5281943559791A6 -CD482A8FAB2A2CBE25A0B4A4788F274CA7095AA24508C00DBB78DD12BFB11C37 -EAC52E802DB76B51058752D7EFA91BCB1212AB96B589F9A88465195C1DE3242E -96CC75952A513AB5FE62A69AB6CDDA93C2156A3EA607C25B3201CE7284B3DAA9 -986E71EE87E860192141A1453929E575706E3FE72B7A9FEF5ACA696388649EB6 -FFF89FECC1C01FA3F266B95BDEF61A16F514E59599DAA07E908C604E9FE686C0 -ACC159D4AE78E26B5A1468F69D961028D0BF962D03E865415E7FE746553FEF15 -0FF46B2F9D4E907B9924675081D17C38C09957AA2F4C3C1F5568461DBA850F6301328CDC0FCEE83C7E8BA00CF8FC0F97 -7FD793C05E499739C3C4F8CC1D2D205A55928AB5BC60752A81D86DFBE30C50BD -CE444F4A1BEB38C9E93579E1B7FB3E90B4F85D8DA94DFC622272DED093A35192 -C7C31D8AB9D717FAF842F850A3E0D6B404EB9A980D9679362ABA90594B1957AB -1D48A6CFFBB6F14DD5BED1F8E74E6CC0292150546EDD76361792677434A8FE5F -F7335B8877DDF17F0737ECF77E2E6286E78534CE28E72077360E8E24D7750DFE -51051D9A8D5941661EBCF437B4640E6DA0B9B27518D761F3EF26BF7EABC691D4 -79F279733E18393FEDB50D654A0D0A532A64BED5ACBD13319439EEC007BC359C -646666FDB75D439C0427A9E3EF47F145DBD4FF5FE2E244909D74F075B24FF5A9B47E7AF98271210057D937A0E4B1F46D -DE7E814A2BD4D8823D9F2380EFAFFA1380A90391F87CBF24CE46BD46205EABAB -1335C4C3E819E942F5C212E9BEFAF5D984316C0A2FF6E9886886B565625618A9 -65386F906F18FF9C3A20AB57F3241D4975FE312ACDEB7FB1B91F2B816CAA46E7 -DF8A8B33782D56667F4C98F8F91B49B71A9E83AF015D8841986D41663233A0DC -27264455248878BB226FA1DED0922BD10313FF65F8A6A0E3CCDFB77890C838BB -43A08F784F36A3E8049BA63A401F3F15B3CA2ED263F8638595B5F22A0B081369 -F9F82F89C15AD970320E3D7A83B272EB00CD0ED657E4D230AB33C995859EA77F -70AD020D172E18E1011DF88E5F934F03F34DCE8148F8053B4FFA6F92CAC9FC93 -2B845F67BAB432CED64F2D68454A2B4B3BC46FFDC2A16D3340360C7BEA110BBB -B85F16A2370B278FDB3C85E6455B8DA239D6413B727839DEFBCB5628A6C747266291AB9D9F4F5DA1826B219C1A29F956 -FFB7B10D96F241FDB994008AF85EC85D147A97AA599D05F5EE1BB2FC27644A26 -0BD42CA312CBBCAE556AA0159EC2CC2FA70BBB00D8DF7B63BBEA60A282481AED -9CC73810056A21EA6F311B01BA7F44655A075D1F60947FBC9B6924C3BD0ED819 -024FCB96977ECA1C0D4B9C7C461361329D96E5AFF315124FEFC0DF2A400DE312F45D602DB40CD4EB088F144EB0B8DF41 +IV: 46 75 72 72 79 20 52 6F 63 6B 65 74 21 21 21 30 +05176EEFAC177FE261FE3EB5C8E103BE7CF9F2FEB32BDD6BB63D22EE9C17B9D2 +B645E3CAC0D5E26891249D326BCEB09850E4FB8F8E86A466E97E83437A9E0041 +AA4255FFA1ADE8FB840F80A93F8F1A2D1E39051131D24DE7258D66A8CF2066CF +13ACA390FD5254B024084D5D1F41B8DDF5304FF00C3C85A9C26CD13A7A268654 +4CFBF498D5E2C85496985E83D91B0F4229A925E16A90C6712750032C3699EE0AA5D04123E579B6121573FC61766E89AD +93DADC2AE4235470E171E0E85D24D04A84C37187284C38D1CBB48666FDA8CD6C +DB13D8CCC0CB07685F29F33AE07DA2FD14C2AE4F4D001DB88465D5CFE8CFDAA9 +E51CD1B5074B63D26E274218A0AB3B2E435454EE094DCA5679F35477658A72F9 +10AFD5FD9C296E67EDD9504A60BA9EF84556F40213DEC4DE44F99B088BCC6A57 +EF7AA55F6A473DE093D648240D5FCEB05F8B3295DC37B3E83239A4AF320CD688 +A22892E71B9D0D7FAF92B27C724E76C4A6824DBE5F083F1006D11E42D153C4AC98D0A11C6A8D62F5921A24ECC7437485 +7A25416E390D81DA68A59C3BA30D4B7FC8269B5E0DAF77CA3A857B6F478A050585918485AEE72D375F02D177CB296E31 +94004BA0BB1E47965E60025949EF4CC2738C463F57C97FD2A89C76CCCDEA5397 +111CB1C19863A0165521D974F838CE718DA07948A8D9A8A7490E75032A62ECA2 +17B6E27C69FA002F6CF23D719DFE595140BEFA5083D12E774CF89E2CED53D68D +73311E0FF8ABB3E9461AD14A4F52791647A50E2102D3B74188A73C35BC14EB55 +54E15840A6A6DCA85275E38E4218EE2B539E9E468E24C49428DA363C955C5FC81ACEE79EEB941B83EE4147A0817043BD +7D0FBB417B99B3C6AB18C7B2DC82582D2DCD1E10515028874E73254188F7FEE9 +3F6E89BBCC133B85945234A8201539ECD8796909CC81FE67673F8DE1ECA63045 +39554C0DC1C3694FAAFF65537FF710D9593B7B461E011FC39D014F253F0432533A40276D8259AFD8C957A378237D574F +E60F6CD7063B85F0F20ACB7E7A42B03DE4A9F6CCA54CB7F036AFA23A27D3E9E006BD523E5356260AA78206D9276E6E57 +9EB252EDA9352B966EC4F053D5B013772361D2AD4B217EF33F46A5CEC97A00F3 +AA6773E79BC6D76314BB523FDF203358E01ECB2BBCF3B5DD1EBD043663C74B05 +29B29A50F3F27F4D8C7B0FADA98CC004A7871078DAD1CBAC4846862C3DF82E02 +6E3A479D4334FF05606899B0383116125056A316621B279F904A02B842918C59 +3991732015F4A213E9912E34AC92515D88010C07DA0B118AD6F64A05DC38D2C5 +550B1866F7493C75812DF85DDADC38AF21D9B58189E4EE99A021328523881A9D +77960CA031D28362586100F17DF94FF4E7D6EFAFAF23952887F9DF0507825A99 +01E6FC89E97B7729BF4D1ED8041F69005181BF3639F939C5833B009E96B9F2F7 +D1CC7C536706ECFC5826C8933135D2B110996F1CB13388A702B8453DA40E40AD +B64D2F1E1A80E6DAB92283A512B40DB7FFC519F394AA94CC86C8532F69949723 +6399409A0AC0298DEDA76037C83042FC0870132CFF7F82E54AD0966BE16AC882 +D310536FA78F95BB0B408676990AA937117717BADE9D3B975C0ECE10FB586A1B +A8149C0581DCC291D037E96EF321DB6214BD7CB25F1696226A9FE750AA23B334 +BA3BEBD564D8F571202CD6FE89BC33F89C8E01C03AE0814F2BEF37C33CE874B4 +88CD81AC7605A7F6EFF85FD62C65E0C9945335CFC085B92B27B69648C6E5BF6B +8057C7CB5071DFFFAE4804FD9EC1EC1D3F54D06514906A34B17F6B6CB45A9D473992DF6BC8A9F9E146E39D6163209CC6 +9ABC8814C8FD1AB254374150177616F5C7B43049473C84329BEC855578B96002 +8BCA39A498B00245C71D94E3160CEE8ACA5BEB18AE0AD64A385AFCC018E99744 +5AD75C51CA5AE5FA9BBC6A41576C745F265CC28FC4DA2AD230B6692CF151FD61 +E86092E04CD72D874A92DE838035E811E75E411049C0A7BD0FE2AA9C802BE5AB +CE70ADB22E85747FDC064F0B5974385CD57D41D376CE1C7490C1BEC8A3FC5A7A +8F096E0A11682DB315825213D3DB5D725555C1CDF444169EB919E47E0F0FA6F7 +AD9C9A694D807BA77E5A54B248A88B55000757203D931506255BF8F4215C00D3 +F0E804B6C6B6E91916CB73EB44FB2D1992400BC90ED8B22DF5D038317588341207D74E08C00E529DF2CF2A64F2C7C0FF +72212FCEED35E9C3A176B67DCDB84B284F4DFDCD0ECE8D3F6089C58C2B8A616C +000F9F746BFB47FC10B23E3F08C2A84BCB3870D0C5AE974472849699884BC929 +7B8F9AB04E5F86D6DDCF6164A25EA927788A03F57977FC5C55E1D565279B09C4 +0E9CDCD07D1D4F1429E59F81B524960A75F19A464798C7E822E52728AC83784A +F2DE2B108A1476BB6F85DD3CCB0F0527627B45179092BA7A56D5971490E3875C +7F307358D988FEA12648739F58DD249EBDF0B1C44B73BA547C50EB576C071DAE +2DFBA988592CEF3B62A76183DBA727E734359B89F53AFF3160441EF8709FC633 +57F7DC38DDC87C19CE956BC44C638DEF34D814A7BAB0AC8AD61855143FD984FD +A8AADB687251FA6AC2BBC8EF1E3FA621893293DFBD8C1D07971BF82F22A00DC3 +65AEA1EE34E316C769E551AC2309D07FC2ED92EA044674E3A99CD7B543C730EB +968ECC790E5590E7EB22AFD3546C28F4EB87EA4CEE35F72DDFE7153F74611EAA +0F937930D4E1BDF0B729277CF94A47064BCB959938C70CDB3AC3C65DA68DA1FB +A8AB66375D59E112104CD81B819D618BE43D6A6F159BAD35583653EF3547D25D +A81D5DE2102F05D50750DC37C26E9C9502FA89EF98A2EB1EA546EE48C628E9C4 +EAFDE0A8936AF8EF718027937BC17CEF691E570996B403CF4762240D267EB305 +C48686348F0A94B07BC60AB825C1A0791C20DBBDD7DAE0ED47E8A7FBD9334EACF8E33DCEC36963E87929260DF769520B +493D53BD7BB2B3E081AE793A3BADB3AB0F33C95B83677715D6DE2922F2BEC892 +63FFD3D8CAB980E45D49253A69C99A6813CBE6013992EFBC862173BAD0E26373 +2EF88F43C5A76EC87E02B780585B10957F4EA386F96710FAB98BC2C1E214DBFA +A021CFA0E72AADFD75BC67FBE9345082B0A8B31782E933E81196F84B1797D83E8B2F81E1CF5C3F026D11B9DFC95222E2 diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index 3f2fd9eff..b6ff75768 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -30,6 +30,7 @@ static const char* submenu_names[SetTypeMAX] = { [SetTypeComunello868] = "KL: Comunello 868MHz", [SetTypeAllmatic433] = "KL: Allmatic 433MHz", [SetTypeAllmatic868] = "KL: Allmatic 868MHz", + [SetTypeMotorline433] = "KL: Motorline 433MHz", [SetTypeCenturion433] = "KL: Centurion 433MHz", [SetTypeMonarch433] = "KL: Monarch 433MHz", [SetTypeJollyMotors433] = "KL: Jolly Mot. 433MHz", @@ -680,6 +681,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { .somfy_telis.btn = 0x02, .somfy_telis.cnt = 0x03}; break; + case SetTypeMotorline433: + gen_info = (GenInfo){ + .type = GenKeeloq, + .mod = "AM650", + .freq = 433920000, + .keeloq.serial = key & 0x0FFFFFFF, + .keeloq.btn = 0x01, + .keeloq.cnt = 0x03, + .keeloq.manuf = "Motorline"}; + break; case SetTypeDoorHan_433_92: gen_info = (GenInfo){ .type = GenKeeloq, diff --git a/lib/subghz/protocols/keeloq.c b/lib/subghz/protocols/keeloq.c index a774e5825..e1ccf8c2f 100644 --- a/lib/subghz/protocols/keeloq.c +++ b/lib/subghz/protocols/keeloq.c @@ -237,9 +237,15 @@ static bool subghz_protocol_keeloq_gen_data( (strcmp(instance->manufacture_name, "Mutanco_Mutancode") == 0) || (strcmp(instance->manufacture_name, "Came_Space") == 0) || (strcmp(instance->manufacture_name, "Genius_Bravo") == 0) || - (strcmp(instance->manufacture_name, "GSN") == 0)) { + (strcmp(instance->manufacture_name, "GSN") == 0) || + (strcmp(instance->manufacture_name, "Rosh") == 0) || + (strcmp(instance->manufacture_name, "Rossi") == 0) || + (strcmp(instance->manufacture_name, "Pecinin") == 0) || + (strcmp(instance->manufacture_name, "Steelmate") == 0)) { // DTM Neo, Came_Space uses 12bit serial -> simple learning // FAAC_RC,XT , Mutanco_Mutancode, Genius_Bravo, GSN 12bit serial -> normal learning + // Rosh, Rossi, Pecinin -> 12bit serial - simple learning + // Steelmate -> 12bit serial - normal learning decrypt = btn << 28 | (instance->generic.serial & 0xFFF) << 16 | instance->generic.cnt; } else if( @@ -249,9 +255,12 @@ static bool subghz_protocol_keeloq_gen_data( // Nice Smilo, MHouse, JCM -> 8bit serial - simple learning decrypt = btn << 28 | (instance->generic.serial & 0xFF) << 16 | instance->generic.cnt; - } else if(strcmp(instance->manufacture_name, "Beninca") == 0) { + } else if( + (strcmp(instance->manufacture_name, "Beninca") == 0) || + (strcmp(instance->manufacture_name, "Merlin") == 0)) { decrypt = btn << 28 | (0x000) << 16 | instance->generic.cnt; // Beninca / Allmatic -> no serial - simple XOR + // Merlin -> no serial - simple XOR } else if(strcmp(instance->manufacture_name, "Centurion") == 0) { decrypt = btn << 28 | (0x1CE) << 16 | instance->generic.cnt; // Centurion -> no serial in hop, uses fixed value 0x1CE - normal learning From 1bed4d29cba326285f3c9942c249aa45991b7cef Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 9 Jul 2025 08:01:36 +0300 Subject: [PATCH 13/18] Nero Radio static - better parsing --- lib/subghz/protocols/nero_radio.c | 42 +++++++++++++++++++++++++++++-- 1 file changed, 40 insertions(+), 2 deletions(-) diff --git a/lib/subghz/protocols/nero_radio.c b/lib/subghz/protocols/nero_radio.c index 7e787ffd0..d7822ac1c 100644 --- a/lib/subghz/protocols/nero_radio.c +++ b/lib/subghz/protocols/nero_radio.c @@ -385,6 +385,36 @@ SubGhzProtocolStatus } } +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_nero_radio_parse_data(SubGhzBlockGeneric* instance) { + // Key samples from unit tests + // 57250501049DD3 + // 57250502049D13 + // + // Samples from remote + // 36E4E80104A644 + // 36E4E80204A684 + // 36E4E80304A604 + // 36E4E80404A6E4 + + // possible contents + // serial button serial/const crc?? + // 5725050 1 049D D3 + // 5725050 2 049D 13 + // 36E4E80 1 04A6 44 + // 36E4E80 2 04A6 84 + // 36E4E80 3 04A6 04 + // 36E4E80 4 04A6 E4 + + // serial is larger than uint32 can't fit into serial field + // using data2 var since its uint64_t + instance->btn = (instance->data >> 24) & 0xF; + instance->data_2 = ((instance->data >> 28) << 16) | ((instance->data >> 8) & 0xFFFF); +} + void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* output) { furi_assert(context); SubGhzProtocolDecoderNeroRadio* instance = context; @@ -398,15 +428,23 @@ void subghz_protocol_decoder_nero_radio_get_string(void* context, FuriString* ou uint32_t code_found_reverse_hi = code_found_reverse >> 32; uint32_t code_found_reverse_lo = code_found_reverse & 0x00000000ffffffff; + subghz_protocol_nero_radio_parse_data(&instance->generic); + furi_string_cat_printf( output, "%s %dbit\r\n" "Key:0x%lX%08lX\r\n" - "Yek:0x%lX%08lX\r\n", + "Yek:0x%lX%08lX\r\n" + "Sn: 0x%llX \r\n" + "CRC?: 0x%02X\r\n" + "Btn: %X\r\n", instance->generic.protocol_name, instance->generic.data_count_bit, code_found_hi, code_found_lo, code_found_reverse_hi, - code_found_reverse_lo); + code_found_reverse_lo, + instance->generic.data_2, + (uint8_t)(instance->generic.data & 0xFF), + instance->generic.btn); } From 3c3d06bae093925a6158d1ce437f33ce06b97e4e Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 9 Jul 2025 08:02:05 +0300 Subject: [PATCH 14/18] Roger that --- .../main/subghz/helpers/subghz_custom_event.h | 3 +- .../subghz/scenes/subghz_scene_set_type.c | 11 + lib/subghz/protocols/protocol_items.c | 1 + lib/subghz/protocols/protocol_items.h | 1 + lib/subghz/protocols/roger.c | 442 ++++++++++++++++++ lib/subghz/protocols/roger.h | 109 +++++ 6 files changed, 566 insertions(+), 1 deletion(-) create mode 100644 lib/subghz/protocols/roger.c create mode 100644 lib/subghz/protocols/roger.h diff --git a/applications/main/subghz/helpers/subghz_custom_event.h b/applications/main/subghz/helpers/subghz_custom_event.h index cffad2929..9fc607889 100644 --- a/applications/main/subghz/helpers/subghz_custom_event.h +++ b/applications/main/subghz/helpers/subghz_custom_event.h @@ -75,7 +75,6 @@ typedef enum { SetTypeANMotorsAT4, SetTypeAlutechAT4N, SetTypePhoenix_V2_433, - SetTypeMotorline433, SetTypeHCS101_433_92, SetTypeDoorHan_315_00, SetTypeDoorHan_433_92, @@ -88,6 +87,7 @@ typedef enum { SetTypeCenturion433, SetTypeMonarch433, SetTypeJollyMotors433, + SetTypeMotorline433, SetTypeSommer_FM_434, SetTypeSommer_FM_868, SetTypeSommer_FM238_434, @@ -130,6 +130,7 @@ typedef enum { SetTypeMarantec24_868, SetTypeMarantec_433, SetTypeMarantec_868, + SetTypeRoger_433, SetTypeLinear_300_00, // SetTypeNeroSketch, //Deleted in OFW // SetTypeNeroRadio, //Deleted in OFW diff --git a/applications/main/subghz/scenes/subghz_scene_set_type.c b/applications/main/subghz/scenes/subghz_scene_set_type.c index b6ff75768..b986c2c5d 100644 --- a/applications/main/subghz/scenes/subghz_scene_set_type.c +++ b/applications/main/subghz/scenes/subghz_scene_set_type.c @@ -20,6 +20,7 @@ static const char* submenu_names[SetTypeMAX] = { [SetTypeSomfyTelis] = "Somfy Telis 433MHz", [SetTypeANMotorsAT4] = "AN-Motors AT4 433MHz", [SetTypeAlutechAT4N] = "Alutech AT4N 433MHz", + [SetTypeRoger_433] = "Roger 433MHz", [SetTypePhoenix_V2_433] = "V2 Phoenix 433MHz", [SetTypeHCS101_433_92] = "KL: HCS101 433MHz", [SetTypeDoorHan_315_00] = "KL: DoorHan 315MHz", @@ -286,6 +287,16 @@ bool subghz_scene_set_type_on_event(void* context, SceneManagerEvent event) { .data.bits = 24, .data.te = 0}; break; + case SetTypeRoger_433: + gen_info = (GenInfo){ + .type = GenData, + .mod = "AM650", + .freq = 433920000, + .data.name = SUBGHZ_PROTOCOL_ROGER_NAME, + .data.key = (key & 0xFFFF000) | 0x0000101, // button code 0x1 and (crc?) is 0x01 + .data.bits = 28, + .data.te = 0}; + break; case SetTypeLinear_300_00: gen_info = (GenInfo){ .type = GenData, diff --git a/lib/subghz/protocols/protocol_items.c b/lib/subghz/protocols/protocol_items.c index c73923c7a..465585d77 100644 --- a/lib/subghz/protocols/protocol_items.c +++ b/lib/subghz/protocols/protocol_items.c @@ -53,6 +53,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = { &subghz_protocol_hay21, &subghz_protocol_revers_rb2, &subghz_protocol_feron, + &subghz_protocol_roger, }; const SubGhzProtocolRegistry subghz_protocol_registry = { diff --git a/lib/subghz/protocols/protocol_items.h b/lib/subghz/protocols/protocol_items.h index 6165d748a..4f63b030e 100644 --- a/lib/subghz/protocols/protocol_items.h +++ b/lib/subghz/protocols/protocol_items.h @@ -54,3 +54,4 @@ #include "hay21.h" #include "revers_rb2.h" #include "feron.h" +#include "roger.h" diff --git a/lib/subghz/protocols/roger.c b/lib/subghz/protocols/roger.c new file mode 100644 index 000000000..d14547876 --- /dev/null +++ b/lib/subghz/protocols/roger.c @@ -0,0 +1,442 @@ +#include "roger.h" +#include "../blocks/const.h" +#include "../blocks/decoder.h" +#include "../blocks/encoder.h" +#include "../blocks/generic.h" +#include "../blocks/math.h" + +#include "../blocks/custom_btn_i.h" + +#define TAG "SubGhzProtocolRoger" + +static const SubGhzBlockConst subghz_protocol_roger_const = { + .te_short = 500, + .te_long = 1000, + .te_delta = 270, + .min_count_bit_for_found = 28, +}; + +struct SubGhzProtocolDecoderRoger { + SubGhzProtocolDecoderBase base; + + SubGhzBlockDecoder decoder; + SubGhzBlockGeneric generic; +}; + +struct SubGhzProtocolEncoderRoger { + SubGhzProtocolEncoderBase base; + + SubGhzProtocolBlockEncoder encoder; + SubGhzBlockGeneric generic; +}; + +typedef enum { + RogerDecoderStepReset = 0, + RogerDecoderStepSaveDuration, + RogerDecoderStepCheckDuration, +} RogerDecoderStep; + +const SubGhzProtocolDecoder subghz_protocol_roger_decoder = { + .alloc = subghz_protocol_decoder_roger_alloc, + .free = subghz_protocol_decoder_roger_free, + + .feed = subghz_protocol_decoder_roger_feed, + .reset = subghz_protocol_decoder_roger_reset, + + .get_hash_data = subghz_protocol_decoder_roger_get_hash_data, + .serialize = subghz_protocol_decoder_roger_serialize, + .deserialize = subghz_protocol_decoder_roger_deserialize, + .get_string = subghz_protocol_decoder_roger_get_string, +}; + +const SubGhzProtocolEncoder subghz_protocol_roger_encoder = { + .alloc = subghz_protocol_encoder_roger_alloc, + .free = subghz_protocol_encoder_roger_free, + + .deserialize = subghz_protocol_encoder_roger_deserialize, + .stop = subghz_protocol_encoder_roger_stop, + .yield = subghz_protocol_encoder_roger_yield, +}; + +const SubGhzProtocol subghz_protocol_roger = { + .name = SUBGHZ_PROTOCOL_ROGER_NAME, + .type = SubGhzProtocolTypeStatic, + .flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_868 | SubGhzProtocolFlag_AM | + SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | + SubGhzProtocolFlag_Send, + + .decoder = &subghz_protocol_roger_decoder, + .encoder = &subghz_protocol_roger_encoder, +}; + +void* subghz_protocol_encoder_roger_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolEncoderRoger* instance = malloc(sizeof(SubGhzProtocolEncoderRoger)); + + instance->base.protocol = &subghz_protocol_roger; + instance->generic.protocol_name = instance->base.protocol->name; + + instance->encoder.repeat = 10; + instance->encoder.size_upload = 256; + instance->encoder.upload = malloc(instance->encoder.size_upload * sizeof(LevelDuration)); + instance->encoder.is_running = false; + return instance; +} + +void subghz_protocol_encoder_roger_free(void* context) { + furi_assert(context); + SubGhzProtocolEncoderRoger* instance = context; + free(instance->encoder.upload); + free(instance); +} + +// Get custom button code +static uint8_t subghz_protocol_roger_get_btn_code(void) { + uint8_t custom_btn_id = subghz_custom_btn_get(); + uint8_t original_btn_code = subghz_custom_btn_get_original(); + uint8_t btn = original_btn_code; + + // Set custom button + if((custom_btn_id == SUBGHZ_CUSTOM_BTN_OK) && (original_btn_code != 0)) { + // Restore original button code + btn = original_btn_code; + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_UP) { + switch(original_btn_code) { + case 0x1: + btn = 0x2; + break; + case 0x2: + btn = 0x1; + break; + case 0x4: + btn = 0x1; + break; + case 0x8: + btn = 0x1; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_DOWN) { + switch(original_btn_code) { + case 0x1: + btn = 0x4; + break; + case 0x2: + btn = 0x4; + break; + case 0x4: + btn = 0x2; + break; + case 0x8: + btn = 0x4; + break; + + default: + break; + } + } else if(custom_btn_id == SUBGHZ_CUSTOM_BTN_LEFT) { + switch(original_btn_code) { + case 0x1: + btn = 0x8; + break; + case 0x2: + btn = 0x8; + break; + case 0x4: + btn = 0x8; + break; + case 0x8: + btn = 0x2; + break; + + default: + break; + } + } + + return btn; +} + +/** + * Generating an upload from data. + * @param instance Pointer to a SubGhzProtocolEncoderRoger instance + */ +static void subghz_protocol_encoder_roger_get_upload(SubGhzProtocolEncoderRoger* instance) { + furi_assert(instance); + size_t index = 0; + + uint8_t btn = instance->generic.btn; + + // Save original button for later use + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(btn); + } + + // Get custom button code + // This will override the btn variable if a custom button is set + btn = subghz_protocol_roger_get_btn_code(); + + // If CRC is not == button - transmit as is, no custom button allowed + if((instance->generic.data & 0xFF) == instance->generic.btn) { + instance->generic.data = (uint64_t)instance->generic.serial << 12 | ((uint64_t)btn << 8) | + btn; + } + + // Send key and GAP + for(uint8_t i = instance->generic.data_count_bit; i > 0; i--) { + if(bit_read(instance->generic.data, i - 1)) { + // Send bit 1 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_roger_const.te_long); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_roger_const.te_short * 19); + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_roger_const.te_short); + } + } else { + // Send bit 0 + instance->encoder.upload[index++] = + level_duration_make(true, (uint32_t)subghz_protocol_roger_const.te_short); + if(i == 1) { + //Send gap if bit was last + instance->encoder.upload[index++] = level_duration_make( + false, (uint32_t)subghz_protocol_roger_const.te_short * 19); + } else { + instance->encoder.upload[index++] = + level_duration_make(false, (uint32_t)subghz_protocol_roger_const.te_long); + } + } + } + + instance->encoder.size_upload = index; + return; +} + +/** + * Analysis of received data + * @param instance Pointer to a SubGhzBlockGeneric* instance + */ +static void subghz_protocol_roger_check_remote_controller(SubGhzBlockGeneric* instance) { + // Roger Decoder + // 2025.07 - @xMasterX (MMX) + + // Key samples + // 0010001111111001 0001 00100000 // S/N: 0x23F9 Btn: 0x1 CRC: 0x20 + // 0010001111111001 0010 00100011 // S/N: 0x23F9 Btn: 0x2 CRC: 0x23 + // 0101011001010110 0001 00000001 // S/N: 0x5656 Btn: 0x1 CRC: 0x01 + // 0101011001010110 0010 00000010 // S/N: 0x5656 Btn: 0x2 CRC: 0x02 + // 0000110111111110 0001 00000001 // S/N: 0x0DFE Btn: 0x1 CRC: 0x01 + // 0000110111111110 0100 00000100 // S/N: 0x0DFE Btn: 0x4 CRC: 0x04 + // 0000110111111110 0010 00000010 // S/N: 0x0DFE Btn: 0x2 CRC: 0x02 + // 0000110111111110 1000 00001000 // S/N: 0x0DFE Btn: 0x8 CRC: 0x08 + + instance->serial = instance->data >> 12; + instance->btn = (instance->data >> 8) & 0xF; + + // Save original button for later use + if(subghz_custom_btn_get_original() == 0) { + subghz_custom_btn_set_original(instance->btn); + } + subghz_custom_btn_set_max(3); +} + +SubGhzProtocolStatus + subghz_protocol_encoder_roger_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolEncoderRoger* instance = context; + SubGhzProtocolStatus ret = SubGhzProtocolStatusError; + do { + ret = subghz_block_generic_deserialize_check_count_bit( + &instance->generic, + flipper_format, + subghz_protocol_roger_const.min_count_bit_for_found); + if(ret != SubGhzProtocolStatusOk) { + break; + } + //optional parameter parameter + flipper_format_read_uint32( + flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1); + + subghz_protocol_roger_check_remote_controller(&instance->generic); + subghz_protocol_encoder_roger_get_upload(instance); + + uint8_t key_data[sizeof(uint64_t)] = {0}; + for(size_t i = 0; i < sizeof(uint64_t); i++) { + key_data[sizeof(uint64_t) - i - 1] = (instance->generic.data >> i * 8) & 0xFF; + } + if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(uint64_t))) { + FURI_LOG_E(TAG, "Unable to add Key"); + break; + } + + instance->encoder.is_running = true; + } while(false); + + return ret; +} + +void subghz_protocol_encoder_roger_stop(void* context) { + SubGhzProtocolEncoderRoger* instance = context; + instance->encoder.is_running = false; +} + +LevelDuration subghz_protocol_encoder_roger_yield(void* context) { + SubGhzProtocolEncoderRoger* instance = context; + + if(instance->encoder.repeat == 0 || !instance->encoder.is_running) { + instance->encoder.is_running = false; + return level_duration_reset(); + } + + LevelDuration ret = instance->encoder.upload[instance->encoder.front]; + + if(++instance->encoder.front == instance->encoder.size_upload) { + instance->encoder.repeat--; + instance->encoder.front = 0; + } + + return ret; +} + +void* subghz_protocol_decoder_roger_alloc(SubGhzEnvironment* environment) { + UNUSED(environment); + SubGhzProtocolDecoderRoger* instance = malloc(sizeof(SubGhzProtocolDecoderRoger)); + instance->base.protocol = &subghz_protocol_roger; + instance->generic.protocol_name = instance->base.protocol->name; + return instance; +} + +void subghz_protocol_decoder_roger_free(void* context) { + furi_assert(context); + SubGhzProtocolDecoderRoger* instance = context; + free(instance); +} + +void subghz_protocol_decoder_roger_reset(void* context) { + furi_assert(context); + SubGhzProtocolDecoderRoger* instance = context; + instance->decoder.parser_step = RogerDecoderStepReset; +} + +void subghz_protocol_decoder_roger_feed(void* context, bool level, volatile uint32_t duration) { + furi_assert(context); + SubGhzProtocolDecoderRoger* instance = context; + + switch(instance->decoder.parser_step) { + case RogerDecoderStepReset: + if((!level) && (DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) < + subghz_protocol_roger_const.te_delta * 3)) { + //Found GAP + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = RogerDecoderStepSaveDuration; + } + break; + case RogerDecoderStepSaveDuration: + if(level) { + instance->decoder.te_last = duration; + instance->decoder.parser_step = RogerDecoderStepCheckDuration; + } else { + instance->decoder.parser_step = RogerDecoderStepReset; + } + break; + case RogerDecoderStepCheckDuration: + if(!level) { + // Bit 1 is long and short timing = 1000us HIGH (te_last) and 500us LOW + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_long) < + subghz_protocol_roger_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_roger_const.te_short) < + subghz_protocol_roger_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + instance->decoder.parser_step = RogerDecoderStepSaveDuration; + // Bit 0 is short and long timing = 500us HIGH (te_last) and 1000us LOW + } else if( + (DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_short) < + subghz_protocol_roger_const.te_delta) && + (DURATION_DIFF(duration, subghz_protocol_roger_const.te_long) < + subghz_protocol_roger_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + instance->decoder.parser_step = RogerDecoderStepSaveDuration; + } else if( + // End of the key + DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) < + subghz_protocol_roger_const.te_delta * 3) { + //Found next GAP and add bit 1 or 0 + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_long) < + subghz_protocol_roger_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 1); + } + if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_short) < + subghz_protocol_roger_const.te_delta)) { + subghz_protocol_blocks_add_bit(&instance->decoder, 0); + } + // If got full 28 bits key reading is finished + if(instance->decoder.decode_count_bit == + subghz_protocol_roger_const.min_count_bit_for_found) { + instance->generic.data = instance->decoder.decode_data; + instance->generic.data_count_bit = instance->decoder.decode_count_bit; + if(instance->base.callback) + instance->base.callback(&instance->base, instance->base.context); + } + instance->decoder.decode_data = 0; + instance->decoder.decode_count_bit = 0; + instance->decoder.parser_step = RogerDecoderStepReset; + } else { + instance->decoder.parser_step = RogerDecoderStepReset; + } + } else { + instance->decoder.parser_step = RogerDecoderStepReset; + } + break; + } +} + +uint8_t subghz_protocol_decoder_roger_get_hash_data(void* context) { + furi_assert(context); + SubGhzProtocolDecoderRoger* instance = context; + return subghz_protocol_blocks_get_hash_data( + &instance->decoder, (instance->decoder.decode_count_bit / 8) + 1); +} + +SubGhzProtocolStatus subghz_protocol_decoder_roger_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset) { + furi_assert(context); + SubGhzProtocolDecoderRoger* instance = context; + return subghz_block_generic_serialize(&instance->generic, flipper_format, preset); +} + +SubGhzProtocolStatus + subghz_protocol_decoder_roger_deserialize(void* context, FlipperFormat* flipper_format) { + furi_assert(context); + SubGhzProtocolDecoderRoger* instance = context; + return subghz_block_generic_deserialize_check_count_bit( + &instance->generic, flipper_format, subghz_protocol_roger_const.min_count_bit_for_found); +} + +void subghz_protocol_decoder_roger_get_string(void* context, FuriString* output) { + furi_assert(context); + SubGhzProtocolDecoderRoger* instance = context; + + subghz_protocol_roger_check_remote_controller(&instance->generic); + + furi_string_cat_printf( + output, + "%s %db\r\n" + "Key: 0x%07lX\r\n" + "Serial: 0x%04lX\r\n" + "CRC: 0x%02lX\r\n" + "Btn: %01X", + instance->generic.protocol_name, + instance->generic.data_count_bit, + (uint32_t)(instance->generic.data & 0xFFFFFFF), + instance->generic.serial, + (uint32_t)(instance->generic.data & 0xFF), + instance->generic.btn); +} diff --git a/lib/subghz/protocols/roger.h b/lib/subghz/protocols/roger.h new file mode 100644 index 000000000..c279164f9 --- /dev/null +++ b/lib/subghz/protocols/roger.h @@ -0,0 +1,109 @@ +#pragma once + +#include "base.h" + +#define SUBGHZ_PROTOCOL_ROGER_NAME "Roger" + +typedef struct SubGhzProtocolDecoderRoger SubGhzProtocolDecoderRoger; +typedef struct SubGhzProtocolEncoderRoger SubGhzProtocolEncoderRoger; + +extern const SubGhzProtocolDecoder subghz_protocol_roger_decoder; +extern const SubGhzProtocolEncoder subghz_protocol_roger_encoder; +extern const SubGhzProtocol subghz_protocol_roger; + +/** + * Allocate SubGhzProtocolEncoderRoger. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolEncoderRoger* pointer to a SubGhzProtocolEncoderRoger instance + */ +void* subghz_protocol_encoder_roger_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolEncoderRoger. + * @param context Pointer to a SubGhzProtocolEncoderRoger instance + */ +void subghz_protocol_encoder_roger_free(void* context); + +/** + * Deserialize and generating an upload to send. + * @param context Pointer to a SubGhzProtocolEncoderRoger instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_encoder_roger_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Forced transmission stop. + * @param context Pointer to a SubGhzProtocolEncoderRoger instance + */ +void subghz_protocol_encoder_roger_stop(void* context); + +/** + * Getting the level and duration of the upload to be loaded into DMA. + * @param context Pointer to a SubGhzProtocolEncoderRoger instance + * @return LevelDuration + */ +LevelDuration subghz_protocol_encoder_roger_yield(void* context); + +/** + * Allocate SubGhzProtocolDecoderRoger. + * @param environment Pointer to a SubGhzEnvironment instance + * @return SubGhzProtocolDecoderRoger* pointer to a SubGhzProtocolDecoderRoger instance + */ +void* subghz_protocol_decoder_roger_alloc(SubGhzEnvironment* environment); + +/** + * Free SubGhzProtocolDecoderRoger. + * @param context Pointer to a SubGhzProtocolDecoderRoger instance + */ +void subghz_protocol_decoder_roger_free(void* context); + +/** + * Reset decoder SubGhzProtocolDecoderRoger. + * @param context Pointer to a SubGhzProtocolDecoderRoger instance + */ +void subghz_protocol_decoder_roger_reset(void* context); + +/** + * Parse a raw sequence of levels and durations received from the air. + * @param context Pointer to a SubGhzProtocolDecoderRoger instance + * @param level Signal level true-high false-low + * @param duration Duration of this level in, us + */ +void subghz_protocol_decoder_roger_feed(void* context, bool level, uint32_t duration); + +/** + * Getting the hash sum of the last randomly received parcel. + * @param context Pointer to a SubGhzProtocolDecoderRoger instance + * @return hash Hash sum + */ +uint8_t subghz_protocol_decoder_roger_get_hash_data(void* context); + +/** + * Serialize data SubGhzProtocolDecoderRoger. + * @param context Pointer to a SubGhzProtocolDecoderRoger instance + * @param flipper_format Pointer to a FlipperFormat instance + * @param preset The modulation on which the signal was received, SubGhzRadioPreset + * @return status + */ +SubGhzProtocolStatus subghz_protocol_decoder_roger_serialize( + void* context, + FlipperFormat* flipper_format, + SubGhzRadioPreset* preset); + +/** + * Deserialize data SubGhzProtocolDecoderRoger. + * @param context Pointer to a SubGhzProtocolDecoderRoger instance + * @param flipper_format Pointer to a FlipperFormat instance + * @return status + */ +SubGhzProtocolStatus + subghz_protocol_decoder_roger_deserialize(void* context, FlipperFormat* flipper_format); + +/** + * Getting a textual representation of the received data. + * @param context Pointer to a SubGhzProtocolDecoderRoger instance + * @param output Resulting text + */ +void subghz_protocol_decoder_roger_get_string(void* context, FuriString* output); From bddebff1341592c18211d1c72563970e66ea80f3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 9 Jul 2025 08:03:03 +0300 Subject: [PATCH 15/18] upd changelog --- CHANGELOG.md | 1 + 1 file changed, 1 insertion(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index feb903787..bcc118151 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ * SubGHz: **Roger (static 28 bit) with add manually support** (by @xMasterX & @mishamyte) * SubGHz: **V2 Phoenix full support** (button switch, add manually, counter decrypt/encrypt) (by @xMasterX & @RocketGod-git, original code by @Skorpionm) * SubGHz: **Keeloq: Add support for - Motorline (with add manually support), Rosh, Pecinin, Rossi, Merlin, Steelmate** (by @xMasterX & @RocketGod-git) +* SubGHz: Nero Radio static parse and display more data * SubGHz: Reduce less popular freqs in default hopper preset, **make it faster** * SubGHz: **Marantec protocol implement crc verification display and add manually support** (by @xMasterX & @li0ard, original code by @Skorpionm) * SubGHz: **Keeloq: Comunello - add manually support** From 269cbd66e1b683cf4fb879bf55be41e0386d5e39 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 9 Jul 2025 08:14:31 +0300 Subject: [PATCH 16/18] A little better naming for display in v2phox --- lib/subghz/protocols/phoenix_v2.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/subghz/protocols/phoenix_v2.c b/lib/subghz/protocols/phoenix_v2.c index 0229aeaeb..a6a8a7108 100644 --- a/lib/subghz/protocols/phoenix_v2.c +++ b/lib/subghz/protocols/phoenix_v2.c @@ -583,12 +583,11 @@ void subghz_protocol_decoder_phoenix_v2_get_string(void* context, FuriString* ou subghz_protocol_phoenix_v2_check_remote_controller(&instance->generic); furi_string_cat_printf( output, - "%s %dbit\r\n" + "V2 Phoenix %dbit\r\n" "Key:%05lX%08lX\r\n" "Sn:0x%07lX \r\n" "Cnt: 0x%04lX\r\n" "Btn: %X\r\n", - instance->generic.protocol_name, instance->generic.data_count_bit, (uint32_t)(instance->generic.data >> 32) & 0xFFFFFFFF, (uint32_t)(instance->generic.data & 0xFFFFFFFF), From 3b29bd65088046bca065f39a945f8b5ab94655a3 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Sat, 12 Jul 2025 03:57:14 +0300 Subject: [PATCH 17/18] Roger decoder allow bigger gap and extend buttons functionality --- lib/subghz/protocols/roger.c | 31 +++++++++++++++++++------------ 1 file changed, 19 insertions(+), 12 deletions(-) diff --git a/lib/subghz/protocols/roger.c b/lib/subghz/protocols/roger.c index d14547876..7b57cbab4 100644 --- a/lib/subghz/protocols/roger.c +++ b/lib/subghz/protocols/roger.c @@ -178,10 +178,17 @@ static void subghz_protocol_encoder_roger_get_upload(SubGhzProtocolEncoderRoger* // This will override the btn variable if a custom button is set btn = subghz_protocol_roger_get_btn_code(); - // If CRC is not == button - transmit as is, no custom button allowed + // If End is not == button - transmit as is, no custom button allowed + // For "End" values 23 and 20 - transmit correct ending used for their buttons if((instance->generic.data & 0xFF) == instance->generic.btn) { instance->generic.data = (uint64_t)instance->generic.serial << 12 | ((uint64_t)btn << 8) | btn; + } else if(((instance->generic.data & 0xFF) == 0x23) && btn == 0x1) { + instance->generic.data = (uint64_t)instance->generic.serial << 12 | ((uint64_t)btn << 8) | + 0x20; + } else if(((instance->generic.data & 0xFF) == 0x20) && btn == 0x2) { + instance->generic.data = (uint64_t)instance->generic.serial << 12 | ((uint64_t)btn << 8) | + 0x23; } // Send key and GAP @@ -226,14 +233,14 @@ static void subghz_protocol_roger_check_remote_controller(SubGhzBlockGeneric* in // 2025.07 - @xMasterX (MMX) // Key samples - // 0010001111111001 0001 00100000 // S/N: 0x23F9 Btn: 0x1 CRC: 0x20 - // 0010001111111001 0010 00100011 // S/N: 0x23F9 Btn: 0x2 CRC: 0x23 - // 0101011001010110 0001 00000001 // S/N: 0x5656 Btn: 0x1 CRC: 0x01 - // 0101011001010110 0010 00000010 // S/N: 0x5656 Btn: 0x2 CRC: 0x02 - // 0000110111111110 0001 00000001 // S/N: 0x0DFE Btn: 0x1 CRC: 0x01 - // 0000110111111110 0100 00000100 // S/N: 0x0DFE Btn: 0x4 CRC: 0x04 - // 0000110111111110 0010 00000010 // S/N: 0x0DFE Btn: 0x2 CRC: 0x02 - // 0000110111111110 1000 00001000 // S/N: 0x0DFE Btn: 0x8 CRC: 0x08 + // 0010001111111001 0001 00100000 // S/N: 0x23F9 Btn: 0x1 End: 0x20 + // 0010001111111001 0010 00100011 // S/N: 0x23F9 Btn: 0x2 End: 0x23 + // 0101011001010110 0001 00000001 // S/N: 0x5656 Btn: 0x1 End: 0x01 + // 0101011001010110 0010 00000010 // S/N: 0x5656 Btn: 0x2 End: 0x02 + // 0000110111111110 0001 00000001 // S/N: 0x0DFE Btn: 0x1 End: 0x01 + // 0000110111111110 0100 00000100 // S/N: 0x0DFE Btn: 0x4 End: 0x04 + // 0000110111111110 0010 00000010 // S/N: 0x0DFE Btn: 0x2 End: 0x02 + // 0000110111111110 1000 00001000 // S/N: 0x0DFE Btn: 0x8 End: 0x08 instance->serial = instance->data >> 12; instance->btn = (instance->data >> 8) & 0xF; @@ -330,7 +337,7 @@ void subghz_protocol_decoder_roger_feed(void* context, bool level, volatile uint switch(instance->decoder.parser_step) { case RogerDecoderStepReset: if((!level) && (DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) < - subghz_protocol_roger_const.te_delta * 3)) { + subghz_protocol_roger_const.te_delta * 5)) { //Found GAP instance->decoder.decode_data = 0; instance->decoder.decode_count_bit = 0; @@ -365,7 +372,7 @@ void subghz_protocol_decoder_roger_feed(void* context, bool level, volatile uint } else if( // End of the key DURATION_DIFF(duration, subghz_protocol_roger_const.te_short * 19) < - subghz_protocol_roger_const.te_delta * 3) { + subghz_protocol_roger_const.te_delta * 5) { //Found next GAP and add bit 1 or 0 if((DURATION_DIFF(instance->decoder.te_last, subghz_protocol_roger_const.te_long) < subghz_protocol_roger_const.te_delta)) { @@ -431,7 +438,7 @@ void subghz_protocol_decoder_roger_get_string(void* context, FuriString* output) "%s %db\r\n" "Key: 0x%07lX\r\n" "Serial: 0x%04lX\r\n" - "CRC: 0x%02lX\r\n" + "End: 0x%02lX\r\n" "Btn: %01X", instance->generic.protocol_name, instance->generic.data_count_bit, From ac6621cdcbdbfce9f90c64b907c8ef943e54bd41 Mon Sep 17 00:00:00 2001 From: MX <10697207+xMasterX@users.noreply.github.com> Date: Wed, 16 Jul 2025 02:38:19 +0300 Subject: [PATCH 18/18] upd changelog --- CHANGELOG.md | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index bcc118151..e33b7843f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,17 +3,17 @@ * SubGHz: **Roger (static 28 bit) with add manually support** (by @xMasterX & @mishamyte) * SubGHz: **V2 Phoenix full support** (button switch, add manually, counter decrypt/encrypt) (by @xMasterX & @RocketGod-git, original code by @Skorpionm) * SubGHz: **Keeloq: Add support for - Motorline (with add manually support), Rosh, Pecinin, Rossi, Merlin, Steelmate** (by @xMasterX & @RocketGod-git) -* SubGHz: Nero Radio static parse and display more data +* SubGHz: **Nero Radio static parse** and display more data * SubGHz: Reduce less popular freqs in default hopper preset, **make it faster** -* SubGHz: **Marantec protocol implement crc verification display and add manually support** (by @xMasterX & @li0ard, original code by @Skorpionm) +* SubGHz: **Marantec protocol implement CRC verification display and Add manually support** (by @xMasterX & @li0ard, original code by @Skorpionm) * SubGHz: **Keeloq: Comunello - add manually support** * iButton: **TM01x Dallas write support** (PR #899 | by @Leptopt1los) * SubGHz: Rename and **extend Alarms, Sensors, Cars ignore options** (Alarms: Hollarm, GangQi | Cars: Kia, Starline, ScherKhan | Sensors: Magellan, Honeywell, Honeywell WDB (doorbells), Legrand (doorbells), Feron (RGB lights)) -* SubGHz: V2 Phoenix show counter value -* SubGHz: **Add keeloq ironlogic (aka il100) smart clone cloners support** (thanks to Vitaly for RAWs) +* SubGHz: V2 Phoenix show counter value (upd: see above, now decrypted) +* SubGHz: **Add Keeloq IronLogic (aka IL100) smart clone remote copiers support** (thanks to Vitaly for RAWs) * SubGHz: **Fix CAME 24bit decoder** * SubGHz: Add 462.750 MHz & 868.46 MHz to default subghz freqs list -* SubGHz: **Tune holtek ht12x to decode holtek only** and not conflict with came 12bit +* SubGHz: **Tune Holtek HT12x to decode Holtek only** and not conflict with came 12bit * SubGHz: Fix Rename scene bug, that was replacing file name with random name when Rename is opened then closed then opened again * Display: Backlight option "always on" and RGB bug removed (PR #900 | by @Dmitry422) * NFC: Ultralight C - Attempt of authentication with default key (PR #898 | by @mishamyte)