Subghz decoder (#1646)

* Initial commit - wip

* Half part of the transition of baseband processor.

* More SGD

* WIP, Weather refactor, UI improv

* Rename

* Added 4msps, and fixes

* Fixes

* princeton working

* Renamed proc_weather, bc now multifunctional

* Proto: bett

* FPS_CAME = 4,
    FPS_PRASTEL = 5,
    FPS_AIRFORCE = 6,

* Came Atomo, fixes

* Separate weather and sgd, bc of baseband size limit

* Fix display

* Save space

* More protos

* Dooya proto added

* More protos

* add protos

* More protos

* Move weather to ext app

* nw

* Revert "Move weather to ext app"

This reverts commit 8a84aac2f5.

* revert

* Fix merge

* Better naming

* More protos

* More protos

* Add protos

* Fix warning

* Add NeroRadio

* more protos

* more protos

* More protos

* Shrink a bit

* fixes

* More protos

* Nicer code

* Fix naming

* Fix format

* Remove unused

* Fix some protos, that needs a LOOOONG part with the same lo/high

* Modify key calculation
This commit is contained in:
Totoo 2023-12-16 23:37:51 +01:00 committed by GitHub
parent 02810bf527
commit 2ccda5aebd
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 4952 additions and 248 deletions

View File

@ -301,6 +301,7 @@ set(CPPSRC
apps/ui_spectrum_painter.cpp
apps/ui_ss_viewer.cpp
apps/ui_sstvtx.cpp
apps/ui_subghzd.cpp
# apps/ui_test.cpp
apps/ui_text_editor.cpp
apps/ui_tone_search.cpp

View File

@ -0,0 +1,243 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_subghzd.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace ui;
namespace ui {
void SubGhzDRecentEntryDetailView::update_data() {
// set text elements
text_type.set(SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry_.sensorType));
text_id.set("0x" + to_string_hex(entry_.serial));
if (entry_.bits > 0) console.writeln("Bits: " + to_string_dec_uint(entry_.bits));
if (entry_.btn != SD_NO_BTN) console.writeln("Btn: " + to_string_dec_uint(entry_.btn));
if (entry_.cnt != SD_NO_CNT) console.writeln("Cnt: " + to_string_dec_uint(entry_.cnt));
if (entry_.data != 0) console.writeln("Data: " + to_string_hex(entry_.data));
}
SubGhzDRecentEntryDetailView::SubGhzDRecentEntryDetailView(NavigationView& nav, const SubGhzDRecentEntry& entry)
: nav_{nav},
entry_{entry} {
add_children({&button_done,
&text_type,
&text_id,
&console,
&labels});
button_done.on_select = [&nav](const ui::Button&) {
nav.pop();
};
update_data();
}
void SubGhzDRecentEntryDetailView::focus() {
button_done.focus();
}
void SubGhzDView::focus() {
field_frequency.focus();
}
SubGhzDView::SubGhzDView(NavigationView& nav)
: nav_{nav} {
add_children({&rssi,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&button_clear_list,
&recent_entries_view});
baseband::run_image(portapack::spi_flash::image_tag_subghzd);
button_clear_list.on_select = [this](Button&) {
recent.clear();
recent_entries_view.set_dirty();
};
field_frequency.set_step(100000);
const Rect content_rect{0, header_height, screen_width, screen_height - header_height};
recent_entries_view.set_parent_rect(content_rect);
recent_entries_view.on_select = [this](const SubGhzDRecentEntry& entry) {
nav_.push<SubGhzDRecentEntryDetailView>(entry);
};
baseband::set_subghzd(0); // am
receiver_model.set_sampling_rate(4'000'000);
receiver_model.enable();
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
on_tick_second();
};
}
void SubGhzDView::on_tick_second() {
for (auto& entry : recent) {
entry.inc_age(1);
}
recent_entries_view.set_dirty();
}
void SubGhzDView::on_data(const SubGhzDDataMessage* data) {
SubGhzDRecentEntry key{data->sensorType, data->serial, data->bits, data->data, data->btn, data->cnt};
auto matching_recent = find(recent, key.key());
if (matching_recent != std::end(recent)) {
// Found within. Move to front of list, increment counter.
(*matching_recent).reset_age();
recent.push_front(*matching_recent);
recent.erase(matching_recent);
} else {
recent.emplace_front(key);
truncate_entries(recent, 64);
}
recent_entries_view.set_dirty();
}
SubGhzDView::~SubGhzDView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
receiver_model.disable();
baseband::shutdown();
}
const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
switch (type) {
case FPS_PRINCETON:
return "Princeton";
case FPS_BETT:
return "Bett";
case FPS_CAME:
return "Came";
case FPS_PRASTEL:
return "Prastel";
case FPS_AIRFORCE:
return "Airforce";
case FPS_CAMEATOMO:
return "Came Atomo";
case FPS_CAMETWEE:
return "Came Twee";
case FPS_CHAMBCODE:
return "Chamb Code";
case FPS_CLEMSA:
return "Clemsa";
case FPS_DOITRAND:
return "Doitrand";
case FPS_DOOYA:
return "Dooya";
case FPS_FAAC:
return "Faac";
case FPS_GATETX:
return "Gate TX";
case FPS_HOLTEK:
return "Holtek";
case FPS_HOLTEKHT12X:
return "Holtek HT12X";
case FPS_HONEYWELL:
return "Honeywell";
case FPS_HONEYWELLWDB:
return "Honeywell Wdb";
case FPS_HORMANN:
return "Hormann";
case FPS_IDO:
return "Ido 11x";
case FPS_INTERTECHNOV3:
return "InterTehcno v3";
case FPS_KEELOQ:
return "KeeLoq";
case FPS_KINGGATESSTYLO4K:
return "Kinggate Stylo4K";
case FPS_LINEAR:
return "Linear";
case FPS_LINEARDELTA3:
return "Linear Delta3";
case FPS_MAGELLAN:
return "Magellan";
case FPS_MARANTEC:
return "Marantec";
case FPS_MASTERCODE:
return "Mastercode";
case FPS_MEGACODE:
return "Megacode";
case FPS_NERORADIO:
return "Nero Radio";
case FPS_NERO_SKETCH:
return "Nero Sketch";
case FPS_NICEFLO:
return "Nice Flo";
case FPS_NICEFLORS:
return "Nice Flor S";
case FPS_PHOENIXV2:
return "Phoenix V2";
case FPS_POWERSMART:
return "PowerSmart";
case FPS_SECPLUSV1:
return "SecPlus V1";
case FPS_SECPLUSV2:
return "SecPlus V2";
case FPS_SMC5326:
return "SMC5326";
case FPS_STARLINE:
return "Star Line";
case FPS_X10:
return "X10";
case FPS_Invalid:
default:
return "Unknown";
}
}
std::string SubGhzDView::pad_string_with_spaces(int snakes) {
std::string paddedStr(snakes, ' ');
return paddedStr;
}
template <>
void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
std::string line{};
line.reserve(30);
line = SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry.sensorType);
line = line + " " + to_string_hex(entry.serial);
if (line.length() < 19) {
line += SubGhzDView::pad_string_with_spaces(19 - line.length());
} else {
line = truncate(line, 19);
}
std::string ageStr = to_string_dec_uint(entry.age);
std::string bitsStr = to_string_dec_uint(entry.bits);
line += SubGhzDView::pad_string_with_spaces(5 - bitsStr.length()) + bitsStr;
line += SubGhzDView::pad_string_with_spaces(4 - ageStr.length()) + ageStr;
line.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.location(), style, line);
}
} // namespace ui

View File

@ -0,0 +1,171 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_SUBGHZD_H__
#define __UI_SUBGHZD_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "utility.hpp"
#include "recent_entries.hpp"
#include "../baseband/fprotos/subghztypes.hpp"
using namespace ui;
namespace ui {
struct SubGhzDRecentEntry {
using Key = uint64_t;
static constexpr Key invalid_key = 0x0fffffff;
uint8_t sensorType = FPS_Invalid;
uint8_t btn = SD_NO_BTN;
uint32_t serial = SD_NO_SERIAL;
uint16_t bits = 0;
uint16_t age = 0; // updated on each seconds, show how long the signal was last seen
uint32_t cnt = SD_NO_CNT;
uint64_t data = 0;
SubGhzDRecentEntry() {}
SubGhzDRecentEntry(
uint8_t sensorType,
uint32_t serial,
uint16_t bits = 0,
uint64_t data = 0,
uint8_t btn = SD_NO_BTN,
uint32_t cnt = SD_NO_CNT)
: sensorType{sensorType},
btn{btn},
serial{serial},
bits{bits},
cnt{cnt},
data{data} {
}
Key key() const {
return (data ^ ((static_cast<uint64_t>(serial) << 32) | (static_cast<uint64_t>(sensorType) & 0xFF) << 0));
}
void inc_age(int delta) {
if (UINT16_MAX - delta > age) age += delta;
}
void reset_age() {
age = 0;
}
};
using SubGhzDRecentEntries = RecentEntries<SubGhzDRecentEntry>;
using SubGhzDRecentEntriesView = RecentEntriesView<SubGhzDRecentEntries>;
class SubGhzDView : public View {
public:
SubGhzDView(NavigationView& nav);
~SubGhzDView();
void focus() override;
std::string title() const override { return "SubGhzD"; };
static const char* getSensorTypeName(FPROTO_SUBGHZD_SENSOR type);
static std::string pad_string_with_spaces(int snakes);
private:
void on_tick_second();
void on_data(const SubGhzDDataMessage* data);
NavigationView& nav_;
RxRadioState radio_state_{
433'920'000 /* frequency */,
1'750'000 /* bandwidth */,
4'000'000 /* sampling rate */,
ReceiverModel::Mode::AMAudio};
app_settings::SettingsManager settings_{
"rx_subghzd",
app_settings::Mode::RX,
{}};
SubGhzDRecentEntries recent{};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
SignalToken signal_token_tick_second{};
Button button_clear_list{
{0, 16, 7 * 8, 32},
"Clear"};
static constexpr auto header_height = 3 * 16;
const RecentEntriesColumns columns{{
{"Type", 19},
{"Bits", 4},
{"Age", 3},
}};
SubGhzDRecentEntriesView recent_entries_view{columns, recent};
MessageHandlerRegistration message_handler_packet{
Message::ID::SubGhzDData,
[this](Message* const p) {
const auto message = static_cast<const SubGhzDDataMessage*>(p);
this->on_data(message);
}};
};
class SubGhzDRecentEntryDetailView : public View {
public:
SubGhzDRecentEntryDetailView(NavigationView& nav, const SubGhzDRecentEntry& entry);
void update_data();
void focus() override;
private:
NavigationView& nav_;
SubGhzDRecentEntry entry_{};
Text text_type{{0 * 8, 1 * 16, 15 * 8, 16}, "?"};
Text text_id{{6 * 8, 2 * 16, 10 * 8, 16}, "?"};
Console console{
{0, 4 * 16, 240, screen_height - (4 * 16) - 36}};
Labels labels{
{{0 * 8, 0 * 16}, "Type:", Color::light_grey()},
{{0 * 8, 2 * 16}, "Serial: ", Color::light_grey()},
{{0 * 8, 3 * 16}, "Data:", Color::light_grey()},
};
Button button_done{
{screen_width - 96 - 4, screen_height - 32 - 12, 96, 32},
"Done"};
};
} // namespace ui
#endif /*__UI_SUBGHZD_H__*/

View File

@ -35,11 +35,26 @@ namespace ui {
void WeatherRecentEntryDetailView::update_data() {
// set text elements
text_type.set(WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)entry_.sensorType));
text_id.set("0x" + to_string_hex(entry_.id));
text_temp.set(weather_units_fahr ? to_string_decimal((entry_.temp * 9 / 5) + 32, 1) + STR_DEGREES_F : to_string_decimal(entry_.temp, 2) + STR_DEGREES_C);
text_hum.set(to_string_dec_uint(entry_.humidity) + "%");
text_ch.set(to_string_dec_uint(entry_.channel));
text_batt.set(to_string_dec_uint(entry_.battery_low) + " " + ((entry_.battery_low == 0) ? "OK" : "LOW"));
if (entry_.id != WS_NO_ID)
text_id.set("0x" + to_string_hex(entry_.id));
else
text_id.set("-");
if (entry_.temp != WS_NO_TEMPERATURE)
text_temp.set(weather_units_fahr ? to_string_decimal((entry_.temp * 9 / 5) + 32, 1) + STR_DEGREES_F : to_string_decimal(entry_.temp, 2) + STR_DEGREES_C);
else
text_temp.set("-");
if (entry_.humidity != WS_NO_HUMIDITY)
text_hum.set(to_string_dec_uint(entry_.humidity) + "%");
else
text_hum.set("-");
if (entry_.channel != WS_NO_CHANNEL)
text_ch.set(to_string_dec_uint(entry_.channel));
else
text_ch.set("-");
if (entry_.battery_low != WS_NO_BATT)
text_batt.set(to_string_dec_uint(entry_.battery_low) + " " + ((entry_.battery_low == 0) ? "OK" : "LOW"));
else
text_batt.set("-");
text_age.set(to_string_dec_uint(entry_.age) + " sec");
}
@ -205,8 +220,8 @@ void RecentEntriesTable<ui::WeatherRecentEntries>::draw(
}
std::string temp = (weather_units_fahr ? to_string_decimal((entry.temp * 9 / 5) + 32, 1) : to_string_decimal(entry.temp, 1));
std::string humStr = to_string_dec_uint(entry.humidity) + "%";
std::string chStr = to_string_dec_uint(entry.channel);
std::string humStr = (entry.humidity != WS_NO_HUMIDITY) ? to_string_dec_uint(entry.humidity) + "%" : "-";
std::string chStr = (entry.channel != WS_NO_CHANNEL) ? to_string_dec_uint(entry.channel) : "-";
std::string ageStr = to_string_dec_uint(entry.age);
line += WeatherView::pad_string_with_spaces(6 - temp.length()) + temp;

View File

@ -43,11 +43,11 @@ struct WeatherRecentEntry {
using Key = uint64_t;
static constexpr Key invalid_key = 0x0fffffff; // todo calc the invalid all
uint8_t sensorType = FPW_Invalid;
uint32_t id = 0xFFFFFFFF;
float temp = -273.0f;
uint8_t humidity = 0xFF;
uint8_t battery_low = 0xFF;
uint8_t channel = 0xFF;
uint32_t id = WS_NO_ID;
float temp = WS_NO_TEMPERATURE;
uint8_t humidity = WS_NO_HUMIDITY;
uint8_t battery_low = WS_NO_BATT;
uint8_t channel = WS_NO_CHANNEL;
uint16_t age = 0; // updated on each seconds, show how long the signal was last seen
WeatherRecentEntry() {}
@ -57,7 +57,7 @@ struct WeatherRecentEntry {
float temp,
uint8_t humidity,
uint8_t channel,
uint8_t battery_low = 0xff)
uint8_t battery_low = WS_NO_BATT)
: sensorType{sensorType},
id{id},
temp{temp},

View File

@ -320,7 +320,12 @@ void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bo
}
void set_weather() {
const WeatherRxConfigureMessage message{};
const SubGhzFPRxConfigureMessage message{0};
send_message(&message);
}
void set_subghzd(uint8_t modulation = 0) {
const SubGhzFPRxConfigureMessage message{modulation};
send_message(&message);
}

View File

@ -89,6 +89,7 @@ void set_siggen_tone(const uint32_t tone);
void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t duration);
void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw);
void set_weather();
void set_subghzd(uint8_t modulation);
void request_beep();
void run_image(const portapack::spi_flash::image_tag_t image_tag);

View File

@ -80,6 +80,7 @@
#include "ui_touchtunes.hpp"
#include "ui_view_wav.hpp"
#include "ui_weatherstation.hpp"
#include "ui_subghzd.hpp"
#include "ui_whipcalc.hpp"
#include "ui_external_items_menu_loader.hpp"
@ -567,6 +568,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
{"Search", Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push<SearchView>(); }},
{"TPMS Cars", Color::green(), &bitmap_icon_tpms, [&nav]() { nav.push<TPMSAppView>(); }},
{"Weather", Color::green(), &bitmap_icon_thermometer, [&nav]() { nav.push<WeatherView>(); }},
{"SubGhzD", Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push<SubGhzDView>(); }},
// {"FSK RX", Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push<FskxRxMainView>(); }},
// {"DMR", Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push<NotImplementedView>(); }},
// {"SIGFOX", Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push<NotImplementedView>(); }},

View File

@ -532,8 +532,15 @@ set(MODE_CPPSRC
)
DeclareTargets(PWFM wfm_audio)
### Weather Stations
### SubGhz Decoders
set(MODE_CPPSRC
proc_subghzd.cpp
)
DeclareTargets(PSGD subghzd)
### Weather Stations
set(MODE_CPPSRC
proc_weather.cpp
)

View File

@ -0,0 +1,200 @@
#ifndef __FPROTO_GENERAL_H__
#define __FPROTO_GENERAL_H__
// useful methods for both weather and subghzd
#include <stdint.h>
#include <stddef.h>
#define bit_read(value, bit) (((value) >> (bit)) & 0x01)
#define bit_set(value, bit) \
({ \
__typeof__(value) _one = (1); \
(value) |= (_one << (bit)); \
})
#define bit_clear(value, bit) \
({ \
__typeof__(value) _one = (1); \
(value) &= ~(_one << (bit)); \
})
#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit))
#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
typedef enum {
ManchesterStateStart1 = 0,
ManchesterStateMid1 = 1,
ManchesterStateMid0 = 2,
ManchesterStateStart0 = 3
} ManchesterState;
typedef enum {
ManchesterEventShortLow = 0,
ManchesterEventShortHigh = 2,
ManchesterEventLongLow = 4,
ManchesterEventLongHigh = 6,
ManchesterEventReset = 8
} ManchesterEvent;
class FProtoGeneral {
public:
static bool manchester_advance(
ManchesterState state,
ManchesterEvent event,
ManchesterState* next_state,
bool* data) {
bool result = false;
ManchesterState new_state;
if (event == ManchesterEventReset) {
new_state = ManchesterStateMid1;
} else {
new_state = (ManchesterState)(transitions[state] >> event & 0x3);
if (new_state == state) {
new_state = ManchesterStateMid1;
} else {
if (new_state == ManchesterStateMid0) {
if (data) *data = false;
result = true;
} else if (new_state == ManchesterStateMid1) {
if (data) *data = true;
result = true;
}
}
}
*next_state = new_state;
return result;
}
static uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) {
uint8_t parity = 0;
for (uint8_t i = 0; i < bit_count; i++) {
parity += bit_read(key, i);
}
return parity & 0x01;
}
static uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) {
uint32_t result = 0;
for (size_t i = 0; i < size; ++i) {
result += message[i];
}
return (uint8_t)result;
}
static uint8_t subghz_protocol_blocks_parity8(uint8_t byte) {
byte ^= byte >> 4;
byte &= 0xf;
return (0x6996 >> byte) & 1;
}
static uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) {
uint8_t result = 0;
for (size_t i = 0; i < size; ++i) {
result ^= subghz_protocol_blocks_parity8(message[i]);
}
return result;
}
static uint8_t subghz_protocol_blocks_lfsr_digest8(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key) {
uint8_t sum = 0;
for (size_t byte = 0; byte < size; ++byte) {
uint8_t data = message[byte];
for (int i = 7; i >= 0; --i) {
// XOR key into sum if data bit is set
if ((data >> i) & 1) sum ^= key;
// roll the key right (actually the LSB is dropped here)
// and apply the gen (needs to include the dropped LSB as MSB)
if (key & 1)
key = (key >> 1) ^ gen;
else
key = (key >> 1);
}
}
return sum;
}
static float locale_fahrenheit_to_celsius(float temp_f) {
return (temp_f - 32.f) / 1.8f;
}
static uint8_t subghz_protocol_blocks_crc4(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = init << 4; // LSBs are unused
uint8_t poly = polynomial << 4;
uint8_t bit;
while (size--) {
remainder ^= *message++;
for (bit = 0; bit < 8; bit++) {
if (remainder & 0x80) {
remainder = (remainder << 1) ^ poly;
} else {
remainder = (remainder << 1);
}
}
}
return remainder >> 4 & 0x0f; // discard the LSBs
}
static uint8_t subghz_protocol_blocks_lfsr_digest8_reflect(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key) {
uint8_t sum = 0;
// Process message from last byte to first byte (reflected)
for (int byte = size - 1; byte >= 0; --byte) {
uint8_t data = message[byte];
// Process individual bits of each byte (reflected)
for (uint8_t i = 0; i < 8; ++i) {
// XOR key into sum if data bit is set
if ((data >> i) & 1) {
sum ^= key;
}
// roll the key left (actually the LSB is dropped here)
// and apply the gen (needs to include the dropped lsb as MSB)
if (key & 0x80)
key = (key << 1) ^ gen;
else
key = (key << 1);
}
}
return sum;
}
static uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) {
uint64_t reverse_key = 0;
for (uint8_t i = 0; i < bit_count; i++) {
reverse_key = reverse_key << 1 | bit_read(key, i);
}
return reverse_key;
}
static uint8_t subghz_protocol_blocks_crc8(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = init;
for (size_t byte = 0; byte < size; ++byte) {
remainder ^= message[byte];
for (uint8_t bit = 0; bit < 8; ++bit) {
if (remainder & 0x80) {
remainder = (remainder << 1) ^ polynomial;
} else {
remainder = (remainder << 1);
}
}
}
return remainder;
}
private:
static inline const uint8_t transitions[] = {0b00000001, 0b10010001, 0b10011011, 0b11111011};
};
#endif

View File

@ -0,0 +1,16 @@
#ifndef __FPROTO_PROTOLISTGENERAL_H__
#define __FPROTO_PROTOLISTGENERAL_H__
#include <stdint.h>
class FProtoListGeneral {
public:
FProtoListGeneral() {}
virtual ~FProtoListGeneral() {}
virtual void feed(bool level, uint32_t duration) = 0;
void setModulation(uint8_t modulation) { modulation_ = modulation; }
protected:
uint8_t modulation_ = 0;
};
#endif

View File

@ -0,0 +1,83 @@
#ifndef __FPROTO_BETT_H__
#define __FPROTO_BETT_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
BETTDecoderStepReset = 0,
BETTDecoderStepSaveDuration,
BETTDecoderStepCheckDuration,
} BETTDecoderStep;
class FProtoSubGhzDBett : public FProtoSubGhzDBase {
public:
FProtoSubGhzDBett() {
sensorType = FPS_BETT;
te_short = 340;
te_long = 2000;
te_delta = 150;
min_count_bit_for_found = 18;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case BETTDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 44) <
(te_delta * 15))) {
decode_data = 0;
decode_count_bit = 0;
parser_step = BETTDecoderStepCheckDuration;
}
break;
case BETTDecoderStepSaveDuration:
if (!level) {
if (DURATION_DIFF(duration, te_short * 44) <
(te_delta * 15)) {
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// dip decoder needed
if (callback) callback(this);
} else {
parser_step = BETTDecoderStepReset;
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
if ((DURATION_DIFF(duration, te_short) <
te_delta) ||
(DURATION_DIFF(duration, te_long) <
te_delta * 3)) {
parser_step = BETTDecoderStepCheckDuration;
} else {
parser_step = BETTDecoderStepReset;
}
}
}
break;
case BETTDecoderStepCheckDuration:
if (level) {
if (DURATION_DIFF(duration, te_long) <
te_delta * 3) {
subghz_protocol_blocks_add_bit(1);
parser_step = BETTDecoderStepSaveDuration;
} else if (
DURATION_DIFF(duration, te_short) <
te_delta) {
subghz_protocol_blocks_add_bit(0);
parser_step = BETTDecoderStepSaveDuration;
} else {
parser_step = BETTDecoderStepReset;
}
} else {
parser_step = BETTDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,95 @@
#ifndef __FPROTO_CAME_H__
#define __FPROTO_CAME_H__
#include "subghzdbase.hpp"
#define CAME_12_COUNT_BIT 12
#define CAME_24_COUNT_BIT 24
#define PRASTEL_COUNT_BIT 25
#define AIRFORCE_COUNT_BIT 18
typedef enum : uint8_t {
CameDecoderStepReset = 0,
CameDecoderStepFoundStartBit,
CameDecoderStepSaveDuration,
CameDecoderStepCheckDuration,
} CameDecoderStep;
class FProtoSubGhzDCame : public FProtoSubGhzDBase {
public:
FProtoSubGhzDCame() {
sensorType = FPS_CAME;
te_short = 320;
te_long = 640;
te_delta = 150;
min_count_bit_for_found = 12;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case CameDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 56) < te_delta * 47)) {
// Found header CAME
parser_step = CameDecoderStepFoundStartBit;
}
break;
case CameDecoderStepFoundStartBit:
if (!level) {
break;
} else if (
DURATION_DIFF(duration, te_short) < te_delta) {
// Found start bit CAME
parser_step = CameDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = CameDecoderStepReset;
}
break;
case CameDecoderStepSaveDuration:
if (!level) { // save interval
if (duration >= (te_short * 4)) {
parser_step = CameDecoderStepFoundStartBit;
if ((decode_count_bit == min_count_bit_for_found) || (decode_count_bit == AIRFORCE_COUNT_BIT) ||
(decode_count_bit == PRASTEL_COUNT_BIT) || (decode_count_bit == CAME_24_COUNT_BIT)) {
serial = SD_NO_SERIAL;
btn = SD_NO_BTN;
data = decode_data;
data_count_bit = decode_count_bit;
// if flippa hacky, i hacky
sensorType = FPS_CAME;
if (decode_count_bit == PRASTEL_COUNT_BIT) sensorType = FPS_PRASTEL;
if (decode_count_bit == AIRFORCE_COUNT_BIT) sensorType = FPS_AIRFORCE;
if (callback) callback(this);
}
break;
}
te_last = duration;
parser_step = CameDecoderStepCheckDuration;
} else {
parser_step = CameDecoderStepReset;
}
break;
case CameDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = CameDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = CameDecoderStepSaveDuration;
} else
parser_step = CameDecoderStepReset;
} else {
parser_step = CameDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,133 @@
#ifndef __FPROTO_CAMEATOMO_H__
#define __FPROTO_CAMEATOMO_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
CameAtomoDecoderStepReset = 0,
CameAtomoDecoderStepDecoderData,
} CameAtomoDecoderStep;
class FProtoSubGhzDCameAtomo : public FProtoSubGhzDBase {
public:
FProtoSubGhzDCameAtomo() {
sensorType = FPS_CAMEATOMO;
te_short = 600;
te_long = 1200;
te_delta = 250;
min_count_bit_for_found = 62;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case CameAtomoDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 60) < te_delta * 40)) {
// Found header CAME
parser_step = CameAtomoDecoderStepDecoderData;
decode_data = 0;
decode_count_bit = 1;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
}
break;
case CameAtomoDecoderStepDecoderData:
if (!level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortLow;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongLow;
} else if (
duration >= ((uint32_t)te_long * 2 + te_delta)) {
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
data ^= 0xFFFFFFFFFFFFFFFF;
data <<= 4;
uint8_t pack[8] = {};
pack[0] = (data >> 56);
pack[1] = ((data >> 48) & 0xFF);
pack[2] = ((data >> 40) & 0xFF);
pack[3] = ((data >> 32) & 0xFF);
pack[4] = ((data >> 24) & 0xFF);
pack[5] = ((data >> 16) & 0xFF);
pack[6] = ((data >> 8) & 0xFF);
pack[7] = (data & 0xFF);
atomo_decrypt(pack);
cnt = (uint16_t)pack[1] << 8 | pack[2];
serial = (uint32_t)(pack[3]) << 24 | pack[4] << 16 | pack[5] << 8 | pack[6];
uint8_t btn_decode = (pack[7] >> 4);
if (btn_decode == 0x0) {
btn = 0x1;
} else if (btn_decode == 0x2) {
btn = 0x2;
} else if (btn_decode == 0x4) {
btn = 0x3;
} else if (btn_decode == 0x6) {
btn = 0x4;
}
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 1;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
} else {
parser_step = CameAtomoDecoderStepReset;
}
} else {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortHigh;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongHigh;
} else {
parser_step = CameAtomoDecoderStepReset;
}
}
if (event != ManchesterEventReset) {
bool bit;
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit);
if (data_ok) {
decode_data = (decode_data << 1) | !bit;
decode_count_bit++;
}
}
break;
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
void atomo_decrypt(uint8_t* buff) {
buff[0] = (buff[0] ^ 5) & 0x7F;
uint8_t tmpB = (-buff[0]) & 0x7F;
uint8_t bitCnt = 8;
while (bitCnt < 59) {
if ((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) {
tmpB = ((tmpB << 1) & 0xFF) | 1;
} else {
tmpB = (tmpB << 1) & 0xFF;
}
if (tmpB & 0x80) {
buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7));
}
bitCnt++;
}
}
};
#endif

View File

@ -0,0 +1,147 @@
#ifndef __FPROTO_CAMETWEE_H__
#define __FPROTO_CAMETWEE_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
CameTweeDecoderStepReset = 0,
CameTweeDecoderStepDecoderData,
} CameTweeDecoderStep;
class FProtoSubGhzDCameTwee : public FProtoSubGhzDBase {
public:
FProtoSubGhzDCameTwee() {
sensorType = FPS_CAMETWEE;
te_short = 500;
te_long = 1000;
te_delta = 250;
min_count_bit_for_found = 54;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case CameTweeDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 51) < te_delta * 20)) {
// Found header CAME
parser_step = CameTweeDecoderStepDecoderData;
decode_data = 0;
decode_count_bit = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongLow, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
}
break;
case CameTweeDecoderStepDecoderData:
if (!level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortLow;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongLow;
} else if (
duration >= ((uint32_t)te_long * 2 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
subghz_protocol_came_twee_remote_controller();
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongLow, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
} else {
parser_step = CameTweeDecoderStepReset;
}
} else {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortHigh;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongHigh;
} else {
parser_step = CameTweeDecoderStepReset;
}
}
if (event != ManchesterEventReset) {
bool bit;
if (FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit)) {
decode_data = (decode_data << 1) | !bit;
decode_count_bit++;
}
}
break;
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
void subghz_protocol_came_twee_remote_controller() {
/* Came Twee 54 bit, rolling code 15 parcels with
* a decreasing counter from 0xE to 0x0
* with originally coded dip switches on the console 10 bit code
*
* 0x003FFF72E04A6FEE
* 0x003FFF72D17B5EDD
* 0x003FFF72C2684DCC
* 0x003FFF72B3193CBB
* 0x003FFF72A40E2BAA
* 0x003FFF72953F1A99
* 0x003FFF72862C0988
* 0x003FFF7277DDF877
* 0x003FFF7268C2E766
* 0x003FFF7259F3D655
* 0x003FFF724AE0C544
* 0x003FFF723B91B433
* 0x003FFF722C86A322
* 0x003FFF721DB79211
* 0x003FFF720EA48100
*
* decryption
* the last 32 bits, do XOR by the desired number, divide the result by 4,
* convert the first 16 bits of the resulting 32-bit number to bin and do
* bit-by-bit mirroring, adding up to 10 bits
*
* Example
* Step 1. 0x003FFF721DB79211 => 0x1DB79211
* Step 4. 0x1DB79211 xor 0x1D1D1D11 => 0x00AA8F00
* Step 4. 0x00AA8F00 / 4 => 0x002AA3C0
* Step 5. 0x002AA3C0 => 0x002A
* Step 6. 0x002A bin => b101010
* Step 7. b101010 => b0101010000
* Step 8. b0101010000 => (Dip) Off ON Off ON Off ON Off Off Off Off
*/
uint8_t cnt_parcel = (uint8_t)(data & 0xF);
serial = (uint32_t)(data & 0x0FFFFFFFF);
data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]);
data /= 4;
btn = (data >> 4) & 0x0F;
data >>= 16;
data = (uint16_t)FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 16);
cnt = data >> 6;
}
inline static const uint32_t came_twee_magic_numbers_xor[15] = {
0x0E0E0E00,
0x1D1D1D11,
0x2C2C2C22,
0x3B3B3B33,
0x4A4A4A44,
0x59595955,
0x68686866,
0x77777777,
0x86868688,
0x95959599,
0xA4A4A4AA,
0xB3B3B3BB,
0xC2C2C2CC,
0xD1D1D1DD,
0xE0E0E0EE,
};
};
#endif

View File

@ -0,0 +1,151 @@
#ifndef __FPROTO_CHAMBCODE_H__
#define __FPROTO_CHAMBCODE_H__
#include "subghzdbase.hpp"
#define CHAMBERLAIN_CODE_BIT_STOP 0b0001
#define CHAMBERLAIN_CODE_BIT_1 0b0011
#define CHAMBERLAIN_CODE_BIT_0 0b0111
#define CHAMBERLAIN_7_CODE_MASK 0xF000000FF0F
#define CHAMBERLAIN_8_CODE_MASK 0xF00000F00F
#define CHAMBERLAIN_9_CODE_MASK 0xF000000000F
#define CHAMBERLAIN_7_CODE_MASK_CHECK 0x10000001101
#define CHAMBERLAIN_8_CODE_MASK_CHECK 0x1000001001
#define CHAMBERLAIN_9_CODE_MASK_CHECK 0x10000000001
typedef enum : uint8_t {
Chamb_CodeDecoderStepReset = 0,
Chamb_CodeDecoderStepFoundStartBit,
Chamb_CodeDecoderStepSaveDuration,
Chamb_CodeDecoderStepCheckDuration,
} Chamb_CodeDecoderStep;
class FProtoSubGhzDChambCode : public FProtoSubGhzDBase {
public:
FProtoSubGhzDChambCode() {
sensorType = FPS_CHAMBCODE;
te_short = 1000;
te_long = 3000;
te_delta = 200;
min_count_bit_for_found = 10;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case Chamb_CodeDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 39) < te_delta * 20)) {
// Found header Chamb_Code
parser_step = Chamb_CodeDecoderStepFoundStartBit;
}
break;
case Chamb_CodeDecoderStepFoundStartBit:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
// Found start bit Chamb_Code
decode_data = 0;
decode_count_bit = 0;
decode_data = decode_data << 4 | CHAMBERLAIN_CODE_BIT_STOP;
decode_count_bit++;
parser_step = Chamb_CodeDecoderStepSaveDuration;
} else {
parser_step = Chamb_CodeDecoderStepReset;
}
break;
case Chamb_CodeDecoderStepSaveDuration:
if (!level) { // save interval
if (duration > te_short * 5) {
if (decode_count_bit >= min_count_bit_for_found) {
serial = SD_NO_SERIAL;
btn = SD_NO_BTN;
if (subghz_protocol_decoder_chamb_code_check_mask_and_parse()) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
}
parser_step = Chamb_CodeDecoderStepReset;
} else {
te_last = duration;
parser_step = Chamb_CodeDecoderStepCheckDuration;
}
} else {
parser_step = Chamb_CodeDecoderStepReset;
}
break;
case Chamb_CodeDecoderStepCheckDuration:
if (level) { // Found stop bit Chamb_Code
if ((DURATION_DIFF(te_last, te_short * 3) <
te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
decode_data = decode_data << 4 | CHAMBERLAIN_CODE_BIT_STOP;
decode_count_bit++;
parser_step = Chamb_CodeDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short * 2) < te_delta) &&
(DURATION_DIFF(duration, te_short * 2) < te_delta)) {
decode_data = decode_data << 4 | CHAMBERLAIN_CODE_BIT_1;
decode_count_bit++;
parser_step = Chamb_CodeDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short * 3) < te_delta)) {
decode_data = decode_data << 4 | CHAMBERLAIN_CODE_BIT_0;
decode_count_bit++;
parser_step = Chamb_CodeDecoderStepSaveDuration;
} else {
parser_step = Chamb_CodeDecoderStepReset;
}
} else {
parser_step = Chamb_CodeDecoderStepReset;
}
break;
}
}
protected:
bool subghz_protocol_decoder_chamb_code_check_mask_and_parse() {
if (decode_count_bit > min_count_bit_for_found + 1)
return false;
if ((decode_data & CHAMBERLAIN_7_CODE_MASK) == CHAMBERLAIN_7_CODE_MASK_CHECK) {
decode_count_bit = 7;
decode_data &= ~CHAMBERLAIN_7_CODE_MASK;
decode_data = (decode_data >> 12) | ((decode_data >> 4) & 0xF);
} else if (
(decode_data & CHAMBERLAIN_8_CODE_MASK) == CHAMBERLAIN_8_CODE_MASK_CHECK) {
decode_count_bit = 8;
decode_data &= ~CHAMBERLAIN_8_CODE_MASK;
decode_data = decode_data >> 4 | CHAMBERLAIN_CODE_BIT_0 << 8; // DIP 6 no use
} else if (
(decode_data & CHAMBERLAIN_9_CODE_MASK) == CHAMBERLAIN_9_CODE_MASK_CHECK) {
decode_count_bit = 9;
decode_data &= ~CHAMBERLAIN_9_CODE_MASK;
decode_data >>= 4;
} else {
return false;
}
return subghz_protocol_chamb_code_to_bit(&decode_data, decode_count_bit);
}
bool subghz_protocol_chamb_code_to_bit(uint64_t* data, uint8_t size) {
uint64_t data_tmp = data[0];
uint64_t data_res = 0;
for (uint8_t i = 0; i < size; i++) {
if ((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_0) {
bit_write(data_res, i, 0);
} else if ((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_1) {
bit_write(data_res, i, 1);
} else {
return false;
}
data_tmp >>= 4;
}
data[0] = data_res;
return true;
}
};
#endif

View File

@ -0,0 +1,89 @@
#ifndef __FPROTO_CLEMSA_H__
#define __FPROTO_CLEMSA_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
ClemsaDecoderStepReset = 0,
ClemsaDecoderStepSaveDuration,
ClemsaDecoderStepCheckDuration,
} ClemsaDecoderStep;
class FProtoSubGhzDClemsa : public FProtoSubGhzDBase {
public:
FProtoSubGhzDClemsa() {
sensorType = FPS_CLEMSA;
te_short = 385;
te_long = 2695;
te_delta = 150;
min_count_bit_for_found = 18;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case ClemsaDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 51) < te_delta * 25)) {
parser_step = ClemsaDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
}
break;
case ClemsaDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = ClemsaDecoderStepCheckDuration;
} else {
parser_step = ClemsaDecoderStepReset;
}
break;
case ClemsaDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = ClemsaDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = ClemsaDecoderStepSaveDuration;
} else if (
DURATION_DIFF(duration, te_short * 51) < te_delta * 25) {
if ((DURATION_DIFF(te_last, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
} else if ((DURATION_DIFF(te_last, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(1);
} else {
parser_step = ClemsaDecoderStepReset;
}
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
serial = (data >> 2) & 0xFFFF;
btn = (data & 0x03);
if (callback) callback(this);
}
parser_step = ClemsaDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = ClemsaDecoderStepReset;
}
} else {
parser_step = ClemsaDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,86 @@
#ifndef __FPROTO_DOITRAND_H__
#define __FPROTO_DOITRAND_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
DoitrandDecoderStepReset = 0,
DoitrandDecoderStepFoundStartBit,
DoitrandDecoderStepSaveDuration,
DoitrandDecoderStepCheckDuration,
} DoitrandDecoderStep;
class FProtoSubGhzDDoitrand : public FProtoSubGhzDBase {
public:
FProtoSubGhzDDoitrand() {
sensorType = FPS_DOITRAND;
te_short = 400;
te_long = 1100;
te_delta = 150;
min_count_bit_for_found = 37;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case DoitrandDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 62) < te_delta * 30)) {
// Found Preambula
parser_step = DoitrandDecoderStepFoundStartBit;
}
break;
case DoitrandDecoderStepFoundStartBit:
if (level && ((DURATION_DIFF(duration, (te_short * 2)) < te_delta * 3))) {
// Found start bit
parser_step = DoitrandDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = DoitrandDecoderStepReset;
}
break;
case DoitrandDecoderStepSaveDuration:
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
parser_step = DoitrandDecoderStepFoundStartBit;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
cnt = (data >> 24) | ((data >> 15) & 0x1);
btn = ((data >> 18) & 0x3);
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = DoitrandDecoderStepCheckDuration;
}
}
break;
case DoitrandDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = DoitrandDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = DoitrandDecoderStepSaveDuration;
} else {
parser_step = DoitrandDecoderStepReset;
}
} else {
parser_step = DoitrandDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,111 @@
#ifndef __FPROTO_DOOYA_H__
#define __FPROTO_DOOYA_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
DooyaDecoderStepReset = 0,
DooyaDecoderStepFoundStartBit,
DooyaDecoderStepSaveDuration,
DooyaDecoderStepCheckDuration,
} DooyaDecoderStep;
class FProtoSubGhzDDooya : public FProtoSubGhzDBase {
public:
FProtoSubGhzDDooya() {
sensorType = FPS_DOOYA;
te_short = 366;
te_long = 733;
te_delta = 120;
min_count_bit_for_found = 40;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case DooyaDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 12) < te_delta * 20)) {
parser_step = DooyaDecoderStepFoundStartBit;
}
break;
case DooyaDecoderStepFoundStartBit:
if (!level) {
if (DURATION_DIFF(duration, te_long * 2) < te_delta * 3) {
parser_step = DooyaDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = DooyaDecoderStepReset;
}
} else if (
DURATION_DIFF(duration, te_short * 13) < te_delta * 5) {
break;
} else {
parser_step = DooyaDecoderStepReset;
}
break;
case DooyaDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = DooyaDecoderStepCheckDuration;
} else {
parser_step = DooyaDecoderStepReset;
}
break;
case DooyaDecoderStepCheckDuration:
if (!level) {
if (duration >= (te_long * 4)) {
// add last bit
if (DURATION_DIFF(te_last, te_short) < te_delta) {
subghz_protocol_blocks_add_bit(0);
} else if (
DURATION_DIFF(te_last, te_long) <
te_delta * 2) {
subghz_protocol_blocks_add_bit(1);
} else {
parser_step = DooyaDecoderStepReset;
break;
}
parser_step = DooyaDecoderStepFoundStartBit;
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller:
serial = (data >> 16);
if ((data >> 12) & 0x0F) {
cnt = (data >> 8) & 0x0F;
} else {
cnt = 0xFF;
}
btn = data & 0xFF;
if (callback) callback(this);
}
break;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
subghz_protocol_blocks_add_bit(0);
parser_step = DooyaDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = DooyaDecoderStepSaveDuration;
} else {
parser_step = DooyaDecoderStepReset;
}
} else {
parser_step = DooyaDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,85 @@
#ifndef __FPROTO_FAAC_H__
#define __FPROTO_FAAC_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
FaacSLHDecoderStepReset = 0,
FaacSLHDecoderStepFoundPreambula,
FaacSLHDecoderStepSaveDuration,
FaacSLHDecoderStepCheckDuration,
} FaacSLHDecoderStep;
class FProtoSubGhzDFaac : public FProtoSubGhzDBase {
public:
FProtoSubGhzDFaac() {
sensorType = FPS_FAAC;
te_short = 255;
te_long = 595;
te_delta = 100;
min_count_bit_for_found = 64;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case FaacSLHDecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_long * 2) < te_delta * 3)) {
parser_step = FaacSLHDecoderStepFoundPreambula;
}
break;
case FaacSLHDecoderStepFoundPreambula:
if ((!level) && (DURATION_DIFF(duration, te_long * 2) < te_delta * 3)) {
// Found Preambula
parser_step = FaacSLHDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = FaacSLHDecoderStepReset;
}
break;
case FaacSLHDecoderStepSaveDuration:
if (level) {
if (duration >= ((uint32_t)te_short * 3 + te_delta)) {
parser_step = FaacSLHDecoderStepFoundPreambula;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// remark controller skipped
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = FaacSLHDecoderStepCheckDuration;
}
} else {
parser_step = FaacSLHDecoderStepReset;
}
break;
case FaacSLHDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = FaacSLHDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = FaacSLHDecoderStepSaveDuration;
} else {
parser_step = FaacSLHDecoderStepReset;
}
} else {
parser_step = FaacSLHDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,88 @@
#ifndef __FPROTO_GATETX_H__
#define __FPROTO_GATETX_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
GateTXDecoderStepReset = 0,
GateTXDecoderStepFoundStartBit,
GateTXDecoderStepSaveDuration,
GateTXDecoderStepCheckDuration,
} GateTXDecoderStep;
class FProtoSubGhzDGateTx : public FProtoSubGhzDBase {
public:
FProtoSubGhzDGateTx() {
sensorType = FPS_GATETX;
te_short = 350;
te_long = 700;
te_delta = 100;
min_count_bit_for_found = 24;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case GateTXDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 47) < te_delta * 47)) {
// Found Preambula
parser_step = GateTXDecoderStepFoundStartBit;
}
break;
case GateTXDecoderStepFoundStartBit:
if (level && ((DURATION_DIFF(duration, te_long) < te_delta * 3))) {
// Found start bit
parser_step = GateTXDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = GateTXDecoderStepReset;
}
break;
case GateTXDecoderStepSaveDuration:
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
parser_step = GateTXDecoderStepFoundStartBit;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
uint32_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
serial = (code_found_reverse & 0xFF) << 12 | ((code_found_reverse >> 8) & 0xFF) << 4 | ((code_found_reverse >> 20) & 0x0F);
btn = ((code_found_reverse >> 16) & 0x0F);
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = GateTXDecoderStepCheckDuration;
}
}
break;
case GateTXDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = GateTXDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = GateTXDecoderStepSaveDuration;
} else {
parser_step = GateTXDecoderStepReset;
}
} else {
parser_step = GateTXDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,107 @@
#ifndef __FPROTO_HOLTEK_H__
#define __FPROTO_HOLTEK_H__
#include "subghzdbase.hpp"
#define HOLTEK_HEADER_MASK 0xF000000000
#define HOLTEK_HEADER 0x5000000000
typedef enum : uint8_t {
HoltekDecoderStepReset = 0,
HoltekDecoderStepFoundStartBit,
HoltekDecoderStepSaveDuration,
HoltekDecoderStepCheckDuration,
} HoltekDecoderStep;
class FProtoSubGhzDHoltek : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHoltek() {
sensorType = FPS_HOLTEK;
te_short = 430;
te_long = 870;
te_delta = 100;
min_count_bit_for_found = 40;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case HoltekDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 36) < te_delta * 36)) {
// Found Preambula
parser_step = HoltekDecoderStepFoundStartBit;
}
break;
case HoltekDecoderStepFoundStartBit:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
// Found StartBit
parser_step = HoltekDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = HoltekDecoderStepReset;
}
break;
case HoltekDecoderStepSaveDuration:
// save duration
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
if (decode_count_bit ==
min_count_bit_for_found) {
if ((decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
serial = FProtoGeneral::subghz_protocol_blocks_reverse_key((data >> 16) & 0xFFFFF, 20);
uint16_t btn = data & 0xFFFF;
if ((btn & 0xf) != 0xA) {
btn = 0x1 << 4 | (btn & 0xF);
} else if (((btn >> 4) & 0xF) != 0xA) {
btn = 0x2 << 4 | ((btn >> 4) & 0xF);
} else if (((btn >> 8) & 0xF) != 0xA) {
btn = 0x3 << 4 | ((btn >> 8) & 0xF);
} else if (((btn >> 12) & 0xF) != 0xA) {
btn = 0x4 << 4 | ((btn >> 12) & 0xF);
} else {
btn = 0;
}
if (callback) callback(this);
}
}
decode_data = 0;
decode_count_bit = 0;
parser_step = HoltekDecoderStepFoundStartBit;
break;
} else {
te_last = duration;
parser_step = HoltekDecoderStepCheckDuration;
}
} else {
parser_step = HoltekDecoderStepReset;
}
break;
case HoltekDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
subghz_protocol_blocks_add_bit(0);
parser_step = HoltekDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = HoltekDecoderStepSaveDuration;
} else {
parser_step = HoltekDecoderStepReset;
}
} else {
parser_step = HoltekDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,90 @@
#ifndef __FPROTO_HOLTEKTH12X_H__
#define __FPROTO_HOLTEKTH12X_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
Holtek_HT12XDecoderStepReset = 0,
Holtek_HT12XDecoderStepFoundStartBit,
Holtek_HT12XDecoderStepSaveDuration,
Holtek_HT12XDecoderStepCheckDuration,
} Holtek_HT12XDecoderStep;
class FProtoSubGhzDHoltekHt12x : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHoltekHt12x() {
sensorType = FPS_HOLTEKHT12X;
te_short = 320;
te_long = 640;
te_delta = 200;
min_count_bit_for_found = 12;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case Holtek_HT12XDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 36) < te_delta * 36)) {
// Found Preambula
parser_step = Holtek_HT12XDecoderStepFoundStartBit;
}
break;
case Holtek_HT12XDecoderStepFoundStartBit:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
// Found StartBit
parser_step = Holtek_HT12XDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = Holtek_HT12XDecoderStepReset;
}
break;
case Holtek_HT12XDecoderStepSaveDuration:
// save duration
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
if (data != decode_data) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
btn = data & 0x0F;
cnt = (data >> 4) & 0xFF;
if (callback) callback(this);
}
}
decode_data = 0;
decode_count_bit = 0;
parser_step = Holtek_HT12XDecoderStepFoundStartBit;
break;
} else {
te_last = duration;
parser_step = Holtek_HT12XDecoderStepCheckDuration;
}
} else {
parser_step = Holtek_HT12XDecoderStepReset;
}
break;
case Holtek_HT12XDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = Holtek_HT12XDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
subghz_protocol_blocks_add_bit(0);
parser_step = Holtek_HT12XDecoderStepSaveDuration;
} else {
parser_step = Holtek_HT12XDecoderStepReset;
}
} else {
parser_step = Holtek_HT12XDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,108 @@
#ifndef __FPROTO_HONEYWELL_H__
#define __FPROTO_HONEYWELL_H__
#include "subghzdbase.hpp"
class FProtoSubGhzDHoneywell : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHoneywell() {
sensorType = FPS_HONEYWELL;
te_short = 280;
te_long = 143;
te_delta = 51;
min_count_bit_for_found = 62;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
if (!level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortLow;
} else if (
DURATION_DIFF(duration, te_long) < te_delta * 2) {
event = ManchesterEventLongLow;
}
} else {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortHigh;
} else if (
DURATION_DIFF(duration, te_long) < te_delta * 2) {
event = ManchesterEventLongHigh;
}
}
if (event != ManchesterEventReset) {
bool bit;
bool data_ok = FProtoGeneral::manchester_advance(
manchester_saved_state, event, &manchester_saved_state, &bit);
if (data_ok) {
subghz_protocol_decoder_honeywell_addbit(bit);
}
} else {
decode_data = 0;
decode_count_bit = 0;
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
void subghz_protocol_decoder_honeywell_addbit(bool bit) {
decode_data = (decode_data << 1) | bit;
decode_count_bit++;
uint16_t preamble = (decode_data >> 48) & 0xFFFF;
// can be multiple, since flipper can't read it well..
if (preamble == 0b0011111111111110 || preamble == 0b0111111111111110 ||
preamble == 0b1111111111111110) {
uint8_t datatocrc[4];
datatocrc[0] = (decode_data >> 40) & 0xFFFF;
datatocrc[1] = (decode_data >> 32) & 0xFFFF;
datatocrc[2] = (decode_data >> 24) & 0xFFFF;
datatocrc[3] = (decode_data >> 16) & 0xFFFF;
uint8_t channel = (decode_data >> 44) & 0xF;
uint16_t crc_calc = 0;
if (channel == 0x2 || channel == 0x4 || channel == 0xA) {
// 2GIG brand
crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8050, 0);
} else { // channel == 0x8
crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8005, 0);
}
uint16_t crc = decode_data & 0xFFFF;
if (crc == crc_calc) {
// the data is good. process it.
data = decode_data;
data_count_bit = decode_count_bit; // maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it
serial = (decode_data >> 24) & 0xFFFFF;
btn = (decode_data >> 16) & 0xFF; // not exactly button, but can contain btn data too.
if (callback) callback(this);
decode_data = 0;
decode_count_bit = 0;
} else {
return;
}
}
}
uint16_t subghz_protocol_honeywell_crc16(
uint8_t const message[],
unsigned nBytes,
uint16_t polynomial,
uint16_t init) {
uint16_t remainder = init;
unsigned byte, bit;
for (byte = 0; byte < nBytes; ++byte) {
remainder ^= message[byte] << 8;
for (bit = 0; bit < 8; ++bit) {
if (remainder & 0x8000) {
remainder = (remainder << 1) ^ polynomial;
} else {
remainder = (remainder << 1);
}
}
}
return remainder;
}
};
#endif

View File

@ -0,0 +1,74 @@
#ifndef __FPROTO_HONEYWELLWDB_H__
#define __FPROTO_HONEYWELLWDB_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
Honeywell_WDBDecoderStepReset = 0,
Honeywell_WDBDecoderStepFoundStartBit,
Honeywell_WDBDecoderStepSaveDuration,
Honeywell_WDBDecoderStepCheckDuration,
} Honeywell_WDBDecoderStep;
class FProtoSubGhzDHoneywellWdb : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHoneywellWdb() {
sensorType = FPS_HONEYWELLWDB;
te_short = 160;
te_long = 320;
te_delta = 61;
min_count_bit_for_found = 48;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case Honeywell_WDBDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 3) < te_delta)) {
// Found header Honeywell_WDB
decode_count_bit = 0;
decode_data = 0;
parser_step = Honeywell_WDBDecoderStepSaveDuration;
}
break;
case Honeywell_WDBDecoderStepSaveDuration:
if (level) { // save interval
if (DURATION_DIFF(duration, te_short * 3) < te_delta) {
if ((decode_count_bit == min_count_bit_for_found) &&
((decode_data & 0x01) == FProtoGeneral::subghz_protocol_blocks_get_parity(decode_data >> 1, min_count_bit_for_found - 1))) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller has too much, should be done on ui side
if (callback) callback(this);
}
parser_step = Honeywell_WDBDecoderStepReset;
break;
}
te_last = duration;
parser_step = Honeywell_WDBDecoderStepCheckDuration;
} else {
parser_step = Honeywell_WDBDecoderStepReset;
}
break;
case Honeywell_WDBDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = Honeywell_WDBDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = Honeywell_WDBDecoderStepSaveDuration;
} else
parser_step = Honeywell_WDBDecoderStepReset;
} else {
parser_step = Honeywell_WDBDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,85 @@
#ifndef __FPROTO_HORMANN_H__
#define __FPROTO_HORMANN_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
HormannDecoderStepReset = 0,
HormannDecoderStepFoundStartHeader,
HormannDecoderStepFoundHeader,
HormannDecoderStepFoundStartBit,
HormannDecoderStepSaveDuration,
HormannDecoderStepCheckDuration,
} HormannDecoderStep;
#define HORMANN_HSM_PATTERN 0xFF000000003
class FProtoSubGhzDHormann : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHormann() {
sensorType = FPS_HORMANN;
te_short = 500;
te_long = 1000;
te_delta = 200;
min_count_bit_for_found = 44;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case HormannDecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short * 24) < te_delta * 24)) {
parser_step = HormannDecoderStepFoundStartBit;
}
break;
case HormannDecoderStepFoundStartBit:
if ((!level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = HormannDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = HormannDecoderStepReset;
}
break;
case HormannDecoderStepSaveDuration:
if (level) { // save interval
if (duration >= (te_short * 5) && (decode_data & HORMANN_HSM_PATTERN) == HORMANN_HSM_PATTERN) {
parser_step = HormannDecoderStepFoundStartBit;
if (decode_count_bit >=
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
btn = (data >> 4) & 0xF;
if (callback) callback(this);
}
break;
}
te_last = duration;
parser_step = HormannDecoderStepCheckDuration;
} else {
parser_step = HormannDecoderStepReset;
}
break;
case HormannDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = HormannDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = HormannDecoderStepSaveDuration;
} else
parser_step = HormannDecoderStepReset;
} else {
parser_step = HormannDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,91 @@
#ifndef __FPROTO_IDO_H__
#define __FPROTO_IDO_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
IDoDecoderStepReset = 0,
IDoDecoderStepFoundPreambula,
IDoDecoderStepSaveDuration,
IDoDecoderStepCheckDuration,
} IDoDecoderStep;
class FProtoSubGhzDIdo : public FProtoSubGhzDBase {
public:
FProtoSubGhzDIdo() {
sensorType = FPS_IDO;
te_short = 450;
te_long = 1450;
te_delta = 150;
min_count_bit_for_found = 48;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case IDoDecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short * 10) < te_delta * 5)) {
parser_step = IDoDecoderStepFoundPreambula;
}
break;
case IDoDecoderStepFoundPreambula:
if ((!level) && (DURATION_DIFF(duration, te_short * 10) < te_delta * 5)) {
// Found Preambula
parser_step = IDoDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = IDoDecoderStepReset;
}
break;
case IDoDecoderStepSaveDuration:
if (level) {
if (duration >= ((uint32_t)te_short * 5 + te_delta)) {
parser_step = IDoDecoderStepFoundPreambula;
if (decode_count_bit >=
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
uint64_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
uint32_t code_fix = code_found_reverse & 0xFFFFFF;
serial = code_fix & 0xFFFFF;
btn = (code_fix >> 20) & 0x0F;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = IDoDecoderStepCheckDuration;
}
} else {
parser_step = IDoDecoderStepReset;
}
break;
case IDoDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = IDoDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = IDoDecoderStepSaveDuration;
} else {
parser_step = IDoDecoderStepReset;
}
} else {
parser_step = IDoDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,148 @@
#ifndef __FPROTO_INTERTECHNOV3_H__
#define __FPROTO_INTERTECHNOV3_H__
#include "subghzdbase.hpp"
#define INTERTECHNO_V3_DIMMING_COUNT_BIT 36
typedef enum : uint8_t {
IntertechnoV3DecoderStepReset = 0,
IntertechnoV3DecoderStepStartSync,
IntertechnoV3DecoderStepFoundSync,
IntertechnoV3DecoderStepStartDuration,
IntertechnoV3DecoderStepSaveDuration,
IntertechnoV3DecoderStepCheckDuration,
IntertechnoV3DecoderStepEndDuration,
} IntertechnoV3DecoderStep;
class FProtoSubGhzDIntertechnoV3 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDIntertechnoV3() {
sensorType = FPS_INTERTECHNOV3;
te_short = 275;
te_long = 1375;
te_delta = 150;
min_count_bit_for_found = 32;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case IntertechnoV3DecoderStepReset:
if ((!level) &&
(DURATION_DIFF(duration, te_short * 37) < te_delta * 15)) {
parser_step = IntertechnoV3DecoderStepStartSync;
}
break;
case IntertechnoV3DecoderStepStartSync:
if (level && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = IntertechnoV3DecoderStepFoundSync;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepFoundSync:
if (!level && (DURATION_DIFF(duration, te_short * 10) < te_delta * 3)) {
parser_step = IntertechnoV3DecoderStepStartDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepStartDuration:
if (level && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = IntertechnoV3DecoderStepSaveDuration;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepSaveDuration:
if (!level) { // save interval
if (duration >= (te_short * 11)) {
parser_step = IntertechnoV3DecoderStepStartSync;
if ((decode_count_bit == min_count_bit_for_found) ||
(decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) {
data = decode_data;
data_count_bit = decode_count_bit;
remote_controller();
if (callback) callback(this);
}
break;
}
te_last = duration;
parser_step = IntertechnoV3DecoderStepCheckDuration;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepCheckDuration:
if (level) {
// Add 0 bit
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = IntertechnoV3DecoderStepEndDuration;
} else if (
// Add 1 bit
(DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = IntertechnoV3DecoderStepEndDuration;
} else if (
// Add dimm_state
(DURATION_DIFF(te_last, te_short) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta) &&
(decode_count_bit == 27)) {
subghz_protocol_blocks_add_bit(0);
parser_step = IntertechnoV3DecoderStepEndDuration;
} else
parser_step = IntertechnoV3DecoderStepReset;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepEndDuration:
if (!level && ((DURATION_DIFF(duration, te_short) < te_delta) ||
(DURATION_DIFF(duration, te_long) < te_delta * 2))) {
parser_step = IntertechnoV3DecoderStepStartDuration;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
}
}
protected:
void remote_controller() {
if (data_count_bit == min_count_bit_for_found) {
serial = (data >> 6) & 0x3FFFFFF;
if ((data >> 5) & 0x1) {
cnt = 1 << 5;
} else {
cnt = (~data & 0xF);
}
btn = (data >> 4) & 0x1;
} else if (data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) {
serial = (data >> 10) & 0x3FFFFFF;
if ((data >> 9) & 0x1) {
cnt = 1 << 5;
} else {
cnt = (~(data >> 4) & 0xF);
}
btn = data & 0xF;
} else {
serial = 0;
cnt = 0;
btn = 0;
}
}
};
#endif

View File

@ -0,0 +1,107 @@
#ifndef __FPROTO_KEELOQ_H__
#define __FPROTO_KEELOQ_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
KeeloqDecoderStepReset = 0,
KeeloqDecoderStepCheckPreambula,
KeeloqDecoderStepSaveDuration,
KeeloqDecoderStepCheckDuration,
} KeeloqDecoderStep;
class FProtoSubGhzDKeeLoq : public FProtoSubGhzDBase {
public:
FProtoSubGhzDKeeLoq() {
sensorType = FPS_KEELOQ;
te_short = 400;
te_long = 800;
te_delta = 140;
min_count_bit_for_found = 64;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case KeeloqDecoderStepReset:
if ((level) && DURATION_DIFF(duration, te_short) < te_delta) {
parser_step = KeeloqDecoderStepCheckPreambula;
header_count++;
}
break;
case KeeloqDecoderStepCheckPreambula:
if ((!level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = KeeloqDecoderStepReset;
break;
}
if ((header_count > 2) && (DURATION_DIFF(duration, te_short * 10) < te_delta * 10)) {
// Found header
parser_step = KeeloqDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = KeeloqDecoderStepReset;
header_count = 0;
}
break;
case KeeloqDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = KeeloqDecoderStepCheckDuration;
}
break;
case KeeloqDecoderStepCheckDuration:
if (!level) {
if (duration >= ((uint32_t)te_short * 2 + te_delta)) {
// Found end TX
parser_step = KeeloqDecoderStepReset;
if ((decode_count_bit >= min_count_bit_for_found) &&
(decode_count_bit <= min_count_bit_for_found + 2)) {
if (data != decode_data) {
data = decode_data;
data_count_bit = min_count_bit_for_found;
// controller
uint64_t key = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
uint32_t key_fix = key >> 32;
// uint32_t key_hop = key & 0x00000000ffffffff; //unused
serial = key_fix & 0x0FFFFFFF;
btn = key_fix >> 28;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
header_count = 0;
}
break;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
if (decode_count_bit < min_count_bit_for_found) {
subghz_protocol_blocks_add_bit(1);
} else {
decode_count_bit++;
}
parser_step = KeeloqDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
if (decode_count_bit < min_count_bit_for_found) {
subghz_protocol_blocks_add_bit(0);
} else {
decode_count_bit++;
}
parser_step = KeeloqDecoderStepSaveDuration;
} else {
parser_step = KeeloqDecoderStepReset;
header_count = 0;
}
} else {
parser_step = KeeloqDecoderStepReset;
header_count = 0;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,124 @@
#ifndef __FPROTO_KINGGATES_STYLO_4K_H__
#define __FPROTO_KINGGATES_STYLO_4K_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
KingGates_stylo_4kDecoderStepReset = 0,
KingGates_stylo_4kDecoderStepCheckPreambula,
KingGates_stylo_4kDecoderStepCheckStartBit,
KingGates_stylo_4kDecoderStepSaveDuration,
KingGates_stylo_4kDecoderStepCheckDuration,
} KingGates_stylo_4kDecoderStep;
class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
public:
FProtoSubGhzDKinggatesStylo4K() {
sensorType = FPS_KINGGATESSTYLO4K;
te_short = 400;
te_long = 1100;
te_delta = 140;
min_count_bit_for_found = 89;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case KingGates_stylo_4kDecoderStepReset:
if ((level) && DURATION_DIFF(duration, te_short) < te_delta) {
parser_step = KingGates_stylo_4kDecoderStepCheckPreambula;
header_count++;
}
break;
case KingGates_stylo_4kDecoderStepCheckPreambula:
if ((!level) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = KingGates_stylo_4kDecoderStepReset;
break;
}
if ((header_count > 2) &&
(DURATION_DIFF(duration, te_long * 2) < te_delta * 2)) {
// Found header
parser_step = KingGates_stylo_4kDecoderStepCheckStartBit;
} else {
parser_step = KingGates_stylo_4kDecoderStepReset;
header_count = 0;
}
break;
case KingGates_stylo_4kDecoderStepCheckStartBit:
if ((level) &&
DURATION_DIFF(duration, te_short * 2) < te_delta * 2) {
parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
decode_data = 0;
data_2 = 0;
decode_count_bit = 0;
header_count = 0;
}
break;
case KingGates_stylo_4kDecoderStepSaveDuration:
if (!level) {
if (duration >= ((uint32_t)te_long * 3)) {
if (decode_count_bit ==
min_count_bit_for_found) {
data = data_2;
data_2 = decode_data;
data_count_bit = decode_count_bit;
// controller
uint64_t fix = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 53);
btn = (fix >> 17) & 0x0F;
serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF);
if (callback) callback(this);
}
parser_step = KingGates_stylo_4kDecoderStepReset;
decode_data = 0;
data_2 = 0;
decode_count_bit = 0;
header_count = 0;
break;
} else {
te_last = duration;
parser_step = KingGates_stylo_4kDecoderStepCheckDuration;
}
} else {
parser_step = KingGates_stylo_4kDecoderStepReset;
header_count = 0;
}
break;
case KingGates_stylo_4kDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(
te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
subghz_protocol_blocks_add_bit(1);
parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(
te_last, te_long) <
te_delta * 2) &&
(DURATION_DIFF(duration, te_short) <
te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
} else {
parser_step = KingGates_stylo_4kDecoderStepReset;
header_count = 0;
}
if (decode_count_bit == 53) {
data_2 = decode_data;
decode_data = 0;
}
} else {
parser_step = KingGates_stylo_4kDecoderStepReset;
header_count = 0;
}
break;
}
}
protected:
uint64_t data_2 = 0;
};
#endif

View File

@ -0,0 +1,88 @@
#ifndef __FPROTO_LINEAR_H__
#define __FPROTO_LINEAR_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
LinearDecoderStepReset = 0,
LinearDecoderStepSaveDuration,
LinearDecoderStepCheckDuration,
} LinearDecoderStep;
class FProtoSubGhzDLinear : public FProtoSubGhzDBase {
public:
FProtoSubGhzDLinear() {
sensorType = FPS_LINEAR;
te_short = 500;
te_long = 1500;
te_delta = 150;
min_count_bit_for_found = 10;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case LinearDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 42) < te_delta * 20)) {
// Found header Linear
decode_data = 0;
decode_count_bit = 0;
parser_step = LinearDecoderStepSaveDuration;
}
break;
case LinearDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = LinearDecoderStepCheckDuration;
} else {
parser_step = LinearDecoderStepReset;
}
break;
case LinearDecoderStepCheckDuration:
if (!level) { // save interval
if (duration >= (te_short * 5)) {
parser_step = LinearDecoderStepReset;
// checking that the duration matches the guardtime
if ((DURATION_DIFF(duration, te_short * 42) > te_delta * 20)) {
break;
}
if (DURATION_DIFF(te_last, te_short) < te_delta) {
subghz_protocol_blocks_add_bit(0);
} else if (
DURATION_DIFF(te_last, te_long) <
te_delta) {
subghz_protocol_blocks_add_bit(1);
}
if (decode_count_bit == min_count_bit_for_found) {
serial = SD_NO_SERIAL;
btn = SD_NO_BTN;
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
break;
}
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = LinearDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = LinearDecoderStepSaveDuration;
} else {
parser_step = LinearDecoderStepReset;
}
} else {
parser_step = LinearDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,87 @@
#ifndef __FPROTO_LINEARDELTA3_H__
#define __FPROTO_LINEARDELTA3_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
LinearD3DecoderStepReset = 0,
LinearD3DecoderStepSaveDuration,
LinearD3DecoderStepCheckDuration,
} LinearD3DecoderStep;
class FProtoSubGhzDLinearDelta3 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDLinearDelta3() {
sensorType = FPS_LINEARDELTA3;
te_short = 500;
te_long = 2000;
te_delta = 150;
min_count_bit_for_found = 8;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case LinearD3DecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 70) < te_delta * 24)) {
// Found header Linear
decode_data = 0;
decode_count_bit = 0;
parser_step = LinearD3DecoderStepSaveDuration;
}
break;
case LinearD3DecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = LinearD3DecoderStepCheckDuration;
} else {
parser_step = LinearD3DecoderStepReset;
}
break;
case LinearD3DecoderStepCheckDuration:
if (!level) {
if (duration >= (te_short * 10)) {
parser_step = LinearD3DecoderStepReset;
if (DURATION_DIFF(te_last, te_short) < te_delta) {
subghz_protocol_blocks_add_bit(1);
} else if (
DURATION_DIFF(te_last, te_long) < te_delta) {
subghz_protocol_blocks_add_bit(0);
}
if (decode_count_bit == min_count_bit_for_found) {
if ((data == decode_data) && data) {
serial = SD_NO_SERIAL;
btn = SD_NO_BTN;
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
parser_step = LinearD3DecoderStepSaveDuration;
}
break;
}
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short * 7) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = LinearD3DecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = LinearD3DecoderStepSaveDuration;
} else {
parser_step = LinearD3DecoderStepReset;
}
} else {
parser_step = LinearD3DecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,139 @@
#ifndef __FPROTO_MAGELLAN_H__
#define __FPROTO_MAGELLAN_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
MagellanDecoderStepReset = 0,
MagellanDecoderStepCheckPreambula,
MagellanDecoderStepFoundPreambula,
MagellanDecoderStepSaveDuration,
MagellanDecoderStepCheckDuration,
} MagellanDecoderStep;
class FProtoSubGhzDMagellan : public FProtoSubGhzDBase {
public:
FProtoSubGhzDMagellan() {
sensorType = FPS_MAGELLAN;
te_short = 200;
te_long = 400;
te_delta = 100;
min_count_bit_for_found = 32;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case MagellanDecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = MagellanDecoderStepCheckPreambula;
te_last = duration;
header_count = 0;
}
break;
case MagellanDecoderStepCheckPreambula:
if (level) {
te_last = duration;
} else {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
// Found header
header_count++;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2) &&
(header_count > 10)) {
parser_step = MagellanDecoderStepFoundPreambula;
} else {
parser_step = MagellanDecoderStepReset;
}
}
break;
case MagellanDecoderStepFoundPreambula:
if (level) {
te_last = duration;
} else {
if ((DURATION_DIFF(te_last, te_short * 6) < te_delta * 3) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
parser_step = MagellanDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = MagellanDecoderStepReset;
}
}
break;
case MagellanDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = MagellanDecoderStepCheckDuration;
} else {
parser_step = MagellanDecoderStepReset;
}
break;
case MagellanDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = MagellanDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = MagellanDecoderStepSaveDuration;
} else if (duration >= (te_long * 3)) {
// Found stop bit
if ((decode_count_bit == min_count_bit_for_found) &&
subghz_protocol_magellan_check_crc()) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(data >> 8, 24);
serial = data_rev & 0xFFFF;
btn = (data_rev >> 16) & 0xFF;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
parser_step = MagellanDecoderStepReset;
} else {
parser_step = MagellanDecoderStepReset;
}
} else {
parser_step = MagellanDecoderStepReset;
}
break;
}
}
protected:
bool subghz_protocol_magellan_check_crc() {
uint8_t data[3] = {
(uint8_t)(decode_data >> 24),
(uint8_t)(decode_data >> 16),
(uint8_t)(decode_data >> 8)};
return (decode_data & 0xFF) == subghz_protocol_magellan_crc8(data, sizeof(data));
}
uint8_t subghz_protocol_magellan_crc8(uint8_t* data, size_t len) {
uint8_t crc = 0x00;
uint8_t i, j;
for (i = 0; i < len; i++) {
crc ^= data[i];
for (j = 0; j < 8; j++) {
if ((crc & 0x80) != 0)
crc = (uint8_t)((crc << 1) ^ 0x31);
else
crc <<= 1;
}
}
return crc;
}
};
#endif

View File

@ -0,0 +1,86 @@
#ifndef __FPROTO_MARANTEC_H__
#define __FPROTO_MARANTEC_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
MarantecDecoderStepReset = 0,
MarantecDecoderFoundHeader,
MarantecDecoderStepDecoderData,
} MarantecDecoderStep;
class FProtoSubGhzDMarantec : public FProtoSubGhzDBase {
public:
FProtoSubGhzDMarantec() {
sensorType = FPS_MARANTEC;
te_short = 1000;
te_long = 2000;
te_delta = 200;
min_count_bit_for_found = 49;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case MarantecDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 5) < te_delta * 8)) {
// Found header marantec
parser_step = MarantecDecoderStepDecoderData;
decode_data = 1;
decode_count_bit = 1;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
}
break;
case MarantecDecoderStepDecoderData:
if (!level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortLow;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongLow;
} else if (
duration >= ((uint32_t)te_long * 2 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
btn = (data >> 16) & 0xF;
serial = ((data >> 12) & 0xFFFFFF00) | ((data >> 8) & 0xFF);
if (callback) callback(this);
}
decode_data = 1;
decode_count_bit = 1;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
} else {
parser_step = MarantecDecoderStepReset;
}
} else {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortHigh;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongHigh;
} else {
parser_step = MarantecDecoderStepReset;
}
}
if (event != ManchesterEventReset) {
bool bitstate;
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bitstate);
if (data_ok) {
decode_data = (decode_data << 1) | bitstate;
decode_count_bit++;
}
}
break;
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
};
#endif

View File

@ -0,0 +1,86 @@
#ifndef __FPROTO_MASTERCODE_H__
#define __FPROTO_MASTERCODE_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
MastercodeDecoderStepReset = 0,
MastercodeDecoderStepSaveDuration,
MastercodeDecoderStepCheckDuration,
} MastercodeDecoderStep;
class FProtoSubGhzDMastercode : public FProtoSubGhzDBase {
public:
FProtoSubGhzDMastercode() {
sensorType = FPS_MASTERCODE;
te_short = 1072;
te_long = 2145;
te_delta = 150;
min_count_bit_for_found = 36;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case MastercodeDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 15) < te_delta * 15)) {
parser_step = MastercodeDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
}
break;
case MastercodeDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = MastercodeDecoderStepCheckDuration;
} else {
parser_step = MastercodeDecoderStepReset;
}
break;
case MastercodeDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 8)) {
subghz_protocol_blocks_add_bit(0);
parser_step = MastercodeDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 8) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = MastercodeDecoderStepSaveDuration;
} else if (
DURATION_DIFF(duration, te_short * 15) < te_delta * 15) {
if ((DURATION_DIFF(te_last, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
} else if ((DURATION_DIFF(te_last, te_long) < te_delta * 8)) {
subghz_protocol_blocks_add_bit(1);
} else {
parser_step = MastercodeDecoderStepReset;
}
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
serial = (data >> 4) & 0xFFFF;
btn = (data >> 2 & 0x03);
if (callback) callback(this);
}
parser_step = MastercodeDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = MastercodeDecoderStepReset;
}
} else {
parser_step = MastercodeDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,104 @@
#ifndef __FPROTO_MEGACODE_H__
#define __FPROTO_MEGACODE_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
MegaCodeDecoderStepReset = 0,
MegaCodeDecoderStepFoundStartBit,
MegaCodeDecoderStepSaveDuration,
MegaCodeDecoderStepCheckDuration,
} MegaCodeDecoderStep;
class FProtoSubGhzDMegacode : public FProtoSubGhzDBase {
public:
FProtoSubGhzDMegacode() {
sensorType = FPS_MEGACODE;
te_short = 1000;
te_long = 1000;
te_delta = 200;
min_count_bit_for_found = 24;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case MegaCodeDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 13) < te_delta * 17)) { // 10..16ms
// Found header MegaCode
parser_step = MegaCodeDecoderStepFoundStartBit;
}
break;
case MegaCodeDecoderStepFoundStartBit:
if (level && (DURATION_DIFF(duration, te_short) < te_delta)) {
// Found start bit MegaCode
parser_step = MegaCodeDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
subghz_protocol_blocks_add_bit(1);
last_bit = 1;
} else {
parser_step = MegaCodeDecoderStepReset;
}
break;
case MegaCodeDecoderStepSaveDuration:
if (!level) { // save interval
if (duration >= (te_short * 10)) {
parser_step = MegaCodeDecoderStepReset;
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
if ((data >> 23) == 1) {
serial = (data >> 3) & 0xFFFF;
btn = data & 0b111;
cnt = (data >> 19) & 0b1111;
} else {
serial = 0;
btn = 0;
cnt = 0;
}
if (callback) callback(this);
}
break;
}
if (!last_bit) {
te_last = duration - te_short * 3;
} else {
te_last = duration;
}
parser_step = MegaCodeDecoderStepCheckDuration;
} else {
parser_step = MegaCodeDecoderStepReset;
}
break;
case MegaCodeDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short * 5) < te_delta * 5) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
last_bit = 1;
parser_step = MegaCodeDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short * 2) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
last_bit = 0;
parser_step = MegaCodeDecoderStepSaveDuration;
} else
parser_step = MegaCodeDecoderStepReset;
} else {
parser_step = MegaCodeDecoderStepReset;
}
break;
}
}
protected:
uint8_t last_bit = false;
};
#endif

View File

@ -0,0 +1,108 @@
#ifndef __FPROTO_NEROSKETCH_H__
#define __FPROTO_NEROSKETCH_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
NeroSketchDecoderStepReset = 0,
NeroSketchDecoderStepCheckPreambula,
NeroSketchDecoderStepSaveDuration,
NeroSketchDecoderStepCheckDuration,
} NeroSketchDecoderStep;
class FProtoSubGhzDNeroSketch : public FProtoSubGhzDBase {
public:
FProtoSubGhzDNeroSketch() {
sensorType = FPS_NERO_SKETCH;
te_short = 330;
te_long = 660;
te_delta = 150;
min_count_bit_for_found = 40;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case NeroSketchDecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = NeroSketchDecoderStepCheckPreambula;
te_last = duration;
header_count = 0;
}
break;
case NeroSketchDecoderStepCheckPreambula:
if (level) {
if ((DURATION_DIFF(duration, te_short) < te_delta) ||
(DURATION_DIFF(duration, te_short * 4) < te_delta)) {
te_last = duration;
} else {
parser_step = NeroSketchDecoderStepReset;
}
} else if (
DURATION_DIFF(duration, te_short) < te_delta) {
if (DURATION_DIFF(te_last, te_short) < te_delta) {
// Found header
header_count++;
break;
} else if (
DURATION_DIFF(te_last, te_short * 4) < te_delta) {
// Found start bit
if (header_count > 40) {
parser_step = NeroSketchDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = NeroSketchDecoderStepReset;
}
} else {
parser_step = NeroSketchDecoderStepReset;
}
} else {
parser_step = NeroSketchDecoderStepReset;
}
break;
case NeroSketchDecoderStepSaveDuration:
if (level) {
if (duration >= (te_short * 2 + te_delta * 2)) {
// Found stop bit
parser_step = NeroSketchDecoderStepReset;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = NeroSketchDecoderStepCheckDuration;
}
} else {
parser_step = NeroSketchDecoderStepReset;
}
break;
case NeroSketchDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = NeroSketchDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = NeroSketchDecoderStepSaveDuration;
} else {
parser_step = NeroSketchDecoderStepReset;
}
} else {
parser_step = NeroSketchDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,115 @@
#ifndef __FPROTO_NERORADIO_H__
#define __FPROTO_NERORADIO_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
NeroRadioDecoderStepReset = 0,
NeroRadioDecoderStepCheckPreambula,
NeroRadioDecoderStepSaveDuration,
NeroRadioDecoderStepCheckDuration,
} NeroRadioDecoderStep;
class FProtoSubGhzDNeroRadio : public FProtoSubGhzDBase {
public:
FProtoSubGhzDNeroRadio() {
sensorType = FPS_NERORADIO;
te_short = 200;
te_long = 400;
te_delta = 80;
min_count_bit_for_found = 56;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case NeroRadioDecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = NeroRadioDecoderStepCheckPreambula;
te_last = duration;
header_count = 0;
}
break;
case NeroRadioDecoderStepCheckPreambula:
if (level) {
if ((DURATION_DIFF(duration, te_short) < te_delta) ||
(DURATION_DIFF(duration, te_short * 4) < te_delta)) {
te_last = duration;
} else {
parser_step = NeroRadioDecoderStepReset;
}
} else if (
DURATION_DIFF(duration, te_short) < te_delta) {
if (DURATION_DIFF(te_last, te_short) < te_delta) {
// Found header
header_count++;
break;
} else if (
DURATION_DIFF(te_last, te_short * 4) < te_delta) {
// Found start bit
if (header_count > 40) {
parser_step = NeroRadioDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = NeroRadioDecoderStepReset;
}
} else {
parser_step = NeroRadioDecoderStepReset;
}
} else {
parser_step = NeroRadioDecoderStepReset;
}
break;
case NeroRadioDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = NeroRadioDecoderStepCheckDuration;
} else {
parser_step = NeroRadioDecoderStepReset;
}
break;
case NeroRadioDecoderStepCheckDuration:
if (!level) {
if (duration >= ((uint32_t)1250)) {
// Found stop bit
if (DURATION_DIFF(te_last, te_short) < te_delta) {
subghz_protocol_blocks_add_bit(0);
} else if (
DURATION_DIFF(te_last, te_long) < te_delta) {
subghz_protocol_blocks_add_bit(1);
}
parser_step = NeroRadioDecoderStepReset;
if ((decode_count_bit == min_count_bit_for_found) ||
(decode_count_bit == min_count_bit_for_found + 1)) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
parser_step = NeroRadioDecoderStepReset; //-V1048
break;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = NeroRadioDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = NeroRadioDecoderStepSaveDuration;
} else {
parser_step = NeroRadioDecoderStepReset;
}
} else {
parser_step = NeroRadioDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,86 @@
#ifndef __FPROTO_NICE_FLO_H__
#define __FPROTO_NICE_FLO_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
NiceFloDecoderStepReset = 0,
NiceFloDecoderStepFoundStartBit,
NiceFloDecoderStepSaveDuration,
NiceFloDecoderStepCheckDuration,
} NiceFloDecoderStep;
class FProtoSubGhzDNiceflo : public FProtoSubGhzDBase {
public:
FProtoSubGhzDNiceflo() {
sensorType = FPS_NICEFLO;
te_short = 700;
te_long = 1400;
te_delta = 200;
min_count_bit_for_found = 12;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case NiceFloDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 36) < te_delta * 36)) {
// Found header Nice Flo
parser_step = NiceFloDecoderStepFoundStartBit;
}
break;
case NiceFloDecoderStepFoundStartBit:
if (!level) {
break;
} else if (
DURATION_DIFF(duration, te_short) < te_delta) {
// Found start bit Nice Flo
parser_step = NiceFloDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = NiceFloDecoderStepReset;
}
break;
case NiceFloDecoderStepSaveDuration:
if (!level) { // save interval
if (duration >= (te_short * 4)) {
parser_step = NiceFloDecoderStepFoundStartBit;
if (decode_count_bit >= min_count_bit_for_found) {
serial = SD_NO_SERIAL;
btn = SD_NO_BTN;
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
break;
}
te_last = duration;
parser_step = NiceFloDecoderStepCheckDuration;
} else {
parser_step = NiceFloDecoderStepReset;
}
break;
case NiceFloDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = NiceFloDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = NiceFloDecoderStepSaveDuration;
} else
parser_step = NiceFloDecoderStepReset;
} else {
parser_step = NiceFloDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,100 @@
#ifndef __FPROTO_NICE_FLORS_H__
#define __FPROTO_NICE_FLORS_H__
#include "subghzdbase.hpp"
#define NICE_ONE_COUNT_BIT 72
typedef enum : uint8_t {
NiceFlorSDecoderStepReset = 0,
NiceFlorSDecoderStepCheckHeader,
NiceFlorSDecoderStepFoundHeader,
NiceFlorSDecoderStepSaveDuration,
NiceFlorSDecoderStepCheckDuration,
} NiceFlorSDecoderStep;
class FProtoSubGhzDNiceflors : public FProtoSubGhzDBase {
public:
FProtoSubGhzDNiceflors() {
sensorType = FPS_NICEFLORS;
te_short = 500;
te_long = 1000;
te_delta = 300;
min_count_bit_for_found = 52;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case NiceFlorSDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 38) < te_delta * 38)) {
// Found start header Nice Flor-S
parser_step = NiceFlorSDecoderStepCheckHeader;
}
break;
case NiceFlorSDecoderStepCheckHeader:
if ((level) && (DURATION_DIFF(duration, te_short * 3) < te_delta * 3)) {
// Found next header Nice Flor-S
parser_step = NiceFlorSDecoderStepFoundHeader;
} else {
parser_step = NiceFlorSDecoderStepReset;
}
break;
case NiceFlorSDecoderStepFoundHeader:
if ((!level) && (DURATION_DIFF(duration, te_short * 3) < te_delta * 3)) {
// Found header Nice Flor-S
parser_step = NiceFlorSDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = NiceFlorSDecoderStepReset;
}
break;
case NiceFlorSDecoderStepSaveDuration:
if (level) {
if (DURATION_DIFF(duration, te_short * 3) < te_delta) {
// Found STOP bit
parser_step = NiceFlorSDecoderStepReset;
if ((decode_count_bit == min_count_bit_for_found) || (decode_count_bit == NICE_ONE_COUNT_BIT)) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller-
cnt = SD_NO_CNT;
serial = SD_NO_SERIAL;
btn = SD_NO_BTN;
if (callback) callback(this);
}
break;
} else {
// save interval
te_last = duration;
parser_step = NiceFlorSDecoderStepCheckDuration;
}
}
break;
case NiceFlorSDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = NiceFlorSDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = NiceFlorSDecoderStepSaveDuration;
} else
parser_step = NiceFlorSDecoderStepReset;
} else {
parser_step = NiceFlorSDecoderStepReset;
}
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
decode_data = 0;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,89 @@
#ifndef __FPROTO_PHOENIX_V2_H__
#define __FPROTO_PHOENIX_V2_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
Phoenix_V2DecoderStepReset = 0,
Phoenix_V2DecoderStepFoundStartBit,
Phoenix_V2DecoderStepSaveDuration,
Phoenix_V2DecoderStepCheckDuration,
} Phoenix_V2DecoderStep;
class FProtoSubGhzDPhoenixV2 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDPhoenixV2() {
sensorType = FPS_PHOENIXV2;
te_short = 427;
te_long = 853;
te_delta = 100;
min_count_bit_for_found = 52;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case Phoenix_V2DecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 60) < te_delta * 30)) {
// Found Preambula
parser_step = Phoenix_V2DecoderStepFoundStartBit;
}
break;
case Phoenix_V2DecoderStepFoundStartBit:
if (level && ((DURATION_DIFF(duration, (te_short * 6)) < te_delta * 4))) {
// Found start bit
parser_step = Phoenix_V2DecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = Phoenix_V2DecoderStepReset;
}
break;
case Phoenix_V2DecoderStepSaveDuration:
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
parser_step = Phoenix_V2DecoderStepFoundStartBit;
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit + 4);
serial = data_rev & 0xFFFFFFFF;
cnt = (data_rev >> 40) & 0xFFFF;
btn = (data_rev >> 32) & 0xF;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = Phoenix_V2DecoderStepCheckDuration;
}
}
break;
case Phoenix_V2DecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(1);
parser_step = Phoenix_V2DecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = Phoenix_V2DecoderStepSaveDuration;
} else {
parser_step = Phoenix_V2DecoderStepReset;
}
} else {
parser_step = Phoenix_V2DecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,84 @@
#ifndef __FPROTO_POWER_SMART_H__
#define __FPROTO_POWER_SMART_H__
#include "subghzdbase.hpp"
#define POWER_SMART_PACKET_HEADER 0xFD000000AA000000
#define POWER_SMART_PACKET_HEADER_MASK 0xFF000000FF000000
typedef enum : uint8_t {
PowerSmartDecoderStepReset = 0,
PowerSmartDecoderFoundHeader,
PowerSmartDecoderStepDecoderData,
} PowerSmartDecoderStep;
class FProtoSubGhzDPowerSmart : public FProtoSubGhzDBase {
public:
FProtoSubGhzDPowerSmart() {
sensorType = FPS_POWERSMART;
te_short = 225;
te_long = 450;
te_delta = 100;
min_count_bit_for_found = 64;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
if (!level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortLow;
} else if (
DURATION_DIFF(duration, te_long) < te_delta * 2) {
event = ManchesterEventLongLow;
}
} else {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortHigh;
} else if (
DURATION_DIFF(duration, te_long) < te_delta * 2) {
event = ManchesterEventLongHigh;
}
}
if (event != ManchesterEventReset) {
bool bit_val;
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit_val);
if (data_ok) {
decode_data = (decode_data << 1) | !bit_val;
}
if ((decode_data & POWER_SMART_PACKET_HEADER_MASK) == POWER_SMART_PACKET_HEADER) {
if (subghz_protocol_power_smart_chek_valid(decode_data)) {
data = decode_data;
data_count_bit = min_count_bit_for_found;
// controller
btn = ((data >> 54) & 0x02) | ((data >> 40) & 0x1);
serial = ((data >> 33) & 0x3FFF00) | ((data >> 32) & 0xFF);
cnt = ((data >> 49) & 0x3F);
if (callback) callback(this);
decode_data = 0;
decode_count_bit = 0;
}
}
} else {
decode_data = 0;
decode_count_bit = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
bool subghz_protocol_power_smart_chek_valid(uint64_t packet) {
uint32_t data_1 = (uint32_t)((packet >> 40) & 0xFFFF);
uint32_t data_2 = (uint32_t)((~packet >> 8) & 0xFFFF);
uint8_t data_3 = (uint8_t)(packet >> 32) & 0xFF;
uint8_t data_4 = (uint8_t)(((~packet) & 0xFF) - 1);
return (data_1 == data_2) && (data_3 == data_4);
}
};
#endif

View File

@ -0,0 +1,77 @@
#ifndef __FPROTO_PRINCETON_H__
#define __FPROTO_PRINCETON_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
PrincetonDecoderStepReset = 0,
PrincetonDecoderStepSaveDuration,
PrincetonDecoderStepCheckDuration,
} PrincetonDecoderStep;
class FProtoSubGhzDPrinceton : public FProtoSubGhzDBase {
public:
FProtoSubGhzDPrinceton() {
sensorType = FPS_PRINCETON;
te_short = 390;
te_long = 1170;
te_delta = 300;
min_count_bit_for_found = 24;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case PrincetonDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 36) < te_delta * 36)) {
// Found Preambula
parser_step = PrincetonDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
}
break;
case PrincetonDecoderStepSaveDuration:
// save duration
if (level) {
te_last = duration;
parser_step = PrincetonDecoderStepCheckDuration;
}
break;
case PrincetonDecoderStepCheckDuration:
if (!level) {
if (duration >= ((uint32_t)te_long * 2)) {
parser_step = PrincetonDecoderStepSaveDuration;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
serial = data >> 4;
btn = data & 0xF;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
}
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = PrincetonDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = PrincetonDecoderStepSaveDuration;
} else {
parser_step = PrincetonDecoderStepReset;
}
} else {
parser_step = PrincetonDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,134 @@
#ifndef __FPROTO_SECPLUSV1_H__
#define __FPROTO_SECPLUSV1_H__
#include "subghzdbase.hpp"
#include <string.h>
#define SECPLUS_V1_BIT_ERR -1 // 0b0000
#define SECPLUS_V1_BIT_0 0 // 0b0001
#define SECPLUS_V1_BIT_1 1 // 0b0011
#define SECPLUS_V1_BIT_2 2 // 0b0111
#define SECPLUS_V1_PACKET_1_HEADER 0x00
#define SECPLUS_V1_PACKET_2_HEADER 0x02
#define SECPLUS_V1_PACKET_1_INDEX_BASE 0
#define SECPLUS_V1_PACKET_2_INDEX_BASE 21
#define SECPLUS_V1_PACKET_1_ACCEPTED (1 << 0)
#define SECPLUS_V1_PACKET_2_ACCEPTED (1 << 1)
typedef enum : uint8_t {
SecPlus_v1DecoderStepReset = 0,
SecPlus_v1DecoderStepSearchStartBit,
SecPlus_v1DecoderStepSaveDuration,
SecPlus_v1DecoderStepDecoderData,
} SecPlus_v1DecoderStep;
class FProtoSubGhzDSecPlusV1 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDSecPlusV1() {
sensorType = FPS_SECPLUSV1;
te_short = 500;
te_long = 1500;
te_delta = 100;
min_count_bit_for_found = 21;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case SecPlus_v1DecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 120) < te_delta * 120)) {
// Found header Security+ 1.0
parser_step = SecPlus_v1DecoderStepSearchStartBit;
decode_data = 0;
decode_count_bit = 0;
packet_accepted = 0;
memset(data_array, 0, sizeof(data_array));
}
break;
case SecPlus_v1DecoderStepSearchStartBit:
if (level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
base_packet_index = SECPLUS_V1_PACKET_1_INDEX_BASE;
data_array[decode_count_bit + base_packet_index] = SECPLUS_V1_BIT_0;
decode_count_bit++;
parser_step = SecPlus_v1DecoderStepSaveDuration;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
base_packet_index = SECPLUS_V1_PACKET_2_INDEX_BASE;
data_array[decode_count_bit + base_packet_index] = SECPLUS_V1_BIT_2;
decode_count_bit++;
parser_step = SecPlus_v1DecoderStepSaveDuration;
} else {
parser_step = SecPlus_v1DecoderStepReset;
}
} else {
parser_step = SecPlus_v1DecoderStepReset;
}
break;
case SecPlus_v1DecoderStepSaveDuration:
if (!level) { // save interval
if (DURATION_DIFF(duration, te_short * 120) < te_delta * 120) {
if (decode_count_bit == min_count_bit_for_found) {
if (base_packet_index == SECPLUS_V1_PACKET_1_INDEX_BASE)
packet_accepted |= SECPLUS_V1_PACKET_1_ACCEPTED;
if (base_packet_index == SECPLUS_V1_PACKET_2_INDEX_BASE)
packet_accepted |= SECPLUS_V1_PACKET_2_ACCEPTED;
if (packet_accepted == (SECPLUS_V1_PACKET_1_ACCEPTED | SECPLUS_V1_PACKET_2_ACCEPTED)) {
// subghz_protocol_secplus_v1_decode(); // disabled doe to lack of flash
// controller
// uint32_t fixed = (data >> 32) & 0xFFFFFFFF;
// cnt = data & 0xFFFFFFFF;
// btn = fixed % 3;
if (callback) callback(this);
parser_step = SecPlus_v1DecoderStepReset;
}
}
parser_step = SecPlus_v1DecoderStepSearchStartBit;
decode_data = 0;
decode_count_bit = 0;
} else {
te_last = duration;
parser_step = SecPlus_v1DecoderStepDecoderData;
}
} else {
parser_step = SecPlus_v1DecoderStepReset;
}
break;
case SecPlus_v1DecoderStepDecoderData:
if (level && (decode_count_bit <= min_count_bit_for_found)) {
if ((DURATION_DIFF(te_last, te_short * 3) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
data_array[decode_count_bit + base_packet_index] = SECPLUS_V1_BIT_0;
decode_count_bit++;
parser_step = SecPlus_v1DecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short * 2) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short * 2) < te_delta * 2)) {
data_array[decode_count_bit + base_packet_index] = SECPLUS_V1_BIT_1;
decode_count_bit++;
parser_step = SecPlus_v1DecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short * 3) < te_delta * 3)) {
data_array[decode_count_bit + base_packet_index] = SECPLUS_V1_BIT_2;
decode_count_bit++;
parser_step = SecPlus_v1DecoderStepSaveDuration;
} else {
parser_step = SecPlus_v1DecoderStepReset;
}
} else {
parser_step = SecPlus_v1DecoderStepReset;
}
break;
}
}
protected:
uint8_t packet_accepted = 0;
uint8_t base_packet_index = 0;
uint8_t data_array[44];
};
#endif

View File

@ -0,0 +1,110 @@
#ifndef __FPROTO_SECPLUSV2_H__
#define __FPROTO_SECPLUSV2_H__
#include "subghzdbase.hpp"
#define SECPLUS_V2_HEADER 0x3C0000000000
#define SECPLUS_V2_HEADER_MASK 0xFFFF3C0000000000
#define SECPLUS_V2_PACKET_1 0x000000000000
#define SECPLUS_V2_PACKET_2 0x010000000000
#define SECPLUS_V2_PACKET_MASK 0x30000000000
typedef enum : uint8_t {
SecPlus_v2DecoderStepReset = 0,
SecPlus_v2DecoderStepDecoderData,
} SecPlus_v2DecoderStep;
class FProtoSubGhzDSecPlusV2 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDSecPlusV2() {
sensorType = FPS_SECPLUSV2;
te_short = 250;
te_long = 500;
te_delta = 110;
min_count_bit_for_found = 62;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case SecPlus_v2DecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 130) < te_delta * 100)) {
// Found header Security+ 2.0
parser_step = SecPlus_v2DecoderStepDecoderData;
decode_data = 0;
decode_count_bit = 0;
secplus_packet_1 = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
}
break;
case SecPlus_v2DecoderStepDecoderData:
if (!level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortLow;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongLow;
} else if (
duration >= (te_long * 2UL + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (subghz_protocol_secplus_v2_check_packet()) {
// controller too big
if (callback) callback(this);
parser_step = SecPlus_v2DecoderStepReset;
}
}
decode_data = 0;
decode_count_bit = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
} else {
parser_step = SecPlus_v2DecoderStepReset;
}
} else {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortHigh;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongHigh;
} else {
parser_step = SecPlus_v2DecoderStepReset;
}
}
if (event != ManchesterEventReset) {
bool bit;
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit);
if (data_ok) {
decode_data = (decode_data << 1) | bit;
decode_count_bit++;
}
}
break;
}
}
protected:
uint64_t secplus_packet_1 = 0;
ManchesterState manchester_saved_state = ManchesterStateMid0;
bool subghz_protocol_secplus_v2_check_packet() {
if ((decode_data & SECPLUS_V2_HEADER_MASK) == SECPLUS_V2_HEADER) {
if ((decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_1) {
secplus_packet_1 = decode_data;
} else if (
((decode_data & SECPLUS_V2_PACKET_MASK) == SECPLUS_V2_PACKET_2) &&
(secplus_packet_1)) {
return true;
}
}
return false;
}
};
#endif

View File

@ -0,0 +1,74 @@
#ifndef __FPROTO_SMC5326_H__
#define __FPROTO_SMC5326_H__
#include "subghzdbase.hpp"
typedef enum {
SMC5326DecoderStepReset = 0,
SMC5326DecoderStepSaveDuration,
SMC5326DecoderStepCheckDuration,
} SMC5326DecoderStep;
class FProtoSubGhzDSmc5326 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDSmc5326() {
sensorType = FPS_SECPLUSV2;
te_short = 300;
te_long = 900;
te_delta = 200;
min_count_bit_for_found = 25;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case SMC5326DecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 24) < te_delta * 12)) {
// Found Preambula
parser_step = SMC5326DecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
}
break;
case SMC5326DecoderStepSaveDuration:
// save duration
if (level) {
te_last = duration;
parser_step = SMC5326DecoderStepCheckDuration;
}
break;
case SMC5326DecoderStepCheckDuration:
if (!level) {
if (duration >= ((uint32_t)te_long * 2)) {
parser_step = SMC5326DecoderStepSaveDuration;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
}
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = SMC5326DecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = SMC5326DecoderStepSaveDuration;
} else {
parser_step = SMC5326DecoderStepReset;
}
} else {
parser_step = SMC5326DecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,106 @@
#ifndef __FPROTO_STARLINE_H__
#define __FPROTO_STARLINE_H__
#include "subghzdbase.hpp"
typedef enum {
StarLineDecoderStepReset = 0,
StarLineDecoderStepCheckPreambula,
StarLineDecoderStepSaveDuration,
StarLineDecoderStepCheckDuration,
} StarLineDecoderStep;
class FProtoSubGhzDStarLine : public FProtoSubGhzDBase {
public:
FProtoSubGhzDStarLine() {
sensorType = FPS_STARLINE;
te_short = 250;
te_long = 500;
te_delta = 120;
min_count_bit_for_found = 64;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case StarLineDecoderStepReset:
if (level) {
if (DURATION_DIFF(duration, te_long * 2) < te_delta * 2) {
parser_step = StarLineDecoderStepCheckPreambula;
header_count++;
} else if (header_count > 4) {
decode_data = 0;
decode_count_bit = 0;
te_last = duration;
parser_step = StarLineDecoderStepCheckDuration;
}
} else {
header_count = 0;
}
break;
case StarLineDecoderStepCheckPreambula:
if ((!level) && (DURATION_DIFF(duration, te_long * 2) < te_delta * 2)) {
// Found Preambula
parser_step = StarLineDecoderStepReset;
} else {
header_count = 0;
parser_step = StarLineDecoderStepReset;
}
break;
case StarLineDecoderStepSaveDuration:
if (level) {
if (duration >= (te_long + te_delta)) {
parser_step = StarLineDecoderStepReset;
if ((decode_count_bit >= min_count_bit_for_found) &&
(decode_count_bit <= min_count_bit_for_found + 2)) {
if (data != decode_data) {
data = decode_data;
data_count_bit = min_count_bit_for_found;
if (callback) callback(this);
}
}
decode_data = 0;
decode_count_bit = 0;
header_count = 0;
break;
} else {
te_last = duration;
parser_step = StarLineDecoderStepCheckDuration;
}
} else {
parser_step = StarLineDecoderStepReset;
}
break;
case StarLineDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
if (decode_count_bit < min_count_bit_for_found) {
subghz_protocol_blocks_add_bit(0);
} else {
decode_count_bit++;
}
parser_step = StarLineDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
if (decode_count_bit <
min_count_bit_for_found) {
subghz_protocol_blocks_add_bit(1);
} else {
decode_count_bit++;
}
parser_step = StarLineDecoderStepSaveDuration;
} else {
parser_step = StarLineDecoderStepReset;
}
} else {
parser_step = StarLineDecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,99 @@
#ifndef __FPROTO_X10_H__
#define __FPROTO_X10_H__
#include "subghzdbase.hpp"
typedef enum {
X10DecoderStepReset = 0,
X10DecoderStepFoundPreambula,
X10DecoderStepSaveDuration,
X10DecoderStepCheckDuration,
} X10DecoderStep;
class FProtoSubGhzDX10 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDX10() {
sensorType = FPS_X10;
te_short = 600;
te_long = 1800;
te_delta = 100;
min_count_bit_for_found = 32;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case X10DecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short * 16) < te_delta * 7)) {
parser_step = X10DecoderStepFoundPreambula;
}
break;
case X10DecoderStepFoundPreambula:
if ((!level) && (DURATION_DIFF(duration, te_short * 8) < te_delta * 5)) {
parser_step = X10DecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
decode_data = 0;
decode_count_bit = 0;
parser_step = X10DecoderStepReset;
}
break;
case X10DecoderStepSaveDuration:
if (level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
if (decode_count_bit == min_count_bit_for_found) {
parser_step = X10DecoderStepReset;
if (decode_count_bit >= min_count_bit_for_found &&
((((decode_data >> 24) ^ (decode_data >> 16)) & 0xFF) == 0xFF) &&
((((decode_data >> 8) ^ (decode_data)) & 0xFF) == 0xFF)) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
parser_step = X10DecoderStepReset;
} else {
te_last = duration;
parser_step = X10DecoderStepCheckDuration;
}
} else {
decode_data = 0;
decode_count_bit = 0;
parser_step = X10DecoderStepReset;
}
} else {
decode_data = 0;
decode_count_bit = 0;
parser_step = X10DecoderStepReset;
}
break;
case X10DecoderStepCheckDuration:
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 = X10DecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
subghz_protocol_blocks_add_bit(1);
parser_step = X10DecoderStepSaveDuration;
} else {
decode_data = 0;
decode_count_bit = 0;
parser_step = X10DecoderStepReset;
}
} else {
decode_data = 0;
decode_count_bit = 0;
parser_step = X10DecoderStepReset;
}
break;
}
}
};
#endif

View File

@ -0,0 +1,56 @@
/*
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_SBASE_H__
#define __FPROTO_SBASE_H__
#include "fprotogeneral.hpp"
#include "subghztypes.hpp"
#include <string>
// default values to indicate 'no value'
class FProtoSubGhzDBase;
typedef void (*SubGhzDProtocolDecoderBaseRxCallback)(FProtoSubGhzDBase* instance);
class FProtoSubGhzDBase {
public:
FProtoSubGhzDBase() {}
virtual ~FProtoSubGhzDBase() {}
virtual void feed(bool level, uint32_t duration) = 0; // need to be implemented on each protocol handler.
void setCallback(SubGhzDProtocolDecoderBaseRxCallback cb) { callback = cb; } // this is called when there is a hit.
// General data holder, these will be passed
uint8_t sensorType = FPS_Invalid;
uint8_t btn = SD_NO_BTN;
uint16_t data_count_bit = 0;
uint32_t cnt = SD_NO_CNT;
uint32_t serial = SD_NO_SERIAL;
uint64_t data = 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;
SubGhzDProtocolDecoderBaseRxCallback callback = NULL;
uint16_t header_count = 0;
uint8_t parser_step = 0;
uint32_t te_last = 0;
uint32_t decode_count_bit = 0;
uint64_t decode_data = 0;
//
};
#endif

View File

@ -0,0 +1,132 @@
/*
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 "subghzdbase.hpp"
#include "s-princeton.hpp"
#include "s-bett.hpp"
#include "s-came.hpp"
#include "s-came_atomo.hpp"
#include "s-came_twee.hpp"
#include "s-chambcode.hpp"
#include "s-clemsa.hpp"
#include "s-doitrand.hpp"
#include "s-dooya.hpp"
#include "s-faac.hpp"
#include "s-gate_tx.hpp"
#include "s-holtek.hpp"
#include "s-holtek_ht12x.hpp"
#include "s-honeywell.hpp"
#include "s-honeywellwdb.hpp"
#include "s-hormann.hpp"
#include "s-ido.hpp"
#include "s-intertechnov3.hpp"
#include "s-keeloq.hpp"
#include "s-kinggates_stylo_4k.hpp"
#include "s-linear.hpp"
#include "s-linear_delta3.hpp"
#include "s-magellan.hpp"
#include "s-marantec.hpp"
#include "s-mastercode.hpp"
#include "s-megacode.hpp"
#include "s-neroradio.hpp"
#include "s-nero_sketch.hpp"
#include "s-nice_flo.hpp"
#include "s-nice_flors.hpp"
#include "s-phoenix_v2.hpp"
#include "s-power_smart.hpp"
#include "s-secplus_v1.hpp"
#include "s-secplus_v2.hpp"
#include "s-smc5326.hpp"
#include "s-star_line.hpp"
#include "s-x10.hpp"
// GENIE FROM PR
#ifndef __FPROTO_PROTOLISTSGZ_H__
#define __FPROTO_PROTOLISTSGZ_H__
class SubGhzDProtos : public FProtoListGeneral {
public:
SubGhzDProtos(const SubGhzDProtos&) { SubGhzDProtos(); }; // won't use, but makes compiler happy
SubGhzDProtos& operator=(const SubGhzDProtos&) { return *this; } // won't use, but makes compiler happy
SubGhzDProtos() {
// add protos
protos[FPS_PRINCETON] = new FProtoSubGhzDPrinceton();
protos[FPS_BETT] = new FProtoSubGhzDBett();
protos[FPS_CAME] = new FProtoSubGhzDCame();
protos[FPS_CAMEATOMO] = new FProtoSubGhzDCameAtomo();
protos[FPS_CAMETWEE] = new FProtoSubGhzDCameTwee();
protos[FPS_CHAMBCODE] = new FProtoSubGhzDChambCode();
protos[FPS_CLEMSA] = new FProtoSubGhzDClemsa();
protos[FPS_DOITRAND] = new FProtoSubGhzDDoitrand();
protos[FPS_DOOYA] = new FProtoSubGhzDDooya();
protos[FPS_FAAC] = new FProtoSubGhzDFaac();
protos[FPS_GATETX] = new FProtoSubGhzDGateTx();
protos[FPS_HOLTEK] = new FProtoSubGhzDHoltek();
protos[FPS_HOLTEKHT12X] = new FProtoSubGhzDHoltekHt12x();
protos[FPS_HONEYWELL] = new FProtoSubGhzDHoneywell();
protos[FPS_HONEYWELLWDB] = new FProtoSubGhzDHoneywellWdb();
protos[FPS_HORMANN] = new FProtoSubGhzDHormann();
protos[FPS_IDO] = new FProtoSubGhzDIdo();
protos[FPS_INTERTECHNOV3] = new FProtoSubGhzDIntertechnoV3();
protos[FPS_KEELOQ] = new FProtoSubGhzDKeeLoq();
protos[FPS_KINGGATESSTYLO4K] = new FProtoSubGhzDKinggatesStylo4K();
protos[FPS_LINEAR] = new FProtoSubGhzDLinear();
protos[FPS_LINEARDELTA3] = new FProtoSubGhzDLinearDelta3();
protos[FPS_MAGELLAN] = new FProtoSubGhzDMagellan();
protos[FPS_MARANTEC] = new FProtoSubGhzDMarantec();
protos[FPS_MASTERCODE] = new FProtoSubGhzDMastercode();
protos[FPS_MEGACODE] = new FProtoSubGhzDMegacode();
protos[FPS_NERORADIO] = new FProtoSubGhzDNeroRadio();
protos[FPS_NERO_SKETCH] = new FProtoSubGhzDNeroSketch();
protos[FPS_NICEFLO] = new FProtoSubGhzDNiceflo();
protos[FPS_NICEFLORS] = new FProtoSubGhzDNiceflors();
protos[FPS_PHOENIXV2] = new FProtoSubGhzDPhoenixV2();
protos[FPS_POWERSMART] = new FProtoSubGhzDPowerSmart();
protos[FPS_SECPLUSV1] = new FProtoSubGhzDSecPlusV1();
protos[FPS_SECPLUSV2] = new FProtoSubGhzDSecPlusV2();
protos[FPS_SMC5326] = new FProtoSubGhzDSmc5326();
// somify keytis skipped
// somify telis skipped
protos[FPS_STARLINE] = new FProtoSubGhzDStarLine();
protos[FPS_X10] = new FProtoSubGhzDX10();
// genie skipped
for (uint8_t i = 0; i < FPS_COUNT; ++i) {
if (protos[i] != NULL) protos[i]->setCallback(callbackTarget);
}
}
~SubGhzDProtos() { // not needed for current operation logic, but a bit more elegant :)
for (uint8_t i = 0; i < FPS_COUNT; ++i) {
if (protos[i] != NULL) {
free(protos[i]);
protos[i] = NULL;
}
}
};
static void callbackTarget(FProtoSubGhzDBase* instance) {
SubGhzDDataMessage packet_message{instance->sensorType, instance->btn, instance->data_count_bit, instance->serial, instance->data, instance->cnt};
shared_memory.application_queue.push(packet_message);
}
void feed(bool level, uint32_t duration) {
for (uint8_t i = 0; i < FPS_COUNT; ++i) {
if (protos[i] != NULL) protos[i]->feed(level, duration);
}
}
protected:
FProtoSubGhzDBase* protos[FPS_COUNT] = {NULL};
};
#endif

View File

@ -0,0 +1,63 @@
#ifndef __FPROTO_SUBGHZDTYPES_H__
#define __FPROTO_SUBGHZDTYPES_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
#define SD_NO_SERIAL 0xFFFFFFFF
#define SD_NO_BTN 0xFF
#define SD_NO_CNT 0xFF
enum FPROTO_SUBGHZD_SENSOR : uint8_t {
FPS_Invalid = 0,
FPS_PRINCETON,
FPS_BETT,
FPS_CAME,
FPS_PRASTEL,
FPS_AIRFORCE,
FPS_CAMEATOMO,
FPS_CAMETWEE,
FPS_CHAMBCODE,
FPS_CLEMSA,
FPS_DOITRAND,
FPS_DOOYA,
FPS_FAAC,
FPS_GATETX,
FPS_HOLTEK,
FPS_HOLTEKHT12X,
FPS_HONEYWELL,
FPS_HONEYWELLWDB,
FPS_HORMANN,
FPS_IDO,
FPS_INTERTECHNOV3,
FPS_KEELOQ,
FPS_KINGGATESSTYLO4K,
FPS_LINEAR,
FPS_LINEARDELTA3,
FPS_MAGELLAN,
FPS_MARANTEC,
FPS_MASTERCODE,
FPS_MEGACODE,
FPS_NERORADIO,
FPS_NERO_SKETCH,
FPS_NICEFLO,
FPS_NICEFLORS,
FPS_PHOENIXV2,
FPS_POWERSMART,
FPS_SECPLUSV1,
FPS_SECPLUSV2,
FPS_SMC5326,
FPS_STARLINE,
FPS_X10,
FPS_COUNT
};
#endif

View File

@ -130,9 +130,9 @@ class FProtoWeatherAcurite592TXR : public FProtoWeatherBase {
static_cast<uint8_t>(decode_data >> 16),
static_cast<uint8_t>(decode_data >> 8)};
if ((subghz_protocol_blocks_add_bytes(msg, 6) ==
if ((FProtoGeneral::subghz_protocol_blocks_add_bytes(msg, 6) ==
(uint8_t)(decode_data & 0xFF)) &&
(!subghz_protocol_blocks_parity_bytes(&msg[2], 4))) {
(!FProtoGeneral::subghz_protocol_blocks_parity_bytes(&msg[2], 4))) {
return true;
} else {
return false;

View File

@ -102,7 +102,7 @@ class FProtoWeatherAcurite606TX : public FProtoWeatherBase {
static_cast<uint8_t>(decode_data >> 16),
static_cast<uint8_t>(decode_data >> 8)};
uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1);
uint8_t crc = FProtoGeneral::subghz_protocol_blocks_lfsr_digest8(msg, 3, 0x98, 0xF1);
return (crc == (decode_data & 0xFF));
}
};

View File

@ -108,16 +108,16 @@ class FProtoWeatherAcurite986 : public FProtoWeatherBase {
void ws_protocol_acurite_986_remote_controller() {
int temp;
id = subghz_protocol_blocks_reverse_key(data >> 24, 8);
id = (id << 8) | subghz_protocol_blocks_reverse_key(data >> 16, 8);
id = FProtoGeneral::subghz_protocol_blocks_reverse_key(data >> 24, 8);
id = (id << 8) | FProtoGeneral::subghz_protocol_blocks_reverse_key(data >> 16, 8);
battery_low = (data >> 14) & 1;
channel = ((data >> 15) & 1) + 1;
temp = subghz_protocol_blocks_reverse_key(data >> 32, 8);
temp = FProtoGeneral::subghz_protocol_blocks_reverse_key(data >> 32, 8);
if (temp & 0x80) {
temp = -(temp & 0x7F);
}
temp = locale_fahrenheit_to_celsius((float)temp);
temp = FProtoGeneral::locale_fahrenheit_to_celsius((float)temp);
btn = WS_NO_BTN;
humidity = WS_NO_HUMIDITY;
}
@ -130,7 +130,7 @@ class FProtoWeatherAcurite986 : public FProtoWeatherBase {
(uint8_t)(decode_data >> 16),
(uint8_t)(decode_data >> 8)};
uint8_t crc = subghz_protocol_blocks_crc8(msg, 4, 0x07, 0x00);
uint8_t crc = FProtoGeneral::subghz_protocol_blocks_crc8(msg, 4, 0x07, 0x00);
return (crc == (decode_data & 0xFF));
}
};

View File

@ -32,11 +32,11 @@ class FProtoWeatherAmbient : public FProtoWeatherBase {
}
}
if (event != ManchesterEventReset) {
bool data;
bool data_ok = manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
bool bit;
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit);
if (data_ok) {
decode_data = (decode_data << 1) | !data;
decode_data = (decode_data << 1) | !bit;
}
if (((decode_data & AMBIENT_WEATHER_PACKET_HEADER_MASK) == AMBIENT_WEATHER_PACKET_HEADER_1) ||
@ -53,7 +53,7 @@ class FProtoWeatherAmbient : public FProtoWeatherBase {
} else {
decode_data = 0;
decode_count_bit = 0;
manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
}
}
@ -71,7 +71,7 @@ class FProtoWeatherAmbient : public FProtoWeatherBase {
static_cast<uint8_t>(decode_data >> 16),
static_cast<uint8_t>(decode_data >> 8)};
uint8_t crc = subghz_protocol_blocks_lfsr_digest8(msg, 5, 0x98, 0x3e) ^ 0x64;
uint8_t crc = FProtoGeneral::subghz_protocol_blocks_lfsr_digest8(msg, 5, 0x98, 0x3e) ^ 0x64;
return (crc == (uint8_t)(decode_data & 0xFF));
}
@ -79,7 +79,7 @@ class FProtoWeatherAmbient : public FProtoWeatherBase {
id = (data >> 32) & 0xFF;
battery_low = (data >> 31) & 1;
channel = ((data >> 28) & 0x07) + 1;
temp = locale_fahrenheit_to_celsius(((float)((data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
humidity = (data >> 8) & 0xFF;
btn = WS_NO_BTN;
}

View File

@ -118,19 +118,16 @@ class FProtoWeatherInfactory : public FProtoWeatherBase {
static_cast<uint8_t>(decode_data >> 8),
static_cast<uint8_t>(decode_data)};
uint8_t crc =
subghz_protocol_blocks_crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704
crc ^= msg[4] >> 4; // last nibble is only XORed
uint8_t crc = FProtoGeneral::subghz_protocol_blocks_crc4(msg, 4, 0x13, 0); // Koopmann 0x9, CCITT-4; FP-4; ITU-T G.704
crc ^= msg[4] >> 4; // last nibble is only XORed
return (crc == ((decode_data >> 28) & 0x0F));
}
void ws_protocol_infactory_remote_controller() {
id = data >> 32;
battery_low = (data >> 26) & 1;
btn = WS_NO_BTN;
temp =
locale_fahrenheit_to_celsius(((float)((data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
humidity =
(((data >> 8) & 0x0F) * 10) + ((data >> 4) & 0x0F); // BCD, 'A0'=100%rH
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
humidity = (((data >> 8) & 0x0F) * 10) + ((data >> 4) & 0x0F); // BCD, 'A0'=100%rH
channel = data & 0x03;
}
};

View File

@ -155,7 +155,7 @@ class FProtoWeatherLaCrosseTx : public FProtoWeatherBase {
static_cast<uint8_t>((decode_data >> 8) & 0x0F),
static_cast<uint8_t>((decode_data >> 4) & 0x0F)};
uint8_t crc = subghz_protocol_blocks_add_bytes(msg, 9);
uint8_t crc = FProtoGeneral::subghz_protocol_blocks_add_bytes(msg, 9);
return ((crc & 0x0F) == (decode_data & 0x0F));
}
};

View File

@ -104,7 +104,7 @@ class FProtoWeatherLaCrosseTx141thbv2 : public FProtoWeatherBase {
}
uint8_t msg[] = {static_cast<uint8_t>(data >> 32), static_cast<uint8_t>(data >> 24), static_cast<uint8_t>(data >> 16), static_cast<uint8_t>(data >> 8)};
uint8_t crc = subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4);
uint8_t crc = FProtoGeneral::subghz_protocol_blocks_lfsr_digest8_reflect(msg, 4, 0x31, 0xF4);
return (crc == (data & 0xFF));
}
void ws_protocol_lacrosse_tx141thbv2_remote_controller() {

View File

@ -77,7 +77,7 @@ class FProtoWeatherOregon2 : public FProtoWeatherBase {
decode_data = 0UL;
decode_count_bit = 0;
}
if (manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit_value)) {
if (FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit_value)) {
if (have_bit) {
if (!prev_bit && bit_value) {
subghz_protocol_blocks_add_bit(1);
@ -165,7 +165,7 @@ class FProtoWeatherOregon2 : public FProtoWeatherBase {
parser_step = Oregon2DecoderStepReset;
decode_data = 0UL;
decode_count_bit = 0;
manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
have_bit = false;
var_data = 0;
var_bits = 0;

View File

@ -47,7 +47,7 @@ class FProtoWeatherOregon3 : public FProtoWeatherBase {
decode_data = 0UL;
decode_count_bit = 0;
}
if (manchester_advance(
if (FProtoGeneral::manchester_advance(
manchester_saved_state, event, &manchester_saved_state, &prev_bit)) {
subghz_protocol_blocks_add_bit(prev_bit);
}

View File

@ -64,11 +64,7 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase {
// found all the necessary patterns
decode_data = 0;
decode_count_bit = 1;
manchester_advance(
manchester_saved_state,
ManchesterEventReset,
&manchester_saved_state,
NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
parser_step = Oregon_V1DecoderStepParse;
if (duration < te_short * 4) {
first_bit = 1;
@ -114,22 +110,17 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase {
}
decode_data = 0;
decode_count_bit = 0;
manchester_advance(
manchester_saved_state,
ManchesterEventReset,
&manchester_saved_state,
NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
} else {
parser_step = Oregon_V1DecoderStepReset;
}
}
if (event != ManchesterEventReset) {
bool data;
bool data_ok = manchester_advance(
manchester_saved_state, event, &manchester_saved_state, &data);
bool bit;
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit);
if (data_ok) {
decode_data = (decode_data << 1) | !data;
decode_data = (decode_data << 1) | !bit;
decode_count_bit++;
}
}
@ -149,13 +140,13 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase {
bool ws_protocol_oregon_v1_check() {
if (!decode_data) return false;
uint64_t data = subghz_protocol_blocks_reverse_key(decode_data, 32);
uint64_t data = FProtoGeneral::subghz_protocol_blocks_reverse_key(decode_data, 32);
uint16_t crc = (data & 0xff) + ((data >> 8) & 0xff) + ((data >> 16) & 0xff);
crc = (crc & 0xff) + ((crc >> 8) & 0xff);
return (crc == ((data >> 24) & 0xFF));
}
void ws_protocol_oregon_v1_remote_controller() {
uint64_t data2 = subghz_protocol_blocks_reverse_key(data, 32);
uint64_t data2 = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 32);
id = data2 & 0xFF;
channel = ((data2 >> 6) & 0x03) + 1;

View File

@ -128,7 +128,7 @@ class FProtoWeatherWendoxW6726 : public FProtoWeatherBase {
static_cast<uint8_t>(decode_data >> 12),
static_cast<uint8_t>(decode_data >> 4)};
uint8_t crc = subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD);
uint8_t crc = FProtoGeneral::subghz_protocol_blocks_crc4(msg, 4, 0x9, 0xD);
return (crc == (decode_data & 0x0F));
}
void ws_protocol_wendox_w6726_remote_controller() {

View File

@ -7,33 +7,12 @@ For comments in a protocol implementation check w-nexus-th.hpp
#ifndef __FPROTO_BASE_H__
#define __FPROTO_BASE_H__
#define bit_read(value, bit) (((value) >> (bit)) & 0x01)
#include "fprotogeneral.hpp"
#include "weathertypes.hpp"
#include <string>
// default walues to indicate 'no value'
#define WS_NO_ID 0xFFFFFFFF
#define WS_NO_BATT 0xFF
#define WS_NO_HUMIDITY 0xFF
#define WS_NO_CHANNEL 0xFF
#define WS_NO_BTN 0xFF
#define WS_NO_TEMPERATURE -273.0f
#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
typedef enum {
ManchesterStateStart1 = 0,
ManchesterStateMid1 = 1,
ManchesterStateMid0 = 2,
ManchesterStateStart0 = 3
} ManchesterState;
typedef enum {
ManchesterEventShortLow = 0,
ManchesterEventShortHigh = 2,
ManchesterEventLongLow = 4,
ManchesterEventLongHigh = 6,
ManchesterEventReset = 8
} ManchesterEvent;
class FProtoWeatherBase;
typedef void (*SubGhzProtocolDecoderBaseRxCallback)(FProtoWeatherBase* instance);
@ -49,7 +28,6 @@ class FProtoWeatherBase {
float getTemp() { return temp; }
uint8_t getHumidity() { return humidity; }
uint8_t getBattLow() { return battery_low; }
uint32_t getTimestamp() { return timestamp; }
uint8_t getChannel() { return channel; }
uint8_t getButton() { return btn; }
@ -59,149 +37,6 @@ class FProtoWeatherBase {
decode_data = decode_data << 1 | bit;
decode_count_bit++;
}
uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) {
uint32_t result = 0;
for (size_t i = 0; i < size; ++i) {
result += message[i];
}
return (uint8_t)result;
}
uint8_t subghz_protocol_blocks_parity8(uint8_t byte) {
byte ^= byte >> 4;
byte &= 0xf;
return (0x6996 >> byte) & 1;
}
uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) {
uint8_t result = 0;
for (size_t i = 0; i < size; ++i) {
result ^= subghz_protocol_blocks_parity8(message[i]);
}
return result;
}
uint8_t subghz_protocol_blocks_lfsr_digest8(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key) {
uint8_t sum = 0;
for (size_t byte = 0; byte < size; ++byte) {
uint8_t data = message[byte];
for (int i = 7; i >= 0; --i) {
// XOR key into sum if data bit is set
if ((data >> i) & 1) sum ^= key;
// roll the key right (actually the LSB is dropped here)
// and apply the gen (needs to include the dropped LSB as MSB)
if (key & 1)
key = (key >> 1) ^ gen;
else
key = (key >> 1);
}
}
return sum;
}
float locale_fahrenheit_to_celsius(float temp_f) {
return (temp_f - 32.f) / 1.8f;
}
bool manchester_advance(
ManchesterState state,
ManchesterEvent event,
ManchesterState* next_state,
bool* data) {
bool result = false;
ManchesterState new_state;
if (event == ManchesterEventReset) {
new_state = manchester_reset_state;
} else {
new_state = (ManchesterState)(transitions[state] >> event & 0x3);
if (new_state == state) {
new_state = manchester_reset_state;
} else {
if (new_state == ManchesterStateMid0) {
if (data) *data = false;
result = true;
} else if (new_state == ManchesterStateMid1) {
if (data) *data = true;
result = true;
}
}
}
*next_state = new_state;
return result;
}
uint8_t subghz_protocol_blocks_crc4(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = init << 4; // LSBs are unused
uint8_t poly = polynomial << 4;
uint8_t bit;
while (size--) {
remainder ^= *message++;
for (bit = 0; bit < 8; bit++) {
if (remainder & 0x80) {
remainder = (remainder << 1) ^ poly;
} else {
remainder = (remainder << 1);
}
}
}
return remainder >> 4 & 0x0f; // discard the LSBs
}
uint8_t subghz_protocol_blocks_lfsr_digest8_reflect(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key) {
uint8_t sum = 0;
// Process message from last byte to first byte (reflected)
for (int byte = size - 1; byte >= 0; --byte) {
uint8_t data = message[byte];
// Process individual bits of each byte (reflected)
for (uint8_t i = 0; i < 8; ++i) {
// XOR key into sum if data bit is set
if ((data >> i) & 1) {
sum ^= key;
}
// roll the key left (actually the LSB is dropped here)
// and apply the gen (needs to include the dropped lsb as MSB)
if (key & 0x80)
key = (key << 1) ^ gen;
else
key = (key << 1);
}
}
return sum;
}
uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) {
uint64_t reverse_key = 0;
for (uint8_t i = 0; i < bit_count; i++) {
reverse_key = reverse_key << 1 | bit_read(key, i);
}
return reverse_key;
}
uint8_t subghz_protocol_blocks_crc8(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = init;
for (size_t byte = 0; byte < size; ++byte) {
remainder ^= message[byte];
for (uint8_t bit = 0; bit < 8; ++bit) {
if (remainder & 0x80) {
remainder = (remainder << 1) ^ polynomial;
} else {
remainder = (remainder << 1);
}
}
}
return remainder;
}
// General weather data holder
uint8_t sensorType = FPW_Invalid;
@ -209,11 +44,10 @@ class FProtoWeatherBase {
float temp = WS_NO_TEMPERATURE;
uint8_t humidity = WS_NO_HUMIDITY;
uint8_t battery_low = WS_NO_BATT;
uint32_t timestamp = 0;
uint8_t channel = WS_NO_CHANNEL;
uint8_t btn = WS_NO_BTN;
// inner logic stuff, also for flipper compatibility. //todo revork a bit, so won't have dupes (decode_data + data, ..), but check if any of the protos uses it in the same time or not. (shouldn't)
// inner logic stuff, also for flipper compatibility.
SubGhzProtocolDecoderBaseRxCallback callback = NULL;
uint16_t header_count = 0;
uint8_t parser_step = 0;
@ -223,8 +57,6 @@ class FProtoWeatherBase {
uint64_t decode_data = 0;
uint32_t decode_count_bit = 0;
ManchesterState manchester_saved_state = ManchesterStateMid1;
static const ManchesterState manchester_reset_state = ManchesterStateMid1;
static inline const uint8_t transitions[] = {0b00000001, 0b10010001, 0b10011011, 0b11111011};
};
#endif

View File

@ -3,6 +3,9 @@ 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 "fprotolistgeneral.hpp"
#include "w-nexus-th.hpp"
#include "w-acurite592txr.hpp"
#include "w-acurite606tx.hpp"
@ -26,10 +29,10 @@ So include here the .hpp, and add a new element to the protos vector in the cons
#include <memory>
#include "portapack_shared_memory.hpp"
#ifndef __FPROTO_PROTOLIST_H__
#define __FPROTO_PROTOLIST_H__
#ifndef __FPROTO_PROTOLISTWTH_H__
#define __FPROTO_PROTOLISTWTH_H__
class WeatherProtos {
class WeatherProtos : public FProtoListGeneral {
public:
WeatherProtos() {
// add protos

View File

@ -7,6 +7,12 @@ Define known protocols.
These values must be present on the protocol's constructor, like FProtoWeatherAcurite592TXR() { sensorType = FPW_Acurite592TXR; }
Also it must have a switch-case element in the getWeatherSensorTypeName() function, to display it's name.
*/
#define WS_NO_ID 0xFFFFFFFF
#define WS_NO_BATT 0xFF
#define WS_NO_HUMIDITY 0xFF
#define WS_NO_CHANNEL 0xFF
#define WS_NO_BTN 0xFF
#define WS_NO_TEMPERATURE -273.0f
enum FPROTO_WEATHER_SENSOR {
FPW_Invalid = 0,

View File

@ -0,0 +1,87 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "proc_subghzd.hpp"
#include "portapack_shared_memory.hpp"
#include "event_m4.hpp"
void SubGhzDProcessor::execute(const buffer_c8_t& buffer) {
if (!configured) return;
// SR = 4Mhz , and we are decimating by /8 in total , decim1_out clock 4Mhz /8= 500khz samples/sec.
// buffer has 2048 complex i8 I,Q signed samples
// decim0 out: 2048/4 = 512 complex i16 I,Q signed samples
// decim1 out: 512/2 = 256 complex i16 I,Q signed samples
// Regarding Filters, we are re-using existing FIR filters, @4Mhz, FIR decim1 ilter, BW =+-220Khz (at -3dB's). BW = 440kHZ.
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // Input:2048 complex/4 (decim factor) = 512_output complex (1024 I/Q samples)
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // Input:512 complex/2 (decim factor) = 256_output complex ( 512 I/Q samples)
for (size_t i = 0; i < decim_1_out.count; i++) {
int16_t re = decim_1_out.p[i].real();
int16_t im = decim_1_out.p[i].imag();
uint32_t mag = ((uint32_t)re * (uint32_t)re) + ((uint32_t)im * (uint32_t)im);
mag = (mag >> 12); // Decim samples are calculated with saturated gain . (we could also reduce that sat. param at configure time)
bool meashl = (mag > threshold);
tm += mag;
if (meashl == currentHiLow && currentDuration < 30'000'000) // allow pass 'end' signal
{
if (currentDuration < UINT32_MAX) currentDuration += nsPerDecSamp;
} else { // called on change, so send the last duration and dir.
if (protoList) protoList->feed(currentHiLow, currentDuration / 1000);
currentDuration = nsPerDecSamp;
currentHiLow = meashl;
}
}
cnt += decim_1_out.count; // TODO , check if it is necessary that xdecim factor.
if (cnt > 90'000) {
threshold = (tm / cnt) / 2;
cnt = 0;
tm = 0;
if (threshold < 50) threshold = 50;
if (threshold > 1700) threshold = 1700;
}
}
void SubGhzDProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::SubGhzFPRxConfigure)
configure(*reinterpret_cast<const SubGhzFPRxConfigureMessage*>(message));
}
void SubGhzDProcessor::configure(const SubGhzFPRxConfigureMessage& message) {
// constexpr size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor; //unused
// constexpr size_t decim_1_output_fs = decim_0_output_fs / decim_1.decimation_factor; //unused
decim_0.configure(taps_200k_wfm_decim_0.taps);
decim_1.configure(taps_200k_wfm_decim_1.taps);
configured = true;
}
int main() {
EventDispatcher event_dispatcher{std::make_unique<SubGhzDProcessor>()};
event_dispatcher.run();
return 0;
}

View File

@ -0,0 +1,73 @@
/*
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
/*
Creator: @htotoo
*/
#ifndef __PROC_WEATHER_H__
#define __PROC_WEATHER_H__
#include "baseband_processor.hpp"
#include "baseband_thread.hpp"
#include "rssi_thread.hpp"
#include "message.hpp"
#include "dsp_decimate.hpp"
#include "fprotos/subghzdprotos.hpp"
class SubGhzDProcessor : public BasebandProcessor {
public:
void execute(const buffer_c8_t& buffer) override;
void on_message(const Message* const message) override;
private:
static constexpr size_t baseband_fs = 4'000'000; // it works, I think we need to write that master clock in the baseband_threat , even later we decimate it.
static constexpr uint32_t nsPerDecSamp = 1'000'000'000 / baseband_fs * 8; // 10 exp9/baseband_fs * 8
/* Array Buffer aux. used in decim0 and decim1 IQ c16 signed data ; (decim0 defines the max length of the array) */
std::array<complex16_t, 512> dst{}; // decim0 /4 , 2048/4 = 512 complex I,Q
const buffer_c16_t dst_buffer{
dst.data(),
dst.size()};
/* Decimates */
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{};
dsp::decimate::FIRC16xR16x16Decim2 decim_1{};
uint32_t currentDuration = 0;
uint32_t threshold = 0x0630; // will overwrite after the first iteration
bool currentHiLow = false;
bool configured{false};
// for threshold
uint32_t cnt = 0;
uint32_t tm = 0;
FProtoListGeneral* protoList = new SubGhzDProtos(); // holds all the protocols we can parse
void configure(const SubGhzFPRxConfigureMessage& message);
/* NB: Threads should be the last members in the class definition. */
BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive};
RSSIThread rssi_thread{};
};
#endif /*__PROC_WEATHER_H__*/

View File

@ -45,18 +45,18 @@ void WeatherProcessor::execute(const buffer_c8_t& buffer) {
bool meashl = (mag > threshold);
tm += mag;
if (meashl == currentHiLow && currentDuration < 10'000'000) // allow pass 'end' signal
if (meashl == currentHiLow && currentDuration < 30'000'000) // allow pass 'end' signal
{
if (currentDuration < UINT32_MAX) currentDuration += nsPerDecSamp;
} else { // called on change, so send the last duration and dir.
protoList.feed(currentHiLow, currentDuration / 1000);
if (protoList) protoList->feed(currentHiLow, currentDuration / 1000);
currentDuration = nsPerDecSamp;
currentHiLow = meashl;
}
}
cnt += decim_1_out.count; // TODO , check if it is necessary that xdecim factor.
if (cnt > 30'000) {
if (cnt > 90'000) {
threshold = (tm / cnt) / 2;
cnt = 0;
tm = 0;
@ -66,19 +66,18 @@ void WeatherProcessor::execute(const buffer_c8_t& buffer) {
}
void WeatherProcessor::on_message(const Message* const message) {
if (message->id == Message::ID::WeatherRxConfigure)
configure(*reinterpret_cast<const WeatherRxConfigureMessage*>(message));
if (message->id == Message::ID::SubGhzFPRxConfigure)
configure(*reinterpret_cast<const SubGhzFPRxConfigureMessage*>(message));
}
void WeatherProcessor::configure(const WeatherRxConfigureMessage& message) {
// unused:
// constexpr size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor;
// constexpr size_t decim_1_output_fs = decim_0_output_fs / decim_1.decimation_factor;
void WeatherProcessor::configure(const SubGhzFPRxConfigureMessage& message) {
// constexpr size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor; //unused
// constexpr size_t decim_1_output_fs = decim_0_output_fs / decim_1.decimation_factor; //unused
(void)message; // unused
decim_0.configure(taps_200k_wfm_decim_0.taps);
decim_1.configure(taps_200k_wfm_decim_1.taps);
(void)message;
configured = true;
}

View File

@ -58,12 +58,14 @@ class WeatherProcessor : public BasebandProcessor {
bool currentHiLow = false;
bool configured{false};
// for debug
uint8_t modulation = 255; // 0 AM, 1 FM 255 = Not set
uint8_t protoMode = 255; // 0 weather, 1 subghzd, 255 = Not set
// for threshold
uint32_t cnt = 0;
uint32_t tm = 0;
WeatherProtos protoList{}; // holds all the protocols we can parse
void configure(const WeatherRxConfigureMessage& message);
FProtoListGeneral* protoList = new WeatherProtos(); // holds all the protocols we can parse
void configure(const SubGhzFPRxConfigureMessage& message);
/* NB: Threads should be the last members in the class definition. */
BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive};

View File

@ -115,8 +115,9 @@ class Message {
FSKRxConfigure = 58,
BlePacket = 58,
BTLETxConfigure = 59,
WeatherRxConfigure = 60,
SubGhzFPRxConfigure = 60,
WeatherData = 61,
SubGhzDData = 62,
MAX
};
@ -1238,11 +1239,12 @@ class SpectrumPainterBufferConfigureResponseMessage : public Message {
SpectrumPainterFIFO* fifo{nullptr};
};
class WeatherRxConfigureMessage : public Message {
class SubGhzFPRxConfigureMessage : public Message {
public:
constexpr WeatherRxConfigureMessage()
: Message{ID::WeatherRxConfigure} {
constexpr SubGhzFPRxConfigureMessage(uint8_t modulation = 0)
: Message{ID::SubGhzFPRxConfigure}, modulation{modulation} {
}
uint8_t modulation = 0; // 0 am, 1 fm
};
class WeatherDataMessage : public Message {
@ -1273,4 +1275,29 @@ class WeatherDataMessage : public Message {
uint8_t btn = 0xFF;
};
class SubGhzDDataMessage : public Message {
public:
constexpr SubGhzDDataMessage(
uint8_t sensorType = 0,
uint8_t btn = 0xFF,
uint16_t bits = 0,
uint32_t serial = 0xFFFFFFFF,
uint64_t data = 0,
uint32_t cnt = 0xFF)
: Message{ID::SubGhzDData},
sensorType{sensorType},
btn{btn},
bits{bits},
serial{serial},
cnt{cnt},
data{data} {
}
uint8_t sensorType = 0;
uint8_t btn = 0xFF;
uint16_t bits = 0;
uint32_t serial = 0xFFFFFFFF;
uint32_t cnt = 0xFF;
uint64_t data = 0;
};
#endif /*__MESSAGE_H__*/

View File

@ -113,6 +113,7 @@ constexpr image_tag_t image_tag_flash_utility{'P', 'F', 'U', 'T'};
constexpr image_tag_t image_tag_usb_sd{'P', 'U', 'S', 'B'};
constexpr image_tag_t image_tag_weather{'P', 'W', 'T', 'H'};
constexpr image_tag_t image_tag_subghzd{'P', 'S', 'G', 'D'};
constexpr image_tag_t image_tag_noop{'P', 'N', 'O', 'P'};