From 2ccda5aebd55662e8b05bd762d1c81c8bf8af152 Mon Sep 17 00:00:00 2001 From: Totoo Date: Sat, 16 Dec 2023 23:37:51 +0100 Subject: [PATCH] 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 8a84aac2f59274b72de7c7803deb137a21838076. * 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 --- firmware/application/CMakeLists.txt | 1 + firmware/application/apps/ui_subghzd.cpp | 243 ++++++++++++++++++ firmware/application/apps/ui_subghzd.hpp | 171 ++++++++++++ .../application/apps/ui_weatherstation.cpp | 29 ++- .../application/apps/ui_weatherstation.hpp | 12 +- firmware/application/baseband_api.cpp | 7 +- firmware/application/baseband_api.hpp | 1 + firmware/application/ui_navigation.cpp | 2 + firmware/baseband/CMakeLists.txt | 9 +- firmware/baseband/fprotos/fprotogeneral.hpp | 200 ++++++++++++++ .../baseband/fprotos/fprotolistgeneral.hpp | 16 ++ firmware/baseband/fprotos/s-bett.hpp | 83 ++++++ firmware/baseband/fprotos/s-came.hpp | 95 +++++++ firmware/baseband/fprotos/s-came_atomo.hpp | 133 ++++++++++ firmware/baseband/fprotos/s-came_twee.hpp | 147 +++++++++++ firmware/baseband/fprotos/s-chambcode.hpp | 151 +++++++++++ firmware/baseband/fprotos/s-clemsa.hpp | 89 +++++++ firmware/baseband/fprotos/s-doitrand.hpp | 86 +++++++ firmware/baseband/fprotos/s-dooya.hpp | 111 ++++++++ firmware/baseband/fprotos/s-faac.hpp | 85 ++++++ firmware/baseband/fprotos/s-gate_tx.hpp | 88 +++++++ firmware/baseband/fprotos/s-holtek.hpp | 107 ++++++++ firmware/baseband/fprotos/s-holtek_ht12x.hpp | 90 +++++++ firmware/baseband/fprotos/s-honeywell.hpp | 108 ++++++++ firmware/baseband/fprotos/s-honeywellwdb.hpp | 74 ++++++ firmware/baseband/fprotos/s-hormann.hpp | 85 ++++++ firmware/baseband/fprotos/s-ido.hpp | 91 +++++++ firmware/baseband/fprotos/s-intertechnov3.hpp | 148 +++++++++++ firmware/baseband/fprotos/s-keeloq.hpp | 107 ++++++++ .../baseband/fprotos/s-kinggates_stylo_4k.hpp | 124 +++++++++ firmware/baseband/fprotos/s-linear.hpp | 88 +++++++ firmware/baseband/fprotos/s-linear_delta3.hpp | 87 +++++++ firmware/baseband/fprotos/s-magellan.hpp | 139 ++++++++++ firmware/baseband/fprotos/s-marantec.hpp | 86 +++++++ firmware/baseband/fprotos/s-mastercode.hpp | 86 +++++++ firmware/baseband/fprotos/s-megacode.hpp | 104 ++++++++ firmware/baseband/fprotos/s-nero_sketch.hpp | 108 ++++++++ firmware/baseband/fprotos/s-neroradio.hpp | 115 +++++++++ firmware/baseband/fprotos/s-nice_flo.hpp | 86 +++++++ firmware/baseband/fprotos/s-nice_flors.hpp | 100 +++++++ firmware/baseband/fprotos/s-phoenix_v2.hpp | 89 +++++++ firmware/baseband/fprotos/s-power_smart.hpp | 84 ++++++ firmware/baseband/fprotos/s-princeton.hpp | 77 ++++++ firmware/baseband/fprotos/s-secplus_v1.hpp | 134 ++++++++++ firmware/baseband/fprotos/s-secplus_v2.hpp | 110 ++++++++ firmware/baseband/fprotos/s-smc5326.hpp | 74 ++++++ firmware/baseband/fprotos/s-star_line.hpp | 106 ++++++++ firmware/baseband/fprotos/s-x10.hpp | 99 +++++++ firmware/baseband/fprotos/subghzdbase.hpp | 56 ++++ firmware/baseband/fprotos/subghzdprotos.hpp | 132 ++++++++++ firmware/baseband/fprotos/subghztypes.hpp | 63 +++++ firmware/baseband/fprotos/w-acurite592txr.hpp | 4 +- firmware/baseband/fprotos/w-acurite606tx.hpp | 2 +- firmware/baseband/fprotos/w-acurite986.hpp | 10 +- firmware/baseband/fprotos/w-ambient.hpp | 12 +- firmware/baseband/fprotos/w-infactory.hpp | 11 +- firmware/baseband/fprotos/w-lacrosse-tx.hpp | 2 +- .../fprotos/w-lacrosse-tx141thbv2.hpp | 2 +- firmware/baseband/fprotos/w-oregon2.hpp | 4 +- firmware/baseband/fprotos/w-oregon3.hpp | 2 +- firmware/baseband/fprotos/w-oregonv1.hpp | 23 +- firmware/baseband/fprotos/w-wendox-w6726.hpp | 2 +- firmware/baseband/fprotos/weatherbase.hpp | 172 +------------ firmware/baseband/fprotos/weatherprotos.hpp | 9 +- firmware/baseband/fprotos/weathertypes.hpp | 6 + firmware/baseband/proc_subghzd.cpp | 87 +++++++ firmware/baseband/proc_subghzd.hpp | 73 ++++++ firmware/baseband/proc_weather.cpp | 19 +- firmware/baseband/proc_weather.hpp | 8 +- firmware/common/message.hpp | 35 ++- firmware/common/spi_image.hpp | 1 + 71 files changed, 4952 insertions(+), 248 deletions(-) create mode 100644 firmware/application/apps/ui_subghzd.cpp create mode 100644 firmware/application/apps/ui_subghzd.hpp create mode 100644 firmware/baseband/fprotos/fprotogeneral.hpp create mode 100644 firmware/baseband/fprotos/fprotolistgeneral.hpp create mode 100644 firmware/baseband/fprotos/s-bett.hpp create mode 100644 firmware/baseband/fprotos/s-came.hpp create mode 100644 firmware/baseband/fprotos/s-came_atomo.hpp create mode 100644 firmware/baseband/fprotos/s-came_twee.hpp create mode 100644 firmware/baseband/fprotos/s-chambcode.hpp create mode 100644 firmware/baseband/fprotos/s-clemsa.hpp create mode 100644 firmware/baseband/fprotos/s-doitrand.hpp create mode 100644 firmware/baseband/fprotos/s-dooya.hpp create mode 100644 firmware/baseband/fprotos/s-faac.hpp create mode 100644 firmware/baseband/fprotos/s-gate_tx.hpp create mode 100644 firmware/baseband/fprotos/s-holtek.hpp create mode 100644 firmware/baseband/fprotos/s-holtek_ht12x.hpp create mode 100644 firmware/baseband/fprotos/s-honeywell.hpp create mode 100644 firmware/baseband/fprotos/s-honeywellwdb.hpp create mode 100644 firmware/baseband/fprotos/s-hormann.hpp create mode 100644 firmware/baseband/fprotos/s-ido.hpp create mode 100644 firmware/baseband/fprotos/s-intertechnov3.hpp create mode 100644 firmware/baseband/fprotos/s-keeloq.hpp create mode 100644 firmware/baseband/fprotos/s-kinggates_stylo_4k.hpp create mode 100644 firmware/baseband/fprotos/s-linear.hpp create mode 100644 firmware/baseband/fprotos/s-linear_delta3.hpp create mode 100644 firmware/baseband/fprotos/s-magellan.hpp create mode 100644 firmware/baseband/fprotos/s-marantec.hpp create mode 100644 firmware/baseband/fprotos/s-mastercode.hpp create mode 100644 firmware/baseband/fprotos/s-megacode.hpp create mode 100644 firmware/baseband/fprotos/s-nero_sketch.hpp create mode 100644 firmware/baseband/fprotos/s-neroradio.hpp create mode 100644 firmware/baseband/fprotos/s-nice_flo.hpp create mode 100644 firmware/baseband/fprotos/s-nice_flors.hpp create mode 100644 firmware/baseband/fprotos/s-phoenix_v2.hpp create mode 100644 firmware/baseband/fprotos/s-power_smart.hpp create mode 100644 firmware/baseband/fprotos/s-princeton.hpp create mode 100644 firmware/baseband/fprotos/s-secplus_v1.hpp create mode 100644 firmware/baseband/fprotos/s-secplus_v2.hpp create mode 100644 firmware/baseband/fprotos/s-smc5326.hpp create mode 100644 firmware/baseband/fprotos/s-star_line.hpp create mode 100644 firmware/baseband/fprotos/s-x10.hpp create mode 100644 firmware/baseband/fprotos/subghzdbase.hpp create mode 100644 firmware/baseband/fprotos/subghzdprotos.hpp create mode 100644 firmware/baseband/fprotos/subghztypes.hpp create mode 100644 firmware/baseband/proc_subghzd.cpp create mode 100644 firmware/baseband/proc_subghzd.hpp diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index a7dcaecd..b93efc02 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -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 diff --git a/firmware/application/apps/ui_subghzd.cpp b/firmware/application/apps/ui_subghzd.cpp new file mode 100644 index 00000000..3a150deb --- /dev/null +++ b/firmware/application/apps/ui_subghzd.cpp @@ -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(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::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 \ No newline at end of file diff --git a/firmware/application/apps/ui_subghzd.hpp b/firmware/application/apps/ui_subghzd.hpp new file mode 100644 index 00000000..d26ce67e --- /dev/null +++ b/firmware/application/apps/ui_subghzd.hpp @@ -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(serial) << 32) | (static_cast(sensorType) & 0xFF) << 0)); + } + void inc_age(int delta) { + if (UINT16_MAX - delta > age) age += delta; + } + void reset_age() { + age = 0; + } +}; +using SubGhzDRecentEntries = RecentEntries; +using SubGhzDRecentEntriesView = RecentEntriesView; + +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(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__*/ diff --git a/firmware/application/apps/ui_weatherstation.cpp b/firmware/application/apps/ui_weatherstation.cpp index 5b573df8..9d502ab9 100644 --- a/firmware/application/apps/ui_weatherstation.cpp +++ b/firmware/application/apps/ui_weatherstation.cpp @@ -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::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; diff --git a/firmware/application/apps/ui_weatherstation.hpp b/firmware/application/apps/ui_weatherstation.hpp index 36807212..816e1f25 100644 --- a/firmware/application/apps/ui_weatherstation.hpp +++ b/firmware/application/apps/ui_weatherstation.hpp @@ -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}, diff --git a/firmware/application/baseband_api.cpp b/firmware/application/baseband_api.cpp index 31adcc08..7317ab0c 100644 --- a/firmware/application/baseband_api.cpp +++ b/firmware/application/baseband_api.cpp @@ -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); } diff --git a/firmware/application/baseband_api.hpp b/firmware/application/baseband_api.hpp index 6979bfb8..9963ef5c 100644 --- a/firmware/application/baseband_api.hpp +++ b/firmware/application/baseband_api.hpp @@ -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); diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 921204b1..8bd21b75 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -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(); }}, {"TPMS Cars", Color::green(), &bitmap_icon_tpms, [&nav]() { nav.push(); }}, {"Weather", Color::green(), &bitmap_icon_thermometer, [&nav]() { nav.push(); }}, + {"SubGhzD", Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push(); }}, // {"FSK RX", Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push(); }}, // {"DMR", Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push(); }}, // {"SIGFOX", Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push(); }}, diff --git a/firmware/baseband/CMakeLists.txt b/firmware/baseband/CMakeLists.txt index dff2da68..65051262 100644 --- a/firmware/baseband/CMakeLists.txt +++ b/firmware/baseband/CMakeLists.txt @@ -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 ) diff --git a/firmware/baseband/fprotos/fprotogeneral.hpp b/firmware/baseband/fprotos/fprotogeneral.hpp new file mode 100644 index 00000000..09708b39 --- /dev/null +++ b/firmware/baseband/fprotos/fprotogeneral.hpp @@ -0,0 +1,200 @@ +#ifndef __FPROTO_GENERAL_H__ +#define __FPROTO_GENERAL_H__ + +// useful methods for both weather and subghzd + +#include +#include + +#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 \ No newline at end of file diff --git a/firmware/baseband/fprotos/fprotolistgeneral.hpp b/firmware/baseband/fprotos/fprotolistgeneral.hpp new file mode 100644 index 00000000..14bbee34 --- /dev/null +++ b/firmware/baseband/fprotos/fprotolistgeneral.hpp @@ -0,0 +1,16 @@ +#ifndef __FPROTO_PROTOLISTGENERAL_H__ +#define __FPROTO_PROTOLISTGENERAL_H__ +#include + +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 \ No newline at end of file diff --git a/firmware/baseband/fprotos/s-bett.hpp b/firmware/baseband/fprotos/s-bett.hpp new file mode 100644 index 00000000..f5dac0cb --- /dev/null +++ b/firmware/baseband/fprotos/s-bett.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-came.hpp b/firmware/baseband/fprotos/s-came.hpp new file mode 100644 index 00000000..9f6b59c0 --- /dev/null +++ b/firmware/baseband/fprotos/s-came.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-came_atomo.hpp b/firmware/baseband/fprotos/s-came_atomo.hpp new file mode 100644 index 00000000..96f71f72 --- /dev/null +++ b/firmware/baseband/fprotos/s-came_atomo.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-came_twee.hpp b/firmware/baseband/fprotos/s-came_twee.hpp new file mode 100644 index 00000000..92c938ad --- /dev/null +++ b/firmware/baseband/fprotos/s-came_twee.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-chambcode.hpp b/firmware/baseband/fprotos/s-chambcode.hpp new file mode 100644 index 00000000..c106c4a2 --- /dev/null +++ b/firmware/baseband/fprotos/s-chambcode.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-clemsa.hpp b/firmware/baseband/fprotos/s-clemsa.hpp new file mode 100644 index 00000000..118fe39f --- /dev/null +++ b/firmware/baseband/fprotos/s-clemsa.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-doitrand.hpp b/firmware/baseband/fprotos/s-doitrand.hpp new file mode 100644 index 00000000..843bfed8 --- /dev/null +++ b/firmware/baseband/fprotos/s-doitrand.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-dooya.hpp b/firmware/baseband/fprotos/s-dooya.hpp new file mode 100644 index 00000000..0c51c2a8 --- /dev/null +++ b/firmware/baseband/fprotos/s-dooya.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-faac.hpp b/firmware/baseband/fprotos/s-faac.hpp new file mode 100644 index 00000000..cefe020e --- /dev/null +++ b/firmware/baseband/fprotos/s-faac.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-gate_tx.hpp b/firmware/baseband/fprotos/s-gate_tx.hpp new file mode 100644 index 00000000..6db6027d --- /dev/null +++ b/firmware/baseband/fprotos/s-gate_tx.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-holtek.hpp b/firmware/baseband/fprotos/s-holtek.hpp new file mode 100644 index 00000000..2cba2209 --- /dev/null +++ b/firmware/baseband/fprotos/s-holtek.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-holtek_ht12x.hpp b/firmware/baseband/fprotos/s-holtek_ht12x.hpp new file mode 100644 index 00000000..24c1f870 --- /dev/null +++ b/firmware/baseband/fprotos/s-holtek_ht12x.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-honeywell.hpp b/firmware/baseband/fprotos/s-honeywell.hpp new file mode 100644 index 00000000..d106d6ad --- /dev/null +++ b/firmware/baseband/fprotos/s-honeywell.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-honeywellwdb.hpp b/firmware/baseband/fprotos/s-honeywellwdb.hpp new file mode 100644 index 00000000..cfb4a455 --- /dev/null +++ b/firmware/baseband/fprotos/s-honeywellwdb.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-hormann.hpp b/firmware/baseband/fprotos/s-hormann.hpp new file mode 100644 index 00000000..f43a7f51 --- /dev/null +++ b/firmware/baseband/fprotos/s-hormann.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-ido.hpp b/firmware/baseband/fprotos/s-ido.hpp new file mode 100644 index 00000000..59bfa3fb --- /dev/null +++ b/firmware/baseband/fprotos/s-ido.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-intertechnov3.hpp b/firmware/baseband/fprotos/s-intertechnov3.hpp new file mode 100644 index 00000000..63552b46 --- /dev/null +++ b/firmware/baseband/fprotos/s-intertechnov3.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-keeloq.hpp b/firmware/baseband/fprotos/s-keeloq.hpp new file mode 100644 index 00000000..9e09a3c3 --- /dev/null +++ b/firmware/baseband/fprotos/s-keeloq.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-kinggates_stylo_4k.hpp b/firmware/baseband/fprotos/s-kinggates_stylo_4k.hpp new file mode 100644 index 00000000..4122dc17 --- /dev/null +++ b/firmware/baseband/fprotos/s-kinggates_stylo_4k.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-linear.hpp b/firmware/baseband/fprotos/s-linear.hpp new file mode 100644 index 00000000..c5432761 --- /dev/null +++ b/firmware/baseband/fprotos/s-linear.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-linear_delta3.hpp b/firmware/baseband/fprotos/s-linear_delta3.hpp new file mode 100644 index 00000000..96aa09eb --- /dev/null +++ b/firmware/baseband/fprotos/s-linear_delta3.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-magellan.hpp b/firmware/baseband/fprotos/s-magellan.hpp new file mode 100644 index 00000000..1d997aea --- /dev/null +++ b/firmware/baseband/fprotos/s-magellan.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-marantec.hpp b/firmware/baseband/fprotos/s-marantec.hpp new file mode 100644 index 00000000..b41f02b0 --- /dev/null +++ b/firmware/baseband/fprotos/s-marantec.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-mastercode.hpp b/firmware/baseband/fprotos/s-mastercode.hpp new file mode 100644 index 00000000..6d836557 --- /dev/null +++ b/firmware/baseband/fprotos/s-mastercode.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-megacode.hpp b/firmware/baseband/fprotos/s-megacode.hpp new file mode 100644 index 00000000..14fb7c6a --- /dev/null +++ b/firmware/baseband/fprotos/s-megacode.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-nero_sketch.hpp b/firmware/baseband/fprotos/s-nero_sketch.hpp new file mode 100644 index 00000000..07b438ed --- /dev/null +++ b/firmware/baseband/fprotos/s-nero_sketch.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-neroradio.hpp b/firmware/baseband/fprotos/s-neroradio.hpp new file mode 100644 index 00000000..ddf674fd --- /dev/null +++ b/firmware/baseband/fprotos/s-neroradio.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-nice_flo.hpp b/firmware/baseband/fprotos/s-nice_flo.hpp new file mode 100644 index 00000000..ec14771f --- /dev/null +++ b/firmware/baseband/fprotos/s-nice_flo.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-nice_flors.hpp b/firmware/baseband/fprotos/s-nice_flors.hpp new file mode 100644 index 00000000..bf70f55b --- /dev/null +++ b/firmware/baseband/fprotos/s-nice_flors.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-phoenix_v2.hpp b/firmware/baseband/fprotos/s-phoenix_v2.hpp new file mode 100644 index 00000000..3c7e4214 --- /dev/null +++ b/firmware/baseband/fprotos/s-phoenix_v2.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-power_smart.hpp b/firmware/baseband/fprotos/s-power_smart.hpp new file mode 100644 index 00000000..05940e8c --- /dev/null +++ b/firmware/baseband/fprotos/s-power_smart.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-princeton.hpp b/firmware/baseband/fprotos/s-princeton.hpp new file mode 100644 index 00000000..9cdf425c --- /dev/null +++ b/firmware/baseband/fprotos/s-princeton.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-secplus_v1.hpp b/firmware/baseband/fprotos/s-secplus_v1.hpp new file mode 100644 index 00000000..98413486 --- /dev/null +++ b/firmware/baseband/fprotos/s-secplus_v1.hpp @@ -0,0 +1,134 @@ + +#ifndef __FPROTO_SECPLUSV1_H__ +#define __FPROTO_SECPLUSV1_H__ + +#include "subghzdbase.hpp" +#include + +#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 diff --git a/firmware/baseband/fprotos/s-secplus_v2.hpp b/firmware/baseband/fprotos/s-secplus_v2.hpp new file mode 100644 index 00000000..41bc2048 --- /dev/null +++ b/firmware/baseband/fprotos/s-secplus_v2.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-smc5326.hpp b/firmware/baseband/fprotos/s-smc5326.hpp new file mode 100644 index 00000000..c5b9ebf7 --- /dev/null +++ b/firmware/baseband/fprotos/s-smc5326.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-star_line.hpp b/firmware/baseband/fprotos/s-star_line.hpp new file mode 100644 index 00000000..9103edf4 --- /dev/null +++ b/firmware/baseband/fprotos/s-star_line.hpp @@ -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 diff --git a/firmware/baseband/fprotos/s-x10.hpp b/firmware/baseband/fprotos/s-x10.hpp new file mode 100644 index 00000000..7acaa3a3 --- /dev/null +++ b/firmware/baseband/fprotos/s-x10.hpp @@ -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 diff --git a/firmware/baseband/fprotos/subghzdbase.hpp b/firmware/baseband/fprotos/subghzdbase.hpp new file mode 100644 index 00000000..bc374d9f --- /dev/null +++ b/firmware/baseband/fprotos/subghzdbase.hpp @@ -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 +// 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 \ No newline at end of file diff --git a/firmware/baseband/fprotos/subghzdprotos.hpp b/firmware/baseband/fprotos/subghzdprotos.hpp new file mode 100644 index 00000000..caeb043b --- /dev/null +++ b/firmware/baseband/fprotos/subghzdprotos.hpp @@ -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 +#include +#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 diff --git a/firmware/baseband/fprotos/subghztypes.hpp b/firmware/baseband/fprotos/subghztypes.hpp new file mode 100644 index 00000000..872fc22e --- /dev/null +++ b/firmware/baseband/fprotos/subghztypes.hpp @@ -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 diff --git a/firmware/baseband/fprotos/w-acurite592txr.hpp b/firmware/baseband/fprotos/w-acurite592txr.hpp index e9747210..5727b710 100644 --- a/firmware/baseband/fprotos/w-acurite592txr.hpp +++ b/firmware/baseband/fprotos/w-acurite592txr.hpp @@ -130,9 +130,9 @@ class FProtoWeatherAcurite592TXR : public FProtoWeatherBase { static_cast(decode_data >> 16), static_cast(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; diff --git a/firmware/baseband/fprotos/w-acurite606tx.hpp b/firmware/baseband/fprotos/w-acurite606tx.hpp index ba1cdee2..60584b27 100644 --- a/firmware/baseband/fprotos/w-acurite606tx.hpp +++ b/firmware/baseband/fprotos/w-acurite606tx.hpp @@ -102,7 +102,7 @@ class FProtoWeatherAcurite606TX : public FProtoWeatherBase { static_cast(decode_data >> 16), static_cast(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)); } }; diff --git a/firmware/baseband/fprotos/w-acurite986.hpp b/firmware/baseband/fprotos/w-acurite986.hpp index 24dce719..3e4ba996 100644 --- a/firmware/baseband/fprotos/w-acurite986.hpp +++ b/firmware/baseband/fprotos/w-acurite986.hpp @@ -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)); } }; diff --git a/firmware/baseband/fprotos/w-ambient.hpp b/firmware/baseband/fprotos/w-ambient.hpp index 20ec1ff4..c01d8064 100644 --- a/firmware/baseband/fprotos/w-ambient.hpp +++ b/firmware/baseband/fprotos/w-ambient.hpp @@ -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(decode_data >> 16), static_cast(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; } diff --git a/firmware/baseband/fprotos/w-infactory.hpp b/firmware/baseband/fprotos/w-infactory.hpp index 1e70709b..bc5e2343 100644 --- a/firmware/baseband/fprotos/w-infactory.hpp +++ b/firmware/baseband/fprotos/w-infactory.hpp @@ -118,19 +118,16 @@ class FProtoWeatherInfactory : public FProtoWeatherBase { static_cast(decode_data >> 8), static_cast(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; } }; diff --git a/firmware/baseband/fprotos/w-lacrosse-tx.hpp b/firmware/baseband/fprotos/w-lacrosse-tx.hpp index 684cbbe1..6bbcd93a 100644 --- a/firmware/baseband/fprotos/w-lacrosse-tx.hpp +++ b/firmware/baseband/fprotos/w-lacrosse-tx.hpp @@ -155,7 +155,7 @@ class FProtoWeatherLaCrosseTx : public FProtoWeatherBase { static_cast((decode_data >> 8) & 0x0F), static_cast((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)); } }; diff --git a/firmware/baseband/fprotos/w-lacrosse-tx141thbv2.hpp b/firmware/baseband/fprotos/w-lacrosse-tx141thbv2.hpp index d726267f..72d3c494 100644 --- a/firmware/baseband/fprotos/w-lacrosse-tx141thbv2.hpp +++ b/firmware/baseband/fprotos/w-lacrosse-tx141thbv2.hpp @@ -104,7 +104,7 @@ class FProtoWeatherLaCrosseTx141thbv2 : public FProtoWeatherBase { } uint8_t msg[] = {static_cast(data >> 32), static_cast(data >> 24), static_cast(data >> 16), static_cast(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() { diff --git a/firmware/baseband/fprotos/w-oregon2.hpp b/firmware/baseband/fprotos/w-oregon2.hpp index 87b44bdf..94bf5124 100644 --- a/firmware/baseband/fprotos/w-oregon2.hpp +++ b/firmware/baseband/fprotos/w-oregon2.hpp @@ -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; diff --git a/firmware/baseband/fprotos/w-oregon3.hpp b/firmware/baseband/fprotos/w-oregon3.hpp index 5572208b..629d3474 100644 --- a/firmware/baseband/fprotos/w-oregon3.hpp +++ b/firmware/baseband/fprotos/w-oregon3.hpp @@ -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); } diff --git a/firmware/baseband/fprotos/w-oregonv1.hpp b/firmware/baseband/fprotos/w-oregonv1.hpp index 932ed48c..14a34b72 100644 --- a/firmware/baseband/fprotos/w-oregonv1.hpp +++ b/firmware/baseband/fprotos/w-oregonv1.hpp @@ -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; diff --git a/firmware/baseband/fprotos/w-wendox-w6726.hpp b/firmware/baseband/fprotos/w-wendox-w6726.hpp index 95667b0c..f6629c33 100644 --- a/firmware/baseband/fprotos/w-wendox-w6726.hpp +++ b/firmware/baseband/fprotos/w-wendox-w6726.hpp @@ -128,7 +128,7 @@ class FProtoWeatherWendoxW6726 : public FProtoWeatherBase { static_cast(decode_data >> 12), static_cast(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() { diff --git a/firmware/baseband/fprotos/weatherbase.hpp b/firmware/baseband/fprotos/weatherbase.hpp index aa65940d..f5be5930 100644 --- a/firmware/baseband/fprotos/weatherbase.hpp +++ b/firmware/baseband/fprotos/weatherbase.hpp @@ -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 // 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 \ No newline at end of file diff --git a/firmware/baseband/fprotos/weatherprotos.hpp b/firmware/baseband/fprotos/weatherprotos.hpp index dcbccf54..ab0ca1d5 100644 --- a/firmware/baseband/fprotos/weatherprotos.hpp +++ b/firmware/baseband/fprotos/weatherprotos.hpp @@ -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 #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 diff --git a/firmware/baseband/fprotos/weathertypes.hpp b/firmware/baseband/fprotos/weathertypes.hpp index bf1798c8..37129705 100644 --- a/firmware/baseband/fprotos/weathertypes.hpp +++ b/firmware/baseband/fprotos/weathertypes.hpp @@ -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, diff --git a/firmware/baseband/proc_subghzd.cpp b/firmware/baseband/proc_subghzd.cpp new file mode 100644 index 00000000..435258d6 --- /dev/null +++ b/firmware/baseband/proc_subghzd.cpp @@ -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(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()}; + event_dispatcher.run(); + return 0; +} diff --git a/firmware/baseband/proc_subghzd.hpp b/firmware/baseband/proc_subghzd.hpp new file mode 100644 index 00000000..a902754c --- /dev/null +++ b/firmware/baseband/proc_subghzd.hpp @@ -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 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__*/ diff --git a/firmware/baseband/proc_weather.cpp b/firmware/baseband/proc_weather.cpp index 1b630231..f8acbee4 100644 --- a/firmware/baseband/proc_weather.cpp +++ b/firmware/baseband/proc_weather.cpp @@ -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(message)); + if (message->id == Message::ID::SubGhzFPRxConfigure) + configure(*reinterpret_cast(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; } diff --git a/firmware/baseband/proc_weather.hpp b/firmware/baseband/proc_weather.hpp index c8372bc5..6821a306 100644 --- a/firmware/baseband/proc_weather.hpp +++ b/firmware/baseband/proc_weather.hpp @@ -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}; diff --git a/firmware/common/message.hpp b/firmware/common/message.hpp index b2193be6..287a821e 100644 --- a/firmware/common/message.hpp +++ b/firmware/common/message.hpp @@ -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__*/ diff --git a/firmware/common/spi_image.hpp b/firmware/common/spi_image.hpp index 2294abd9..27b6fda5 100644 --- a/firmware/common/spi_image.hpp +++ b/firmware/common/spi_image.hpp @@ -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'};