This commit is contained in:
Totoo
2025-12-21 11:21:55 +01:00
committed by GitHub
parent f71f19e719
commit f86d3e51f1
27 changed files with 3163 additions and 4 deletions
+169
View File
@@ -0,0 +1,169 @@
#pragma once
#include "subcarbase.hpp"
#include <cstring>
typedef enum {
BMWDecoderStepReset = 0,
BMWDecoderStepCheckPreambula,
BMWDecoderStepSaveDuration,
BMWDecoderStepCheckDuration,
} BMWDecoderStep;
class FProtoSubCarBMWV0 : public FProtoSubCarBase {
public:
FProtoSubCarBMWV0() {
sensorType = FPC_BMWV0;
te_short = 350;
te_long = 700;
te_delta = 120;
min_count_bit_for_found = 61;
}
uint8_t subghz_protocol_bmw_crc8(uint8_t* data, size_t len) {
uint8_t crc = 0x00;
for (size_t i = 0; i < len; i++) {
crc ^= data[i];
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x80)
crc = (uint8_t)((crc << 1) ^ 0x31);
else
crc <<= 1;
}
}
return crc;
}
uint16_t subghz_protocol_bmw_crc16(uint8_t* data, size_t len) {
uint16_t crc = 0xFFFF;
for (size_t i = 0; i < len; i++) {
crc ^= ((uint16_t)data[i] << 8);
for (uint8_t j = 0; j < 8; j++) {
if (crc & 0x8000)
crc = (crc << 1) ^ 0x1021;
else
crc <<= 1;
}
}
return crc;
}
void subghz_protocol_decoder_bmw_reset_internal() {
decode_data = 0;
decode_count_bit = 0;
decode_data2 = 0;
parser_step = BMWDecoderStepReset;
header_count = 0;
crc_type = 0;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case BMWDecoderStepReset:
if (level && (DURATION_DIFF(duration, te_short) <
te_delta)) {
parser_step = BMWDecoderStepCheckPreambula;
te_last = duration;
header_count = 0;
decode_data = 0;
decode_count_bit = 0;
}
break;
case BMWDecoderStepCheckPreambula:
if (level) {
if ((DURATION_DIFF(duration, te_short) <
te_delta) ||
(DURATION_DIFF(duration, te_long) <
te_delta)) {
te_last = duration;
} else {
parser_step = BMWDecoderStepReset;
}
} else if (
(DURATION_DIFF(duration, te_short) <
te_delta) &&
(DURATION_DIFF(te_last, te_short) <
te_delta)) {
header_count++;
} else if (
(DURATION_DIFF(duration, te_long) <
te_delta) &&
(DURATION_DIFF(te_last, te_long) <
te_delta)) {
if (header_count > 15) {
parser_step = BMWDecoderStepSaveDuration;
decode_data = 0ULL;
decode_count_bit = 0;
} else {
parser_step = BMWDecoderStepReset;
}
} else {
parser_step = BMWDecoderStepReset;
}
break;
case BMWDecoderStepSaveDuration:
if (level) {
if (duration >=
(te_long + te_delta * 2UL)) {
if (decode_count_bit >=
min_count_bit_for_found) {
// instance->generic.data = decode_data;
data_count_bit = decode_count_bit;
// Perform CRC check with both CRC8 and CRC16
uint8_t* raw_bytes = (uint8_t*)decode_data;
size_t raw_len = (decode_count_bit + 7) / 8;
uint8_t crc8 = subghz_protocol_bmw_crc8(raw_bytes, raw_len - 1);
if (crc8 == raw_bytes[raw_len - 1]) {
crc_type = 8;
} else {
uint16_t crc16 = subghz_protocol_bmw_crc16(raw_bytes, raw_len - 2);
uint16_t rx_crc16 = (raw_bytes[raw_len - 2] << 8) | raw_bytes[raw_len - 1];
if (crc16 == rx_crc16) {
crc_type = 16;
} else {
crc_type = 0; // invalid
}
}
if (crc_type != 0 && callback) {
callback(this);
}
}
subghz_protocol_decoder_bmw_reset_internal();
} else {
te_last = duration;
parser_step = BMWDecoderStepCheckDuration;
}
} else {
parser_step = BMWDecoderStepReset;
}
break;
case BMWDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) <
te_delta) &&
(DURATION_DIFF(duration, te_short) <
te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = BMWDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) <
te_delta) &&
(DURATION_DIFF(duration, te_long) <
te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = BMWDecoderStepSaveDuration;
} else {
parser_step = BMWDecoderStepReset;
}
} else {
parser_step = BMWDecoderStepReset;
}
break;
}
}
uint16_t header_count = 0;
uint8_t crc_type = 0;
};
+204
View File
@@ -0,0 +1,204 @@
#pragma once
#include "subcarbase.hpp"
#include <cstring>
typedef enum {
FiatV0DecoderStepReset = 0,
FiatV0DecoderStepPreamble = 1,
FiatV0DecoderStepData = 2,
} FiatV0DecoderStep;
class FProtoSubCarFiatV0 : public FProtoSubCarBase {
public:
FProtoSubCarFiatV0() {
sensorType = FPC_FIATV0;
te_short = 200;
te_long = 400;
te_delta = 100;
min_count_bit_for_found = 64;
}
void feed(bool level, uint32_t duration) {
uint32_t gap_threshold = 800;
uint32_t diff;
switch (decoder_state) {
case FiatV0DecoderStepReset:
if (!level) {
return;
}
if (duration < te_short) {
diff = te_short - duration;
} else {
diff = duration - te_short;
}
if (diff < te_delta) {
data_low = 0;
data_high = 0;
decoder_state = FiatV0DecoderStepPreamble;
te_last = duration;
preamble_count = 0;
bit_count = 0;
FProtoGeneral::manchester_advance(
manchester_state,
ManchesterEventReset,
&manchester_state,
NULL);
}
break;
case FiatV0DecoderStepPreamble:
if (level) {
return;
}
if (duration < te_short) {
diff = te_short - duration;
if (diff < te_delta) {
preamble_count++;
te_last = duration;
if (preamble_count >= 0x96) {
if (duration < gap_threshold) {
diff = gap_threshold - duration;
} else {
diff = duration - gap_threshold;
}
if (diff < te_delta) {
decoder_state = FiatV0DecoderStepData;
preamble_count = 0;
data_low = 0;
data_high = 0;
bit_count = 0;
te_last = duration;
return;
}
}
} else {
decoder_state = FiatV0DecoderStepReset;
if (preamble_count >= 0x96) {
if (duration < gap_threshold) {
diff = gap_threshold - duration;
} else {
diff = duration - gap_threshold;
}
if (diff < te_delta) {
decoder_state = FiatV0DecoderStepData;
preamble_count = 0;
data_low = 0;
data_high = 0;
bit_count = 0;
te_last = duration;
return;
}
}
}
} else {
diff = duration - te_short;
if (diff < te_delta) {
preamble_count++;
te_last = duration;
} else {
decoder_state = FiatV0DecoderStepReset;
}
if (preamble_count >= 0x96) {
if (duration >= 799) {
diff = duration - gap_threshold;
} else {
diff = gap_threshold - duration;
}
if (diff < te_delta) {
decoder_state = FiatV0DecoderStepData;
preamble_count = 0;
data_low = 0;
data_high = 0;
bit_count = 0;
te_last = duration;
return;
}
}
}
break;
case FiatV0DecoderStepData:
ManchesterEvent event = ManchesterEventReset;
if (duration < te_short) {
diff = te_short - duration;
if (diff < te_delta) {
event = level ? ManchesterEventShortLow : ManchesterEventShortHigh;
}
} else {
diff = duration - te_short;
if (diff < te_delta) {
event = level ? ManchesterEventShortLow : ManchesterEventShortHigh;
} else {
if (duration < te_long) {
diff = te_long - duration;
} else {
diff = duration - te_long;
}
if (diff < te_delta) {
event = level ? ManchesterEventLongLow : ManchesterEventLongHigh;
}
}
}
if (event != ManchesterEventReset) {
bool data_bit_bool;
if (FProtoGeneral::manchester_advance(
manchester_state,
event,
&manchester_state,
&data_bit_bool)) {
uint32_t new_bit = data_bit_bool ? 1 : 0;
uint32_t carry = (data_low >> 31) & 1;
data_low = (data_low << 1) | new_bit;
data_high = (data_high << 1) | carry;
bit_count++;
if (bit_count == 0x40) {
fix = data_low;
hop = data_high;
data_low = 0;
data_high = 0;
}
if (bit_count > 0x46) {
final_count = bit_count;
endbyte = (uint8_t)data_low;
/*
generic.data = ((uint64_t)hop << 32) | fix;
generic.data_count_bit = 64;
generic.serial = fix;
generic.btn = endbyte; // still exported as btn for UI compatibility
generic.cnt = hop;
*/
decode_data = ((uint64_t)hop << 32) | fix; // this is my own data passer, not the original
decode_data2 = endbyte;
data_count_bit = 64;
if (callback) {
callback(this);
}
data_low = 0;
data_high = 0;
bit_count = 0;
decoder_state = FiatV0DecoderStepReset;
}
}
}
te_last = duration;
break;
}
}
ManchesterState manchester_state = ManchesterStateMid1;
uint8_t decoder_state = 0;
uint16_t preamble_count = 0;
uint32_t data_low = 0;
uint32_t data_high = 0;
uint8_t bit_count = 0;
uint32_t hop = 0;
uint32_t fix = 0;
uint8_t endbyte = 0;
uint8_t final_count = 0;
uint32_t te_last = 0;
};
+150
View File
@@ -0,0 +1,150 @@
#pragma once
#include "subcarbase.hpp"
#include <cstring>
typedef enum {
FordV0DecoderStepReset = 0,
FordV0DecoderStepPreamble,
FordV0DecoderStepPreambleCheck,
FordV0DecoderStepGap,
FordV0DecoderStepData,
} FordV0DecoderStep;
class FProtoSubCarFordV0 : public FProtoSubCarBase {
public:
FProtoSubCarFordV0() {
sensorType = FPC_FORDV0;
te_short = 250;
te_long = 500;
te_delta = 100;
min_count_bit_for_found = 64;
}
void ford_v0_add_bit(bool bit) {
uint32_t low = (uint32_t)data_low;
data_low = (data_low << 1) | (bit ? 1 : 0);
data_high = (data_high << 1) | ((low >> 31) & 1);
bit_count++;
}
bool ford_v0_process_data() {
if (bit_count == 64) {
uint64_t combined = ((uint64_t)data_high << 32) | data_low;
key1 = ~combined;
data_low = 0;
data_high = 0;
return false;
}
if (bit_count == 80) {
uint16_t key2_raw = (uint16_t)(data_low & 0xFFFF);
uint16_t key2 = ~key2_raw;
decode_data = key1;
decode_data2 = key2;
// decode_ford_v0(key1, key2, &serial, &button, &count);
return true;
}
return false;
}
void feed(bool level, uint32_t duration) {
uint32_t gap_threshold = 3500;
switch (parser_step) {
case FordV0DecoderStepReset:
if (level && (DURATION_DIFF(duration, te_short) < te_delta)) {
data_low = 0;
data_high = 0;
parser_step = FordV0DecoderStepPreamble;
te_last = duration;
header_count = 0;
bit_count = 0;
FProtoGeneral::manchester_advance(manchester_state, ManchesterEventReset, &manchester_state, NULL);
}
break;
case FordV0DecoderStepPreamble:
if (!level) {
if (DURATION_DIFF(duration, te_long) < te_delta) {
te_last = duration;
parser_step = FordV0DecoderStepPreambleCheck;
} else {
parser_step = FordV0DecoderStepReset;
}
}
break;
case FordV0DecoderStepPreambleCheck:
if (level) {
if (DURATION_DIFF(duration, te_long) < te_delta) {
header_count++;
te_last = duration;
parser_step = FordV0DecoderStepPreamble;
} else if (DURATION_DIFF(duration, te_short) < te_delta) {
parser_step = FordV0DecoderStepGap;
} else {
parser_step = FordV0DecoderStepReset;
}
}
break;
case FordV0DecoderStepGap:
if (!level && (DURATION_DIFF(duration, gap_threshold) < 250)) {
data_low = 1;
data_high = 0;
bit_count = 1;
parser_step = FordV0DecoderStepData;
} else if (!level && duration > gap_threshold + 250) {
parser_step = FordV0DecoderStepReset;
}
break;
case FordV0DecoderStepData: {
ManchesterEvent event;
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = level ? ManchesterEventShortLow : ManchesterEventShortHigh;
} else if (DURATION_DIFF(duration, te_long) < te_delta) {
event = level ? ManchesterEventLongLow : ManchesterEventLongHigh;
} else {
parser_step = FordV0DecoderStepReset;
break;
}
bool data_bit;
if (FProtoGeneral::manchester_advance(manchester_state, event, &manchester_state, &data_bit)) {
ford_v0_add_bit(data_bit);
if (ford_v0_process_data()) {
/* instance->generic.data = instance->key1;
instance->generic.data_count_bit = 64;
instance->generic.serial = instance->serial;
instance->generic.btn = instance->button;
instance->generic.cnt = instance->count;
*/
if (callback) {
callback(this);
}
data_low = 0;
data_high = 0;
bit_count = 0;
parser_step = FordV0DecoderStepReset;
}
}
te_last = duration;
break;
}
}
}
ManchesterState manchester_state = ManchesterStateMid1;
uint64_t data_low = 0;
uint64_t data_high = 0;
uint8_t bit_count = 0;
uint16_t header_count = 0;
uint64_t key1 = 0;
uint16_t key2 = 0;
};
+121
View File
@@ -0,0 +1,121 @@
#pragma once
#include "subcarbase.hpp"
#include <cstring>
typedef enum {
KIADecoderStepReset = 0,
KIADecoderStepCheckPreambula,
KIADecoderStepSaveDuration,
KIADecoderStepCheckDuration,
} KIADecoderStep;
class FProtoSubCarKiaV0 : public FProtoSubCarBase {
public:
FProtoSubCarKiaV0() {
sensorType = FPC_KIAV0;
te_short = 250;
te_long = 500;
te_delta = 100;
min_count_bit_for_found = 61;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case KIADecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = KIADecoderStepCheckPreambula;
te_last = duration;
header_count = 0;
}
break;
case KIADecoderStepCheckPreambula:
if (level) {
if ((DURATION_DIFF(duration, te_short) < te_delta) ||
(DURATION_DIFF(duration, te_long) < te_delta)) {
te_last = duration;
} else {
parser_step = KIADecoderStepReset;
}
} else if (
(DURATION_DIFF(duration, te_short) < te_delta) &&
(DURATION_DIFF(te_last, te_short) < te_delta)) {
header_count++;
break;
} else if (
(DURATION_DIFF(duration, te_long) < te_delta) &&
(DURATION_DIFF(te_last, te_long) < te_delta)) {
if (header_count > 15) {
parser_step = KIADecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 1;
subghz_protocol_blocks_add_bit(1);
// FURI_LOG_I(TAG, "Starting data decode after %u header pulses", header_count);
} else {
parser_step = KIADecoderStepReset;
}
} else {
parser_step = KIADecoderStepReset;
}
break;
case KIADecoderStepSaveDuration:
if (level) {
if (duration >=
(te_long + te_delta * 2UL)) {
// Signal ended too early!
// FURI_LOG_W(TAG, "Signal ended at %u bits (expected 61). Duration: %lu", decode_count_bit, duration);
parser_step = KIADecoderStepReset;
if (decode_count_bit == min_count_bit_for_found) {
// instance->generic.data = decode_data;
data_count_bit = decode_count_bit;
if (callback)
callback(this);
} else {
// FURI_LOG_E(TAG, "Incomplete signal: only %u bits", decode_count_bit);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = KIADecoderStepCheckDuration;
}
} else {
parser_step = KIADecoderStepReset;
}
break;
case KIADecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
if (decode_count_bit % 10 == 0) {
// FURI_LOG_D(TAG, "Decoded %u bits so far", decode_count_bit);
}
parser_step = KIADecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
if (decode_count_bit % 10 == 0) {
// FURI_LOG_D(TAG, "Decoded %u bits so far", decode_count_bit);
}
parser_step = KIADecoderStepSaveDuration;
} else {
// FURI_LOG_W(TAG, "Timing mismatch at bit %u. Last: %lu, Current: %lu", decode_count_bit, te_last, duration);
parser_step = KIADecoderStepReset;
}
} else {
parser_step = KIADecoderStepReset;
}
break;
}
}
bool is_running = false;
size_t preamble_count = 0;
size_t data_bit_index = 0;
uint8_t last_bit = 0;
bool send_high = false;
uint16_t header_count = 0;
};
+192
View File
@@ -0,0 +1,192 @@
#pragma once
#include "subcarbase.hpp"
#include <cstring>
typedef enum {
KiaV1DecoderStepReset = 0,
KiaV1DecoderStepCheckPreamble,
KiaV1DecoderStepFoundShortLow,
KiaV1DecoderStepCollectRawBits,
} KiaV1DecoderStep;
class FProtoSubCarKiaV1 : public FProtoSubCarBase {
public:
FProtoSubCarKiaV1() {
sensorType = FPC_KIAV1;
te_short = 800;
te_long = 1600;
te_delta = 200;
min_count_bit_for_found = 56;
}
void kia_v1_add_raw_bit(bool bit) {
if (raw_bit_count < 192) {
uint16_t byte_idx = raw_bit_count / 8;
uint8_t bit_idx = 7 - (raw_bit_count % 8);
if (bit) {
raw_bits[byte_idx] |= (1 << bit_idx);
} else {
raw_bits[byte_idx] &= ~(1 << bit_idx);
}
raw_bit_count++;
}
}
inline bool kia_v1_get_raw_bit(uint16_t idx) {
uint16_t byte_idx = idx / 8;
uint8_t bit_idx = 7 - (idx % 8);
return (raw_bits[byte_idx] >> bit_idx) & 1;
}
bool kia_v1_manchester_decode() {
if (raw_bit_count < 113) {
// FURI_LOG_D(TAG, "Not enough raw bits: %u", raw_bit_count);
return false;
}
// Try different offsets to find best alignment (RTL-433 uses -1 bit offset)
uint16_t best_bits = 0;
uint64_t best_data = 0;
// uint16_t best_offset = 0;
for (uint16_t offset = 0; offset < 8; offset++) {
uint64_t data = 0;
uint16_t decoded_bits = 0;
for (uint16_t i = offset; i + 1 < raw_bit_count && decoded_bits < 56; i += 2) {
bool bit1 = kia_v1_get_raw_bit(i);
bool bit2 = kia_v1_get_raw_bit(i + 1);
uint8_t two_bits = (bit1 << 1) | bit2;
// V1 uses: 10=1, 01=0
if (two_bits == 0x02) { // 10 = decoded 1
data = (data << 1) | 1;
decoded_bits++;
} else if (two_bits == 0x01) { // 01 = decoded 0
data = (data << 1);
decoded_bits++;
} else {
break;
}
}
if (decoded_bits > best_bits) {
best_bits = decoded_bits;
best_data = data;
// best_offset = offset;
}
}
// FURI_LOG_I(TAG, "Best: offset=%u bits=%u data=%014llX", best_offset, best_bits, best_data);
decode_data = best_data;
decode_count_bit = best_bits;
return best_bits >= min_count_bit_for_found;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case KiaV1DecoderStepReset:
// Preamble 0xCCCCCCCD produces alternating LONG pulses
if ((level) && (DURATION_DIFF(duration, te_long) <
te_delta)) {
parser_step = KiaV1DecoderStepCheckPreamble;
te_last = duration;
header_count = 1;
}
break;
case KiaV1DecoderStepCheckPreamble:
if (level) {
if (DURATION_DIFF(duration, te_long) <
te_delta) {
te_last = duration;
header_count++;
} else if (
DURATION_DIFF(duration, te_short) <
te_delta) {
te_last = duration;
} else {
parser_step = KiaV1DecoderStepReset;
}
} else {
// LOW pulse
if (DURATION_DIFF(duration, te_long) <
te_delta) {
header_count++;
} else if (
DURATION_DIFF(duration, te_short) <
te_delta) {
// Short LOW - this is the start of sync (0xCD ends: ...long H, short L, short H)
if (header_count > 12) {
parser_step = KiaV1DecoderStepFoundShortLow;
}
} else {
parser_step = KiaV1DecoderStepReset;
}
}
break;
case KiaV1DecoderStepFoundShortLow:
// Expecting SHORT HIGH to complete sync
if (level && (DURATION_DIFF(duration, te_short) <
te_delta)) {
// FURI_LOG_I(TAG, "Sync! hdr=%u", header_count);
parser_step = KiaV1DecoderStepCollectRawBits;
raw_bit_count = 0;
memset(raw_bits, 0, sizeof(raw_bits));
// Add the sync short HIGH as first raw bit
kia_v1_add_raw_bit(true);
} else {
parser_step = KiaV1DecoderStepReset;
}
break;
case KiaV1DecoderStepCollectRawBits:
if (duration > 2400) {
// FURI_LOG_I(TAG, "End! raw_bits=%u", raw_bit_count);
if (kia_v1_manchester_decode()) {
// instance->generic.data = decode_data;
data_count_bit = decode_count_bit;
// Extract fields from 56-bit data per RTL-433:
// Serial: bits 55-24 (32 bits)
// Btn: bits 23-16 (8 bits)
// Count: bits 15-8 (8 bits)
// CRC: bits 7-0 (8 bits)
// instance->generic.serial = (uint32_t)((instance->generic.data >> 24) & 0xFFFFFFFF);
// instance->generic.btn = (uint8_t)((instance->generic.data >> 16) & 0xFF);
// instance->generic.cnt = (uint8_t)((instance->generic.data >> 8) & 0xFF);
if (callback)
callback(this);
}
parser_step = KiaV1DecoderStepReset;
break;
}
int num_bits = 0;
if (DURATION_DIFF(duration, te_short) <
te_delta) {
num_bits = 1;
} else if (
DURATION_DIFF(duration, te_long) <
te_delta) {
num_bits = 2;
} else {
parser_step = KiaV1DecoderStepReset;
break;
}
for (int i = 0; i < num_bits; i++) {
kia_v1_add_raw_bit(level);
}
break;
}
}
uint8_t raw_bits[24]{0};
uint16_t raw_bit_count = 0;
uint16_t header_count = 0;
};
+167
View File
@@ -0,0 +1,167 @@
#pragma once
#include "subcarbase.hpp"
#include <cstring>
typedef enum {
KiaV2DecoderStepReset = 0,
KiaV2DecoderStepCheckPreamble,
KiaV2DecoderStepCollectRawBits,
} KiaV2DecoderStep;
class FProtoSubCarKiaV2 : public FProtoSubCarBase {
public:
FProtoSubCarKiaV2() {
sensorType = FPC_KIAV2;
te_short = 500;
te_long = 1000;
te_delta = 160;
min_count_bit_for_found = 51;
}
void kia_v2_add_raw_bit(bool bit) {
if (raw_bit_count < 160) {
uint16_t byte_idx = raw_bit_count / 8;
uint8_t bit_idx = 7 - (raw_bit_count % 8);
if (bit) {
raw_bits[byte_idx] |= (1 << bit_idx);
} else {
raw_bits[byte_idx] &= ~(1 << bit_idx);
}
raw_bit_count++;
}
}
inline bool kia_v2_get_raw_bit(uint16_t idx) {
uint16_t byte_idx = idx / 8;
uint8_t bit_idx = 7 - (idx % 8);
return (raw_bits[byte_idx] >> bit_idx) & 1;
}
bool kia_v2_manchester_decode() {
if (raw_bit_count < 100) {
return false;
}
uint16_t best_bits = 0;
uint64_t best_data = 0;
for (uint16_t offset = 0; offset < 8; offset++) {
uint64_t data = 0;
uint16_t decoded_bits = 0;
for (uint16_t i = offset; i + 1 < raw_bit_count && decoded_bits < 53; i += 2) {
bool bit1 = kia_v2_get_raw_bit(i);
bool bit2 = kia_v2_get_raw_bit(i + 1);
uint8_t two_bits = (bit1 << 1) | bit2;
if (two_bits == 0x02) {
data = (data << 1) | 1;
decoded_bits++;
} else if (two_bits == 0x01) {
data = (data << 1);
decoded_bits++;
} else {
break;
}
}
if (decoded_bits > best_bits) {
best_bits = decoded_bits;
best_data = data;
}
}
decode_data = best_data;
decode_count_bit = best_bits;
return best_bits >= min_count_bit_for_found;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case KiaV2DecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_long) < te_delta)) {
parser_step = KiaV2DecoderStepCheckPreamble;
te_last = duration;
header_count = 1;
}
break;
case KiaV2DecoderStepCheckPreamble:
if (level) {
if (DURATION_DIFF(duration, te_long) <
te_delta) {
te_last = duration;
header_count++;
} else if (
DURATION_DIFF(duration, te_short) <
te_delta) {
te_last = duration;
} else {
parser_step = KiaV2DecoderStepReset;
}
} else {
if (DURATION_DIFF(duration, te_long) <
te_delta) {
header_count++;
} else if (
DURATION_DIFF(duration, te_short) <
te_delta) {
if (header_count > 10 &&
DURATION_DIFF(te_last, te_short) <
te_delta) {
parser_step = KiaV2DecoderStepCollectRawBits;
raw_bit_count = 0;
memset(raw_bits, 0, sizeof(raw_bits));
}
} else {
parser_step = KiaV2DecoderStepReset;
}
}
break;
case KiaV2DecoderStepCollectRawBits:
if (duration > 1500) {
if (kia_v2_manchester_decode()) {
/*data = decode_data;
data_count_bit = decode_count_bit;
serial = (uint32_t)((data >> 20) & 0xFFFFFFFF);
btn = (uint8_t)((data >> 16) & 0x0F);
uint16_t raw_count = (uint16_t)((data >> 4) & 0xFFF);
cnt = ((raw_count >> 4) | (raw_count << 8)) & 0xFFF;
*/
data_count_bit = decode_count_bit;
if (callback)
callback(this);
}
parser_step = KiaV2DecoderStepReset;
break;
}
int num_bits = 0;
if (DURATION_DIFF(duration, te_short) <
te_delta) {
num_bits = 1;
} else if (
DURATION_DIFF(duration, te_long) <
te_delta) {
num_bits = 2;
} else {
parser_step = KiaV2DecoderStepReset;
break;
}
for (int i = 0; i < num_bits; i++) {
kia_v2_add_raw_bit(level);
}
break;
}
}
uint8_t raw_bits[20]{0};
uint16_t raw_bit_count = 0;
uint16_t header_count = 0;
};
+185
View File
@@ -0,0 +1,185 @@
#pragma once
#include "subcarbase.hpp"
#include <cstring>
typedef enum {
KiaV3V4DecoderStepReset = 0,
KiaV3V4DecoderStepCheckPreamble,
KiaV3V4DecoderStepCollectRawBits,
} KiaV3V4DecoderStep;
class FProtoSubCarKiaV3V4 : public FProtoSubCarBase {
public:
FProtoSubCarKiaV3V4() {
sensorType = FPC_KIAV3V4;
te_short = 400;
te_long = 800;
te_delta = 150;
min_count_bit_for_found = 64;
}
uint8_t reverse8(uint8_t byte) {
byte = (byte & 0xF0) >> 4 | (byte & 0x0F) << 4;
byte = (byte & 0xCC) >> 2 | (byte & 0x33) << 2;
byte = (byte & 0xAA) >> 1 | (byte & 0x55) << 1;
return byte;
}
void kia_v3_v4_add_raw_bit(bool bit) {
if (raw_bit_count < 256) {
uint16_t byte_idx = raw_bit_count / 8;
uint8_t bit_idx = 7 - (raw_bit_count % 8);
if (bit) {
raw_bits[byte_idx] |= (1 << bit_idx);
} else {
raw_bits[byte_idx] &= ~(1 << bit_idx);
}
raw_bit_count++;
}
}
bool kia_v3_v4_process_buffer() {
if (raw_bit_count < 64) {
return false;
}
uint8_t* b = raw_bits;
// For V3-style (long LOW sync), data is inverted
if (is_v3_sync) {
uint16_t num_bytes = (raw_bit_count + 7) / 8;
for (uint16_t i = 0; i < num_bytes; i++) {
b[i] = ~b[i];
}
}
// Extract fields
// uint32_t encrypted = ((uint32_t)reverse8(b[3]) << 24) | ((uint32_t)reverse8(b[2]) << 16) | ((uint32_t)reverse8(b[1]) << 8) | (uint32_t)reverse8(b[0]);
uint32_t serial = ((uint32_t)reverse8(b[7] & 0xF0) << 24) | ((uint32_t)reverse8(b[6]) << 16) | ((uint32_t)reverse8(b[5]) << 8) | (uint32_t)reverse8(b[4]);
uint8_t btn = (reverse8(b[7]) & 0xF0) >> 4;
decode_data = serial;
decode_count_bit = 64;
decode_data2 = btn;
data_count_bit = decode_count_bit;
if (callback)
callback(this);
// uint8_t our_serial_lsb = serial & 0xFF;
// Decrypt --skipped, no keeloq decoding
/* uint32_t decrypted = keeloq_common_decrypt(encrypted, kia_mf_key);
uint8_t dec_btn = (decrypted >> 28) & 0x0F;
uint8_t dec_serial_lsb = (decrypted >> 16) & 0xFF;
// Validate
if (dec_btn != btn || dec_serial_lsb != our_serial_lsb) {
return false;
}
// Valid decode - version determined by sync type
instance->encrypted = encrypted;
instance->decrypted = decrypted;
instance->generic.serial = serial;
instance->generic.btn = btn;
instance->generic.cnt = decrypted & 0xFFFF;
instance->version = is_v3_sync ? 1 : 0;
uint64_t key_data = ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) | ((uint64_t)b[2] << 40) |
((uint64_t)b[3] << 32) | ((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
((uint64_t)b[6] << 8) | (uint64_t)b[7];
instance->generic.data = key_data;
instance->generic.data_count_bit = 64;
*/
return true;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case KiaV3V4DecoderStepReset:
if (level && DURATION_DIFF(duration, te_short) <
te_delta) {
parser_step = KiaV3V4DecoderStepCheckPreamble;
te_last = duration;
header_count = 1;
}
break;
case KiaV3V4DecoderStepCheckPreamble:
if (level) {
if (DURATION_DIFF(duration, te_short) <
te_delta) {
te_last = duration;
} else if (duration > 1000 && duration < 1500) {
// V4 style: Sync is LONG HIGH
if (header_count >= 8) {
parser_step = KiaV3V4DecoderStepCollectRawBits;
raw_bit_count = 0;
is_v3_sync = false;
memset(raw_bits, 0, sizeof(raw_bits));
} else {
parser_step = KiaV3V4DecoderStepReset;
}
} else {
parser_step = KiaV3V4DecoderStepReset;
}
} else {
if (duration > 1000 && duration < 1500) {
// V3 style: Sync is LONG LOW
if (header_count >= 8) {
parser_step = KiaV3V4DecoderStepCollectRawBits;
raw_bit_count = 0;
is_v3_sync = true;
memset(raw_bits, 0, sizeof(raw_bits));
} else {
parser_step = KiaV3V4DecoderStepReset;
}
} else if (
DURATION_DIFF(duration, te_short) <
te_delta &&
DURATION_DIFF(te_last, te_short) <
te_delta) {
header_count++;
} else if (duration > 1500) {
parser_step = KiaV3V4DecoderStepReset;
}
}
break;
case KiaV3V4DecoderStepCollectRawBits:
if (level) {
if (duration > 1000 && duration < 1500) {
// Next sync pulse (V4 style) - end this packet
kia_v3_v4_process_buffer();
parser_step = KiaV3V4DecoderStepReset;
} else if (
DURATION_DIFF(duration, te_short) <
te_delta) {
kia_v3_v4_add_raw_bit(false);
} else if (
DURATION_DIFF(duration, te_long) <
te_delta) {
kia_v3_v4_add_raw_bit(true);
} else {
parser_step = KiaV3V4DecoderStepReset;
}
} else {
if (duration > 1000 && duration < 1500) {
// Next sync pulse (V3 style) - end this packet
kia_v3_v4_process_buffer();
parser_step = KiaV3V4DecoderStepReset;
} else if (duration > 1500) {
// Long gap - end of transmission
kia_v3_v4_process_buffer();
parser_step = KiaV3V4DecoderStepReset;
}
}
break;
}
}
bool is_v3_sync = false; // true = V3 (long LOW sync), false = V4 (long HIGH sync)
uint8_t version = 0; // 0 = V4, 1 = V3
uint8_t raw_bits[32]{0};
uint16_t raw_bit_count = 0;
uint16_t header_count = 0;
// uint32_t encrypted;
// uint32_t decrypted;
};
+176
View File
@@ -0,0 +1,176 @@
#pragma once
#include "subcarbase.hpp"
#include <cstring>
typedef enum {
KiaV5DecoderStepReset = 0,
KiaV5DecoderStepCheckPreamble,
KiaV5DecoderStepCollectRawBits,
} KiaV5DecoderStep;
class FProtoSubCarKiaV5 : public FProtoSubCarBase {
public:
FProtoSubCarKiaV5() {
sensorType = FPC_KIAV5;
te_short = 400;
te_long = 800;
te_delta = 150;
min_count_bit_for_found = 64;
}
inline bool kia_v5_get_raw_bit(uint16_t idx) {
uint16_t byte_idx = idx / 8;
uint8_t bit_idx = 7 - (idx % 8);
return (raw_bits[byte_idx] >> bit_idx) & 1;
}
void kia_v5_add_raw_bit(bool bit) {
if (raw_bit_count < 256) {
uint16_t byte_idx = raw_bit_count / 8;
uint8_t bit_idx = 7 - (raw_bit_count % 8);
if (bit) {
raw_bits[byte_idx] |= (1 << bit_idx);
} else {
raw_bits[byte_idx] &= ~(1 << bit_idx);
}
raw_bit_count++;
}
}
bool kia_v5_manchester_decode() {
if (raw_bit_count < 130) {
return false;
}
decode_data = 0;
decode_count_bit = 0;
// Start at offset 2 for proper Manchester alignment
const uint16_t start_bit = 2;
for (uint16_t i = start_bit;
i + 1 < raw_bit_count && decode_count_bit < 64;
i += 2) {
bool bit1 = kia_v5_get_raw_bit(i);
bool bit2 = kia_v5_get_raw_bit(i + 1);
uint8_t two_bits = (bit1 << 1) | bit2;
if (two_bits == 0x01) { // 01 = decoded 1
decode_data = (decode_data << 1) | 1;
decode_count_bit++;
} else if (two_bits == 0x02) { // 10 = decoded 0
decode_data = (decode_data << 1);
decode_count_bit++;
} else {
break;
}
}
return decode_count_bit >= min_count_bit_for_found;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case KiaV5DecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short) <
te_delta)) {
parser_step = KiaV5DecoderStepCheckPreamble;
te_last = duration;
header_count = 1;
}
break;
case KiaV5DecoderStepCheckPreamble:
if (level) {
if ((DURATION_DIFF(duration, te_short) <
te_delta) ||
(DURATION_DIFF(duration, te_long) <
te_delta)) {
te_last = duration;
} else {
parser_step = KiaV5DecoderStepReset;
}
} else {
if ((DURATION_DIFF(duration, te_short) <
te_delta) &&
(DURATION_DIFF(te_last, te_short) <
te_delta)) {
header_count++;
} else if (
(DURATION_DIFF(duration, te_long) <
te_delta) &&
(DURATION_DIFF(te_last, te_short) <
te_delta)) {
if (header_count > 40) {
parser_step = KiaV5DecoderStepCollectRawBits;
raw_bit_count = 0;
memset(raw_bits, 0, sizeof(raw_bits));
} else {
header_count++;
}
} else if (
DURATION_DIFF(te_last, te_long) <
te_delta) {
header_count++;
} else {
parser_step = KiaV5DecoderStepReset;
}
}
break;
case KiaV5DecoderStepCollectRawBits:
if (duration > 1200) {
if (kia_v5_manchester_decode()) {
// generic.data = decode_data;
// generic.data_count_bit = decode_count_bit;
data_count_bit = decode_count_bit;
// Compute yek (bit-reverse each byte)
uint64_t yek = 0;
for (int i = 0; i < 8; i++) {
uint8_t byte = (decode_data >> (i * 8)) & 0xFF;
uint8_t reversed = 0;
for (int b = 0; b < 8; b++) {
if (byte & (1 << b))
reversed |= (1 << (7 - b));
}
yek |= ((uint64_t)reversed << ((7 - i) * 8));
}
decode_data = yek;
// Shift serial right by 1 to correct alignment
// generic.serial = (uint32_t)(((yek >> 32) & 0x0FFFFFFF) >> 1);
// generic.btn = (uint8_t)((yek >> 61) & 0x07); // Shift btn too
// generic.cnt = (uint16_t)(yek & 0xFFFF);
if (callback)
callback(this);
}
parser_step = KiaV5DecoderStepReset;
break;
}
int num_bits = 0;
if (DURATION_DIFF(duration, te_short) <
te_delta) {
num_bits = 1;
} else if (
DURATION_DIFF(duration, te_long) <
te_delta) {
num_bits = 2;
} else {
parser_step = KiaV5DecoderStepReset;
break;
}
for (int i = 0; i < num_bits; i++) {
kia_v5_add_raw_bit(level);
}
break;
}
}
uint8_t raw_bits[32]{};
uint16_t raw_bit_count = 0;
uint16_t header_count = 0;
};
+172
View File
@@ -0,0 +1,172 @@
#pragma once
#include "subcarbase.hpp"
#include <cstring>
typedef enum {
SubaruDecoderStepReset = 0,
SubaruDecoderStepCheckPreamble,
SubaruDecoderStepFoundGap,
SubaruDecoderStepFoundSync,
SubaruDecoderStepSaveDuration,
SubaruDecoderStepCheckDuration,
} SubaruDecoderStep;
class FProtoSubCarSubaru : public FProtoSubCarBase {
public:
FProtoSubCarSubaru() {
sensorType = FPC_SUBARU;
te_short = 800;
te_long = 1600;
te_delta = 260;
min_count_bit_for_found = 64;
}
void subghz_protocol_decoder_subaru_reset() {
parser_step = SubaruDecoderStepReset;
te_last = 0;
header_count = 0;
bit_count = 0;
memset(data, 0, sizeof(data));
}
void subaru_add_bit(bool bit) {
if (bit_count < 64) {
uint8_t byte_idx = bit_count / 8;
uint8_t bit_idx = 7 - (bit_count % 8);
if (bit) {
data[byte_idx] |= (1 << bit_idx);
} else {
data[byte_idx] &= ~(1 << bit_idx);
}
bit_count++;
}
}
bool subaru_process_data() {
if (bit_count < 64) {
return false;
}
uint8_t* b = data;
uint64_t key = ((uint64_t)b[0] << 56) | ((uint64_t)b[1] << 48) |
((uint64_t)b[2] << 40) | ((uint64_t)b[3] << 32) |
((uint64_t)b[4] << 24) | ((uint64_t)b[5] << 16) |
((uint64_t)b[6] << 8) | ((uint64_t)b[7]);
decode_data = key;
// uint32_t serial = ((uint32_t)b[1] << 16) | ((uint32_t)b[2] << 8) | b[3];
// uint8_t button = b[0] & 0x0F;
// uint16_t cnt;
// subaru_decode_count(b, &cnt);
data_count_bit = decode_count_bit;
if (callback) {
callback(this);
}
return true;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case SubaruDecoderStepReset:
if (level && DURATION_DIFF(duration, te_long) < te_delta) {
parser_step = SubaruDecoderStepCheckPreamble;
te_last = duration;
header_count = 1;
}
break;
case SubaruDecoderStepCheckPreamble:
if (!level) {
if (DURATION_DIFF(duration, te_long) < te_delta) {
header_count++;
} else if (duration > 2000 && duration < 3500) {
if (header_count > 20) {
parser_step = SubaruDecoderStepFoundGap;
} else {
parser_step = SubaruDecoderStepReset;
}
} else {
parser_step = SubaruDecoderStepReset;
}
} else {
if (DURATION_DIFF(duration, te_long) < te_delta) {
te_last = duration;
header_count++;
} else {
parser_step = SubaruDecoderStepReset;
}
}
break;
case SubaruDecoderStepFoundGap:
if (level && duration > 2000 && duration < 3500) {
parser_step = SubaruDecoderStepFoundSync;
} else {
parser_step = SubaruDecoderStepReset;
}
break;
case SubaruDecoderStepFoundSync:
if (!level && DURATION_DIFF(duration, te_long) < te_delta) {
parser_step = SubaruDecoderStepSaveDuration;
bit_count = 0;
memset(data, 0, sizeof(data));
} else {
parser_step = SubaruDecoderStepReset;
}
break;
case SubaruDecoderStepSaveDuration:
if (level) {
// HIGH pulse duration encodes the bit:
// Short HIGH (~800µs) = 1
// Long HIGH (~1600µs) = 0
if (DURATION_DIFF(duration, te_short) < te_delta) {
// Short HIGH = bit 1
subaru_add_bit(true);
te_last = duration;
parser_step = SubaruDecoderStepCheckDuration;
} else if (DURATION_DIFF(duration, te_long) < te_delta) {
// Long HIGH = bit 0
subaru_add_bit(false);
te_last = duration;
parser_step = SubaruDecoderStepCheckDuration;
} else if (duration > 3000) {
// End of transmission
if (bit_count >= 64) {
subaru_process_data();
}
parser_step = SubaruDecoderStepReset;
} else {
parser_step = SubaruDecoderStepReset;
}
} else {
parser_step = SubaruDecoderStepReset;
}
break;
case SubaruDecoderStepCheckDuration:
if (!level) {
// LOW pulse - just validates timing, doesn't encode bit
if (DURATION_DIFF(duration, te_short) < te_delta ||
DURATION_DIFF(duration, te_long) < te_delta) {
parser_step = SubaruDecoderStepSaveDuration;
} else if (duration > 3000) {
// Gap - end of packet
if (bit_count >= 64) {
subaru_process_data();
}
parser_step = SubaruDecoderStepReset;
} else {
parser_step = SubaruDecoderStepReset;
}
} else {
parser_step = SubaruDecoderStepReset;
}
break;
}
}
uint16_t header_count = 0;
uint8_t data[8];
uint8_t bit_count = 0;
};
+120
View File
@@ -0,0 +1,120 @@
#pragma once
#include "subcarbase.hpp"
#define SUZUKI_GAP_TIME 2000
#define SUZUKI_GAP_DELTA 400
typedef enum {
SuzukiDecoderStepReset = 0,
SuzukiDecoderStepFoundStartPulse,
SuzukiDecoderStepSaveDuration,
} SuzukiDecoderStep;
class FProtoSubCarSuzuki : public FProtoSubCarBase {
public:
FProtoSubCarSuzuki() {
sensorType = FPC_SUZUKI;
te_short = 250;
te_long = 500;
te_delta = 110;
min_count_bit_for_found = 64;
}
void suzuki_add_bit(uint32_t bit) {
uint32_t carry = data_low >> 31;
data_low = (data_low << 1) | bit;
data_high = (data_high << 1) | carry;
data_count_bit++;
}
void subghz_protocol_decoder_suzuki_reset() {
parser_step = SuzukiDecoderStepReset;
header_count = 0;
data_count_bit = 0;
data_low = 0;
data_high = 0;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case SuzukiDecoderStepReset:
// Wait for short HIGH pulse (~250µs) to start preamble
if (!level)
return;
if (DURATION_DIFF(duration, te_short) > te_delta) {
return;
}
data_low = 0;
data_high = 0;
parser_step = SuzukiDecoderStepFoundStartPulse;
header_count = 0;
data_count_bit = 0;
break;
case SuzukiDecoderStepFoundStartPulse:
if (level) {
// HIGH pulse
if (header_count < 257) {
// Still in preamble - just count
return;
}
// After preamble, look for long HIGH to start data
if (DURATION_DIFF(duration, te_long) < te_delta) {
parser_step = SuzukiDecoderStepSaveDuration;
suzuki_add_bit(1);
}
// Ignore short HIGHs after preamble until we see a long one
} else {
// LOW pulse - count as header if short
if (DURATION_DIFF(duration, te_short) < te_delta) {
te_last = duration;
header_count++;
} else {
parser_step = SuzukiDecoderStepReset;
}
}
break;
case SuzukiDecoderStepSaveDuration:
if (level) {
// HIGH pulse - determines bit value
// Long HIGH (~500µs) = 1, Short HIGH (~250µs) = 0
if (DURATION_DIFF(duration, te_long) < te_delta) {
suzuki_add_bit(1);
} else if (DURATION_DIFF(duration, te_short) < te_delta) {
suzuki_add_bit(0);
} else {
parser_step = SuzukiDecoderStepReset;
}
// Stay in this state for next bit
} else {
// LOW pulse - check for gap (end of transmission)
if (DURATION_DIFF(duration, SUZUKI_GAP_TIME) < SUZUKI_GAP_DELTA) {
// Gap found - end of transmission
if (data_count_bit == 64) {
data_count_bit = 64;
decode_data = ((uint64_t)data_high << 32) | (uint64_t)data_low;
// Check manufacturer nibble (should be 0xF)
uint8_t manufacturer = (data_high >> 28) & 0xF;
if (manufacturer == 0xF) {
// Extract fields
decode_data2 = 0; // Not used
if (callback) {
callback(this);
}
}
}
parser_step = SuzukiDecoderStepReset;
}
// Short LOW pulses are ignored - stay in this state
}
break;
}
}
uint16_t header_count = 0;
uint32_t data_high = 0;
uint32_t data_low = 0;
uint8_t data_count_bit = 0;
};
+236
View File
@@ -0,0 +1,236 @@
#pragma once
#include "subcarbase.hpp"
typedef enum {
VwDecoderStepReset = 0,
VwDecoderStepFoundSync,
VwDecoderStepFoundStart1,
VwDecoderStepFoundStart2,
VwDecoderStepFoundStart3,
VwDecoderStepFoundData,
} VwDecoderStep;
class FProtoSubCarVW : public FProtoSubCarBase {
public:
FProtoSubCarVW() {
sensorType = FPC_VW;
te_short = 500;
te_long = 1000;
te_delta = 130;
min_count_bit_for_found = 80;
}
uint8_t vw_get_bit_index(uint8_t bit) {
uint8_t bit_index = 0;
if (bit < 72 && bit >= 8) {
// use generic.data (bytes 1-8)
bit_index = bit - 8;
} else {
// use data_2
if (bit >= 72) {
bit_index = bit - 64; // byte 0 = type
}
if (bit < 8) {
bit_index = bit; // byte 9 = check digit
}
bit_index |= 0x80; // mark for data_2
}
return bit_index;
}
void vw_add_bit(bool level) {
if (data_count_bit >= min_count_bit_for_found) {
return;
}
uint8_t bit_index_full = min_count_bit_for_found - 1 - data_count_bit;
uint8_t bit_index_masked = vw_get_bit_index(bit_index_full);
uint8_t bit_index = bit_index_masked & 0x7F;
if (bit_index_masked & 0x80) {
// use data_2
if (level) {
decode_data2 |= (1ULL << bit_index);
} else {
decode_data2 &= ~(1ULL << bit_index);
}
} else {
// use data
if (level) {
decode_data |= (1ULL << bit_index);
} else {
decode_data &= ~(1ULL << bit_index);
}
}
data_count_bit++;
if (data_count_bit >= min_count_bit_for_found) {
if (callback) {
callback(this);
}
}
}
void subghz_protocol_decoder_vw_reset() {
parser_step = VwDecoderStepReset;
data_count_bit = 0;
decode_data = 0;
decode_data2 = 0;
manchester_state = ManchesterStateMid1;
}
bool vw_manchester_advance(
ManchesterState state,
ManchesterEvent event,
ManchesterState* next_state,
bool* data) {
bool result = false;
ManchesterState new_state = ManchesterStateMid1;
if (event == ManchesterEventReset) {
new_state = ManchesterStateMid1;
} else if (state == ManchesterStateMid0 || state == ManchesterStateMid1) {
if (event == ManchesterEventShortHigh) {
new_state = ManchesterStateStart1;
} else if (event == ManchesterEventShortLow) {
new_state = ManchesterStateStart0;
} else {
new_state = ManchesterStateMid1;
}
} else if (state == ManchesterStateStart1) {
if (event == ManchesterEventShortLow) {
new_state = ManchesterStateMid1;
result = true;
if (data)
*data = true;
} else if (event == ManchesterEventLongLow) {
new_state = ManchesterStateStart0;
result = true;
if (data)
*data = true;
} else {
new_state = ManchesterStateMid1;
}
} else if (state == ManchesterStateStart0) {
if (event == ManchesterEventShortHigh) {
new_state = ManchesterStateMid0;
result = true;
if (data)
*data = false;
} else if (event == ManchesterEventLongHigh) {
new_state = ManchesterStateStart1;
result = true;
if (data)
*data = false;
} else {
new_state = ManchesterStateMid1;
}
}
*next_state = new_state;
return result;
}
void feed(bool level, uint32_t duration) {
uint32_t te_med = (te_long + te_short) / 2;
uint32_t te_end = te_long * 5;
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case VwDecoderStepReset:
if (DURATION_DIFF(duration, te_short) < te_delta) {
parser_step = VwDecoderStepFoundSync;
}
break;
case VwDecoderStepFoundSync:
if (DURATION_DIFF(duration, te_short) < te_delta) {
// Stay - sync pattern repeats ~43 times
break;
}
if (level && DURATION_DIFF(duration, te_long) < te_delta) {
parser_step = VwDecoderStepFoundStart1;
break;
}
parser_step = VwDecoderStepReset;
break;
case VwDecoderStepFoundStart1:
if (!level && DURATION_DIFF(duration, te_short) < te_delta) {
parser_step = VwDecoderStepFoundStart2;
break;
}
parser_step = VwDecoderStepReset;
break;
case VwDecoderStepFoundStart2:
if (level && DURATION_DIFF(duration, te_med) < te_delta) {
parser_step = VwDecoderStepFoundStart3;
break;
}
parser_step = VwDecoderStepReset;
break;
case VwDecoderStepFoundStart3:
if (DURATION_DIFF(duration, te_med) < te_delta) {
// Stay - med pattern repeats
break;
}
if (level && DURATION_DIFF(duration, te_short) < te_delta) {
// Start data collection
vw_manchester_advance(
manchester_state,
ManchesterEventReset,
&manchester_state,
NULL);
vw_manchester_advance(
manchester_state,
ManchesterEventShortHigh,
&manchester_state,
NULL);
data_count_bit = 0;
decode_data = 0;
decode_data2 = 0;
parser_step = VwDecoderStepFoundData;
break;
}
parser_step = VwDecoderStepReset;
break;
case VwDecoderStepFoundData:
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = level ? ManchesterEventShortHigh : ManchesterEventShortLow;
}
if (DURATION_DIFF(duration, te_long) < te_delta) {
event = level ? ManchesterEventLongHigh : ManchesterEventLongLow;
}
// Last bit can be arbitrarily long
if (data_count_bit == min_count_bit_for_found - 1 &&
!level && duration > te_end) {
event = ManchesterEventShortLow;
}
if (event == ManchesterEventReset) {
subghz_protocol_decoder_vw_reset();
} else {
bool new_level;
if (vw_manchester_advance(
manchester_state,
event,
&manchester_state,
&new_level)) {
vw_add_bit(new_level);
}
}
break;
}
}
ManchesterState manchester_state = ManchesterStateMid1;
};
+54
View File
@@ -0,0 +1,54 @@
/*
Base class for all weather protocols.
This and most of the weather protocols uses code from Flipper XTreme codebase ( https://github.com/Flipper-XFW/Xtreme-Firmware/tree/dev/lib/subghz ). Thanks for their work!
For comments in a protocol implementation check w-nexus-th.hpp
*/
#ifndef __FPROTO_SCARBASE_H__
#define __FPROTO_SCARBASE_H__
#include "fprotogeneral.hpp"
#include "subcartypes.hpp"
#include <string>
// default values to indicate 'no value'
class FProtoSubCarBase;
typedef void (*SubCarProtocolDecoderBaseRxCallback)(FProtoSubCarBase* instance);
class FProtoSubCarBase {
public:
FProtoSubCarBase() {}
virtual ~FProtoSubCarBase() {}
virtual void feed(bool level, uint32_t duration) = 0; // need to be implemented on each protocol handler.
void setCallback(SubCarProtocolDecoderBaseRxCallback cb) { callback = cb; } // this is called when there is a hit.
// General data holder, these will be passed
uint8_t sensorType = FPC_Invalid;
uint16_t data_count_bit = 0;
uint64_t decode_data = 0;
uint64_t decode_data2 = 0;
protected:
// Helper functions to keep it as compatible with flipper as we can, so adding new protos will be easy.
void subghz_protocol_blocks_add_bit(uint8_t bit) {
decode_data = decode_data << 1 | bit;
decode_count_bit++;
}
// inner logic stuff, also for flipper compatibility.
uint32_t te_short = UINT32_MAX;
uint32_t te_long = UINT32_MAX;
uint32_t te_delta = UINT32_MAX;
uint32_t min_count_bit_for_found = UINT32_MAX;
SubCarProtocolDecoderBaseRxCallback callback = NULL;
uint8_t parser_step = 0;
uint32_t te_last = 0;
uint32_t decode_count_bit = 0;
//
};
#endif
@@ -0,0 +1,75 @@
/*
This is the protocol list handler. It holds an instance of all known protocols.
So include here the .hpp, and add a new element to the protos vector in the constructor. That's all you need to do here if you wanna add a new proto.
@htotoo
*/
#include <vector>
#include <memory>
#include "portapack_shared_memory.hpp"
#include "fprotolistgeneral.hpp"
#include "subcarbase.hpp"
#include "c-suzuki.hpp"
#include "c-vw.hpp"
#include "c-subaru.hpp"
#include "c-kia_v5.hpp"
#include "c-kia_v3v4.hpp"
#include "c-kia_v2.hpp"
#include "c-kia_v1.hpp"
#include "c-kia_v0.hpp"
#include "c-ford_v0.hpp"
#include "c-fiat_v0.hpp"
#include "c-bmw_v0.hpp"
#ifndef __FPROTO_PROTOLISTCAR_H__
#define __FPROTO_PROTOLISTCAR_H__
class SubCarProtos : public FProtoListGeneral {
public:
SubCarProtos(const SubCarProtos&) { SubCarProtos(); }; // won't use, but makes compiler happy
SubCarProtos& operator=(const SubCarProtos&) { return *this; } // won't use, but makes compiler happy
SubCarProtos() {
// add protos
protos[FPC_SUZUKI] = new FProtoSubCarSuzuki();
protos[FPC_VW] = new FProtoSubCarVW();
protos[FPC_SUBARU] = new FProtoSubCarSubaru();
protos[FPC_KIAV5] = new FProtoSubCarKiaV5();
protos[FPC_KIAV3V4] = new FProtoSubCarKiaV3V4();
protos[FPC_KIAV2] = new FProtoSubCarKiaV2();
protos[FPC_KIAV1] = new FProtoSubCarKiaV1();
protos[FPC_KIAV0] = new FProtoSubCarKiaV0();
protos[FPC_FORDV0] = new FProtoSubCarFordV0();
protos[FPC_FIATV0] = new FProtoSubCarFiatV0();
protos[FPC_BMWV0] = new FProtoSubCarBMWV0();
for (uint8_t i = 0; i < FPC_COUNT; ++i) {
if (protos[i] != NULL) protos[i]->setCallback(callbackTarget);
}
}
~SubCarProtos() { // not needed for current operation logic, but a bit more elegant :)
for (uint8_t i = 0; i < FPC_COUNT; ++i) {
if (protos[i] != NULL) {
free(protos[i]);
protos[i] = NULL;
}
}
};
static void callbackTarget(FProtoSubCarBase* instance) {
SubCarDataMessage packet_message{instance->sensorType, instance->data_count_bit, instance->decode_data, instance->decode_data2};
shared_memory.application_queue.push(packet_message);
}
void feed(bool level, uint32_t duration) {
for (uint8_t i = 0; i < FPC_COUNT; ++i) {
if (protos[i] != NULL) protos[i]->feed(level, duration);
}
}
protected:
FProtoSubCarBase* protos[FPC_COUNT] = {NULL};
};
#endif
+30
View File
@@ -0,0 +1,30 @@
#ifndef __FPROTO_SUBCARTYPES_H__
#define __FPROTO_SUBCARTYPES_H__
/*
Define known protocols.
These values must be present on the protocol's constructor, like FProtoWeatherAcurite592TXR() { sensorType = FPS_ANSONIC; }
Also it must have a switch-case element in the getSubGhzDSensorTypeName() function, to display it's name.
*/
#define FPM_AM 0
#define FPM_FM 1
enum FPROTO_SUBCAR_SENSOR : uint8_t {
FPC_Invalid = 0,
FPC_SUZUKI = 1,
FPC_VW = 2,
FPC_SUBARU = 3,
FPC_KIAV5 = 4,
FPC_KIAV3V4 = 5,
FPC_KIAV2 = 6,
FPC_KIAV1 = 7,
FPC_KIAV0 = 8,
FPC_FORDV0 = 9,
FPC_FIATV0 = 10,
FPC_BMWV0 = 11,
FPC_COUNT
};
#endif