mirror of
https://github.com/D4C1-Labs/Flipper-ARF.git
synced 2026-05-13 17:13:27 +00:00
Compare commits
3 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 29fef56be1 | |||
| 6a348dd304 | |||
| 32a96e580d |
+9
-1
@@ -81,4 +81,12 @@ node_modules/
|
||||
|
||||
#companion app
|
||||
/companion
|
||||
/Flipper-Android-App
|
||||
/Flipper-Android-App
|
||||
|
||||
#WIP not ready to push protocols
|
||||
|
||||
lib/subghz/protocols/subghz_protocol_honda_pandora.c
|
||||
lib/subghz/protocols/honda_rolling.c
|
||||
lib/subghz/protocols/honda_rolling.h
|
||||
lib/subghz/protocols/honda_pandora.c
|
||||
lib/subghz/protocols/honda_pandora.h
|
||||
|
||||
@@ -48,6 +48,7 @@ This project may incorporate, adapt, or build upon **other open-source projects*
|
||||
| Porsche | Porsche AG | 433/868 MHz | AM | Yes | Yes | No |
|
||||
| PSA (Peugeot/Citroën/DS) | PSA GROUP | 433 MHz | AM/FM | Yes | Yes | Yes |
|
||||
| Ford | Ford V0 | 315/433 MHz | AM | Yes | Yes | Yes |
|
||||
| Ford | Ford V1 | 315/433 MHz | FM | Yes | Yes | Yes |
|
||||
| Fiat | Fiat SpA | 433 MHz | AM | Yes | Yes | Yes |
|
||||
| Fiat | Marelli/Delphi | 433 MHz | AM | No | Yes | Yes |
|
||||
| Renault (old models) | Marelli | 433 MHz | AM | No | Yes | No|
|
||||
@@ -58,6 +59,7 @@ This project may incorporate, adapt, or build upon **other open-source projects*
|
||||
| Kia/Hyundai | KIA/HYU V3/V4 | 315/433 MHz | AM/FM | Yes | Yes | Yes |
|
||||
| Kia/Hyundai | KIA/HYU V5 | 433 MHz | FM | Yes | Yes | Yes |
|
||||
| Kia/Hyundai | KIA/HYU V6 | 433 MHz | FM | Yes | Yes | Yes |
|
||||
| Kia/Hyundai | KIA V7 | 433 MHz | FM | Yes | Yes | Yes |
|
||||
| Subaru | Subaru | 433 MHz | AM | Yes | Yes | No |
|
||||
| Suzuki | Suzuki | 433 MHz | FM | Yes | Yes | Yes |
|
||||
| Mitsubishi | Mitsubishi V0 | 868 MHz | FM | Yes | Yes | No |
|
||||
@@ -67,6 +69,7 @@ This project may incorporate, adapt, or build upon **other open-source projects*
|
||||
| Scher-Khan | Magic Code PRO1/PRO2 | 433 MHz | FM | Yes | Yes | Yes |
|
||||
| Sheriff | Sheriff CFM (ZX-750/930) | 433 MHz | AM | Yes | Yes | No |
|
||||
| Chrysler/Dodge/Jeep | FOBIK GQ43VT | 315/433 MHz | AM | Yes | Yes | No |
|
||||
| Honda | Honda Static | 433 MHz | AM | Yes | Yes | No |
|
||||
|
||||
### Gate / Access Protocols
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,39 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/types.h>
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#define FORD_PROTOCOL_V1_NAME "Ford V1"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderFordV1 SubGhzProtocolDecoderFordV1;
|
||||
|
||||
extern const SubGhzProtocol ford_protocol_v1;
|
||||
|
||||
void* subghz_protocol_decoder_ford_v1_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_ford_v1_free(void* context);
|
||||
void subghz_protocol_decoder_ford_v1_reset(void* context);
|
||||
void subghz_protocol_decoder_ford_v1_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_ford_v1_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_ford_v1_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_ford_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_ford_v1_get_string(void* context, FuriString* output);
|
||||
|
||||
void* subghz_protocol_encoder_ford_v1_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_encoder_ford_v1_free(void* context);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_ford_v1_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_encoder_ford_v1_stop(void* context);
|
||||
LevelDuration subghz_protocol_encoder_ford_v1_yield(void* context);
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_ford_v1_encoder;
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,198 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
|
||||
/* ── Protocol name ─────────────────────────────────────────────────────── */
|
||||
#define SUBGHZ_PROTOCOL_HONDA_NAME "Honda"
|
||||
|
||||
#define HONDA_TE_SHORT 250u
|
||||
#define HONDA_TE_LONG 480u
|
||||
#define HONDA_TE_DELTA 62u
|
||||
|
||||
/* ── FSK adaptive decoder parameters ──────────────────────────────────── */
|
||||
|
||||
#define HONDA_FSK_DUR_MIN_US 35u
|
||||
#define HONDA_FSK_DUR_MAX_US 2000u
|
||||
|
||||
//#define HONDA_FSK_GAP_US 3000u
|
||||
#define HONDA_FSK_GAP_US 400u
|
||||
|
||||
#define HONDA_FSK_COLLECT_N 32u
|
||||
#define HONDA_FSK_TOL_PCT 40u
|
||||
|
||||
#define HONDA_RAW_EDGE_BUF 512u
|
||||
#define HONDA_MAN_BIT_BUF 256u
|
||||
|
||||
#define HONDA_FSK_MIN_PREAMBLE_BITS 16u
|
||||
|
||||
#define HONDA_FRAME_BITS_M1 88u
|
||||
#define HONDA_FRAME_BITS_M2 66u
|
||||
#define HONDA_MIN_BITS HONDA_FRAME_BITS_M2
|
||||
|
||||
/* ── Mode 2 OOK parameters ────────────────────── */
|
||||
#define HONDA_TE_M2 400u
|
||||
#define HONDA_TE_M2_DELTA 100u
|
||||
#define HONDA_MIN_PREAMBLE_PULSES_M1 280u
|
||||
#define HONDA_MIN_PREAMBLE_PULSES_M2 18u
|
||||
#define HONDA_PREAMBLE_CYCLES_M1 312u
|
||||
#define HONDA_PREAMBLE_CYCLES_M2 23u
|
||||
#define HONDA_GAP_M1_MIN_US 500u
|
||||
#define HONDA_GAP_M1_MAX_US 1100u
|
||||
#define HONDA_GAP_M2_MIN_US 3000u
|
||||
#define HONDA_GAP_M2_MAX_US 5500u
|
||||
#define HONDA_GUARD_TIME_US 1000u
|
||||
|
||||
/* ── Button codes ──────────────────────────────────────────────────────── */
|
||||
#define HONDA_BTN_LOCK 0x01u
|
||||
#define HONDA_BTN_UNLOCK 0x02u
|
||||
#define HONDA_BTN_TRUNK 0x04u
|
||||
#define HONDA_BTN_PANIC 0x08u
|
||||
#define HONDA_BTN_RSTART 0x05u
|
||||
#define HONDA_BTN_LOCK2PRESS 0x09u
|
||||
#define HONDA_CUSTOM_BTN_MAX 5u
|
||||
|
||||
/* ── Lookup tables ─────────────────────────────────────────── */
|
||||
#define HONDA_TABLE_A \
|
||||
{0x02,0x06,0x00,0x04,0x0B,0x0F,0x09,0x0D,0x06,0x02,0x04,0x00,0x0F,0x0B,0x0D,0x09}, \
|
||||
{0x08,0x0C,0x0A,0x0E,0x01,0x05,0x03,0x07,0x0C,0x08,0x0E,0x0A,0x05,0x01,0x07,0x03}, \
|
||||
{0x0F,0x0B,0x0D,0x09,0x06,0x02,0x04,0x00,0x0B,0x0F,0x09,0x0D,0x02,0x06,0x00,0x04}, \
|
||||
{0x05,0x01,0x07,0x03,0x0C,0x08,0x0E,0x0A,0x01,0x05,0x03,0x07,0x08,0x0C,0x0A,0x0E}, \
|
||||
{0x04,0x00,0x06,0x02,0x0D,0x09,0x0F,0x0B,0x00,0x04,0x02,0x06,0x09,0x0D,0x0B,0x0F}, \
|
||||
{0x0E,0x0A,0x0C,0x08,0x07,0x03,0x05,0x01,0x0A,0x0E,0x08,0x0C,0x03,0x07,0x01,0x05}, \
|
||||
{0x09,0x0D,0x0B,0x0F,0x00,0x04,0x02,0x06,0x0D,0x09,0x0F,0x0B,0x04,0x00,0x06,0x02}, \
|
||||
{0x03,0x07,0x01,0x05,0x0A,0x0E,0x08,0x0C,0x07,0x03,0x05,0x01,0x0E,0x0A,0x0C,0x08}, \
|
||||
{0x01,0x05,0x03,0x07,0x08,0x0C,0x0A,0x0E,0x05,0x01,0x07,0x03,0x0C,0x08,0x0E,0x0A}, \
|
||||
{0x0B,0x0F,0x09,0x0D,0x02,0x06,0x00,0x04,0x0F,0x0B,0x0D,0x09,0x06,0x02,0x04,0x00}, \
|
||||
{0x0C,0x08,0x0E,0x0A,0x05,0x01,0x07,0x03,0x08,0x0C,0x0A,0x0E,0x01,0x05,0x03,0x07}, \
|
||||
{0x06,0x02,0x04,0x00,0x0F,0x0B,0x0D,0x09,0x02,0x06,0x00,0x04,0x0B,0x0F,0x09,0x0D}, \
|
||||
{0x07,0x03,0x05,0x01,0x0E,0x0A,0x0C,0x08,0x03,0x07,0x01,0x05,0x0A,0x0E,0x08,0x0C}, \
|
||||
{0x0D,0x09,0x0F,0x0B,0x09,0x00,0x06,0x02,0x09,0x0D,0x0B,0x0F,0x00,0x04,0x02,0x06}, \
|
||||
{0x0A,0x0E,0x08,0x0C,0x03,0x07,0x01,0x05,0x0E,0x0A,0x0C,0x08,0x07,0x03,0x05,0x01}, \
|
||||
{0x00,0x04,0x02,0x06,0x09,0x0D,0x0B,0x0F,0x04,0x00,0x06,0x02,0x0D,0x09,0x0F,0x0B}
|
||||
|
||||
#define HONDA_TABLE_B \
|
||||
{0x0C,0x08,0x0E,0x0A,0x05,0x01,0x07,0x03,0x08,0x0C,0x0A,0x0E,0x01,0x05,0x03,0x07}, \
|
||||
{0x06,0x02,0x04,0x00,0x0F,0x0B,0x0D,0x09,0x02,0x06,0x00,0x04,0x0B,0x0F,0x09,0x0D}, \
|
||||
{0x01,0x05,0x03,0x07,0x08,0x0C,0x0A,0x0E,0x05,0x01,0x07,0x03,0x0C,0x08,0x0E,0x0A}, \
|
||||
{0x0B,0x0F,0x09,0x0D,0x02,0x06,0x00,0x04,0x0F,0x0B,0x0D,0x09,0x06,0x02,0x04,0x00}, \
|
||||
{0x0A,0x0E,0x08,0x0C,0x03,0x07,0x01,0x05,0x0E,0x0A,0x0C,0x08,0x07,0x03,0x05,0x01}, \
|
||||
{0x00,0x04,0x02,0x06,0x09,0x0D,0x0B,0x0F,0x04,0x00,0x06,0x02,0x0D,0x09,0x0F,0x0B}, \
|
||||
{0x07,0x03,0x05,0x01,0x0E,0x0A,0x0C,0x08,0x03,0x07,0x01,0x05,0x0A,0x0E,0x08,0x0C}, \
|
||||
{0x0D,0x09,0x0F,0x0B,0x09,0x00,0x06,0x02,0x09,0x0D,0x0B,0x0F,0x00,0x04,0x02,0x06}, \
|
||||
{0x0F,0x0B,0x0D,0x09,0x06,0x02,0x04,0x00,0x0B,0x0F,0x09,0x0D,0x02,0x06,0x00,0x04}, \
|
||||
{0x05,0x01,0x07,0x03,0x0C,0x08,0x0E,0x0A,0x01,0x05,0x03,0x07,0x08,0x0C,0x0A,0x0E}, \
|
||||
{0x02,0x06,0x00,0x04,0x0B,0x0F,0x09,0x0D,0x06,0x02,0x04,0x00,0x0F,0x0B,0x0D,0x09}, \
|
||||
{0x08,0x0C,0x0A,0x0E,0x01,0x05,0x03,0x07,0x0C,0x08,0x0E,0x0A,0x05,0x01,0x07,0x03}, \
|
||||
{0x09,0x0D,0x0B,0x0F,0x00,0x04,0x02,0x06,0x0D,0x09,0x0F,0x0B,0x04,0x00,0x06,0x02}, \
|
||||
{0x03,0x07,0x01,0x05,0x0A,0x0E,0x08,0x0C,0x07,0x03,0x05,0x01,0x0E,0x0A,0x0C,0x08}, \
|
||||
{0x04,0x00,0x06,0x02,0x0D,0x09,0x0F,0x0B,0x00,0x04,0x02,0x06,0x09,0x0D,0x0B,0x0F}, \
|
||||
{0x0E,0x0A,0x0C,0x08,0x07,0x03,0x05,0x01,0x0A,0x0E,0x08,0x0C,0x03,0x07,0x01,0x05}
|
||||
|
||||
#define HONDA_TABLE_C \
|
||||
{0x02,0x08,0x0F,0x05,0x04,0x0E,0x09,0x03,0x01,0x0B,0x0C,0x06,0x07,0x0D,0x0A,0x00}, \
|
||||
{0x0B,0x01,0x06,0x0C,0x0D,0x07,0x00,0x0A,0x08,0x02,0x05,0x0F,0x0E,0x04,0x03,0x09}, \
|
||||
{0x06,0x0C,0x0B,0x01,0x00,0x0A,0x0D,0x07,0x05,0x0F,0x08,0x02,0x03,0x09,0x0E,0x04}, \
|
||||
{0x0F,0x05,0x02,0x08,0x09,0x03,0x04,0x0E,0x0C,0x06,0x01,0x0B,0x0A,0x00,0x07,0x0D}, \
|
||||
{0x08,0x02,0x05,0x0F,0x0E,0x04,0x03,0x09,0x0B,0x01,0x06,0x0C,0x0D,0x07,0x00,0x0A}, \
|
||||
{0x01,0x0B,0x0C,0x06,0x07,0x0D,0x0A,0x00,0x02,0x08,0x0F,0x05,0x04,0x0E,0x09,0x03}, \
|
||||
{0x0C,0x06,0x01,0x0B,0x0A,0x00,0x07,0x0D,0x0F,0x05,0x02,0x08,0x09,0x03,0x04,0x0E}, \
|
||||
{0x05,0x0F,0x08,0x02,0x03,0x09,0x0E,0x04,0x06,0x0C,0x0B,0x01,0x00,0x0A,0x0D,0x07}, \
|
||||
{0x09,0x03,0x04,0x0E,0x0F,0x05,0x02,0x08,0x0A,0x00,0x07,0x0D,0x0C,0x06,0x01,0x0B}, \
|
||||
{0x00,0x0A,0x0D,0x07,0x06,0x0C,0x0B,0x01,0x03,0x09,0x0E,0x04,0x05,0x0F,0x08,0x02}, \
|
||||
{0x0D,0x07,0x00,0x0A,0x0B,0x01,0x06,0x0C,0x0E,0x04,0x03,0x09,0x08,0x02,0x05,0x0F}, \
|
||||
{0x04,0x0E,0x09,0x03,0x02,0x08,0x0F,0x05,0x07,0x0D,0x0A,0x00,0x01,0x0B,0x0C,0x06}, \
|
||||
{0x03,0x09,0x0E,0x04,0x05,0x0F,0x08,0x02,0x00,0x0A,0x0D,0x07,0x06,0x0C,0x0B,0x01}, \
|
||||
{0x0A,0x00,0x07,0x0D,0x0C,0x06,0x01,0x0B,0x09,0x03,0x04,0x0E,0x0F,0x05,0x02,0x08}, \
|
||||
{0x07,0x0D,0x0A,0x00,0x01,0x0B,0x0C,0x06,0x04,0x0E,0x09,0x03,0x02,0x08,0x0F,0x05}, \
|
||||
{0x0E,0x04,0x03,0x09,0x08,0x02,0x05,0x0F,0x0D,0x07,0x00,0x0A,0x0B,0x01,0x06,0x0C}
|
||||
|
||||
#define HONDA_TABLE_D \
|
||||
{0x06,0x0C,0x03,0x09,0x00,0x0A,0x05,0x0F,0x0D,0x07,0x08,0x02,0x0B,0x01,0x0E,0x04}, \
|
||||
{0x07,0x0D,0x02,0x08,0x01,0x0B,0x04,0x0E,0x0C,0x06,0x09,0x03,0x0A,0x00,0x0F,0x05}, \
|
||||
{0x02,0x08,0x07,0x0D,0x04,0x0E,0x01,0x0B,0x09,0x03,0x0C,0x06,0x0F,0x05,0x0A,0x00}, \
|
||||
{0x03,0x09,0x06,0x0C,0x05,0x0F,0x00,0x0A,0x08,0x02,0x0D,0x07,0x0E,0x04,0x0B,0x01}, \
|
||||
{0x0C,0x06,0x09,0x03,0x0A,0x00,0x0F,0x05,0x07,0x0D,0x02,0x08,0x01,0x0B,0x04,0x0E}, \
|
||||
{0x0D,0x07,0x08,0x02,0x0B,0x01,0x0E,0x04,0x06,0x0C,0x03,0x09,0x00,0x0A,0x05,0x0F}, \
|
||||
{0x08,0x02,0x0D,0x07,0x0E,0x04,0x0B,0x01,0x03,0x09,0x06,0x0C,0x05,0x0F,0x00,0x0A}, \
|
||||
{0x09,0x03,0x0C,0x06,0x0F,0x05,0x0A,0x00,0x02,0x08,0x07,0x0D,0x04,0x0E,0x01,0x0B}, \
|
||||
{0x03,0x09,0x06,0x0C,0x05,0x0F,0x00,0x0A,0x08,0x02,0x0D,0x07,0x0E,0x04,0x0B,0x01}, \
|
||||
{0x02,0x08,0x07,0x0D,0x04,0x0E,0x01,0x0B,0x09,0x03,0x0C,0x06,0x0F,0x05,0x0A,0x00}, \
|
||||
{0x07,0x0D,0x02,0x08,0x01,0x0B,0x04,0x0E,0x0C,0x06,0x09,0x03,0x0A,0x00,0x0F,0x05}, \
|
||||
{0x06,0x0C,0x03,0x09,0x00,0x0A,0x05,0x0F,0x0D,0x07,0x08,0x02,0x0B,0x01,0x0E,0x04}, \
|
||||
{0x09,0x03,0x0C,0x06,0x0F,0x05,0x0A,0x00,0x02,0x08,0x07,0x0D,0x04,0x0E,0x01,0x0B}, \
|
||||
{0x08,0x02,0x0D,0x07,0x0E,0x04,0x0B,0x01,0x03,0x09,0x06,0x0C,0x05,0x0F,0x00,0x0A}, \
|
||||
{0x0D,0x07,0x08,0x02,0x0B,0x01,0x0E,0x04,0x06,0x0C,0x03,0x09,0x00,0x0A,0x05,0x0F}, \
|
||||
{0x0C,0x06,0x09,0x03,0x0A,0x00,0x0F,0x05,0x07,0x0D,0x02,0x08,0x01,0x0B,0x04,0x0E}
|
||||
|
||||
#define HONDA_TABLE_E \
|
||||
{0x01,0x00,0x05,0x04,0x0B,0x0A,0x0F,0x0E,0x04,0x05,0x00,0x01,0x0E,0x0F,0x0A,0x0B}, \
|
||||
{0x0F,0x0E,0x0B,0x0A,0x05,0x04,0x01,0x00,0x0A,0x0B,0x0E,0x0F,0x00,0x01,0x04,0x05}, \
|
||||
{0x0E,0x0F,0x0A,0x0B,0x04,0x05,0x00,0x01,0x0B,0x0A,0x0F,0x0E,0x01,0x00,0x05,0x04}, \
|
||||
{0x00,0x01,0x04,0x05,0x0A,0x0B,0x0E,0x0F,0x05,0x04,0x01,0x00,0x0F,0x0E,0x0B,0x0A}, \
|
||||
{0x02,0x03,0x06,0x07,0x08,0x09,0x0C,0x0D,0x07,0x06,0x03,0x02,0x0D,0x0C,0x09,0x08}, \
|
||||
{0x0C,0x0D,0x08,0x09,0x06,0x07,0x02,0x03,0x09,0x08,0x0D,0x0C,0x03,0x02,0x07,0x06}, \
|
||||
{0x0D,0x0C,0x09,0x08,0x07,0x06,0x03,0x02,0x08,0x09,0x0C,0x0D,0x02,0x03,0x06,0x07}, \
|
||||
{0x03,0x02,0x07,0x06,0x09,0x08,0x0D,0x0C,0x06,0x07,0x02,0x03,0x0C,0x0D,0x08,0x09}, \
|
||||
{0x04,0x05,0x00,0x01,0x0E,0x0F,0x0A,0x0B,0x01,0x00,0x05,0x04,0x0B,0x0A,0x0F,0x0E}, \
|
||||
{0x0A,0x0B,0x0E,0x0F,0x00,0x01,0x04,0x05,0x0F,0x0E,0x0B,0x0A,0x05,0x04,0x01,0x00}, \
|
||||
{0x0B,0x0A,0x0F,0x0E,0x01,0x00,0x05,0x04,0x0E,0x0F,0x0A,0x0B,0x04,0x05,0x00,0x01}, \
|
||||
{0x05,0x04,0x01,0x00,0x0F,0x0E,0x0B,0x0A,0x00,0x01,0x04,0x05,0x0A,0x0B,0x0E,0x0F}, \
|
||||
{0x07,0x06,0x03,0x02,0x0D,0x0C,0x09,0x08,0x02,0x03,0x06,0x07,0x08,0x09,0x0C,0x0D}, \
|
||||
{0x09,0x08,0x0D,0x0C,0x03,0x02,0x07,0x06,0x0C,0x0D,0x08,0x09,0x06,0x07,0x02,0x03}, \
|
||||
{0x08,0x09,0x0C,0x0D,0x02,0x0A,0x06,0x07,0x0D,0x0C,0x09,0x08,0x07,0x06,0x03,0x02}, \
|
||||
{0x06,0x07,0x02,0x03,0x0C,0x0D,0x08,0x09,0x03,0x02,0x07,0x06,0x09,0x08,0x0D,0x0C}
|
||||
|
||||
/* ── CC1101 FSK preset ──────────────────────────────────────────────────── */
|
||||
#define HONDA_CC1101_PRESET_DATA \
|
||||
0x02, 0x0D, \
|
||||
0x0B, 0x06, \
|
||||
0x08, 0x32, \
|
||||
0x07, 0x04, \
|
||||
0x14, 0x00, \
|
||||
0x13, 0x02, \
|
||||
0x12, 0x04, \
|
||||
0x11, 0x36, \
|
||||
0x10, 0x69, \
|
||||
0x15, 0x32, \
|
||||
0x18, 0x18, \
|
||||
0x19, 0x16, \
|
||||
0x1D, 0x91, \
|
||||
0x1C, 0x00, \
|
||||
0x1B, 0x07, \
|
||||
0x20, 0xFB, \
|
||||
0x22, 0x10, \
|
||||
0x21, 0x56, \
|
||||
0x00, 0x00, \
|
||||
0xC0, 0x00
|
||||
|
||||
/* ── External declarations ──────────────────────────────────────────────── */
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_honda_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_honda_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_honda;
|
||||
|
||||
void* subghz_protocol_decoder_honda_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_honda_free(void* context);
|
||||
void subghz_protocol_decoder_honda_reset(void* context);
|
||||
void subghz_protocol_decoder_honda_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_honda_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_serialize(
|
||||
void* context, FlipperFormat* flipper_format, SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_deserialize(
|
||||
void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_honda_get_string(void* context, FuriString* output);
|
||||
|
||||
void* subghz_protocol_encoder_honda_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_encoder_honda_free(void* context);
|
||||
void subghz_protocol_encoder_honda_stop(void* context);
|
||||
LevelDuration subghz_protocol_encoder_honda_yield(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_encoder_honda_deserialize(
|
||||
void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_encoder_honda_set_button(void* context, uint8_t btn);
|
||||
|
||||
uint8_t subghz_protocol_honda_btn_to_custom(uint8_t btn);
|
||||
uint8_t subghz_protocol_honda_custom_to_btn(uint8_t custom);
|
||||
@@ -0,0 +1,796 @@
|
||||
#include "honda_static.h"
|
||||
|
||||
#define HONDA_STATIC_BIT_COUNT 64
|
||||
#define HONDA_STATIC_MIN_SYMBOLS 36
|
||||
#define HONDA_STATIC_SHORT_BASE_US 28
|
||||
#define HONDA_STATIC_SHORT_SPAN_US 70
|
||||
#define HONDA_STATIC_LONG_BASE_US 61
|
||||
#define HONDA_STATIC_LONG_SPAN_US 130
|
||||
#define HONDA_STATIC_SYNC_TIME_US 700
|
||||
#define HONDA_STATIC_ELEMENT_TIME_US 63
|
||||
#define HONDA_STATIC_UPLOAD_CAPACITY 512
|
||||
#define HONDA_STATIC_SYMBOL_CAPACITY 512
|
||||
#define HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT 160
|
||||
#define HONDA_STATIC_PREAMBLE_MAX_TRANSITIONS 19
|
||||
|
||||
static const uint8_t honda_static_encoder_button_map[4] = {0x02, 0x04, 0x08, 0x05};
|
||||
static const char* const honda_static_button_names[9] = {
|
||||
"LOCK",
|
||||
"UNLOCK",
|
||||
"UNKNOWN",
|
||||
"TRUNK",
|
||||
"REMOTE START",
|
||||
"UNKNOWN",
|
||||
"UNKNOWN",
|
||||
"PANIC",
|
||||
"LOCK x2",
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t button;
|
||||
uint8_t _reserved_01[3];
|
||||
uint32_t serial;
|
||||
uint32_t counter;
|
||||
uint8_t checksum;
|
||||
uint8_t _reserved_0d[3];
|
||||
} HondaStaticFields;
|
||||
|
||||
struct SubGhzProtocolDecoderHondaStatic {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
uint32_t _reserved_0c;
|
||||
|
||||
SubGhzBlockDecoder decoder;
|
||||
uint32_t _reserved_20;
|
||||
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint16_t packet_bit_count;
|
||||
uint8_t _reserved_5a;
|
||||
uint8_t _reserved_5b;
|
||||
|
||||
uint8_t symbols[HONDA_STATIC_SYMBOL_CAPACITY];
|
||||
uint16_t symbols_count;
|
||||
HondaStaticFields decoded;
|
||||
uint8_t decoded_valid;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderHondaStatic {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
HondaStaticFields decoded;
|
||||
uint8_t tx_button;
|
||||
uint8_t _reserved_69[3];
|
||||
};
|
||||
|
||||
static uint64_t honda_static_bytes_to_u64_be(const uint8_t bytes[8]) {
|
||||
uint64_t value = 0;
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
value = (value << 8U) | bytes[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void honda_static_u64_to_bytes_be(uint64_t value, uint8_t bytes[8]) {
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
bytes[7U - i] = (uint8_t)(value & 0xFFU);
|
||||
value >>= 8U;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t honda_static_get_bits(const uint8_t* data, uint8_t start, uint8_t count) {
|
||||
uint32_t value = 0;
|
||||
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
const uint8_t bit_index = start + i;
|
||||
const uint8_t byte = data[bit_index >> 3U];
|
||||
const uint8_t shift = (uint8_t)(~bit_index) & 0x07U;
|
||||
value = (value << 1U) | ((byte >> shift) & 1U);
|
||||
}
|
||||
|
||||
return (uint8_t)value;
|
||||
}
|
||||
|
||||
static uint32_t honda_static_get_bits_u32(const uint8_t* data, uint8_t start, uint8_t count) {
|
||||
uint32_t value = 0;
|
||||
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
const uint8_t bit_index = start + i;
|
||||
const uint8_t byte = data[bit_index >> 3U];
|
||||
const uint8_t shift = (uint8_t)(~bit_index) & 0x07U;
|
||||
value = (value << 1U) | ((byte >> shift) & 1U);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void honda_static_set_bits(uint8_t* data, uint8_t start, uint8_t count, uint32_t value) {
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
const uint8_t bit_index = start + i;
|
||||
const uint8_t byte_index = bit_index >> 3U;
|
||||
const uint8_t shift = ((uint8_t)~bit_index) & 0x07U;
|
||||
const uint8_t mask = (uint8_t)(1U << shift);
|
||||
const bool bit = ((value >> (count - 1U - i)) & 1U) != 0U;
|
||||
|
||||
if(bit) {
|
||||
data[byte_index] |= mask;
|
||||
} else {
|
||||
data[byte_index] &= (uint8_t)~mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t honda_static_level_u8(bool level) {
|
||||
return level ? 1U : 0U;
|
||||
}
|
||||
|
||||
static uint8_t honda_static_sym_u8(uint8_t stored) {
|
||||
return stored ? 1U : 0U;
|
||||
}
|
||||
|
||||
static uint8_t honda_static_reverse_bits8(uint8_t value) {
|
||||
value = (uint8_t)(((value >> 4U) | (value << 4U)) & 0xFFU);
|
||||
value = (uint8_t)(((value & 0x33U) << 2U) | ((value >> 2U) & 0x33U));
|
||||
value = (uint8_t)(((value & 0x55U) << 1U) | ((value >> 1U) & 0x55U));
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool honda_static_is_valid_button(uint8_t button) {
|
||||
if(button > 9U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((0x336U >> button) & 1U) != 0U;
|
||||
}
|
||||
|
||||
static bool honda_static_is_valid_serial(uint32_t serial) {
|
||||
return (serial != 0U) && (serial != 0x0FFFFFFFU);
|
||||
}
|
||||
|
||||
static uint8_t honda_static_encoder_remap_button(uint8_t button) {
|
||||
if(button < 2U) {
|
||||
return 1U;
|
||||
}
|
||||
button -= 2U;
|
||||
if(button <= 3U) {
|
||||
return honda_static_encoder_button_map[button];
|
||||
}
|
||||
|
||||
return 1U;
|
||||
}
|
||||
|
||||
static const char* honda_static_button_name(uint8_t button) {
|
||||
if((button >= 1U) && (button <= COUNT_OF(honda_static_button_names))) {
|
||||
return honda_static_button_names[button - 1U];
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static uint8_t honda_static_compact_bytes_checksum(const uint8_t compact[8]) {
|
||||
const uint8_t canonical[7] = {
|
||||
(uint8_t)((compact[0] << 4U) | (compact[1] >> 4U)),
|
||||
(uint8_t)((compact[1] << 4U) | (compact[2] >> 4U)),
|
||||
(uint8_t)((compact[2] << 4U) | (compact[3] >> 4U)),
|
||||
(uint8_t)((compact[3] << 4U) | (compact[4] >> 4U)),
|
||||
compact[5],
|
||||
compact[6],
|
||||
compact[7],
|
||||
};
|
||||
|
||||
uint8_t checksum = 0U;
|
||||
for(size_t i = 0; i < COUNT_OF(canonical); i++) {
|
||||
checksum ^= canonical[i];
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static void honda_static_unpack_compact(uint64_t key, HondaStaticFields* fields) {
|
||||
uint8_t compact[8];
|
||||
honda_static_u64_to_bytes_be(key, compact);
|
||||
|
||||
memset(fields, 0, sizeof(*fields));
|
||||
fields->button = compact[0] & 0x0FU;
|
||||
fields->serial = ((uint32_t)compact[1] << 20U) | ((uint32_t)compact[2] << 12U) |
|
||||
((uint32_t)compact[3] << 4U) | ((uint32_t)compact[4] >> 4U);
|
||||
fields->counter = ((uint32_t)compact[5] << 16U) | ((uint32_t)compact[6] << 8U) |
|
||||
(uint32_t)compact[7];
|
||||
fields->checksum = honda_static_compact_bytes_checksum(compact);
|
||||
}
|
||||
|
||||
static uint64_t honda_static_pack_compact(const HondaStaticFields* fields) {
|
||||
uint8_t compact[8];
|
||||
|
||||
compact[0] = fields->button & 0x0FU;
|
||||
compact[1] = (uint8_t)(fields->serial >> 20U);
|
||||
compact[2] = (uint8_t)(fields->serial >> 12U);
|
||||
compact[3] = (uint8_t)(fields->serial >> 4U);
|
||||
compact[4] = (uint8_t)(fields->serial << 4U);
|
||||
compact[5] = (uint8_t)(fields->counter >> 16U);
|
||||
compact[6] = (uint8_t)(fields->counter >> 8U);
|
||||
compact[7] = (uint8_t)fields->counter;
|
||||
|
||||
return honda_static_bytes_to_u64_be(compact);
|
||||
}
|
||||
|
||||
static void honda_static_build_packet_bytes(const HondaStaticFields* fields, uint8_t packet[8]) {
|
||||
memset(packet, 0, 8);
|
||||
|
||||
honda_static_set_bits(packet, 0, 4, fields->button & 0x0FU);
|
||||
honda_static_set_bits(packet, 4, 28, fields->serial);
|
||||
honda_static_set_bits(packet, 32, 24, fields->counter);
|
||||
|
||||
uint8_t checksum = 0U;
|
||||
for(size_t i = 0; i < 7; i++) {
|
||||
checksum ^= packet[i];
|
||||
}
|
||||
|
||||
honda_static_set_bits(packet, 56, 8, checksum);
|
||||
}
|
||||
|
||||
static bool
|
||||
honda_static_validate_forward_packet(const uint8_t packet[9], HondaStaticFields* fields) {
|
||||
const uint8_t button = honda_static_get_bits(packet, 0, 4);
|
||||
const uint32_t serial = honda_static_get_bits_u32(packet, 4, 28);
|
||||
const uint32_t counter = honda_static_get_bits_u32(packet, 32, 24);
|
||||
const uint8_t checksum = honda_static_get_bits(packet, 56, 8);
|
||||
|
||||
uint8_t checksum_calc = 0U;
|
||||
for(size_t i = 0; i < 7; i++) {
|
||||
checksum_calc ^= packet[i];
|
||||
}
|
||||
|
||||
if(checksum != checksum_calc) {
|
||||
return false;
|
||||
}
|
||||
if(!honda_static_is_valid_button(button)) {
|
||||
return false;
|
||||
}
|
||||
if(!honda_static_is_valid_serial(serial)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fields->button = button;
|
||||
fields->serial = serial;
|
||||
fields->counter = counter;
|
||||
fields->checksum = checksum;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
honda_static_validate_reverse_packet(const uint8_t packet[9], HondaStaticFields* fields) {
|
||||
uint8_t reversed[9];
|
||||
for(size_t i = 0; i < COUNT_OF(reversed); i++) {
|
||||
reversed[i] = honda_static_reverse_bits8(packet[i]);
|
||||
}
|
||||
|
||||
const uint8_t button = honda_static_get_bits(reversed, 0, 4);
|
||||
const uint32_t serial = honda_static_get_bits_u32(reversed, 4, 28);
|
||||
const uint32_t counter = honda_static_get_bits_u32(reversed, 32, 24);
|
||||
|
||||
uint8_t checksum = 0U;
|
||||
for(size_t i = 0; i < 7; i++) {
|
||||
checksum ^= reversed[i];
|
||||
}
|
||||
|
||||
if(!honda_static_is_valid_button(button)) {
|
||||
return false;
|
||||
}
|
||||
if(!honda_static_is_valid_serial(serial)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fields->button = button;
|
||||
fields->serial = serial;
|
||||
fields->counter = counter;
|
||||
fields->checksum = checksum;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool honda_static_manchester_pack_64(
|
||||
const uint8_t* symbols,
|
||||
uint16_t count,
|
||||
uint16_t start_pos,
|
||||
bool inverted,
|
||||
uint8_t packet[9],
|
||||
uint16_t* out_bit_count) {
|
||||
memset(packet, 0, 9);
|
||||
|
||||
uint16_t pos = start_pos;
|
||||
uint16_t bit_count = 0U;
|
||||
|
||||
while((uint16_t)(pos + 1U) < count) {
|
||||
if(bit_count >= HONDA_STATIC_BIT_COUNT) {
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t a = honda_static_sym_u8(symbols[pos]);
|
||||
const uint8_t b = honda_static_sym_u8(symbols[pos + 1U]);
|
||||
|
||||
if(a == b) {
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bit = false;
|
||||
if(inverted) {
|
||||
bit = (a == 0U) && (b == 1U);
|
||||
} else {
|
||||
bit = (a == 1U) && (b == 0U);
|
||||
}
|
||||
|
||||
if(bit) {
|
||||
packet[bit_count >> 3U] |= (uint8_t)(1U << (((uint8_t)~bit_count) & 0x07U));
|
||||
}
|
||||
|
||||
bit_count++;
|
||||
pos += 2U;
|
||||
}
|
||||
|
||||
if(out_bit_count) {
|
||||
*out_bit_count = bit_count;
|
||||
}
|
||||
|
||||
return bit_count >= HONDA_STATIC_BIT_COUNT;
|
||||
}
|
||||
|
||||
static bool honda_static_parse_symbols(SubGhzProtocolDecoderHondaStatic* instance, bool inverted) {
|
||||
const uint16_t count = instance->symbols_count;
|
||||
const uint8_t* symbols = instance->symbols;
|
||||
|
||||
uint16_t index = 1U;
|
||||
uint16_t transitions = 0U;
|
||||
|
||||
while(index < count) {
|
||||
if(honda_static_sym_u8(symbols[index]) != honda_static_sym_u8(symbols[index - 1U])) {
|
||||
transitions++;
|
||||
} else {
|
||||
if(transitions > HONDA_STATIC_PREAMBLE_MAX_TRANSITIONS) {
|
||||
break;
|
||||
}
|
||||
transitions = 0U;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if(index >= count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while(((uint16_t)(index + 1U) < count) &&
|
||||
(honda_static_sym_u8(symbols[index]) == honda_static_sym_u8(symbols[index + 1U]))) {
|
||||
index++;
|
||||
}
|
||||
|
||||
const uint16_t data_start = index;
|
||||
|
||||
uint8_t packet[9] = {0};
|
||||
uint16_t bit_count = 0U;
|
||||
|
||||
if(!honda_static_manchester_pack_64(symbols, count, data_start, inverted, packet, &bit_count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(honda_static_validate_forward_packet(packet, &instance->decoded)) {
|
||||
instance->decoded_valid = 1U;
|
||||
return true;
|
||||
}
|
||||
|
||||
if(inverted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(honda_static_validate_reverse_packet(packet, &instance->decoded)) {
|
||||
instance->decoded_valid = 1U;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void honda_static_decoder_commit(SubGhzProtocolDecoderHondaStatic* instance) {
|
||||
instance->packet_bit_count = HONDA_STATIC_BIT_COUNT;
|
||||
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
|
||||
instance->generic.data = honda_static_pack_compact(&instance->decoded);
|
||||
instance->generic.serial = instance->decoded.serial;
|
||||
instance->generic.cnt = instance->decoded.counter;
|
||||
instance->generic.btn = instance->decoded.button;
|
||||
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
}
|
||||
|
||||
static void honda_static_build_upload(SubGhzProtocolEncoderHondaStatic* instance) {
|
||||
uint8_t packet[8];
|
||||
honda_static_build_packet_bytes(&instance->decoded, packet);
|
||||
|
||||
size_t index = 0U;
|
||||
instance->encoder.upload[index++] = level_duration_make(true, HONDA_STATIC_SYNC_TIME_US);
|
||||
|
||||
for(size_t i = 0; i < HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT; i++) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make((i & 1U) != 0U, HONDA_STATIC_ELEMENT_TIME_US);
|
||||
}
|
||||
|
||||
for(uint8_t bit = 0U; bit < HONDA_STATIC_BIT_COUNT; bit++) {
|
||||
const bool value = ((packet[bit >> 3U] >> (((uint8_t)~bit) & 0x07U)) & 1U) != 0U;
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(!value, HONDA_STATIC_ELEMENT_TIME_US);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(value, HONDA_STATIC_ELEMENT_TIME_US);
|
||||
}
|
||||
|
||||
const bool last_bit = (packet[7] & 1U) != 0U;
|
||||
instance->encoder.upload[index++] = level_duration_make(!last_bit, HONDA_STATIC_SYNC_TIME_US);
|
||||
|
||||
instance->encoder.front = 0U;
|
||||
instance->encoder.size_upload = index;
|
||||
}
|
||||
|
||||
static bool honda_static_read_hex_u64(FlipperFormat* ff, uint64_t* out_key) {
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
if(!tmp) return false;
|
||||
bool ok = false;
|
||||
do {
|
||||
if(!flipper_format_rewind(ff) || !flipper_format_read_string(ff, "Key", tmp)) break;
|
||||
|
||||
const char* key_str = furi_string_get_cstr(tmp);
|
||||
uint64_t key = 0;
|
||||
size_t hex_pos = 0;
|
||||
for(size_t i = 0; key_str[i] && hex_pos < 16; i++) {
|
||||
char c = key_str[i];
|
||||
if(c == ' ') continue;
|
||||
uint8_t nibble;
|
||||
if(c >= '0' && c <= '9')
|
||||
nibble = c - '0';
|
||||
else if(c >= 'A' && c <= 'F')
|
||||
nibble = c - 'A' + 10;
|
||||
else if(c >= 'a' && c <= 'f')
|
||||
nibble = c - 'a' + 10;
|
||||
else
|
||||
break;
|
||||
key = (key << 4) | nibble;
|
||||
hex_pos++;
|
||||
}
|
||||
if(hex_pos != 16) break;
|
||||
*out_key = key;
|
||||
ok = true;
|
||||
} while(false);
|
||||
furi_string_free(tmp);
|
||||
return ok;
|
||||
}
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_honda_static_decoder = {
|
||||
.alloc = subghz_protocol_decoder_honda_static_alloc,
|
||||
.free = subghz_protocol_decoder_honda_static_free,
|
||||
.feed = subghz_protocol_decoder_honda_static_feed,
|
||||
.reset = subghz_protocol_decoder_honda_static_reset,
|
||||
.get_hash_data = subghz_protocol_decoder_honda_static_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_honda_static_serialize,
|
||||
.deserialize = subghz_protocol_decoder_honda_static_deserialize,
|
||||
.get_string = subghz_protocol_decoder_honda_static_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_honda_static_encoder = {
|
||||
.alloc = subghz_protocol_encoder_honda_static_alloc,
|
||||
.free = subghz_protocol_encoder_honda_static_free,
|
||||
.deserialize = subghz_protocol_encoder_honda_static_deserialize,
|
||||
.stop = subghz_protocol_encoder_honda_static_stop,
|
||||
.yield = subghz_protocol_encoder_honda_static_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol honda_static_protocol = {
|
||||
.name = HONDA_STATIC_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 |
|
||||
SubGhzProtocolFlag_AM | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load |
|
||||
SubGhzProtocolFlag_Send,
|
||||
.decoder = &subghz_protocol_honda_static_decoder,
|
||||
.encoder = &subghz_protocol_honda_static_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = malloc(sizeof(SubGhzProtocolEncoderHondaStatic));
|
||||
furi_check(instance);
|
||||
memset(instance, 0, sizeof(*instance));
|
||||
|
||||
instance->base.protocol = &honda_static_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->encoder.repeat = 3U;
|
||||
instance->encoder.upload = malloc(HONDA_STATIC_UPLOAD_CAPACITY * sizeof(LevelDuration));
|
||||
furi_check(instance->encoder.upload);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_honda_static_free(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = context;
|
||||
SubGhzProtocolStatus status = SubGhzProtocolStatusError;
|
||||
|
||||
instance->encoder.is_running = false;
|
||||
instance->encoder.front = 0U;
|
||||
|
||||
do {
|
||||
FuriString* pstr = furi_string_alloc();
|
||||
if(!pstr) break;
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_read_string(flipper_format, "Protocol", pstr)) {
|
||||
furi_string_free(pstr);
|
||||
break;
|
||||
}
|
||||
if(!furi_string_equal(pstr, instance->base.protocol->name)) {
|
||||
furi_string_free(pstr);
|
||||
break;
|
||||
}
|
||||
furi_string_free(pstr);
|
||||
|
||||
uint64_t key = 0;
|
||||
if(!honda_static_read_hex_u64(flipper_format, &key)) {
|
||||
break;
|
||||
}
|
||||
|
||||
honda_static_unpack_compact(key, &instance->decoded);
|
||||
|
||||
uint32_t serial = instance->decoded.serial;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Serial", &serial, 1)) {
|
||||
instance->decoded.serial = serial;
|
||||
}
|
||||
|
||||
uint32_t btn_u32 = 0;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Btn", &btn_u32, 1)) {
|
||||
uint8_t b = (uint8_t)btn_u32;
|
||||
if(honda_static_is_valid_button(b)) {
|
||||
instance->decoded.button = b;
|
||||
} else if(b >= 2U && b <= 5U) {
|
||||
instance->decoded.button = honda_static_encoder_remap_button(b);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cnt = instance->decoded.counter & 0x00FFFFFFU;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Cnt", &cnt, 1)) {
|
||||
instance->decoded.counter = cnt & 0x00FFFFFFU;
|
||||
}
|
||||
|
||||
instance->generic.serial = instance->decoded.serial;
|
||||
instance->generic.cnt = instance->decoded.counter;
|
||||
instance->generic.btn = instance->decoded.button;
|
||||
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
|
||||
instance->generic.data = honda_static_pack_compact(&instance->decoded);
|
||||
|
||||
uint8_t key_data[8];
|
||||
honda_static_u64_to_bytes_be(instance->generic.data, key_data);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(key_data))) {
|
||||
status = SubGhzProtocolStatusErrorParserKey;
|
||||
break;
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1)) {
|
||||
instance->encoder.repeat = 3U;
|
||||
}
|
||||
|
||||
honda_static_build_upload(instance);
|
||||
instance->encoder.is_running = true;
|
||||
status = SubGhzProtocolStatusOk;
|
||||
} while(false);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_honda_static_stop(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_honda_static_yield(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = context;
|
||||
if((instance->encoder.repeat == 0U) || !instance->encoder.is_running) {
|
||||
instance->encoder.is_running = false;
|
||||
return level_duration_reset();
|
||||
}
|
||||
|
||||
const LevelDuration current = instance->encoder.upload[instance->encoder.front];
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
instance->encoder.front = 0U;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_honda_static_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = malloc(sizeof(SubGhzProtocolDecoderHondaStatic));
|
||||
furi_check(instance);
|
||||
memset(instance, 0, sizeof(*instance));
|
||||
|
||||
instance->base.protocol = &honda_static_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_static_free(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_static_reset(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
instance->symbols_count = 0U;
|
||||
instance->decoded_valid = 0U;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_static_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
|
||||
const uint8_t sym = honda_static_level_u8(level);
|
||||
|
||||
if((duration >= HONDA_STATIC_SHORT_BASE_US) &&
|
||||
((duration - HONDA_STATIC_SHORT_BASE_US) <= HONDA_STATIC_SHORT_SPAN_US)) {
|
||||
if(instance->symbols_count < HONDA_STATIC_SYMBOL_CAPACITY) {
|
||||
instance->symbols[instance->symbols_count++] = sym;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if((duration >= HONDA_STATIC_LONG_BASE_US) &&
|
||||
((duration - HONDA_STATIC_LONG_BASE_US) <= HONDA_STATIC_LONG_SPAN_US)) {
|
||||
if((uint16_t)(instance->symbols_count + 2U) <= HONDA_STATIC_SYMBOL_CAPACITY) {
|
||||
instance->symbols[instance->symbols_count++] = sym;
|
||||
instance->symbols[instance->symbols_count++] = sym;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t sc = instance->symbols_count;
|
||||
|
||||
if(sc >= HONDA_STATIC_MIN_SYMBOLS) {
|
||||
if(honda_static_parse_symbols(instance, true) ||
|
||||
honda_static_parse_symbols(instance, false)) {
|
||||
honda_static_decoder_commit(instance);
|
||||
}
|
||||
}
|
||||
|
||||
instance->symbols_count = 0U;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_honda_static_get_hash_data(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
const uint64_t data = instance->generic.data;
|
||||
|
||||
return (uint8_t)(data ^ (data >> 8U) ^ (data >> 16U) ^ (data >> 24U) ^ (data >> 32U) ^
|
||||
(data >> 40U) ^ (data >> 48U) ^ (data >> 56U));
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_static_get_string(void* context, FuriString* output) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
if(!instance->decoded_valid && (instance->generic.data != 0ULL)) {
|
||||
honda_static_unpack_compact(instance->generic.data, &instance->decoded);
|
||||
instance->decoded_valid = 1U;
|
||||
}
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %ubit\r\n"
|
||||
"Key:%016llX\r\n",
|
||||
instance->generic.protocol_name,
|
||||
instance->packet_bit_count ? instance->packet_bit_count : HONDA_STATIC_BIT_COUNT,
|
||||
(unsigned long long)instance->generic.data);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"Btn:%s (0x%X)\r\n"
|
||||
"Ser:%07lX\r\n"
|
||||
"Cnt:%06lX Chk:%02X\r\n",
|
||||
honda_static_button_name(instance->decoded.button),
|
||||
instance->decoded.button,
|
||||
(unsigned long)instance->decoded.serial,
|
||||
(unsigned long)instance->decoded.counter,
|
||||
instance->decoded.checksum);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_static_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
|
||||
|
||||
SubGhzProtocolStatus ret =
|
||||
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
|
||||
if(ret == SubGhzProtocolStatusOk) {
|
||||
flipper_format_write_uint32(flipper_format, "Serial", &instance->decoded.serial, 1);
|
||||
|
||||
uint32_t temp = instance->decoded.button;
|
||||
flipper_format_write_uint32(flipper_format, "Btn", &temp, 1);
|
||||
|
||||
flipper_format_write_uint32(flipper_format, "Cnt", &instance->decoded.counter, 1);
|
||||
|
||||
temp = instance->decoded.checksum;
|
||||
flipper_format_write_uint32(flipper_format, "Checksum", &temp, 1);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
SubGhzProtocolStatus status = subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic, flipper_format, HONDA_STATIC_BIT_COUNT);
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
return status;
|
||||
}
|
||||
|
||||
instance->packet_bit_count = HONDA_STATIC_BIT_COUNT;
|
||||
honda_static_unpack_compact(instance->generic.data, &instance->decoded);
|
||||
instance->decoded_valid = 1U;
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
uint32_t s = 0, b = 0, c = 0, k = 0;
|
||||
if(flipper_format_read_uint32(flipper_format, "Serial", &s, 1)) {
|
||||
instance->decoded.serial = s;
|
||||
}
|
||||
if(flipper_format_read_uint32(flipper_format, "Btn", &b, 1)) {
|
||||
instance->decoded.button = (uint8_t)b;
|
||||
}
|
||||
if(flipper_format_read_uint32(flipper_format, "Cnt", &c, 1)) {
|
||||
instance->decoded.counter = c & 0x00FFFFFFU;
|
||||
}
|
||||
if(flipper_format_read_uint32(flipper_format, "Checksum", &k, 1)) {
|
||||
instance->decoded.checksum = (uint8_t)k;
|
||||
}
|
||||
|
||||
instance->generic.serial = instance->decoded.serial;
|
||||
instance->generic.cnt = instance->decoded.counter;
|
||||
instance->generic.btn = instance->decoded.button;
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,811 @@
|
||||
#include "honda_static.h"
|
||||
|
||||
#define HONDA_STATIC_BIT_COUNT 64
|
||||
#define HONDA_STATIC_MIN_SYMBOLS 36
|
||||
#define HONDA_STATIC_SHORT_BASE_US 28
|
||||
#define HONDA_STATIC_SHORT_SPAN_US 70
|
||||
#define HONDA_STATIC_LONG_BASE_US 61
|
||||
#define HONDA_STATIC_LONG_SPAN_US 130
|
||||
#define HONDA_STATIC_SYNC_TIME_US 700
|
||||
#define HONDA_STATIC_ELEMENT_TIME_US 63
|
||||
#define HONDA_STATIC_UPLOAD_CAPACITY \
|
||||
(1U + HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT + (2U * HONDA_STATIC_BIT_COUNT) + 1U)
|
||||
#define HONDA_STATIC_SYMBOL_CAPACITY 512
|
||||
#define HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT 160
|
||||
#define HONDA_STATIC_PREAMBLE_MAX_TRANSITIONS 19
|
||||
#define HONDA_STATIC_SYMBOL_BYTE_COUNT ((HONDA_STATIC_SYMBOL_CAPACITY + 7U) / 8U)
|
||||
|
||||
static const uint8_t honda_static_encoder_button_map[4] = {0x02, 0x04, 0x08, 0x05};
|
||||
static const char* const honda_static_button_names[9] = {
|
||||
"Lock",
|
||||
"Unlock",
|
||||
"Unknown",
|
||||
"Trunk",
|
||||
"Remote Start",
|
||||
"Unknown",
|
||||
"Unknown",
|
||||
"Panic",
|
||||
"Lock x2",
|
||||
};
|
||||
|
||||
typedef struct {
|
||||
uint8_t button;
|
||||
uint8_t _reserved_01[3];
|
||||
uint32_t serial;
|
||||
uint32_t counter;
|
||||
uint8_t checksum;
|
||||
uint8_t _reserved_0d[3];
|
||||
} HondaStaticFields;
|
||||
|
||||
struct SubGhzProtocolDecoderHondaStatic {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint8_t symbols[HONDA_STATIC_SYMBOL_BYTE_COUNT];
|
||||
uint16_t symbols_count;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderHondaStatic {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
HondaStaticFields decoded;
|
||||
uint8_t tx_button;
|
||||
uint8_t _reserved_69[3];
|
||||
};
|
||||
|
||||
static void honda_static_decoder_commit(
|
||||
SubGhzProtocolDecoderHondaStatic* instance,
|
||||
const HondaStaticFields* decoded);
|
||||
|
||||
static uint64_t honda_static_bytes_to_u64_be(const uint8_t bytes[8]) {
|
||||
uint64_t value = 0;
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
value = (value << 8U) | bytes[i];
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static void honda_static_u64_to_bytes_be(uint64_t value, uint8_t bytes[8]) {
|
||||
for(size_t i = 0; i < 8; i++) {
|
||||
bytes[7U - i] = (uint8_t)(value & 0xFFU);
|
||||
value >>= 8U;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t honda_static_get_bits(const uint8_t* data, uint8_t start, uint8_t count) {
|
||||
uint32_t value = 0;
|
||||
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
const uint8_t bit_index = start + i;
|
||||
const uint8_t byte = data[bit_index >> 3U];
|
||||
const uint8_t shift = (uint8_t)(~bit_index) & 0x07U;
|
||||
value = (value << 1U) | ((byte >> shift) & 1U);
|
||||
}
|
||||
|
||||
return (uint8_t)value;
|
||||
}
|
||||
|
||||
static uint32_t honda_static_get_bits_u32(const uint8_t* data, uint8_t start, uint8_t count) {
|
||||
uint32_t value = 0;
|
||||
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
const uint8_t bit_index = start + i;
|
||||
const uint8_t byte = data[bit_index >> 3U];
|
||||
const uint8_t shift = (uint8_t)(~bit_index) & 0x07U;
|
||||
value = (value << 1U) | ((byte >> shift) & 1U);
|
||||
}
|
||||
|
||||
return value;
|
||||
}
|
||||
|
||||
static void honda_static_set_bits(uint8_t* data, uint8_t start, uint8_t count, uint32_t value) {
|
||||
for(uint8_t i = 0; i < count; i++) {
|
||||
const uint8_t bit_index = start + i;
|
||||
const uint8_t byte_index = bit_index >> 3U;
|
||||
const uint8_t shift = ((uint8_t)~bit_index) & 0x07U;
|
||||
const uint8_t mask = (uint8_t)(1U << shift);
|
||||
const bool bit = ((value >> (count - 1U - i)) & 1U) != 0U;
|
||||
|
||||
if(bit) {
|
||||
data[byte_index] |= mask;
|
||||
} else {
|
||||
data[byte_index] &= (uint8_t)~mask;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t honda_static_level_u8(bool level) {
|
||||
return level ? 1U : 0U;
|
||||
}
|
||||
|
||||
static void honda_static_symbol_set(uint8_t* buf, uint16_t index, uint8_t v) {
|
||||
const uint8_t byte_index = (uint8_t)(index >> 3U);
|
||||
const uint8_t shift = (uint8_t)(~index) & 0x07U;
|
||||
const uint8_t mask = (uint8_t)(1U << shift);
|
||||
if(v) {
|
||||
buf[byte_index] |= mask;
|
||||
} else {
|
||||
buf[byte_index] &= (uint8_t)~mask;
|
||||
}
|
||||
}
|
||||
|
||||
static uint8_t honda_static_symbol_get(const uint8_t* buf, uint16_t index) {
|
||||
const uint8_t byte_index = (uint8_t)(index >> 3U);
|
||||
const uint8_t shift = (uint8_t)(~index) & 0x07U;
|
||||
return (uint8_t)((buf[byte_index] >> shift) & 1U);
|
||||
}
|
||||
|
||||
static uint8_t honda_static_reverse_bits8(uint8_t value) {
|
||||
value = (uint8_t)(((value >> 4U) | (value << 4U)) & 0xFFU);
|
||||
value = (uint8_t)(((value & 0x33U) << 2U) | ((value >> 2U) & 0x33U));
|
||||
value = (uint8_t)(((value & 0x55U) << 1U) | ((value >> 1U) & 0x55U));
|
||||
return value;
|
||||
}
|
||||
|
||||
static bool honda_static_is_valid_button(uint8_t button) {
|
||||
if(button > 9U) {
|
||||
return false;
|
||||
}
|
||||
|
||||
return ((0x336U >> button) & 1U) != 0U;
|
||||
}
|
||||
|
||||
static bool honda_static_is_valid_serial(uint32_t serial) {
|
||||
return (serial != 0U) && (serial != 0x0FFFFFFFU);
|
||||
}
|
||||
|
||||
static uint8_t honda_static_encoder_remap_button(uint8_t button) {
|
||||
if(button < 2U) {
|
||||
return 1U;
|
||||
}
|
||||
button -= 2U;
|
||||
if(button <= 3U) {
|
||||
return honda_static_encoder_button_map[button];
|
||||
}
|
||||
|
||||
return 1U;
|
||||
}
|
||||
|
||||
static const char* honda_static_button_name(uint8_t button) {
|
||||
if((button >= 1U) && (button <= COUNT_OF(honda_static_button_names))) {
|
||||
return honda_static_button_names[button - 1U];
|
||||
}
|
||||
|
||||
return "Unknown";
|
||||
}
|
||||
|
||||
static uint8_t honda_static_compact_bytes_checksum(const uint8_t compact[8]) {
|
||||
const uint8_t canonical[7] = {
|
||||
(uint8_t)((compact[0] << 4U) | (compact[1] >> 4U)),
|
||||
(uint8_t)((compact[1] << 4U) | (compact[2] >> 4U)),
|
||||
(uint8_t)((compact[2] << 4U) | (compact[3] >> 4U)),
|
||||
(uint8_t)((compact[3] << 4U) | (compact[4] >> 4U)),
|
||||
compact[5],
|
||||
compact[6],
|
||||
compact[7],
|
||||
};
|
||||
|
||||
uint8_t checksum = 0U;
|
||||
for(size_t i = 0; i < COUNT_OF(canonical); i++) {
|
||||
checksum ^= canonical[i];
|
||||
}
|
||||
|
||||
return checksum;
|
||||
}
|
||||
|
||||
static void honda_static_unpack_compact(uint64_t key, HondaStaticFields* fields) {
|
||||
uint8_t compact[8];
|
||||
honda_static_u64_to_bytes_be(key, compact);
|
||||
|
||||
memset(fields, 0, sizeof(*fields));
|
||||
fields->button = compact[0] & 0x0FU;
|
||||
fields->serial = ((uint32_t)compact[1] << 20U) | ((uint32_t)compact[2] << 12U) |
|
||||
((uint32_t)compact[3] << 4U) | ((uint32_t)compact[4] >> 4U);
|
||||
fields->counter = ((uint32_t)compact[5] << 16U) | ((uint32_t)compact[6] << 8U) |
|
||||
(uint32_t)compact[7];
|
||||
fields->checksum = honda_static_compact_bytes_checksum(compact);
|
||||
}
|
||||
|
||||
static uint64_t honda_static_pack_compact(const HondaStaticFields* fields) {
|
||||
uint8_t compact[8];
|
||||
|
||||
compact[0] = fields->button & 0x0FU;
|
||||
compact[1] = (uint8_t)(fields->serial >> 20U);
|
||||
compact[2] = (uint8_t)(fields->serial >> 12U);
|
||||
compact[3] = (uint8_t)(fields->serial >> 4U);
|
||||
compact[4] = (uint8_t)(fields->serial << 4U);
|
||||
compact[5] = (uint8_t)(fields->counter >> 16U);
|
||||
compact[6] = (uint8_t)(fields->counter >> 8U);
|
||||
compact[7] = (uint8_t)fields->counter;
|
||||
|
||||
return honda_static_bytes_to_u64_be(compact);
|
||||
}
|
||||
|
||||
static void honda_static_build_packet_bytes(const HondaStaticFields* fields, uint8_t packet[8]) {
|
||||
memset(packet, 0, 8);
|
||||
|
||||
honda_static_set_bits(packet, 0, 4, fields->button & 0x0FU);
|
||||
honda_static_set_bits(packet, 4, 28, fields->serial);
|
||||
honda_static_set_bits(packet, 32, 24, fields->counter);
|
||||
|
||||
uint8_t checksum = 0U;
|
||||
for(size_t i = 0; i < 7; i++) {
|
||||
checksum ^= packet[i];
|
||||
}
|
||||
|
||||
honda_static_set_bits(packet, 56, 8, checksum);
|
||||
}
|
||||
|
||||
static bool
|
||||
honda_static_validate_forward_packet(const uint8_t packet[9], HondaStaticFields* fields) {
|
||||
const uint8_t button = honda_static_get_bits(packet, 0, 4);
|
||||
const uint32_t serial = honda_static_get_bits_u32(packet, 4, 28);
|
||||
const uint32_t counter = honda_static_get_bits_u32(packet, 32, 24);
|
||||
const uint8_t checksum = honda_static_get_bits(packet, 56, 8);
|
||||
|
||||
uint8_t checksum_calc = 0U;
|
||||
for(size_t i = 0; i < 7; i++) {
|
||||
checksum_calc ^= packet[i];
|
||||
}
|
||||
|
||||
if(checksum != checksum_calc) {
|
||||
return false;
|
||||
}
|
||||
if(!honda_static_is_valid_button(button)) {
|
||||
return false;
|
||||
}
|
||||
if(!honda_static_is_valid_serial(serial)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fields->button = button;
|
||||
fields->serial = serial;
|
||||
fields->counter = counter;
|
||||
fields->checksum = checksum;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool
|
||||
honda_static_validate_reverse_packet(const uint8_t packet[9], HondaStaticFields* fields) {
|
||||
uint8_t reversed[9];
|
||||
for(size_t i = 0; i < COUNT_OF(reversed); i++) {
|
||||
reversed[i] = honda_static_reverse_bits8(packet[i]);
|
||||
}
|
||||
|
||||
const uint8_t button = honda_static_get_bits(reversed, 0, 4);
|
||||
const uint32_t serial = honda_static_get_bits_u32(reversed, 4, 28);
|
||||
const uint32_t counter = honda_static_get_bits_u32(reversed, 32, 24);
|
||||
|
||||
uint8_t checksum = 0U;
|
||||
for(size_t i = 0; i < 7; i++) {
|
||||
checksum ^= reversed[i];
|
||||
}
|
||||
|
||||
if(!honda_static_is_valid_button(button)) {
|
||||
return false;
|
||||
}
|
||||
if(!honda_static_is_valid_serial(serial)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
fields->button = button;
|
||||
fields->serial = serial;
|
||||
fields->counter = counter;
|
||||
fields->checksum = checksum;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool honda_static_manchester_pack_64(
|
||||
const uint8_t* symbol_bits,
|
||||
uint16_t count,
|
||||
uint16_t start_pos,
|
||||
bool inverted,
|
||||
uint8_t packet[9],
|
||||
uint16_t* out_bit_count) {
|
||||
memset(packet, 0, 9);
|
||||
|
||||
uint16_t pos = start_pos;
|
||||
uint16_t bit_count = 0U;
|
||||
|
||||
while((uint16_t)(pos + 1U) < count) {
|
||||
if(bit_count >= HONDA_STATIC_BIT_COUNT) {
|
||||
break;
|
||||
}
|
||||
|
||||
const uint8_t a = honda_static_symbol_get(symbol_bits, pos);
|
||||
const uint8_t b = honda_static_symbol_get(symbol_bits, pos + 1U);
|
||||
|
||||
if(a == b) {
|
||||
pos++;
|
||||
continue;
|
||||
}
|
||||
|
||||
bool bit = false;
|
||||
if(inverted) {
|
||||
bit = (a == 0U) && (b == 1U);
|
||||
} else {
|
||||
bit = (a == 1U) && (b == 0U);
|
||||
}
|
||||
|
||||
if(bit) {
|
||||
packet[bit_count >> 3U] |= (uint8_t)(1U << (((uint8_t)~bit_count) & 0x07U));
|
||||
}
|
||||
|
||||
bit_count++;
|
||||
pos += 2U;
|
||||
}
|
||||
|
||||
if(out_bit_count) {
|
||||
*out_bit_count = bit_count;
|
||||
}
|
||||
|
||||
return bit_count >= HONDA_STATIC_BIT_COUNT;
|
||||
}
|
||||
|
||||
static bool honda_static_parse_symbols(SubGhzProtocolDecoderHondaStatic* instance, bool inverted) {
|
||||
const uint16_t count = instance->symbols_count;
|
||||
const uint8_t* symbol_bits = instance->symbols;
|
||||
HondaStaticFields decoded;
|
||||
|
||||
uint16_t index = 1U;
|
||||
uint16_t transitions = 0U;
|
||||
|
||||
while(index < count) {
|
||||
if(honda_static_symbol_get(symbol_bits, index) !=
|
||||
honda_static_symbol_get(symbol_bits, index - 1U)) {
|
||||
transitions++;
|
||||
} else {
|
||||
if(transitions > HONDA_STATIC_PREAMBLE_MAX_TRANSITIONS) {
|
||||
break;
|
||||
}
|
||||
transitions = 0U;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
|
||||
if(index >= count) {
|
||||
return false;
|
||||
}
|
||||
|
||||
while(((uint16_t)(index + 1U) < count) && (honda_static_symbol_get(symbol_bits, index) ==
|
||||
honda_static_symbol_get(symbol_bits, index + 1U))) {
|
||||
index++;
|
||||
}
|
||||
|
||||
const uint16_t data_start = index;
|
||||
|
||||
uint8_t packet[9] = {0};
|
||||
uint16_t bit_count = 0U;
|
||||
|
||||
if(!honda_static_manchester_pack_64(
|
||||
symbol_bits, count, data_start, inverted, packet, &bit_count)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(honda_static_validate_forward_packet(packet, &decoded)) {
|
||||
honda_static_decoder_commit(instance, &decoded);
|
||||
return true;
|
||||
}
|
||||
|
||||
if(inverted) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if(honda_static_validate_reverse_packet(packet, &decoded)) {
|
||||
honda_static_decoder_commit(instance, &decoded);
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
static void honda_static_decoder_commit(
|
||||
SubGhzProtocolDecoderHondaStatic* instance,
|
||||
const HondaStaticFields* decoded) {
|
||||
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
|
||||
instance->generic.data = honda_static_pack_compact(decoded);
|
||||
instance->generic.serial = decoded->serial;
|
||||
instance->generic.cnt = decoded->counter;
|
||||
instance->generic.btn = decoded->button;
|
||||
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
}
|
||||
|
||||
static void honda_static_build_upload(SubGhzProtocolEncoderHondaStatic* instance) {
|
||||
uint8_t packet[8];
|
||||
honda_static_build_packet_bytes(&instance->decoded, packet);
|
||||
|
||||
size_t index = 0U;
|
||||
instance->encoder.upload[index++] = level_duration_make(true, HONDA_STATIC_SYNC_TIME_US);
|
||||
|
||||
for(size_t i = 0; i < HONDA_STATIC_PREAMBLE_ALTERNATING_COUNT; i++) {
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make((i & 1U) != 0U, HONDA_STATIC_ELEMENT_TIME_US);
|
||||
}
|
||||
|
||||
for(uint8_t bit = 0U; bit < HONDA_STATIC_BIT_COUNT; bit++) {
|
||||
const bool value = ((packet[bit >> 3U] >> (((uint8_t)~bit) & 0x07U)) & 1U) != 0U;
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(!value, HONDA_STATIC_ELEMENT_TIME_US);
|
||||
instance->encoder.upload[index++] =
|
||||
level_duration_make(value, HONDA_STATIC_ELEMENT_TIME_US);
|
||||
}
|
||||
|
||||
const bool last_bit = (packet[7] & 1U) != 0U;
|
||||
instance->encoder.upload[index++] = level_duration_make(!last_bit, HONDA_STATIC_SYNC_TIME_US);
|
||||
|
||||
instance->encoder.front = 0U;
|
||||
instance->encoder.size_upload = index;
|
||||
}
|
||||
|
||||
static bool honda_static_read_hex_u64(FlipperFormat* ff, uint64_t* out_key) {
|
||||
FuriString* tmp = furi_string_alloc();
|
||||
if(!tmp) return false;
|
||||
bool ok = false;
|
||||
do {
|
||||
if(!flipper_format_rewind(ff) || !flipper_format_read_string(ff, "Key", tmp)) break;
|
||||
|
||||
const char* key_str = furi_string_get_cstr(tmp);
|
||||
uint64_t key = 0;
|
||||
size_t hex_pos = 0;
|
||||
for(size_t i = 0; key_str[i] && hex_pos < 16; i++) {
|
||||
char c = key_str[i];
|
||||
if(c == ' ') continue;
|
||||
uint8_t nibble;
|
||||
if(c >= '0' && c <= '9')
|
||||
nibble = c - '0';
|
||||
else if(c >= 'A' && c <= 'F')
|
||||
nibble = c - 'A' + 10;
|
||||
else if(c >= 'a' && c <= 'f')
|
||||
nibble = c - 'a' + 10;
|
||||
else
|
||||
break;
|
||||
key = (key << 4) | nibble;
|
||||
hex_pos++;
|
||||
}
|
||||
if(hex_pos != 16) break;
|
||||
*out_key = key;
|
||||
ok = true;
|
||||
} while(false);
|
||||
furi_string_free(tmp);
|
||||
return ok;
|
||||
}
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_honda_static_decoder = {
|
||||
.alloc = subghz_protocol_decoder_honda_static_alloc,
|
||||
.free = subghz_protocol_decoder_honda_static_free,
|
||||
.feed = subghz_protocol_decoder_honda_static_feed,
|
||||
.reset = subghz_protocol_decoder_honda_static_reset,
|
||||
.get_hash_data = subghz_protocol_decoder_honda_static_get_hash_data,
|
||||
.serialize = subghz_protocol_decoder_honda_static_serialize,
|
||||
.deserialize = subghz_protocol_decoder_honda_static_deserialize,
|
||||
.get_string = subghz_protocol_decoder_honda_static_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_honda_static_encoder = {
|
||||
.alloc = subghz_protocol_encoder_honda_static_alloc,
|
||||
.free = subghz_protocol_encoder_honda_static_free,
|
||||
.deserialize = subghz_protocol_encoder_honda_static_deserialize,
|
||||
.stop = subghz_protocol_encoder_honda_static_stop,
|
||||
.yield = subghz_protocol_encoder_honda_static_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol honda_static_protocol = {
|
||||
.name = HONDA_STATIC_PROTOCOL_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_Decodable | SubGhzProtocolFlag_315 | SubGhzProtocolFlag_433 |
|
||||
SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Load |
|
||||
SubGhzProtocolFlag_Send,
|
||||
.decoder = &subghz_protocol_honda_static_decoder,
|
||||
.encoder = &subghz_protocol_honda_static_encoder,
|
||||
};
|
||||
|
||||
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = malloc(sizeof(SubGhzProtocolEncoderHondaStatic));
|
||||
furi_check(instance);
|
||||
memset(instance, 0, sizeof(*instance));
|
||||
|
||||
instance->base.protocol = &honda_static_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->encoder.repeat = 3U;
|
||||
instance->encoder.upload = malloc(HONDA_STATIC_UPLOAD_CAPACITY * sizeof(LevelDuration));
|
||||
furi_check(instance->encoder.upload);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_honda_static_free(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = context;
|
||||
SubGhzProtocolStatus status = SubGhzProtocolStatusError;
|
||||
|
||||
instance->encoder.is_running = false;
|
||||
instance->encoder.front = 0U;
|
||||
|
||||
do {
|
||||
FuriString* pstr = furi_string_alloc();
|
||||
if(!pstr) break;
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_read_string(flipper_format, "Protocol", pstr)) {
|
||||
furi_string_free(pstr);
|
||||
break;
|
||||
}
|
||||
if(!furi_string_equal(pstr, instance->base.protocol->name)) {
|
||||
furi_string_free(pstr);
|
||||
break;
|
||||
}
|
||||
furi_string_free(pstr);
|
||||
|
||||
uint64_t key = 0;
|
||||
if(!honda_static_read_hex_u64(flipper_format, &key)) {
|
||||
break;
|
||||
}
|
||||
|
||||
honda_static_unpack_compact(key, &instance->decoded);
|
||||
|
||||
uint32_t serial = instance->decoded.serial;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Serial", &serial, 1)) {
|
||||
instance->decoded.serial = serial;
|
||||
}
|
||||
|
||||
uint32_t btn_u32 = 0;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Btn", &btn_u32, 1)) {
|
||||
uint8_t b = (uint8_t)btn_u32;
|
||||
if(honda_static_is_valid_button(b)) {
|
||||
instance->decoded.button = b;
|
||||
} else if(b >= 2U && b <= 5U) {
|
||||
instance->decoded.button = honda_static_encoder_remap_button(b);
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t cnt = instance->decoded.counter & 0x00FFFFFFU;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Cnt", &cnt, 1)) {
|
||||
instance->decoded.counter = cnt & 0x00FFFFFFU;
|
||||
}
|
||||
|
||||
instance->generic.serial = instance->decoded.serial;
|
||||
instance->generic.cnt = instance->decoded.counter;
|
||||
instance->generic.btn = instance->decoded.button;
|
||||
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
|
||||
instance->generic.data = honda_static_pack_compact(&instance->decoded);
|
||||
|
||||
uint8_t key_data[8];
|
||||
honda_static_u64_to_bytes_be(instance->generic.data, key_data);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(key_data))) {
|
||||
status = SubGhzProtocolStatusErrorParserKey;
|
||||
break;
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_read_uint32(
|
||||
flipper_format, "Repeat", (uint32_t*)&instance->encoder.repeat, 1)) {
|
||||
instance->encoder.repeat = 3U;
|
||||
}
|
||||
|
||||
honda_static_build_upload(instance);
|
||||
instance->encoder.is_running = true;
|
||||
status = SubGhzProtocolStatusOk;
|
||||
} while(false);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
void subghz_protocol_encoder_honda_static_stop(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration subghz_protocol_encoder_honda_static_yield(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderHondaStatic* instance = context;
|
||||
if((instance->encoder.repeat == 0U) || !instance->encoder.is_running) {
|
||||
instance->encoder.is_running = false;
|
||||
return level_duration_reset();
|
||||
}
|
||||
|
||||
const LevelDuration current = instance->encoder.upload[instance->encoder.front];
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
instance->encoder.front = 0U;
|
||||
}
|
||||
|
||||
return current;
|
||||
}
|
||||
|
||||
void* subghz_protocol_decoder_honda_static_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = malloc(sizeof(SubGhzProtocolDecoderHondaStatic));
|
||||
furi_check(instance);
|
||||
memset(instance, 0, sizeof(*instance));
|
||||
|
||||
instance->base.protocol = &honda_static_protocol;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_static_free(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_static_reset(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
instance->symbols_count = 0U;
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_static_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
|
||||
const uint8_t sym = honda_static_level_u8(level);
|
||||
|
||||
if((duration >= HONDA_STATIC_SHORT_BASE_US) &&
|
||||
((duration - HONDA_STATIC_SHORT_BASE_US) <= HONDA_STATIC_SHORT_SPAN_US)) {
|
||||
if(instance->symbols_count < HONDA_STATIC_SYMBOL_CAPACITY) {
|
||||
honda_static_symbol_set(instance->symbols, instance->symbols_count, sym);
|
||||
instance->symbols_count++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
if((duration >= HONDA_STATIC_LONG_BASE_US) &&
|
||||
((duration - HONDA_STATIC_LONG_BASE_US) <= HONDA_STATIC_LONG_SPAN_US)) {
|
||||
if((uint16_t)(instance->symbols_count + 2U) <= HONDA_STATIC_SYMBOL_CAPACITY) {
|
||||
honda_static_symbol_set(instance->symbols, instance->symbols_count, sym);
|
||||
instance->symbols_count++;
|
||||
honda_static_symbol_set(instance->symbols, instance->symbols_count, sym);
|
||||
instance->symbols_count++;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const uint16_t sc = instance->symbols_count;
|
||||
|
||||
if(sc >= HONDA_STATIC_MIN_SYMBOLS) {
|
||||
if(!honda_static_parse_symbols(instance, true)) {
|
||||
honda_static_parse_symbols(instance, false);
|
||||
}
|
||||
}
|
||||
|
||||
instance->symbols_count = 0U;
|
||||
}
|
||||
|
||||
uint8_t subghz_protocol_decoder_honda_static_get_hash_data(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
const uint64_t data = instance->generic.data;
|
||||
|
||||
return (uint8_t)(data ^ (data >> 8U) ^ (data >> 16U) ^ (data >> 24U) ^ (data >> 32U) ^
|
||||
(data >> 40U) ^ (data >> 48U) ^ (data >> 56U));
|
||||
}
|
||||
|
||||
void subghz_protocol_decoder_honda_static_get_string(void* context, FuriString* output) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
HondaStaticFields decoded;
|
||||
honda_static_unpack_compact(instance->generic.data, &decoded);
|
||||
|
||||
furi_string_printf(
|
||||
output,
|
||||
"%s\r\n"
|
||||
"Key:%016llX\r\n"
|
||||
"Btn:%s\r\n"
|
||||
"Ser:%07lX Cnt:%06lX",
|
||||
instance->generic.protocol_name,
|
||||
(unsigned long long)instance->generic.data,
|
||||
honda_static_button_name(decoded.button),
|
||||
(unsigned long)decoded.serial,
|
||||
(unsigned long)decoded.counter);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_static_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
instance->generic.data_count_bit = HONDA_STATIC_BIT_COUNT;
|
||||
HondaStaticFields decoded;
|
||||
honda_static_unpack_compact(instance->generic.data, &decoded);
|
||||
|
||||
SubGhzProtocolStatus status =
|
||||
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
return status;
|
||||
}
|
||||
|
||||
uint32_t temp = decoded.serial;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Serial", &temp, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
temp = decoded.button;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Btn", &temp, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
temp = decoded.counter;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Cnt", &temp, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
temp = decoded.checksum;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Checksum", &temp, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderHondaStatic* instance = context;
|
||||
SubGhzProtocolStatus status = subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic, flipper_format, HONDA_STATIC_BIT_COUNT);
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
return status;
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
HondaStaticFields decoded;
|
||||
honda_static_unpack_compact(instance->generic.data, &decoded);
|
||||
uint32_t s = 0;
|
||||
uint32_t b = 0;
|
||||
uint32_t c = 0;
|
||||
if(flipper_format_read_uint32(flipper_format, "Serial", &s, 1)) {
|
||||
decoded.serial = s;
|
||||
}
|
||||
if(flipper_format_read_uint32(flipper_format, "Btn", &b, 1)) {
|
||||
decoded.button = (uint8_t)b;
|
||||
}
|
||||
if(flipper_format_read_uint32(flipper_format, "Cnt", &c, 1)) {
|
||||
decoded.counter = c & 0x00FFFFFFU;
|
||||
}
|
||||
|
||||
instance->generic.data = honda_static_pack_compact(&decoded);
|
||||
instance->generic.serial = decoded.serial;
|
||||
instance->generic.cnt = decoded.counter;
|
||||
instance->generic.btn = decoded.button;
|
||||
|
||||
return status;
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/types.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#define HONDA_STATIC_PROTOCOL_NAME "Honda Static"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderHondaStatic SubGhzProtocolDecoderHondaStatic;
|
||||
typedef struct SubGhzProtocolEncoderHondaStatic SubGhzProtocolEncoderHondaStatic;
|
||||
|
||||
extern const SubGhzProtocol honda_static_protocol;
|
||||
|
||||
void* subghz_protocol_decoder_honda_static_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_honda_static_free(void* context);
|
||||
void subghz_protocol_decoder_honda_static_reset(void* context);
|
||||
void subghz_protocol_decoder_honda_static_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_honda_static_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_static_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_honda_static_get_string(void* context, FuriString* output);
|
||||
|
||||
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_encoder_honda_static_free(void* context);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_encoder_honda_static_stop(void* context);
|
||||
LevelDuration subghz_protocol_encoder_honda_static_yield(void* context);
|
||||
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/types.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
|
||||
#define HONDA_STATIC_PROTOCOL_NAME "Honda Static"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderHondaStatic SubGhzProtocolDecoderHondaStatic;
|
||||
typedef struct SubGhzProtocolEncoderHondaStatic SubGhzProtocolEncoderHondaStatic;
|
||||
|
||||
extern const SubGhzProtocol honda_static_protocol;
|
||||
|
||||
void* subghz_protocol_decoder_honda_static_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_decoder_honda_static_free(void* context);
|
||||
void subghz_protocol_decoder_honda_static_reset(void* context);
|
||||
void subghz_protocol_decoder_honda_static_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t subghz_protocol_decoder_honda_static_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_honda_static_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_decoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_decoder_honda_static_get_string(void* context, FuriString* output);
|
||||
|
||||
void* subghz_protocol_encoder_honda_static_alloc(SubGhzEnvironment* environment);
|
||||
void subghz_protocol_encoder_honda_static_free(void* context);
|
||||
SubGhzProtocolStatus
|
||||
subghz_protocol_encoder_honda_static_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void subghz_protocol_encoder_honda_static_stop(void* context);
|
||||
LevelDuration subghz_protocol_encoder_honda_static_yield(void* context);
|
||||
|
||||
@@ -0,0 +1,711 @@
|
||||
#include "kia_v7.h"
|
||||
#include <string.h>
|
||||
|
||||
#define KIA_V7_UPLOAD_CAPACITY 0x3A4
|
||||
#define KIA_V7_PREAMBLE_PAIRS 0x13F
|
||||
#define KIA_V7_PREAMBLE_MIN_PAIRS 16
|
||||
#define KIA_V7_HEADER 0x4C
|
||||
#define KIA_V7_TAIL_GAP_US 0x7D0
|
||||
#define KIA_V7_KEY_BITS 64U
|
||||
#define KIA_V7_DEFAULT_TX_REPEAT 10U
|
||||
|
||||
static const SubGhzBlockConst subghz_protocol_kia_v7_const = {
|
||||
.te_short = 250,
|
||||
.te_long = 500,
|
||||
.te_delta = 100,
|
||||
.min_count_bit_for_found = KIA_V7_KEY_BITS,
|
||||
};
|
||||
|
||||
typedef enum {
|
||||
KiaV7DecoderStepReset = 0,
|
||||
KiaV7DecoderStepPreamble = 1,
|
||||
KiaV7DecoderStepSyncLow = 2,
|
||||
KiaV7DecoderStepData = 3,
|
||||
} KiaV7DecoderStep;
|
||||
|
||||
struct SubGhzProtocolDecoderKiaV7 {
|
||||
SubGhzProtocolDecoderBase base;
|
||||
SubGhzBlockDecoder decoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
ManchesterState manchester_state;
|
||||
uint16_t preamble_count;
|
||||
|
||||
uint8_t decoded_button;
|
||||
uint8_t fixed_high_byte;
|
||||
uint8_t crc_calculated;
|
||||
uint8_t crc_raw;
|
||||
bool crc_valid;
|
||||
};
|
||||
|
||||
struct SubGhzProtocolEncoderKiaV7 {
|
||||
SubGhzProtocolEncoderBase base;
|
||||
SubGhzProtocolBlockEncoder encoder;
|
||||
SubGhzBlockGeneric generic;
|
||||
|
||||
uint8_t tx_bit_count;
|
||||
uint8_t decoded_button;
|
||||
uint8_t fixed_high_byte;
|
||||
uint8_t crc_calculated;
|
||||
uint8_t crc_raw;
|
||||
bool crc_valid;
|
||||
};
|
||||
|
||||
static uint8_t kia_v7_crc8(const uint8_t* data, size_t len) {
|
||||
uint8_t crc = 0x4CU;
|
||||
|
||||
for(size_t index = 0; index < len; index++) {
|
||||
crc ^= data[index];
|
||||
for(uint8_t bit = 0; bit < 8; bit++) {
|
||||
const bool msb = (crc & 0x80U) != 0U;
|
||||
crc <<= 1U;
|
||||
if(msb) {
|
||||
crc ^= 0x7FU;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return crc;
|
||||
}
|
||||
|
||||
static void kia_v7_u64_to_bytes_be(uint64_t data, uint8_t bytes[8]) {
|
||||
for(size_t index = 0; index < 8; index++) {
|
||||
bytes[index] = (data >> ((7U - index) * 8U)) & 0xFFU;
|
||||
}
|
||||
}
|
||||
|
||||
static uint64_t kia_v7_bytes_to_u64_be(const uint8_t bytes[8]) {
|
||||
uint64_t data = 0;
|
||||
|
||||
for(size_t index = 0; index < 8; index++) {
|
||||
data = (data << 8U) | bytes[index];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
static bool kia_v7_is_short(uint32_t duration) {
|
||||
return DURATION_DIFF(duration, subghz_protocol_kia_v7_const.te_short) <
|
||||
subghz_protocol_kia_v7_const.te_delta;
|
||||
}
|
||||
|
||||
static bool kia_v7_is_long(uint32_t duration) {
|
||||
return DURATION_DIFF(duration, subghz_protocol_kia_v7_const.te_long) <
|
||||
subghz_protocol_kia_v7_const.te_delta;
|
||||
}
|
||||
|
||||
static const char* kia_v7_get_button_name(uint8_t button) {
|
||||
switch(button) {
|
||||
case 0x01:
|
||||
return "LOCK";
|
||||
case 0x02:
|
||||
return "UNLOCK";
|
||||
case 0x03:
|
||||
case 0x08:
|
||||
return "BOOT";
|
||||
default:
|
||||
return "??";
|
||||
}
|
||||
}
|
||||
|
||||
static SubGhzProtocolStatus
|
||||
kia_v7_write_display(FlipperFormat* flipper_format, const char* protocol_name, uint8_t button) {
|
||||
SubGhzProtocolStatus status = SubGhzProtocolStatusOk;
|
||||
FuriString* display = furi_string_alloc();
|
||||
|
||||
furi_string_printf(display, "%s - %s", protocol_name, kia_v7_get_button_name(button));
|
||||
|
||||
if(!flipper_format_write_string_cstr(flipper_format, "Disp", furi_string_get_cstr(display))) {
|
||||
status = SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
furi_string_free(display);
|
||||
return status;
|
||||
}
|
||||
|
||||
static void kia_v7_decode_key_common(
|
||||
SubGhzBlockGeneric* generic,
|
||||
uint8_t* decoded_button,
|
||||
uint8_t* fixed_high_byte,
|
||||
uint8_t* crc_calculated,
|
||||
uint8_t* crc_raw,
|
||||
bool* crc_valid) {
|
||||
uint8_t bytes[8];
|
||||
kia_v7_u64_to_bytes_be(generic->data, bytes);
|
||||
|
||||
const uint32_t serial = (((uint32_t)bytes[3]) << 20U) | (((uint32_t)bytes[4]) << 12U) |
|
||||
(((uint32_t)bytes[5]) << 4U) | (((uint32_t)bytes[6]) >> 4U);
|
||||
const uint16_t counter = ((uint16_t)bytes[1] << 8U) | (uint16_t)bytes[2];
|
||||
const uint8_t button = bytes[6] & 0x0FU;
|
||||
const uint8_t crc_calc = kia_v7_crc8(bytes, 7);
|
||||
const uint8_t crc_pkt = bytes[7];
|
||||
|
||||
generic->serial = serial & 0x0FFFFFFFU;
|
||||
generic->btn = button;
|
||||
generic->cnt = counter;
|
||||
generic->data_count_bit = KIA_V7_KEY_BITS;
|
||||
|
||||
if(decoded_button) {
|
||||
*decoded_button = button;
|
||||
}
|
||||
if(fixed_high_byte) {
|
||||
*fixed_high_byte = bytes[0];
|
||||
}
|
||||
if(crc_calculated) {
|
||||
*crc_calculated = crc_calc;
|
||||
}
|
||||
if(crc_raw) {
|
||||
*crc_raw = crc_pkt;
|
||||
}
|
||||
if(crc_valid) {
|
||||
*crc_valid = (crc_calc == crc_pkt);
|
||||
}
|
||||
}
|
||||
|
||||
static void kia_v7_decode_key_decoder(SubGhzProtocolDecoderKiaV7* instance) {
|
||||
kia_v7_decode_key_common(
|
||||
&instance->generic,
|
||||
&instance->decoded_button,
|
||||
&instance->fixed_high_byte,
|
||||
&instance->crc_calculated,
|
||||
&instance->crc_raw,
|
||||
&instance->crc_valid);
|
||||
}
|
||||
|
||||
static uint64_t kia_v7_encode_key(
|
||||
uint8_t fixed_high_byte,
|
||||
uint32_t serial,
|
||||
uint8_t button,
|
||||
uint16_t counter,
|
||||
uint8_t* crc_out) {
|
||||
uint8_t bytes[8];
|
||||
|
||||
serial &= 0x0FFFFFFFU;
|
||||
button &= 0x0FU;
|
||||
|
||||
bytes[0] = fixed_high_byte;
|
||||
bytes[1] = (counter >> 8U) & 0xFFU;
|
||||
bytes[2] = counter & 0xFFU;
|
||||
bytes[3] = (serial >> 20U) & 0xFFU;
|
||||
bytes[4] = (serial >> 12U) & 0xFFU;
|
||||
bytes[5] = (serial >> 4U) & 0xFFU;
|
||||
bytes[6] = ((serial & 0x0FU) << 4U) | button;
|
||||
bytes[7] = kia_v7_crc8(bytes, 7);
|
||||
|
||||
if(crc_out) {
|
||||
*crc_out = bytes[7];
|
||||
}
|
||||
|
||||
return kia_v7_bytes_to_u64_be(bytes);
|
||||
}
|
||||
|
||||
static void kia_v7_decode_key_encoder(SubGhzProtocolEncoderKiaV7* instance) {
|
||||
kia_v7_decode_key_common(
|
||||
&instance->generic,
|
||||
&instance->decoded_button,
|
||||
&instance->fixed_high_byte,
|
||||
&instance->crc_calculated,
|
||||
&instance->crc_raw,
|
||||
&instance->crc_valid);
|
||||
}
|
||||
|
||||
static bool kia_v7_encoder_get_upload(SubGhzProtocolEncoderKiaV7* instance) {
|
||||
furi_check(instance);
|
||||
|
||||
const LevelDuration high_short =
|
||||
level_duration_make(true, subghz_protocol_kia_v7_const.te_short);
|
||||
const LevelDuration low_short =
|
||||
level_duration_make(false, subghz_protocol_kia_v7_const.te_short);
|
||||
const LevelDuration low_tail = level_duration_make(false, KIA_V7_TAIL_GAP_US);
|
||||
const size_t max_size = KIA_V7_UPLOAD_CAPACITY;
|
||||
|
||||
const uint8_t bit_count = (instance->tx_bit_count > 0U && instance->tx_bit_count <= 64U) ?
|
||||
instance->tx_bit_count :
|
||||
64U;
|
||||
|
||||
size_t final_size = 0;
|
||||
|
||||
for(uint8_t pass = 0; pass < 2; pass++) {
|
||||
size_t index = pass;
|
||||
|
||||
for(size_t i = 0; i < KIA_V7_PREAMBLE_PAIRS; i++) {
|
||||
if((index + 2U) > max_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
instance->encoder.upload[index++] = high_short;
|
||||
instance->encoder.upload[index++] = low_short;
|
||||
}
|
||||
|
||||
if((index + 1U) > max_size) {
|
||||
return false;
|
||||
}
|
||||
instance->encoder.upload[index++] = high_short;
|
||||
|
||||
for(int32_t bit = (int32_t)bit_count - 1; bit >= 0; bit--) {
|
||||
if((index + 2U) > max_size) {
|
||||
return false;
|
||||
}
|
||||
|
||||
const bool value = ((instance->generic.data >> bit) & 1ULL) != 0ULL;
|
||||
instance->encoder.upload[index++] = value ? high_short : low_short;
|
||||
instance->encoder.upload[index++] = value ? low_short : high_short;
|
||||
}
|
||||
|
||||
if((index + 2U) > max_size) {
|
||||
return false;
|
||||
}
|
||||
instance->encoder.upload[index++] = high_short;
|
||||
instance->encoder.upload[index++] = low_tail;
|
||||
|
||||
final_size = index;
|
||||
}
|
||||
|
||||
instance->encoder.front = 0;
|
||||
instance->encoder.size_upload = final_size;
|
||||
return true;
|
||||
}
|
||||
|
||||
const SubGhzProtocolDecoder subghz_protocol_kia_v7_decoder = {
|
||||
.alloc = kia_protocol_decoder_v7_alloc,
|
||||
.free = kia_protocol_decoder_v7_free,
|
||||
.feed = kia_protocol_decoder_v7_feed,
|
||||
.reset = kia_protocol_decoder_v7_reset,
|
||||
.get_hash_data = kia_protocol_decoder_v7_get_hash_data,
|
||||
.serialize = kia_protocol_decoder_v7_serialize,
|
||||
.deserialize = kia_protocol_decoder_v7_deserialize,
|
||||
.get_string = kia_protocol_decoder_v7_get_string,
|
||||
};
|
||||
|
||||
const SubGhzProtocolEncoder subghz_protocol_kia_v7_encoder = {
|
||||
.alloc = kia_protocol_encoder_v7_alloc,
|
||||
.free = kia_protocol_encoder_v7_free,
|
||||
.deserialize = kia_protocol_encoder_v7_deserialize,
|
||||
.stop = kia_protocol_encoder_v7_stop,
|
||||
.yield = kia_protocol_encoder_v7_yield,
|
||||
};
|
||||
|
||||
const SubGhzProtocol subghz_protocol_kia_v7 = {
|
||||
.name = KIA_PROTOCOL_V7_NAME,
|
||||
.type = SubGhzProtocolTypeDynamic,
|
||||
.flag = SubGhzProtocolFlag_433 | SubGhzProtocolFlag_FM | SubGhzProtocolFlag_Decodable |
|
||||
SubGhzProtocolFlag_Load | SubGhzProtocolFlag_Save | SubGhzProtocolFlag_Send,
|
||||
.decoder = &subghz_protocol_kia_v7_decoder,
|
||||
.encoder = &subghz_protocol_kia_v7_encoder,
|
||||
};
|
||||
|
||||
void* kia_protocol_encoder_v7_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
SubGhzProtocolEncoderKiaV7* instance = calloc(1, sizeof(SubGhzProtocolEncoderKiaV7));
|
||||
furi_check(instance);
|
||||
|
||||
instance->base.protocol = &subghz_protocol_kia_v7;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
instance->encoder.repeat = 1;
|
||||
instance->encoder.size_upload = KIA_V7_UPLOAD_CAPACITY;
|
||||
instance->encoder.upload = malloc(KIA_V7_UPLOAD_CAPACITY * sizeof(LevelDuration));
|
||||
furi_check(instance->encoder.upload);
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void kia_protocol_encoder_v7_free(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderKiaV7* instance = context;
|
||||
free(instance->encoder.upload);
|
||||
free(instance);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
kia_protocol_encoder_v7_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolEncoderKiaV7* instance = context;
|
||||
SubGhzProtocolStatus ret = SubGhzProtocolStatusError;
|
||||
|
||||
instance->encoder.is_running = false;
|
||||
instance->encoder.front = 0;
|
||||
instance->encoder.repeat = KIA_V7_DEFAULT_TX_REPEAT;
|
||||
|
||||
do {
|
||||
FuriString* temp_str = furi_string_alloc();
|
||||
if(!temp_str) {
|
||||
break;
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_read_string(flipper_format, "Protocol", temp_str)) {
|
||||
furi_string_free(temp_str);
|
||||
break;
|
||||
}
|
||||
|
||||
if(!furi_string_equal(temp_str, instance->base.protocol->name)) {
|
||||
furi_string_free(temp_str);
|
||||
break;
|
||||
}
|
||||
furi_string_free(temp_str);
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
SubGhzProtocolStatus load_st = subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic, flipper_format, KIA_V7_KEY_BITS);
|
||||
if(load_st != SubGhzProtocolStatusOk) {
|
||||
break;
|
||||
}
|
||||
|
||||
instance->tx_bit_count =
|
||||
(instance->generic.data_count_bit > 0U && instance->generic.data_count_bit <= 64U) ?
|
||||
(uint8_t)instance->generic.data_count_bit :
|
||||
64U;
|
||||
|
||||
kia_v7_decode_key_encoder(instance);
|
||||
|
||||
uint32_t u32 = 0;
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Serial", &u32, 1)) {
|
||||
instance->generic.serial = u32;
|
||||
}
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Btn", &u32, 1)) {
|
||||
instance->generic.btn = (uint8_t)u32;
|
||||
}
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(flipper_format_read_uint32(flipper_format, "Cnt", &u32, 1)) {
|
||||
instance->generic.cnt = (uint16_t)u32;
|
||||
}
|
||||
|
||||
instance->generic.btn &= 0x0FU;
|
||||
instance->generic.cnt &= 0xFFFFU;
|
||||
instance->generic.serial &= 0x0FFFFFFFU;
|
||||
|
||||
instance->generic.data = kia_v7_encode_key(
|
||||
instance->fixed_high_byte,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn,
|
||||
(uint16_t)instance->generic.cnt,
|
||||
&instance->crc_calculated);
|
||||
instance->generic.data_count_bit = KIA_V7_KEY_BITS;
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
if(!flipper_format_read_uint32(flipper_format, "Repeat", &u32, 1)) {
|
||||
u32 = KIA_V7_DEFAULT_TX_REPEAT;
|
||||
}
|
||||
instance->encoder.repeat = u32;
|
||||
|
||||
if(!kia_v7_encoder_get_upload(instance)) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(instance->encoder.size_upload == 0) {
|
||||
break;
|
||||
}
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
uint8_t key_data[sizeof(uint64_t)];
|
||||
kia_v7_u64_to_bytes_be(instance->generic.data, key_data);
|
||||
if(!flipper_format_update_hex(flipper_format, "Key", key_data, sizeof(key_data))) {
|
||||
break;
|
||||
}
|
||||
|
||||
instance->encoder.is_running = true;
|
||||
ret = SubGhzProtocolStatusOk;
|
||||
} while(false);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
void kia_protocol_encoder_v7_stop(void* context) {
|
||||
SubGhzProtocolEncoderKiaV7* instance = context;
|
||||
instance->encoder.is_running = false;
|
||||
}
|
||||
|
||||
LevelDuration kia_protocol_encoder_v7_yield(void* context) {
|
||||
SubGhzProtocolEncoderKiaV7* instance = context;
|
||||
|
||||
if(instance->encoder.repeat == 0 || !instance->encoder.is_running) {
|
||||
instance->encoder.is_running = false;
|
||||
return level_duration_reset();
|
||||
}
|
||||
|
||||
LevelDuration duration = instance->encoder.upload[instance->encoder.front];
|
||||
|
||||
if(++instance->encoder.front == instance->encoder.size_upload) {
|
||||
instance->encoder.repeat--;
|
||||
instance->encoder.front = 0;
|
||||
}
|
||||
|
||||
return duration;
|
||||
}
|
||||
|
||||
void* kia_protocol_decoder_v7_alloc(SubGhzEnvironment* environment) {
|
||||
UNUSED(environment);
|
||||
|
||||
SubGhzProtocolDecoderKiaV7* instance = calloc(1, sizeof(SubGhzProtocolDecoderKiaV7));
|
||||
furi_check(instance);
|
||||
|
||||
instance->base.protocol = &subghz_protocol_kia_v7;
|
||||
instance->generic.protocol_name = instance->base.protocol->name;
|
||||
|
||||
return instance;
|
||||
}
|
||||
|
||||
void kia_protocol_decoder_v7_free(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderKiaV7* instance = context;
|
||||
free(instance);
|
||||
}
|
||||
|
||||
void kia_protocol_decoder_v7_reset(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderKiaV7* instance = context;
|
||||
instance->decoder.parser_step = KiaV7DecoderStepReset;
|
||||
instance->decoder.te_last = 0;
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->preamble_count = 0;
|
||||
manchester_advance(
|
||||
instance->manchester_state, ManchesterEventReset, &instance->manchester_state, NULL);
|
||||
}
|
||||
|
||||
void kia_protocol_decoder_v7_feed(void* context, bool level, uint32_t duration) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderKiaV7* instance = context;
|
||||
ManchesterEvent event = ManchesterEventReset;
|
||||
bool data = false;
|
||||
|
||||
switch(instance->decoder.parser_step) {
|
||||
case KiaV7DecoderStepReset:
|
||||
if(level && kia_v7_is_short(duration)) {
|
||||
instance->decoder.parser_step = KiaV7DecoderStepPreamble;
|
||||
instance->decoder.te_last = duration;
|
||||
instance->preamble_count = 0;
|
||||
manchester_advance(
|
||||
instance->manchester_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_state,
|
||||
NULL);
|
||||
}
|
||||
break;
|
||||
|
||||
case KiaV7DecoderStepPreamble:
|
||||
if(level) {
|
||||
if(kia_v7_is_long(duration) && kia_v7_is_short(instance->decoder.te_last)) {
|
||||
if(instance->preamble_count > (KIA_V7_PREAMBLE_MIN_PAIRS - 1U)) {
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->preamble_count = 0;
|
||||
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1U);
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 0U);
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1U);
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, 1U);
|
||||
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = KiaV7DecoderStepSyncLow;
|
||||
} else {
|
||||
instance->decoder.parser_step = KiaV7DecoderStepReset;
|
||||
}
|
||||
} else if(kia_v7_is_short(duration)) {
|
||||
instance->decoder.te_last = duration;
|
||||
} else {
|
||||
instance->decoder.parser_step = KiaV7DecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
if(kia_v7_is_short(duration) && kia_v7_is_short(instance->decoder.te_last)) {
|
||||
instance->preamble_count++;
|
||||
} else {
|
||||
instance->decoder.parser_step = KiaV7DecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KiaV7DecoderStepSyncLow:
|
||||
if(!level && kia_v7_is_short(duration) && kia_v7_is_long(instance->decoder.te_last)) {
|
||||
instance->decoder.te_last = duration;
|
||||
instance->decoder.parser_step = KiaV7DecoderStepData;
|
||||
}
|
||||
break;
|
||||
|
||||
case KiaV7DecoderStepData: {
|
||||
if(kia_v7_is_short(duration)) {
|
||||
event = (ManchesterEvent)((uint8_t)(level & 1U) << 1U);
|
||||
} else if(kia_v7_is_long(duration)) {
|
||||
event = level ? ManchesterEventLongHigh : ManchesterEventLongLow;
|
||||
} else {
|
||||
event = ManchesterEventReset;
|
||||
}
|
||||
|
||||
if(kia_v7_is_short(duration) || kia_v7_is_long(duration)) {
|
||||
if(manchester_advance(
|
||||
instance->manchester_state, event, &instance->manchester_state, &data)) {
|
||||
subghz_protocol_blocks_add_bit(&instance->decoder, data);
|
||||
}
|
||||
}
|
||||
|
||||
if(instance->decoder.decode_count_bit == KIA_V7_KEY_BITS) {
|
||||
const uint64_t candidate = ~instance->decoder.decode_data;
|
||||
const uint8_t hdr = (uint8_t)((candidate >> 56U) & 0xFFU);
|
||||
|
||||
if(hdr == KIA_V7_HEADER) {
|
||||
instance->generic.data = candidate;
|
||||
instance->generic.data_count_bit = KIA_V7_KEY_BITS;
|
||||
kia_v7_decode_key_decoder(instance);
|
||||
|
||||
if(instance->crc_valid) {
|
||||
if(instance->base.callback) {
|
||||
instance->base.callback(&instance->base, instance->base.context);
|
||||
}
|
||||
} else {
|
||||
instance->generic.data = 0;
|
||||
instance->generic.data_count_bit = 0;
|
||||
}
|
||||
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->decoder.parser_step = KiaV7DecoderStepReset;
|
||||
manchester_advance(
|
||||
instance->manchester_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_state,
|
||||
NULL);
|
||||
} else {
|
||||
instance->decoder.decode_data = 0;
|
||||
instance->decoder.decode_count_bit = 0;
|
||||
instance->decoder.parser_step = KiaV7DecoderStepReset;
|
||||
manchester_advance(
|
||||
instance->manchester_state,
|
||||
ManchesterEventReset,
|
||||
&instance->manchester_state,
|
||||
NULL);
|
||||
}
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
uint8_t kia_protocol_decoder_v7_get_hash_data(void* context) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderKiaV7* instance = context;
|
||||
return subghz_protocol_blocks_get_hash_data(
|
||||
&instance->decoder, (instance->decoder.decode_count_bit >> 3U) + 1U);
|
||||
}
|
||||
|
||||
void kia_protocol_decoder_v7_get_string(void* context, FuriString* output) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderKiaV7* instance = context;
|
||||
kia_v7_decode_key_decoder(instance);
|
||||
|
||||
furi_string_cat_printf(
|
||||
output,
|
||||
"%s %dbit\r\n"
|
||||
"Key:%016llX\r\n"
|
||||
"Sn:%07lX Cnt:%04lX\r\n"
|
||||
"Btn:%01X [%s] CRC:%02X [%s]",
|
||||
instance->generic.protocol_name,
|
||||
instance->generic.data_count_bit,
|
||||
instance->generic.data,
|
||||
instance->generic.serial & 0x0FFFFFFFU,
|
||||
instance->generic.cnt & 0xFFFFU,
|
||||
instance->decoded_button & 0x0FU,
|
||||
kia_v7_get_button_name(instance->decoded_button),
|
||||
instance->crc_calculated,
|
||||
instance->crc_valid ? "OK" : "ERR");
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus kia_protocol_decoder_v7_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderKiaV7* instance = context;
|
||||
kia_v7_decode_key_decoder(instance);
|
||||
|
||||
SubGhzProtocolStatus status =
|
||||
subghz_block_generic_serialize(&instance->generic, flipper_format, preset);
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
return status;
|
||||
}
|
||||
|
||||
uint32_t serial = instance->generic.serial & 0x0FFFFFFFU;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Serial", &serial, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
uint32_t btn_u32 = (uint32_t)(instance->decoded_button & 0x0FU);
|
||||
if(!flipper_format_write_uint32(flipper_format, "Btn", &btn_u32, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
uint32_t cnt_u32 = (uint32_t)(instance->generic.cnt & 0xFFFFU);
|
||||
if(!flipper_format_write_uint32(flipper_format, "Cnt", &cnt_u32, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
uint32_t repeat_u32 = KIA_V7_DEFAULT_TX_REPEAT;
|
||||
if(!flipper_format_write_uint32(flipper_format, "Repeat", &repeat_u32, 1)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
return kia_v7_write_display(
|
||||
flipper_format, instance->generic.protocol_name, instance->decoded_button);
|
||||
}
|
||||
|
||||
SubGhzProtocolStatus
|
||||
kia_protocol_decoder_v7_deserialize(void* context, FlipperFormat* flipper_format) {
|
||||
furi_check(context);
|
||||
|
||||
SubGhzProtocolDecoderKiaV7* instance = context;
|
||||
SubGhzProtocolStatus status = subghz_block_generic_deserialize_check_count_bit(
|
||||
&instance->generic, flipper_format, KIA_V7_KEY_BITS);
|
||||
|
||||
if(status != SubGhzProtocolStatusOk) {
|
||||
return status;
|
||||
}
|
||||
|
||||
if(!flipper_format_rewind(flipper_format)) {
|
||||
return SubGhzProtocolStatusErrorParserOthers;
|
||||
}
|
||||
|
||||
kia_v7_decode_key_decoder(instance);
|
||||
|
||||
uint32_t ser_u32 = 0;
|
||||
uint32_t btn_u32 = 0;
|
||||
uint32_t cnt_u32 = 0;
|
||||
bool got_serial = false;
|
||||
bool got_btn = false;
|
||||
bool got_cnt = false;
|
||||
|
||||
flipper_format_rewind(flipper_format);
|
||||
got_serial = flipper_format_read_uint32(flipper_format, "Serial", &ser_u32, 1);
|
||||
flipper_format_rewind(flipper_format);
|
||||
got_btn = flipper_format_read_uint32(flipper_format, "Btn", &btn_u32, 1);
|
||||
flipper_format_rewind(flipper_format);
|
||||
got_cnt = flipper_format_read_uint32(flipper_format, "Cnt", &cnt_u32, 1);
|
||||
|
||||
if(got_serial || got_btn || got_cnt) {
|
||||
if(got_serial) {
|
||||
instance->generic.serial = ser_u32 & 0x0FFFFFFFU;
|
||||
}
|
||||
if(got_btn) {
|
||||
instance->generic.btn = (uint8_t)(btn_u32 & 0x0FU);
|
||||
}
|
||||
if(got_cnt) {
|
||||
instance->generic.cnt = (uint16_t)(cnt_u32 & 0xFFFFU);
|
||||
}
|
||||
instance->generic.data = kia_v7_encode_key(
|
||||
instance->fixed_high_byte,
|
||||
instance->generic.serial,
|
||||
instance->generic.btn & 0x0FU,
|
||||
(uint16_t)(instance->generic.cnt & 0xFFFFU),
|
||||
&instance->crc_calculated);
|
||||
kia_v7_decode_key_decoder(instance);
|
||||
}
|
||||
|
||||
return SubGhzProtocolStatusOk;
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
|
||||
#include <furi.h>
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/types.h>
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
#include <flipper_format/flipper_format.h>
|
||||
#include <lib/toolbox/level_duration.h>
|
||||
#include <lib/toolbox/manchester_decoder.h>
|
||||
|
||||
#define KIA_PROTOCOL_V7_NAME "Kia V7"
|
||||
|
||||
typedef struct SubGhzProtocolDecoderKiaV7 SubGhzProtocolDecoderKiaV7;
|
||||
typedef struct SubGhzProtocolEncoderKiaV7 SubGhzProtocolEncoderKiaV7;
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_kia_v7_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_kia_v7_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_kia_v7;
|
||||
|
||||
void* kia_protocol_decoder_v7_alloc(SubGhzEnvironment* environment);
|
||||
void kia_protocol_decoder_v7_free(void* context);
|
||||
void kia_protocol_decoder_v7_reset(void* context);
|
||||
void kia_protocol_decoder_v7_feed(void* context, bool level, uint32_t duration);
|
||||
uint8_t kia_protocol_decoder_v7_get_hash_data(void* context);
|
||||
SubGhzProtocolStatus kia_protocol_decoder_v7_serialize(
|
||||
void* context,
|
||||
FlipperFormat* flipper_format,
|
||||
SubGhzRadioPreset* preset);
|
||||
SubGhzProtocolStatus
|
||||
kia_protocol_decoder_v7_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void kia_protocol_decoder_v7_get_string(void* context, FuriString* output);
|
||||
|
||||
void* kia_protocol_encoder_v7_alloc(SubGhzEnvironment* environment);
|
||||
void kia_protocol_encoder_v7_free(void* context);
|
||||
SubGhzProtocolStatus
|
||||
kia_protocol_encoder_v7_deserialize(void* context, FlipperFormat* flipper_format);
|
||||
void kia_protocol_encoder_v7_stop(void* context);
|
||||
LevelDuration kia_protocol_encoder_v7_yield(void* context);
|
||||
|
||||
@@ -59,6 +59,7 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
|
||||
&subghz_protocol_vag,
|
||||
&subghz_protocol_porsche_cayenne,
|
||||
&subghz_protocol_ford_v0,
|
||||
&ford_protocol_v1,
|
||||
&subghz_protocol_psa,
|
||||
&subghz_protocol_fiat_spa,
|
||||
&subghz_protocol_fiat_marelli,
|
||||
@@ -71,14 +72,15 @@ const SubGhzProtocol* const subghz_protocol_registry_items[] = {
|
||||
&subghz_protocol_kia_v3_v4,
|
||||
&subghz_protocol_kia_v5,
|
||||
&subghz_protocol_kia_v6,
|
||||
&subghz_protocol_kia_v7,
|
||||
&subghz_protocol_suzuki,
|
||||
&subghz_protocol_mitsubishi_v0,
|
||||
&subghz_protocol_star_line,
|
||||
&subghz_protocol_scher_khan,
|
||||
&subghz_protocol_sheriff_cfm,
|
||||
// until fix &subghz_protocol_honda,
|
||||
&subghz_protocol_chrysler,
|
||||
&subghz_protocol_psa2,
|
||||
&honda_static_protocol,
|
||||
//&subghz_protocol_honda,
|
||||
};
|
||||
|
||||
const SubGhzProtocolRegistry subghz_protocol_registry = {
|
||||
|
||||
@@ -72,12 +72,14 @@
|
||||
#include "kia_v3_v4.h"
|
||||
#include "kia_v5.h"
|
||||
#include "kia_v6.h"
|
||||
#include "kia_v7.h"
|
||||
#include "suzuki.h"
|
||||
#include "mitsubishi_v0.h"
|
||||
#include "mazda_siemens.h"
|
||||
#include "star_line.h"
|
||||
#include "scher_khan.h"
|
||||
#include "sheriff_cfm.h"
|
||||
#include "honda.h"
|
||||
#include "chrysler.h"
|
||||
#include "psa2.h"
|
||||
#include "honda_static.h"
|
||||
#include "ford_v1.h"
|
||||
//#include "honda_pandora.h"
|
||||
|
||||
@@ -1069,6 +1069,8 @@ void subghz_protocol_decoder_psa_feed(void* context, bool level, uint32_t durati
|
||||
&instance->manchester_state,
|
||||
&decoded_bit)) {
|
||||
uint32_t carry = (instance->decode_data_low >> 31) & 1;
|
||||
// PSA AM uses inverted Manchester convention
|
||||
decoded_bit = !decoded_bit;
|
||||
instance->decode_data_low = (instance->decode_data_low << 1) | (decoded_bit ? 1 : 0);
|
||||
instance->decode_data_high = (instance->decode_data_high << 1) | carry;
|
||||
instance->decode_count_bit++;
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,160 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <lib/subghz/protocols/base.h>
|
||||
#include <lib/subghz/blocks/const.h>
|
||||
#include <lib/subghz/blocks/decoder.h>
|
||||
#include <lib/subghz/blocks/encoder.h>
|
||||
#include <lib/subghz/blocks/generic.h>
|
||||
#include <lib/subghz/blocks/math.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* =========================================================
|
||||
* PROTOCOL NAME
|
||||
* ========================================================= */
|
||||
|
||||
#define SUBGHZ_PROTOCOL_PSA2_NAME "PSA OLD"
|
||||
|
||||
/* =========================================================
|
||||
* FORWARD DECLARATIONS — opaque handles
|
||||
* ========================================================= */
|
||||
|
||||
typedef struct SubGhzProtocolDecoderPSA SubGhzProtocolDecoderPSA;
|
||||
typedef struct SubGhzProtocolEncoderPSA SubGhzProtocolEncoderPSA;
|
||||
|
||||
/* =========================================================
|
||||
* PROTOCOL DESCRIPTORS — exported singletons
|
||||
* ========================================================= */
|
||||
|
||||
extern const SubGhzProtocolDecoder subghz_protocol_psa_decoder;
|
||||
extern const SubGhzProtocolEncoder subghz_protocol_psa_encoder;
|
||||
extern const SubGhzProtocol subghz_protocol_psa2;
|
||||
|
||||
/* =========================================================
|
||||
* DECODER API
|
||||
* ========================================================= */
|
||||
|
||||
/**
|
||||
* Allocate a PSA decoder instance.
|
||||
*
|
||||
* @param environment SubGHz environment (may be NULL / unused)
|
||||
* @return Opaque pointer to SubGhzProtocolDecoderPSA
|
||||
*/
|
||||
void* subghz_protocol_decoder_psa2_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free a PSA decoder instance.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
|
||||
*/
|
||||
void subghz_protocol_decoder_psa2_free(void* context);
|
||||
|
||||
/**
|
||||
* Reset the decoder state machine to its initial state.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
|
||||
*/
|
||||
void subghz_protocol_decoder_psa2_reset(void* context);
|
||||
|
||||
/**
|
||||
* Feed one pulse/gap sample into the decoder state machine.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
|
||||
* @param level true = RF high (pulse), false = RF low (gap)
|
||||
* @param duration Duration of this level in microseconds
|
||||
*/
|
||||
void subghz_protocol_decoder_psa2_feed(void* context, bool level, uint32_t duration);
|
||||
|
||||
/**
|
||||
* Return a one-byte hash of the most recently decoded packet.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
|
||||
* @return Hash byte
|
||||
*/
|
||||
uint8_t subghz_protocol_decoder_psa2_get_hash_data(void* context);
|
||||
|
||||
/**
|
||||
* Serialize the most recently decoded packet into a FlipperFormat stream.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
|
||||
* @param ff Open FlipperFormat file handle
|
||||
* @param preset Radio preset in use
|
||||
* @return SubGhzProtocolStatusOk on success
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_psa2_serialize(
|
||||
void* context,
|
||||
FlipperFormat* ff,
|
||||
SubGhzRadioPreset* preset);
|
||||
|
||||
/**
|
||||
* Deserialize a previously saved packet from a FlipperFormat stream
|
||||
* into the decoder instance.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
|
||||
* @param ff Open FlipperFormat file handle (positioned at start)
|
||||
* @return SubGhzProtocolStatusOk on success
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_decoder_psa2_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* ff);
|
||||
|
||||
/**
|
||||
* Build a human-readable description of the most recently decoded packet.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_decoder_psa_alloc()
|
||||
* @param output FuriString to write the description into (cleared first)
|
||||
*/
|
||||
void subghz_protocol_decoder_psa2_get_string(void* context, FuriString* output);
|
||||
|
||||
/* =========================================================
|
||||
* ENCODER API
|
||||
* ========================================================= */
|
||||
|
||||
/**
|
||||
* Allocate a PSA encoder instance.
|
||||
*
|
||||
* @param environment SubGHz environment (may be NULL / unused)
|
||||
* @return Opaque pointer to SubGhzProtocolEncoderPSA
|
||||
*/
|
||||
void* subghz_protocol_encoder_psa2_alloc(SubGhzEnvironment* environment);
|
||||
|
||||
/**
|
||||
* Free a PSA encoder instance.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_encoder_psa_alloc()
|
||||
*/
|
||||
void subghz_protocol_encoder_psa2_free(void* context);
|
||||
|
||||
/**
|
||||
* Load transmit data from a FlipperFormat stream into the encoder.
|
||||
* Rebuilds the upload buffer ready for transmission.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_encoder_psa_alloc()
|
||||
* @param ff Open FlipperFormat file handle (will be rewound internally)
|
||||
* @return SubGhzProtocolStatusOk on success
|
||||
*/
|
||||
SubGhzProtocolStatus subghz_protocol_encoder_psa2_deserialize(
|
||||
void* context,
|
||||
FlipperFormat* ff);
|
||||
|
||||
/**
|
||||
* Stop an in-progress transmission immediately.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_encoder_psa_alloc()
|
||||
*/
|
||||
void subghz_protocol_encoder_psa2_stop(void* context);
|
||||
|
||||
/**
|
||||
* Yield the next LevelDuration sample from the upload buffer.
|
||||
* Called repeatedly by the SubGHz radio driver during transmission.
|
||||
*
|
||||
* @param context Pointer returned by subghz_protocol_encoder_psa_alloc()
|
||||
* @return Next LevelDuration, or level_duration_reset() when done
|
||||
*/
|
||||
LevelDuration subghz_protocol_encoder_psa2_yield(void* context);
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
Reference in New Issue
Block a user