mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-15 19:13:00 +00:00
Subcar (#2893)
This commit is contained in:
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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;
|
||||
};
|
||||
@@ -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
|
||||
@@ -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
|
||||
Reference in New Issue
Block a user