Compare commits

...

21 Commits

Author SHA1 Message Date
zxkmm 31c844b48e fix black bg of those bitmap when we have themes enabled (#2239) 2024-09-07 17:39:50 +02:00
Totoo 87069f11e5 invert display option (#2232)
* invert display option

* text fix, format code
2024-09-07 10:58:57 +02:00
Totoo e6afd7744d SubghzD & Weather improvments (#2237)
* Weather restructure

* Added Solight TE44 protocol

* Add disabled Marantec24, and GangQi

* More opt in subghzd
2024-09-06 20:23:11 +02:00
Dongping Guo (Donny) 76763b9bab Add BLE RX filter options (#2236) 2024-09-06 20:22:00 +02:00
Totoo 02b75f567a Setfreq usb command (#2235)
* SetFreq usb command for rx apps
* code format, better check
2024-09-04 20:26:31 +02:00
Erwin Ried a2c4fefe34 Slighty smaller video on readme 2024-09-03 09:27:22 +02:00
Erwin Ried 121352b149 Fully remove references to bountysource + ltt video (#2233)
-Permanently remove the references to the bountysource scammers
-Added LTT video about the device
2024-09-02 21:47:11 +08:00
Totoo 0ae7768f20 Battery info switcher (#2230) 2024-08-28 11:32:24 +02:00
jLynx 1a0555f9eb Update README.md 2024-08-27 22:38:39 +12:00
GitClo 6752bf0279 Slovak FM radio frequencies (#2213)
* Slovak FM radio frequencies

Comprehensive list of most regional and local FM radio stations across Slovakia

* Update RADIOS_FM_SK.txt

Shortened list of Slovak FM radio stations - no longer crashing; transliterated non-english characters to avoid displaying problem
2024-08-21 16:49:25 +08:00
Brumi-2021 6dc7e3dfc5 Needed_changes_to_compile_with_gcc_13.2.1 (#2224)
* Needed_changes_to_compile_with_gcc_13.2.1

* re-activate_length_binary_size_protect
2024-08-15 19:31:34 +02:00
Mark Thompson c992780974 Fix compile error with gcc versions >9.2.1 (#2223) 2024-08-15 08:38:14 +02:00
Mark Thompson 750d592288 Fix small fonts in theme code (#2222) 2024-08-14 19:39:01 +02:00
ArjanOnwezen c77252cca2 ICAO24 database update. (#2221) 2024-08-14 17:11:34 +02:00
Totoo df3981876d Move ADSBTX to ext (#2214) 2024-08-09 23:47:08 +02:00
Totoo 05146638fc Weather size fix (#2211)
* Shrink weather size

* fix compiler warning
2024-07-29 10:46:45 +02:00
Totoo 765e3be55b SubghzD rework (#2210)
* Removed controller code

* Add Legrand

* Added Somify Keytis

* Somify

* better display
2024-07-29 07:47:50 +02:00
Totoo 9211975868 Protoview (#2206)
* ProtoView first tests

* Imp

* fix, reset
2024-07-29 07:45:56 +02:00
Erwin Ried 96a60e82bc Reshuffle the readme videos (#2208)
Does people click because thumb/title or just because the order? 🤔
2024-07-24 11:53:44 +02:00
Totoo 5d9428e568 Weather station update (#2209)
* Acurite609txc fix

* Added Kedsum

* Add auriol 5in1

* prev was: FPW_Acurite5in1, this is EmosE601x

* Remove BTN from weather
Saved flash space by creating chaos.
Fix EmosE601
2024-07-24 11:53:09 +02:00
Totoo 2fdd531fe7 Add fully randomized names to blespam (#2207)
* Add fully randomized names to blespam

* fix for all

* fix warning
2024-07-24 09:53:41 +02:00
149 changed files with 3581 additions and 913 deletions
+6 -11
View File
@@ -1,6 +1,7 @@
> [!WARNING]
> __IF YOU'VE PAID FOR MAYHEM OR ANY PREPACKAGED PACKAGES, YOU'RE BEING SCAMMED.__
> The only legitimate link leading to our repositories is the organization [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware).
> __IF YOU'VE PAID FOR MAYHEM OR ANY PREPACKAGED VERSIONS, YOU'RE BEING SCAMMED.__
>
> The only legitimate link to our repositories is the [portapack-mayhem](https://github.com/portapack-mayhem/mayhem-firmware) organization on GitHub.
# PortaPack Mayhem
@@ -16,9 +17,9 @@ This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmw
If you are new to *HackRF+PortaPack+Mayhem*, check these:
[<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="512">](https://grabify.link/C0J6ZR)
[<img alt="Its TOO Easy to Accidentally Do Illegal Stuff with This" src="https://img.youtube.com/vi/OPckpjBSAOw/maxresdefault.jpg" width="700">](https://grabify.link/X4D5TF)
[<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="254">](https://grabify.link/5MU2VH) [<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="254">](https://grabify.link/9UZGEW)
[<img alt="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="230">](https://grabify.link/9UZGEW) [<img alt="Beginner's Guide To The HackRF & Portapak With Mayhem" src="https://img.youtube.com/vi/H-bqdWfbhpg/maxresdefault.jpg" width="230">](https://grabify.link/5MU2VH) [<img alt="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="230">](https://grabify.link/C0J6ZR)
# Frequently Asked Questions
@@ -54,13 +55,7 @@ Consider that the hardware and firmware has been created and maintain by a [lot]
To support the people behind the hardware, please buy a genuine [HackRF](https://greatscottgadgets.com/hackrf/) and [PortaPack](https://store.sharebrained.com/products/portapack-for-hackrf-one-kit).
## What if I really want something specific?
If what you need can be relevant in general, you can [request a feature](https://github.com/portapack-mayhem/mayhem-firmware/issues/new?labels=enhancement&template=feature_request.md).
<del>You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/portapack-mayhem/mayhem-firmware/issues/new/choose) or [choosing](https://github.com/portapack-mayhem/mayhem-firmware/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).</del>
<del>Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).</del>
Bountysource has not been reliable lately, so until this changes, please **DO NOT** post a bounty there. Go to our Discord by clicking the chat badge on [top](#portapack-mayhem) and discuss there.
If what you need can be relevant in general, you can [request a feature](https://github.com/portapack-mayhem/mayhem-firmware/issues/new?labels=enhancement&template=feature_request.md). Alternatively, go to our Discord by clicking the chat badge on [top](#portapack-mayhem) and discuss there.
## What if I need help?
First, check the [documentation](https://github.com/portapack-mayhem/mayhem-firmware/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/portapack-mayhem/mayhem-firmware/issues/new/choose).
+1 -1
View File
@@ -282,7 +282,7 @@ set(CPPSRC
# apps/tpms_app.cpp
apps/ui_about_simple.cpp
apps/ui_adsb_rx.cpp
apps/ui_adsb_tx.cpp
# apps/ui_adsb_tx.cpp #moved to ext
apps/ui_aprs_rx.cpp
apps/ui_aprs_tx.cpp
apps/ui_battinfo.cpp
@@ -435,4 +435,7 @@ void AnalogAudioView::handle_coded_squelch(uint32_t value) {
text_ctcss.set(tone_key_string_by_value(value, text_ctcss.parent_rect().width() / 8));
}
void AnalogAudioView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
@@ -247,12 +247,21 @@ class AnalogAudioView : public View {
void handle_coded_squelch(uint32_t value);
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
[this](const Message* p) {
const auto message = *reinterpret_cast<const CodedSquelchMessage*>(p);
this->handle_coded_squelch(message.value);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
};
} /* namespace ui */
+38 -9
View File
@@ -428,6 +428,7 @@ BLERxView::BLERxView(NavigationView& nav)
&text_found_count,
&check_serial_log,
&button_filter,
&options_filter,
&button_save_list,
&button_clear_list,
&button_switch,
@@ -499,6 +500,7 @@ BLERxView::BLERxView(NavigationView& nav)
check_name.on_select = [this](Checkbox&, bool v) {
name_enable = v;
// update the include_name instance variable value of each entry in recent entries
setAllMembersToValue(recent, &BleRecentEntry::include_name, v);
recent_entries_view.set_dirty();
};
@@ -525,8 +527,14 @@ BLERxView::BLERxView(NavigationView& nav)
handle_entries_sort(v);
};
options_filter.on_change = [this](size_t index, int32_t v) {
filter_index = (uint8_t)index;
handle_filter_options(v);
};
options_channel.set_selected_index(channel_index, true);
options_sort.set_selected_index(sort_index, true);
options_filter.set_selected_index(filter_index, true);
button_find.on_select = [this](Button&) {
auto open_view = nav_.push<FileLoadView>(".TXT");
@@ -716,10 +724,11 @@ void BLERxView::on_data(BlePacketData* packet) {
updateEntry(packet, entry, (ADV_PDU_TYPE)packet->type);
// Add entries if they meet the criteria.
auto value = filter;
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
// auto value = filter;
// resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
// return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
// });
handle_filter_options(options_filter.selected_index());
handle_entries_sort(options_sort.selected_index());
@@ -756,12 +765,13 @@ void BLERxView::on_data(BlePacketData* packet) {
void BLERxView::on_filter_change(std::string value) {
// New filter? Reset list from recent entries.
if (filter != value) {
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
// resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
// // return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
// return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos) && (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos);
// });
filter = value;
handle_filter_options(options_filter.selected_index());
}
filter = value;
}
void BLERxView::on_file_changed(const std::filesystem::path& new_file_path) {
@@ -852,6 +862,24 @@ void BLERxView::handle_entries_sort(uint8_t index) {
recent_entries_view.set_dirty();
}
void BLERxView::handle_filter_options(uint8_t index) {
auto value = filter;
switch (index) {
case 0: // filter by Data
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (entry.dataString.find(value) == std::string::npos) && (entry.nameString.find(value) == std::string::npos);
});
break;
case 1: // filter by MAC address (All caps: e.g. AA:BB:CC:DD:EE:FF)
resetFilteredEntries(recent, [&value](const BleRecentEntry& entry) {
return (to_string_mac_address(entry.packetData.macAddress, 6, false).find(value) == std::string::npos);
});
break;
default:
break;
}
}
void BLERxView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height - switch_button_height};
@@ -914,6 +942,7 @@ void BLERxView::updateEntry(const BlePacketData* packet, BleRecentEntry& entry,
// Subtract 1 because type is part of the length.
for (int i = 0; i < length - 1; i++) {
// parse the name of bluetooth device: 0x08->Shortened Local Name; 0x09->Complete Local Name
if (type == 0x08 || type == 0x09) {
decoded_data += (char)advertiseData->Data[currentByte];
}
+24 -15
View File
@@ -203,6 +203,7 @@ class BLERxView : public View {
void file_error();
void on_timer();
void handle_entries_sort(uint8_t index);
void handle_filter_options(uint8_t index);
void updateEntry(const BlePacketData* packet, BleRecentEntry& entry, ADV_PDU_TYPE pdu_type);
NavigationView& nav_;
@@ -214,6 +215,7 @@ class BLERxView : public View {
uint8_t channel_index{0};
uint8_t sort_index{0};
uint8_t filter_index{0};
std::string filter{};
bool logging{false};
bool serial_logging{false};
@@ -227,6 +229,7 @@ class BLERxView : public View {
{"sort_index"sv, &sort_index},
{"filter"sv, &filter},
{"log"sv, &logging},
{"filter_index"sv, &filter_index},
// disabled to always start without USB serial activated until we can make it non blocking if not connected
// {"serial_log"sv, &serial_logging},
{"name"sv, &name_enable},
@@ -255,13 +258,13 @@ class BLERxView : public View {
std::filesystem::path log_packets_path{blerx_dir / u"Logs/????.TXT"};
std::filesystem::path packet_save_path{blerx_dir / u"Lists/????.csv"};
static constexpr auto header_height = 4 * 16;
static constexpr auto header_height = 9 * 8;
static constexpr auto switch_button_height = 3 * 16;
OptionsField options_channel{
{0 * 8, 0 * 8},
5,
{{"Ch.37 ", 37},
{{"Ch.37", 37},
{"Ch.38", 38},
{"Ch.39", 39},
{"Auto", 40}}};
@@ -286,10 +289,10 @@ class BLERxView : public View {
{24 * 8, 5, 6 * 8, 4}};
Labels label_sort{
{{0 * 8, 3 * 8}, "Sort:", Theme::getInstance()->fg_light->foreground}};
{{0 * 8, 2 * 8}, "Sort:", Theme::getInstance()->fg_light->foreground}};
OptionsField options_sort{
{5 * 8, 3 * 8},
{5 * 8, 2 * 8},
4,
{{"MAC", 0},
{"Hits", 1},
@@ -298,40 +301,46 @@ class BLERxView : public View {
{"Name", 4}}};
Button button_filter{
{11 * 8, 3 * 8, 4 * 8, 16},
"Filter"};
{11 * 8, 2 * 8, 7 * 8, 16},
"Filter:"};
OptionsField options_filter{
{18 * 8 + 2, 2 * 8},
4,
{{"Data", 0},
{"MAC", 1}}};
Checkbox check_log{
{17 * 8, 3 * 8},
{10 * 8, 4 * 8 + 2},
3,
"Log",
true};
Checkbox check_name{
{23 * 8, 3 * 8},
{0 * 8, 4 * 8 + 2},
3,
"Name",
true};
Button button_find{
{0 * 8, 6 * 8, 4 * 8, 16},
{0 * 8, 7 * 8 - 2, 4 * 8, 16},
"Find"};
Labels label_found{
{{5 * 8, 6 * 8}, "Found:", Theme::getInstance()->fg_light->foreground}};
{{5 * 8, 7 * 8 - 2}, "Found:", Theme::getInstance()->fg_light->foreground}};
Text text_found_count{
{11 * 8, 3 * 16, 20 * 8, 16},
{11 * 8, 7 * 8 - 2, 20 * 8, 16},
"0/0"};
Checkbox check_serial_log{
{17 * 8, 3 * 16 - 2},
{18 * 8 + 2, 4 * 8 + 2},
7,
"USB Log",
true};
Console console{
{0, 4 * 16, 240, 240}};
// Console console{
// {0, 10 * 8, 240, 240}};
Button button_clear_list{
{2 * 8, 320 - (16 + 32), 7 * 8, 32},
@@ -371,7 +380,7 @@ class BLERxView : public View {
[this](const Message* const) {
this->on_timer();
}};
};
}; /* BLERxView */
} /* namespace ui */
@@ -128,4 +128,8 @@ void CaptureAppView::focus() {
record_view.focus();
}
void CaptureAppView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
@@ -108,6 +108,15 @@ class CaptureAppView : public View {
3};
spectrum::WaterfallView waterfall{};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} /* namespace ui */
+4
View File
@@ -178,4 +178,8 @@ void ERTAppView::on_show_list() {
recent_entries_view.focus();
}
void ERTAppView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
+8
View File
@@ -176,6 +176,14 @@ class ERTAppView : public View {
this->on_packet(packet);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
void on_packet(const ert::Packet& packet);
void on_show_list();
};
+4
View File
@@ -321,6 +321,10 @@ void POCSAGAppView::on_stats(const POCSAGStatsMessage* stats) {
widget_frames.set_sync(stats->has_sync);
}
void POCSAGAppView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void BaudIndicator::paint(Painter& painter) {
auto p = screen_pos();
char top = '-';
+9
View File
@@ -292,6 +292,15 @@ class POCSAGAppView : public View {
Console console{
{0, 2 * 16 + 6, screen_width, screen_height - 54}};
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_packet{
Message::ID::POCSAGPacket,
[this](Message* const p) {
+4
View File
@@ -122,6 +122,10 @@ APRSRxView::APRSRxView(NavigationView& nav, Rect parent_rect)
receiver_model.enable();
}
void APRSRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
void APRSRxView::on_packet(const APRSPacketMessage* message) {
std::string str_console = "\x1B";
+8
View File
@@ -185,6 +185,7 @@ class APRSRxView : public View {
std::string title() const override { return "APRS RX"; };
void on_packet(const APRSPacketMessage* message);
void on_freqchg(int64_t freq);
private:
void on_data(uint32_t value, bool is_data);
@@ -271,6 +272,13 @@ class APRSRXView : public View {
this->view_stream.on_packet(message);
this->view_table.on_pkt(message);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->view_stream.on_freqchg(message->freq);
}};
};
} /* namespace ui */
+21 -1
View File
@@ -49,6 +49,7 @@ void BattinfoView::update_result() {
text_voltage.set("UNKNOWN");
text_current.set("-");
text_charge.set("-");
text_method.set("-");
return;
}
bool uichg = false;
@@ -78,6 +79,13 @@ void BattinfoView::update_result() {
text_current.hidden(true);
text_charge.hidden(true);
}
if ((valid_mask & battery::BatteryManagement::BATT_VALID_PERCENT) == battery::BatteryManagement::BATT_VALID_PERCENT) {
text_method.set("IC");
button_mode.set_text("Volt");
} else {
text_method.set("Voltage");
button_mode.set_text("IC");
}
if (uichg) set_dirty();
// to update status bar too, send message in behalf of batt manager
BatteryStateMessage msg{valid_mask, percent, current >= 0, voltage};
@@ -92,12 +100,24 @@ BattinfoView::BattinfoView(NavigationView& nav)
&text_voltage,
&text_current,
&text_charge,
&text_method,
&button_mode,
&button_exit});
button_exit.on_select = [this, &nav](Button&) {
nav.pop();
};
button_mode.on_select = [this, &nav](Button&) {
if (button_mode.text() == "IC") {
battery::BatteryManagement::set_calc_override(false);
persistent_memory::set_ui_override_batt_calc(false);
button_mode.set_text("Volt");
} else {
battery::BatteryManagement::set_calc_override(true);
persistent_memory::set_ui_override_batt_calc(true);
button_mode.set_text("IC");
}
};
update_result();
if (thread == nullptr) thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, BattinfoView::static_fn, this);
}
+15 -5
View File
@@ -53,11 +53,14 @@ class BattinfoView : public View {
Labels labels{
{{2 * 8, 1 * 16}, "Percent:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 2 * 16}, "Voltage:", Theme::getInstance()->fg_light->foreground}};
{{2 * 8, 2 * 16}, "Voltage:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 3 * 16}, "Method:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 7 * 16}, "Change method:", Theme::getInstance()->fg_light->foreground},
};
Labels labels_opt{
{{2 * 8, 3 * 16}, "Current:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 4 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground}};
{{2 * 8, 4 * 16}, "Current:", Theme::getInstance()->fg_light->foreground},
{{2 * 8, 5 * 16}, "Charge:", Theme::getInstance()->fg_light->foreground}};
Text text_percent{
{13 * 8, 1 * 16, 10 * 16, 16},
@@ -65,12 +68,19 @@ class BattinfoView : public View {
Text text_voltage{
{13 * 8, 2 * 16, 10 * 16, 16},
"-"};
Text text_current{
Text text_method{
{13 * 8, 3 * 16, 10 * 16, 16},
"-"};
Text text_charge{
Text text_current{
{13 * 8, 4 * 16, 10 * 16, 16},
"-"};
Text text_charge{
{13 * 8, 5 * 16, 10 * 16, 16},
"-"};
Button button_mode{
{2 * 8, 8 * 16 + 5, 5 * 16, 32},
"Volt"};
Button button_exit{
{72, 17 * 16, 96, 32},
+5
View File
@@ -323,4 +323,9 @@ void LevelView::handle_coded_squelch(const uint32_t value) {
text_ctcss.set(" ");
}
void LevelView::on_freqchg(int64_t freq) {
receiver_model.set_target_frequency(freq);
button_frequency.set_text("<" + to_string_short_freq(freq) + " MHz>");
}
} /* namespace ui */
+9
View File
@@ -188,6 +188,15 @@ class LevelView : public View {
void handle_coded_squelch(const uint32_t value);
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_coded_squelch{
Message::ID::CodedSquelch,
[this](const Message* const p) {
+37 -4
View File
@@ -774,21 +774,27 @@ void SetConfigModeView::focus() {
button_save.focus();
}
/* SetFakeBrightnessView ************************************/
/* SetDisplayView ************************************/
SetFakeBrightnessView::SetFakeBrightnessView(NavigationView& nav) {
SetDisplayView::SetDisplayView(NavigationView& nav) {
add_children({&labels,
&field_fake_brightness,
&button_save,
&button_cancel,
&checkbox_invert_switch,
&checkbox_brightness_switch});
field_fake_brightness.set_by_value(pmem::fake_brightness_level());
checkbox_brightness_switch.set_value(pmem::apply_fake_brightness());
checkbox_invert_switch.set_value(pmem::config_lcd_inverted_mode());
button_save.on_select = [&nav, this](Button&) {
pmem::set_apply_fake_brightness(checkbox_brightness_switch.value());
pmem::set_fake_brightness_level(field_fake_brightness.selected_index_value());
if (checkbox_invert_switch.value() != pmem::config_lcd_inverted_mode()) {
display.set_inverted(checkbox_invert_switch.value());
pmem::set_lcd_inverted_mode(checkbox_invert_switch.value());
}
send_system_refresh();
nav.pop();
};
@@ -798,7 +804,7 @@ SetFakeBrightnessView::SetFakeBrightnessView(NavigationView& nav) {
};
}
void SetFakeBrightnessView::focus() {
void SetDisplayView::focus() {
button_save.focus();
}
@@ -948,6 +954,32 @@ void SetThemeView::focus() {
options.focus();
}
/* SetBatteryView ************************************/
SetBatteryView::SetBatteryView(NavigationView& nav) {
add_children({&labels,
&button_save,
&button_cancel,
&checkbox_overridebatt});
button_save.on_select = [&nav, this](Button&) {
pmem::set_ui_override_batt_calc(checkbox_overridebatt.value());
battery::BatteryManagement::set_calc_override(checkbox_overridebatt.value());
send_system_refresh();
nav.pop();
};
checkbox_overridebatt.set_value(pmem::ui_override_batt_calc());
button_cancel.on_select = [&nav, this](Button&) {
nav.pop();
};
}
void SetBatteryView::focus() {
button_cancel.focus();
}
/* SettingsMenuView **************************************/
SettingsMenuView::SettingsMenuView(NavigationView& nav)
@@ -973,11 +1005,12 @@ void SettingsMenuView::on_populate() {
{"SD Card", ui::Color::dark_cyan(), &bitmap_icon_sdcard, [this]() { nav_.push<SetSDCardView>(); }},
{"User Interface", ui::Color::dark_cyan(), &bitmap_icon_options_ui, [this]() { nav_.push<SetUIView>(); }},
//{"QR Code", ui::Color::dark_cyan(), &bitmap_icon_qr_code, [this]() { nav_.push<SetQRCodeView>(); }},
{"Brightness", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetFakeBrightnessView>(); }},
{"Display", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetDisplayView>(); }},
{"Menu Color", ui::Color::dark_cyan(), &bitmap_icon_brightness, [this]() { nav_.push<SetMenuColorView>(); }},
{"Theme", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetThemeView>(); }},
{"Autostart", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetAutostartView>(); }},
});
if (battery::BatteryManagement::isDetected()) add_item({"Battery", ui::Color::dark_cyan(), &bitmap_icon_setup, [this]() { nav_.push<SetBatteryView>(); }});
}
} /* namespace ui */
+37 -3
View File
@@ -711,13 +711,13 @@ class SetConfigModeView : public View {
using portapack::persistent_memory::fake_brightness_level_options;
class SetFakeBrightnessView : public View {
class SetDisplayView : public View {
public:
SetFakeBrightnessView(NavigationView& nav);
SetDisplayView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Brightness"; };
std::string title() const override { return "Display"; };
private:
Labels labels{
@@ -739,6 +739,11 @@ class SetFakeBrightnessView : public View {
16,
"Enable brightness adjust"};
Checkbox checkbox_invert_switch{
{1 * 8, 10 * 16},
23,
"Invert colors (For IPS)"};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
@@ -888,6 +893,35 @@ class SetThemeView : public View {
};
};
class SetBatteryView : public View {
public:
SetBatteryView(NavigationView& nav);
void focus() override;
std::string title() const override { return "Battery"; };
private:
int32_t selected = 0;
Labels labels{
{{1 * 8, 1 * 16}, "Override batt calculation", Theme::getInstance()->fg_light->foreground},
{{1 * 8, 2 * 16}, "method to voltage based", Theme::getInstance()->fg_light->foreground}};
Button button_save{
{2 * 8, 16 * 16, 12 * 8, 32},
"Save"};
Checkbox checkbox_overridebatt{
{2 * 8, 6 * 16},
23,
"Override"};
Button button_cancel{
{16 * 8, 16 * 16, 12 * 8, 32},
"Cancel",
};
};
class SettingsMenuView : public BtnGridView {
public:
SettingsMenuView(NavigationView& nav);
+4
View File
@@ -187,4 +187,8 @@ void SondeView::on_packet(const sonde::Packet& packet) {
}
}
void SondeView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} /* namespace ui */
+8
View File
@@ -195,6 +195,14 @@ class SondeView : public View {
const auto message = static_cast<const OrientationDataMessage*>(p);
this->on_orientation(message);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
void on_gps(const GPSPosDataMessage* msg);
void on_orientation(const OrientationDataMessage* msg);
+500 -5
View File
@@ -32,12 +32,15 @@ using namespace ui;
namespace ui {
void SubGhzDRecentEntryDetailView::update_data() {
// process protocol data
parseProtocol();
// set text elements
text_type.set(SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry_.sensorType));
text_id.set("0x" + to_string_hex(entry_.serial));
text_id.set("0x" + to_string_hex(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 (btn != SD_NO_BTN) console.writeln("Btn: " + to_string_dec_uint(btn));
if (cnt != SD_NO_CNT) console.writeln("Cnt: " + to_string_dec_uint(cnt));
if (entry_.data != 0) console.writeln("Data: " + to_string_hex(entry_.data));
}
@@ -103,7 +106,7 @@ void SubGhzDView::on_tick_second() {
}
void SubGhzDView::on_data(const SubGhzDDataMessage* data) {
SubGhzDRecentEntry key{data->sensorType, data->serial, data->bits, data->data, data->btn, data->cnt};
SubGhzDRecentEntry key{data->sensorType, data->data, data->bits};
auto matching_recent = find(recent, key.key());
if (matching_recent != std::end(recent)) {
// Found within. Move to front of list, increment counter.
@@ -203,6 +206,16 @@ const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
return "Star Line";
case FPS_X10:
return "X10";
case FPS_LEGRAND:
return "Legrand";
case FPS_SOMIFY_KEYTIS:
return "Somify Keytis";
case FPS_SOMIFY_TELIS:
return "Somify Telis";
case FPS_GANGQI:
return "GangQi";
case FPS_MARANTEC24:
return "Marantec24";
case FPS_Invalid:
default:
return "Unknown";
@@ -214,6 +227,10 @@ std::string SubGhzDView::pad_string_with_spaces(int snakes) {
return paddedStr;
}
void SubGhzDView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
template <>
void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
const Entry& entry,
@@ -224,7 +241,7 @@ void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
line.reserve(30);
line = SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry.sensorType);
line = line + " " + to_string_hex(entry.serial);
line = line + " " + to_string_hex(entry.data << 32);
if (line.length() < 19) {
line += SubGhzDView::pad_string_with_spaces(19 - line.length());
} else {
@@ -239,4 +256,482 @@ void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
painter.draw_string(target_rect.location(), style, line);
}
// decoder helper functions
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++;
}
}
const uint32_t came_twee_magic_numbers_xor[15] = {
0x0E0E0E00,
0x1D1D1D11,
0x2C2C2C22,
0x3B3B3B33,
0x4A4A4A44,
0x59595955,
0x68686866,
0x77777777,
0x86868688,
0x95959599,
0xA4A4A4AA,
0xB3B3B3BB,
0xC2C2C2CC,
0xD1D1D1DD,
0xE0E0E0EE,
};
// to save some byte of fw space, these will be inline. unreadeable? yes. needs a tons of free space? certanly. so sorry for this.
void SubGhzDRecentEntryDetailView::parseProtocol() {
btn = SD_NO_BTN;
cnt = SD_NO_CNT;
serial = 0;
if (entry_.sensorType == FPS_Invalid) return;
if (entry_.sensorType == FPS_BETT) {
return; // needs dip pattern output.
}
if (entry_.sensorType == FPS_AIRFORCE || entry_.sensorType == FPS_PRASTEL || entry_.sensorType == FPS_CAME) {
return; // nothing
}
if (entry_.sensorType == FPS_CAMEATOMO) {
entry_.data ^= 0xFFFFFFFFFFFFFFFF;
entry_.data <<= 4;
uint8_t pack[8] = {};
pack[0] = (entry_.data >> 56);
pack[1] = ((entry_.data >> 48) & 0xFF);
pack[2] = ((entry_.data >> 40) & 0xFF);
pack[3] = ((entry_.data >> 32) & 0xFF);
pack[4] = ((entry_.data >> 24) & 0xFF);
pack[5] = ((entry_.data >> 16) & 0xFF);
pack[6] = ((entry_.data >> 8) & 0xFF);
pack[7] = (entry_.data & 0xFF);
atomo_decrypt(pack);
// cnt_2 = pack[0];
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;
}
return;
}
if (entry_.sensorType == FPS_CAMETWEE) {
uint8_t cnt_parcel = (uint8_t)(entry_.data & 0xF);
uint32_t data = (uint32_t)(entry_.data & 0x0FFFFFFFF);
data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]);
serial = data;
data /= 4;
btn = (data >> 4) & 0x0F;
data >>= 16;
data = (uint16_t)FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 16);
cnt = data >> 6;
return;
}
if (entry_.sensorType == FPS_CHAMBCODE) {
return; // nothing
}
if (entry_.sensorType == FPS_CLEMSA) {
serial = (entry_.data >> 2) & 0xFFFF;
btn = (entry_.data & 0x03);
return;
}
if (entry_.sensorType == FPS_DOITRAND) {
cnt = (entry_.data >> 24) | ((entry_.data >> 15) & 0x1);
btn = ((entry_.data >> 18) & 0x3);
return;
}
if (entry_.sensorType == FPS_DOOYA) {
serial = (entry_.data >> 16);
if ((entry_.data >> 12) & 0x0F) {
cnt = (entry_.data >> 8) & 0x0F;
} else {
cnt = 0xff;
}
btn = entry_.data & 0xFF;
return;
}
if (entry_.sensorType == FPS_FAAC) { // stripped down a lot.
uint32_t code_fix = entry_.data >> 32;
uint32_t code_hop = entry_.data & 0xFFFFFFFF;
// uint32_t decrypt = 0;
// uint64_t man;
uint8_t data_tmp = 0;
uint8_t data_prg[8];
data_prg[0] = (code_hop & 0xFF);
data_prg[1] = ((code_hop >> 8) & 0xFF);
data_prg[2] = ((code_hop >> 16) & 0xFF);
data_prg[3] = (code_hop >> 24);
data_prg[4] = (code_fix & 0xFF);
data_prg[5] = ((code_fix >> 8) & 0xFF);
data_prg[6] = ((code_fix >> 16) & 0xFF);
data_prg[7] = (code_fix >> 24);
if (((data_prg[7] == 0x52) && (data_prg[6] == 0x0F) && (data_prg[0] == 0x00))) {
// ProgMode ON
for (uint8_t i = data_prg[1] & 0xF; i != 0; i--) {
data_tmp = data_prg[2];
data_prg[2] = data_prg[2] >> 1 | (data_prg[3] & 1) << 7;
data_prg[3] = data_prg[3] >> 1 | (data_prg[4] & 1) << 7;
data_prg[4] = data_prg[4] >> 1 | (data_prg[5] & 1) << 7;
data_prg[5] = data_prg[5] >> 1 | (data_tmp & 1) << 7;
}
data_prg[2] ^= data_prg[1];
data_prg[3] ^= data_prg[1];
data_prg[4] ^= data_prg[1];
data_prg[5] ^= data_prg[1];
seed = data_prg[5] << 24 | data_prg[4] << 16 | data_prg[3] << 8 | data_prg[2];
// uint32_t dec_prg_1 = data_prg[7] << 24 | data_prg[6] << 16 | data_prg[5] << 8 | data_prg[4];
// uint32_t dec_prg_2 = data_prg[3] << 24 | data_prg[2] << 16 | data_prg[1] << 8 | data_prg[0];
// entry_.data_2 = (uint64_t)dec_prg_1 << 32 | dec_prg_2;
cnt = data_prg[1];
} else {
if (code_fix != 0x0) {
serial = code_fix >> 4;
btn = code_fix & 0xF;
}
}
return;
}
if (entry_.sensorType == FPS_GATETX) {
uint32_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, entry_.bits);
serial = (code_found_reverse & 0xFF) << 12 | ((code_found_reverse >> 8) & 0xFF) << 4 | ((code_found_reverse >> 20) & 0x0F);
btn = ((code_found_reverse >> 16) & 0x0F);
return;
}
if (entry_.sensorType == FPS_HOLTEK) {
if ((entry_.data & 0xF000000000) == 0x5000000000) {
serial = FProtoGeneral::subghz_protocol_blocks_reverse_key((entry_.data >> 16) & 0xFFFFF, 20);
uint16_t btn_ = entry_.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;
}
} else {
serial = 0;
btn = 0;
cnt = 0;
}
return;
}
if (entry_.sensorType == FPS_HOLTEKHT12X) {
btn = entry_.data & 0x0F;
cnt = (entry_.data >> 4) & 0xFF;
return;
}
if (entry_.sensorType == FPS_HONEYWELL) {
serial = (entry_.data >> 24) & 0xFFFFF;
btn = (entry_.data >> 16) & 0xFF; // not exactly button, but can contain btn data too.
cnt = (entry_.data >> 44) & 0xF;
/*
uint8_t contact = (entry_.databtn & 0x80) >> 7;
uint8_t tamper = (entry_.databtn & 0x40) >> 6;
uint8_t reed = (entry_.databtn & 0x20) >> 5;
uint8_t alarm = (entry_.databtn & 0x10) >> 4;
uint8_t battery_low = (entry_.databtn & 0x08) >> 3;
uint8_t heartbeat = (entry_.databtn & 0x04) >> 2;
*/
return;
}
if (entry_.sensorType == FPS_HONEYWELLWDB) {
serial = (entry_.data >> 28) & 0xFFFFF;
// enabled, when we'll have extra fields and free fw space
/* switch ((entry_.data >> 20) & 0x3) {
case 0x02:
device_type = "Doorbell";
break;
case 0x01:
device_type = "PIR-Motion";
break;
default:
device_type = "Unknown";
break;
}
switch ((entry_.data >> 16) & 0x3) {
case 0x00:
alert = "Normal";
break;
case 0x01:
case 0x02:
alert = "High";
break;
case 0x03:
alert = "Full";
break;
default:
alert = "Unknown";
break;
}
secret_knock = (uint8_t)((entry_.data >> 4) & 0x1);
relay = (uint8_t)((entry_.data >> 3) & 0x1);
lowbat = (uint8_t)((entry_.data >> 1) & 0x1);*/
return;
}
if (entry_.sensorType == FPS_HORMANN) {
btn = (entry_.data >> 8) & 0xF;
return;
}
/* if (entry_.sensorType == FPS_HORMANNBISECURE) { //fm not implemented
serial = 0;
for (uint8_t i = 1; i < 5; i++) {
serial = serial << 8 | ((uint8_t*)(&entry_.data))[i];
}
} */
if (entry_.sensorType == FPS_IDO) {
uint64_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, entry_.bits);
uint32_t code_fix = code_found_reverse & 0xFFFFFF;
serial = code_fix & 0xFFFFF;
btn = (code_fix >> 20) & 0x0F;
return;
}
if (entry_.sensorType == FPS_INTERTECHNOV3) {
if (entry_.bits == 32) {
serial = (entry_.data >> 6) & 0x3FFFFFF;
if ((entry_.data >> 5) & 0x1) {
cnt = 1 << 5;
} else {
cnt = (~entry_.data & 0xF);
}
btn = (entry_.data >> 4) & 0x1;
} else if (entry_.bits == 36) {
serial = (entry_.data >> 10) & 0x3FFFFFF;
if ((entry_.data >> 9) & 0x1) {
cnt = 1 << 5;
} else {
cnt = (~(entry_.data >> 4) & 0xF);
}
btn = (entry_.data) & 0xF;
} else {
serial = 0;
cnt = 0;
btn = 0;
}
return;
}
if (entry_.sensorType == FPS_KEELOQ) {
// too many sub protocol versions, skipping. maybe in future when we'll have much more fw space
return;
}
/* fm not implemented
if (entry_.sensorType == FPS_KIA) {
serial = (uint32_t)((entry_.data >> 12) & 0x0FFFFFFF);
btn = (entry_.data >> 8) & 0x0F;
cnt = (entry_.data >> 40) & 0xFFFF;
return;
}
*/
if (entry_.sensorType == FPS_KINGGATESSTYLO4K) {
uint64_t fix = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, 53);
btn = (fix >> 17) & 0x0F;
serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF);
return;
}
if (entry_.sensorType == FPS_LEGRAND) {
return; // nothing
}
if (entry_.sensorType == FPS_LINEAR || entry_.sensorType == FPS_LINEARDELTA3) {
return; // nothing
}
if (entry_.sensorType == FPS_MAGELLAN) {
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data >> 8, 24);
serial = data_rev & 0xFFFF;
btn = (data_rev >> 16) & 0xFF;
return;
}
if (entry_.sensorType == FPS_MARANTEC) {
btn = (entry_.data >> 16) & 0xF;
serial = ((entry_.data >> 12) & 0xFFFFFF00) | ((entry_.data >> 8) & 0xFF);
return;
}
if (entry_.sensorType == FPS_MASTERCODE) {
serial = (entry_.data >> 4) & 0xFFFF;
btn = (entry_.data >> 2 & 0x03);
return;
}
if (entry_.sensorType == FPS_MEGACODE) {
if ((entry_.data >> 23) == 1) {
serial = (entry_.data >> 3) & 0xFFFF;
btn = entry_.data & 0b111;
cnt = (entry_.data >> 19) & 0b1111;
} else {
serial = 0;
btn = 0;
cnt = 0;
}
return;
}
if (entry_.sensorType == FPS_NERORADIO) {
return; // nothing
}
if (entry_.sensorType == FPS_NERO_SKETCH) {
return; // nothing
}
if (entry_.sensorType == FPS_NICEFLO || entry_.sensorType == FPS_NICEFLORS) {
return; // nothing, and can't
}
if (entry_.sensorType == FPS_PHOENIXV2) {
uint64_t data_rev = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, entry_.bits + 4);
serial = data_rev & 0xFFFFFFFF;
cnt = (data_rev >> 40) & 0xFFFF;
btn = (data_rev >> 32) & 0xF;
return;
}
if (entry_.sensorType == FPS_POWERSMART) {
btn = ((entry_.data >> 54) & 0x02) | ((entry_.data >> 40) & 0x1);
serial = ((entry_.data >> 33) & 0x3FFF00) | ((entry_.data >> 32) & 0xFF);
cnt = ((entry_.data >> 49) & 0x3F);
return;
}
if (entry_.sensorType == FPS_PRINCETON) {
serial = entry_.data >> 4;
btn = entry_.data & 0xF;
return;
}
if (entry_.sensorType == FPS_SECPLUSV1) {
uint32_t fixed = (entry_.data >> 32) & 0xFFFFFFFF;
cnt = entry_.data & 0xFFFFFFFF;
btn = fixed % 3;
// uint8_t id0 = (fixed / 3) % 3;
uint8_t id1 = (fixed / 9) % 3;
// uint16_t pin = 0;
if (id1 == 0) {
// (fixed // 3**3) % (3**7) 3^3=27 3^73=72187
serial = (fixed / 27) % 2187;
// pin = (fixed // 3**10) % (3**9) 3^10=59049 3^9=19683
// pin = (fixed / 59049) % 19683;
/* if (pin <= 9999) {
furi_string_cat_printf(output, " pin:%d", pin);
} else if (pin <= 11029) {
furi_string_cat_printf(output, " pin:enter");
} */
} else {
// id = fixed / 27;
serial = fixed / 27;
}
}
if (entry_.sensorType == FPS_SECPLUSV2) {
return; // fw space saver
}
if (entry_.sensorType == FPS_SMC5326) {
return; // dip pattern output needed. skipping
}
if (entry_.sensorType == FPS_STARLINE) {
uint64_t key = FProtoGeneral::subghz_protocol_blocks_reverse_key(entry_.data, entry_.bits);
uint32_t key_fix = key >> 32;
serial = key_fix & 0x00FFFFFF;
btn = key_fix >> 24;
return;
}
if (entry_.sensorType == FPS_X10) {
serial = (entry_.data & 0xF0000000) >> (24 + 4);
btn = (((entry_.data & 0x07000000) >> 24) | ((entry_.data & 0xF800) >> 8));
return;
}
if (entry_.sensorType == FPS_SOMIFY_KEYTIS) {
uint64_t dataa = entry_.data ^ (entry_.data >> 8);
btn = (dataa >> 48) & 0xF;
cnt = (dataa >> 24) & 0xFFFF;
serial = dataa & 0xFFFFFF;
return;
}
if (entry_.sensorType == FPS_SOMIFY_TELIS) {
uint64_t dataa = entry_.data ^ (entry_.data >> 8);
btn = (dataa >> 44) & 0xF; // ctrl
cnt = (dataa >> 24) & 0xFFFF; // rolling code
serial = dataa & 0xFFFFFF; // address}
return;
}
if (entry_.sensorType == FPS_GANGQI) {
btn = 0; // parser needs some time i think in flipper side.
cnt = (uint8_t)(entry_.data >> 32);
serial = (entry_.data & 0xFFFFFFFF);
return;
}
if (entry_.sensorType == FPS_MARANTEC24) {
serial = (entry_.data >> 4);
btn = entry_.data & 0xf;
return;
}
}
} // namespace ui
+23 -11
View File
@@ -23,6 +23,10 @@
#ifndef __UI_SUBGHZD_H__
#define __UI_SUBGHZD_H__
#define SD_NO_SERIAL 0xFFFFFFFF
#define SD_NO_BTN 0xFF
#define SD_NO_CNT 0xFF
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
@@ -33,6 +37,7 @@
#include "recent_entries.hpp"
#include "../baseband/fprotos/subghztypes.hpp"
#include "../baseband/fprotos/fprotogeneral.hpp"
using namespace ui;
@@ -42,29 +47,20 @@ 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)
uint16_t bits = 0)
: sensorType{sensorType},
btn{btn},
serial{serial},
bits{bits},
cnt{cnt},
data{data} {
}
Key key() const {
return (data ^ ((static_cast<uint64_t>(serial) << 32) | (static_cast<uint64_t>(sensorType) & 0xFF) << 0));
return (data ^ ((static_cast<uint64_t>(sensorType) & 0xFF) << 0));
}
void inc_age(int delta) {
if (UINT16_MAX - delta > age) age += delta;
@@ -131,6 +127,14 @@ class SubGhzDView : public View {
}};
SubGhzDRecentEntriesView recent_entries_view{columns, recent};
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_packet{
Message::ID::SubGhzDData,
[this](Message* const p) {
@@ -149,6 +153,12 @@ class SubGhzDRecentEntryDetailView : public View {
private:
NavigationView& nav_;
SubGhzDRecentEntry entry_{};
uint32_t serial = 0;
uint8_t btn = SD_NO_BTN;
uint32_t cnt = SD_NO_CNT;
uint32_t seed = 0;
Text text_type{{0 * 8, 1 * 16, 15 * 8, 16}, "?"};
Text text_id{{6 * 8, 2 * 16, 10 * 8, 16}, "?"};
@@ -164,6 +174,8 @@ class SubGhzDRecentEntryDetailView : public View {
Button button_done{
{screen_width - 96 - 4, screen_height - 32 - 12, 96, 32},
"Done"};
void parseProtocol();
};
} // namespace ui
+298 -2
View File
@@ -26,6 +26,7 @@
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "../baseband/fprotos/fprotogeneral.hpp"
using namespace portapack;
using namespace ui;
@@ -138,7 +139,8 @@ void WeatherView::on_tick_second() {
}
void WeatherView::on_data(const WeatherDataMessage* data) {
WeatherRecentEntry key{data->sensorType, data->id, data->temp, data->humidity, data->channel, data->battery_low};
WeatherRecentEntry key = process_data(data);
// WeatherRecentEntry key{data->sensorType, data->id, data->temp, data->humidity, data->channel, data->battery_low};
auto matching_recent = find(recent, key.key());
if (matching_recent != std::end(recent)) {
// Found within. Move to front of list, increment counter.
@@ -203,7 +205,14 @@ const char* WeatherView::getWeatherSensorTypeName(FPROTO_WEATHER_SENSOR type) {
return "Wendox W6726";
case FPW_Acurite986:
return "Acurite986";
case FPW_KEDSUM:
return "Kedsum";
case FPW_Acurite5in1:
return "Acurite5in1";
case FPW_EmosE601x:
return "EmosE601x";
case FPW_SolightTE44:
return "SolightTE44";
case FPW_Invalid:
default:
return "Unknown";
@@ -215,6 +224,10 @@ std::string WeatherView::pad_string_with_spaces(int snakes) {
return paddedStr;
}
void WeatherView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
template <>
void RecentEntriesTable<ui::WeatherRecentEntries>::draw(
const Entry& entry,
@@ -245,4 +258,287 @@ void RecentEntriesTable<ui::WeatherRecentEntries>::draw(
painter.draw_string(target_rect.location(), style, line);
}
#define LACROSSE_TX_MSG_TYPE_TEMP 0x00
#define LACROSSE_TX_MSG_TYPE_HUM 0x0E
WeatherRecentEntry WeatherView::process_data(const WeatherDataMessage* data) {
WeatherRecentEntry ret = {};
ret.sensorType = data->sensorType;
int16_t i16 = 0;
uint16_t u16 = 0;
uint64_t u64 = 0;
uint8_t u8 = 0;
uint8_t channel_3021[] = {3, 0, 2, 1};
float flo = 0.0;
switch (data->sensorType) {
case FPW_NexusTH:
ret.id = (data->decode_data >> 28) & 0xFF;
ret.battery_low = !((data->decode_data >> 27) & 1);
ret.channel = ((data->decode_data >> 24) & 0x03) + 1;
if (!((data->decode_data >> 23) & 1)) {
ret.temp = (float)((data->decode_data >> 12) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 12) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = data->decode_data & 0xFF;
if (ret.humidity > 95)
ret.humidity = 95;
else if (ret.humidity < 20)
ret.humidity = 20;
break;
case FPW_Acurite592TXR:
u8 = ((data->decode_data >> 54) & 0x03);
ret.channel = channel_3021[u8];
ret.id = (data->decode_data >> 40) & 0x3FFF;
ret.battery_low = !((data->decode_data >> 38) & 1);
ret.humidity = (data->decode_data >> 24) & 0x7F;
u16 = ((data->decode_data >> 9) & 0xF80) | ((data->decode_data >> 8) & 0x7F);
ret.temp = ((float)(u16)-1000) / 10.0f;
break;
case FPW_Acurite606TX:
ret.id = (data->decode_data >> 24) & 0xFF;
ret.battery_low = (data->decode_data >> 23) & 1;
if (!((data->decode_data >> 19) & 1)) {
ret.temp = (float)((data->decode_data >> 8) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 8) & 0x07FF) + 1) / -10.0f;
}
break;
case FPW_Acurite609TX:
ret.id = (data->decode_data >> 32) & 0xFF;
ret.battery_low = (data->decode_data >> 31) & 1;
ret.channel = WS_NO_CHANNEL;
// Temperature in Celsius is encoded as a 12 bit integer value
// multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2)
// negative values are recovered by sign extend from int16_t.
i16 = (int16_t)(((data->decode_data >> 12) & 0xf000) | ((data->decode_data >> 16) << 4));
ret.temp = (i16 >> 4) * 0.1f;
ret.humidity = (data->decode_data >> 8) & 0xff;
break;
case FPW_Ambient:
id = (data->decode_data >> 32) & 0xFF;
ret.battery_low = (data->decode_data >> 31) & 1;
ret.channel = ((data->decode_data >> 28) & 0x07) + 1;
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data->decode_data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
ret.humidity = (data->decode_data >> 8) & 0xFF;
break;
case FPW_AuriolAhfl:
ret.id = data->decode_data >> 34;
ret.battery_low = (data->decode_data >> 33) & 1;
// btn = (data >> 32) & 1;
ret.channel = ((data->decode_data >> 30) & 0x3) + 1;
if (!((data->decode_data >> 29) & 1)) {
ret.temp = (float)((data->decode_data >> 18) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 18) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = (data->decode_data >> 11) & 0x7F;
break;
case FPW_AuriolTH:
ret.id = (data->decode_data >> 31) & 0xFF;
ret.battery_low = ((data->decode_data >> 30) & 1);
ret.channel = ((data->decode_data >> 25) & 0x03) + 1;
if (!((data->decode_data >> 23) & 1)) {
ret.temp = (float)((data->decode_data >> 13) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 13) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = (data->decode_data >> 1) & 0x7F;
break;
case FPW_GTWT02:
ret.id = (data->decode_data >> 29) & 0xFF;
ret.battery_low = (data->decode_data >> 28) & 1;
// btn = (data->decode_data >> 27) & 1;
ret.channel = ((data->decode_data >> 25) & 0x3) + 1;
if (!((data->decode_data >> 24) & 1)) {
ret.temp = (float)((data->decode_data >> 13) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 13) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = (data->decode_data >> 6) & 0x7F;
if (ret.humidity <= 10) // actually the sensors sends 10 below working range of 20%
ret.humidity = 0;
else if (ret.humidity > 90) // actually the sensors sends 110 above working range of 90%
ret.humidity = 100;
break;
case FPW_GTWT03:
ret.id = data->decode_data >> 33;
ret.humidity = (data->decode_data >> 25) & 0xFF;
if (ret.humidity <= 10) { // actually the sensors sends 10 below working range of 20%
ret.humidity = 0;
} else if (ret.humidity > 95) { // actually the sensors sends 110 above working range of 90%
ret.humidity = 100;
}
ret.battery_low = (data->decode_data >> 24) & 1;
// (data->decode_data >> 23) & 1;
ret.channel = ((data->decode_data >> 21) & 0x03) + 1;
if (!((data->decode_data >> 20) & 1)) {
ret.temp = (float)((data->decode_data >> 9) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 9) & 0x07FF) + 1) / -10.0f;
}
break;
case FPW_INFACTORY:
ret.id = data->decode_data >> 32;
ret.battery_low = (data->decode_data >> 26) & 1;
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data->decode_data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
ret.humidity = (((data->decode_data >> 8) & 0x0F) * 10) + ((data->decode_data >> 4) & 0x0F); // BCD, 'A0'=100%rH
ret.channel = data->decode_data & 0x03;
break;
case FPW_LACROSSETX:
u8 = (data->decode_data >> 32) & 0x0F;
ret.id = (((data->decode_data >> 28) & 0x0F) << 3) | (((data->decode_data >> 24) & 0x0F) >> 1);
flo = (float)((data->decode_data >> 20) & 0x0F) * 10.0f + (float)((data->decode_data >> 16) & 0x0F) + (float)((data->decode_data >> 12) & 0x0F) * 0.1f;
if (u8 == LACROSSE_TX_MSG_TYPE_TEMP) { //-V1051
ret.temp = flo - 50.0f;
ret.humidity = WS_NO_HUMIDITY;
} else if (u8 == LACROSSE_TX_MSG_TYPE_HUM) {
// ToDo for verification, records are needed with sensors maintaining temperature and temperature for this standard
ret.humidity = (uint8_t)flo;
}
break;
case FPW_LACROSSETX141thbv2:
ret.id = data->decode_data >> 32;
ret.battery_low = (data->decode_data >> 31) & 1;
// btn = (data->decode_data >> 30) & 1;
ret.channel = ((data->decode_data >> 28) & 0x03) + 1;
ret.temp = ((float)((data->decode_data >> 16) & 0x0FFF) - 500.0f) / 10.0f;
ret.humidity = (data->decode_data >> 8) & 0xFF;
break;
case FPW_OREGON2:
i16 = ((data->decode_data >> 4) & 0xF) * 10 + (((data->decode_data >> 4) >> 4) & 0xF);
i16 *= 10;
i16 += (data->decode_data >> 12) & 0xF;
if (data->decode_data & 0xF) i16 = -i16;
ret.temp = (float)i16 / 10.0;
// todo fix missing parts.
break;
case FPW_OREGON3:
// todo check
ret.humidity = ((data->decode_data >> 4) & 0xF) * 10 + (((data->decode_data >> 4) >> 4) & 0xF);
i16 = (((data->decode_data >> 12) >> 4) & 0xF) * 10 + ((((data->decode_data >> 12) >> 4) >> 4) & 0xF); // ws_oregon3_bcd_decode_short((data->decode_data >> 12) >> 4);
i16 *= 10;
i16 += ((data->decode_data >> 12) >> 12) & 0xF;
if ((data->decode_data >> 12) & 0xF) i16 = -i16;
ret.temp = (float)i16 / 10.0;
break;
case FPW_OREGONv1:
u64 = FProtoGeneral::subghz_protocol_blocks_reverse_key(data->decode_data, 32);
id = u64 & 0xFF;
ret.channel = ((u64 >> 6) & 0x03) + 1;
ret.temp = ((u64 >> 8) & 0x0F) * 0.1f + ((u64 >> 12) & 0x0F) + ((u64 >> 16) & 0x0F) * 10.0f;
if (!((u64 >> 21) & 1)) {
ret.temp = ret.temp;
} else {
ret.temp = -ret.temp;
}
ret.battery_low = !((u64 >> 23) & 1ULL);
break;
case FPW_THERMOPROTX4:
ret.id = (data->decode_data >> 25) & 0xFF;
ret.battery_low = (data->decode_data >> 24) & 1;
// btn = (data->decode_data >> 23) & 1;
ret.channel = ((data->decode_data >> 21) & 0x03) + 1;
if (!((data->decode_data >> 20) & 1)) {
ret.temp = (float)((data->decode_data >> 9) & 0x07FF) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 9) & 0x07FF) + 1) / -10.0f;
}
ret.humidity = (data->decode_data >> 1) & 0xFF;
break;
case FPW_TX_8300:
ret.humidity = (((data->decode_data >> 28) & 0x0F) * 10) + ((data->decode_data >> 24) & 0x0F);
if (!((data->decode_data >> 22) & 0x03))
ret.battery_low = 0;
else
ret.battery_low = 1;
ret.channel = (data->decode_data >> 20) & 0x03;
ret.id = (data->decode_data >> 12) & 0x7F;
ret.temp = ((data->decode_data >> 8) & 0x0F) * 10.0f + ((data->decode_data >> 4) & 0x0F) +
(data->decode_data & 0x0F) * 0.1f;
if (!((data->decode_data >> 19) & 1)) {
ret.temp = ret.temp;
} else {
ret.temp = -ret.temp;
}
break;
case FPW_WENDOX_W6726:
ret.id = (data->decode_data >> 24) & 0xFF;
ret.battery_low = (data->decode_data >> 6) & 1;
if (((data->decode_data >> 23) & 1)) {
ret.temp = (float)(((data->decode_data >> 14) & 0x1FF) + 12) / 10.0f;
} else {
ret.temp = (float)((~(data->decode_data >> 14) & 0x1FF) + 1 - 12) / -10.0f;
}
if (ret.temp < -50.0f) {
ret.temp = -50.0f;
} else if (ret.temp > 70.0f) {
ret.temp = 70.0f;
}
break;
case FPW_Acurite986:
ret.id = FProtoGeneral::subghz_protocol_blocks_reverse_key(data->decode_data >> 24, 8);
ret.id = (id << 8) | FProtoGeneral::subghz_protocol_blocks_reverse_key(data->decode_data >> 16, 8);
ret.battery_low = (data->decode_data >> 14) & 1;
ret.channel = ((data->decode_data >> 15) & 1) + 1;
i16 = FProtoGeneral::subghz_protocol_blocks_reverse_key(data->decode_data >> 32, 8);
if (i16 & 0x80) {
i16 = -(i16 & 0x7F);
}
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius((float)i16);
break;
case FPW_KEDSUM:
ret.id = data->decode_data >> 32;
if ((data->decode_data >> 30) & 0x3) {
ret.battery_low = 0;
} else {
ret.battery_low = 1;
}
ret.channel = ((data->decode_data >> 28) & 0x3) + 1;
u16 = ((data->decode_data >> 16) & 0x0f) << 8 | ((data->decode_data >> 20) & 0x0f) << 4 | ((data->decode_data >> 24) & 0x0f);
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)u16 - 900.0f) / 10.0f);
ret.humidity = ((data->decode_data >> 8) & 0x0f) << 4 | ((data->decode_data >> 12) & 0x0f);
break;
case FPW_Acurite5in1:
u8 = ((data->decode_data >> 62) & 0x03);
ret.channel = channel_3021[u8];
ret.id = (data->decode_data >> 48) & 0x3FFF;
ret.battery_low = !((data->decode_data >> 46) & 1);
ret.humidity = (data->decode_data >> 8) & 0x7F;
u16 = ((data->decode_data >> (24 - 7)) & 0x780) | ((data->decode_data >> 16) & 0x7F);
ret.temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)(u16)-400) / 10.0f);
break;
case FPW_EmosE601x:
ret.id = 0xffff; //(upper_decode_data >> 24) & 0xff; //todo maybe with oregon, and +64bit usage/message
ret.battery_low = (data->decode_data >> 10) & 1;
i16 = (data->decode_data >> 40) & 0xfff;
/* Handle signed data */
if (i16 & 0x800) {
i16 |= 0xf000;
}
ret.temp = (float)i16 / 10.0;
ret.humidity = (data->decode_data >> 32) & 0xff;
ret.channel = (data->decode_data >> 52) & 0x03;
break;
case FPW_SolightTE44:
ret.id = (data->decode_data >> 28) & 0xff;
ret.battery_low = !(data->decode_data >> 27) & 0x01;
ret.channel = ((data->decode_data >> 24) & 0x03) + 1;
i16 = (data->decode_data >> 12) & 0x0fff;
/* Handle signed data */
if (i16 & 0x0800) {
i16 |= 0xf000;
}
ret.temp = (float)i16 / 10.0;
break;
case FPW_Invalid:
default:
break;
}
return ret;
}
} // namespace ui
@@ -96,7 +96,7 @@ class WeatherView : public View {
private:
void on_tick_second();
void on_data(const WeatherDataMessage* data);
WeatherRecentEntry process_data(const WeatherDataMessage* data);
NavigationView& nav_;
RxRadioState radio_state_{
433'920'000 /* frequency */,
@@ -151,6 +151,14 @@ class WeatherView : public View {
}};
WeatherRecentEntriesView recent_entries_view{columns, recent};
void on_freqchg(int64_t freq);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
MessageHandlerRegistration message_handler_packet{
Message::ID::WeatherData,
[this](Message* const p) {
+2
View File
@@ -281,6 +281,7 @@ void EventDispatcher::emulateTouch(ui::TouchEvent event) {
while (injected_touch_event != nullptr) {
chThdSleepMilliseconds(5);
}
injected_touch_event = nullptr; // to clean event_mo.cpp, compile warning error : "storing the address of local variable 'event' in 'this_4(D)->injected_touch_event' [-Wdangling-pointer=]"
}
void EventDispatcher::emulateKeyboard(ui::KeyboardEvent event) {
@@ -288,6 +289,7 @@ void EventDispatcher::emulateKeyboard(ui::KeyboardEvent event) {
while (injected_keyboard_event != nullptr) {
chThdSleepMilliseconds(5);
}
injected_keyboard_event = nullptr; // to clean event_mo.cpp, compile warning error : "storing the address of local variable 'event' in 'this_4(D)->injected_keyboard_event' [-Wdangling-pointer=]"
}
void EventDispatcher::on_keyboard_event(ui::KeyboardEvent event) {
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* 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.hpp"
#include "ui_adsb_tx.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::adsbtx {
void initialize_app(ui::NavigationView& nav) {
nav.push<ADSBTxView>();
}
} // namespace ui::external_app::adsbtx
extern "C" {
__attribute__((section(".external_app.app_adsbtx.application_information"), used)) application_information_t _application_information_adsbtx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::adsbtx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "ADSB-TX",
/*.bitmap_data = */ {
0x80,
0x01,
0xC0,
0x03,
0xC0,
0x03,
0xC0,
0x03,
0xC0,
0x03,
0xE0,
0x07,
0xF8,
0x1F,
0xFE,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0xC0,
0x03,
0xC0,
0x03,
0xC0,
0x03,
0xE0,
0x07,
0xF0,
0x0F,
0xF8,
0x1F,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_adsbtx */ {'P', 'A', 'D', 'T'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -34,7 +34,7 @@
using namespace adsb;
using namespace portapack;
namespace ui {
namespace ui::external_app::adsbtx {
Compass::Compass(
const Point parent_pos)
@@ -311,7 +311,8 @@ void ADSBTxView::start_tx() {
ADSBTxView::ADSBTxView(
NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_adsb_tx);
// baseband::run_image(portapack::spi_flash::image_tag_adsb_tx);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&tab_view,
&labels,
@@ -341,4 +342,4 @@ ADSBTxView::ADSBTxView(
};
}
} /* namespace ui */
} /* namespace ui::external_app::adsbtx */
@@ -34,7 +34,7 @@
using namespace adsb;
namespace ui {
namespace ui::external_app::adsbtx {
class Compass : public Widget {
public:
@@ -251,4 +251,4 @@ class ADSBTxView : public View {
std::unique_ptr<ADSBTXThread> tx_thread{};
};
} /* namespace ui */
} // namespace ui::external_app::adsbtx
+4
View File
@@ -143,6 +143,10 @@ void AFSKRxView::on_data(uint32_t value, bool is_data) {
}
}
void AFSKRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
AFSKRxView::~AFSKRxView() {
audio::output::stop();
receiver_model.disable();
+9
View File
@@ -117,6 +117,15 @@ class AFSKRxView : public View {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data(message->value, message->is_data);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} // namespace ui::external_app::afsk_rx
@@ -202,4 +202,8 @@ void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) {
receiver_model.enable();
}
void AnalogTvView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} // namespace ui::external_app::analogtv
@@ -108,6 +108,15 @@ class AnalogTvView : public View {
void set_options_widget(std::unique_ptr<Widget> new_widget);
void update_modulation(const ReceiverModel::Mode modulation);
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} // namespace ui::external_app::analogtv
+50
View File
@@ -327,6 +327,52 @@ void BLESpamView::createNameSpamPacket() {
std::copy(res.begin(), res.end(), advertisementData);
}
char BLESpamView::randomNameChar() {
int randVal = rand() % 62; // 26 uppercase + 26 lowercase + 10 digits
if (randVal < 26) {
return 'A' + randVal; // Uppercase letters
} else if (randVal < 52) {
return 'a' + (randVal - 26); // Lowercase letters
} else {
return '0' + (randVal - 52); // Digits
}
}
void BLESpamView::createNameRandomPacket() {
char name[] = " ";
uint8_t name_len = strlen(name);
for (uint8_t i = 0; i < name_len; ++i) {
name[i] = randomNameChar();
}
uint8_t size = 12 + name_len;
uint8_t i = 0;
packet[i++] = 2; // Size
packet[i++] = 0x01; // AD Type (Flags)
packet[i++] = 0x06; // Flags
packet[i++] = name_len + 1; // Size
packet[i++] = 0x09; // AD Type (Complete Local Name)
memcpy(&packet[i], name, name_len); // Device Name
i += name_len;
packet[i++] = 3; // Size
packet[i++] = 0x02; // AD Type (Incomplete Service UUID List)
packet[i++] = 0x12; // Service UUID (Human Interface Device)
packet[i++] = 0x18; // ...
packet[i++] = 2; // Size
packet[i++] = 0x0A; // AD Type (Tx Power Level)
packet[i++] = 0x00; // 0dBm
// size, packet
std::string res = to_string_hex_array(packet, size);
memset(advertisementData, 0, sizeof(advertisementData));
std::copy(res.begin(), res.end(), advertisementData);
}
void BLESpamView::createIosPacket(bool crash = false) {
uint8_t ios_packet_sizes[18] = {0, 0, 0, 0, 0, 24, 0, 31, 0, 12, 0, 0, 20, 0, 12, 11, 11, 17};
ContinuityType type;
@@ -550,6 +596,7 @@ void BLESpamView::createAnyPacket(bool safe) {
ATK_WINDOWS,
ATK_SAMSUNG,
ATK_NAMESPAM,
ATK_NAMERANDOM,
ATK_IOS_CRASH};
ATK_TYPE attackType = type[rand() % (COUNT_OF(type) - (1 ? safe : 0))];
createPacket(attackType);
@@ -572,6 +619,9 @@ void BLESpamView::createPacket(ATK_TYPE attackType) {
case ATK_NAMESPAM:
createNameSpamPacket();
break;
case ATK_NAMERANDOM:
createNameRandomPacket();
break;
case ATK_ALL_SAFE:
createAnyPacket(true);
break;
+6 -2
View File
@@ -51,6 +51,7 @@ enum ATK_TYPE {
ATK_WINDOWS,
ATK_SAMSUNG,
ATK_NAMESPAM,
ATK_NAMERANDOM,
ATK_ALL_SAFE,
ATK_ALL
};
@@ -129,8 +130,9 @@ class BLESpamView : public View {
{"Windows", 3},
{"Samsung", 4},
{"NameSpam", 5},
{"All-Safe", 6},
{"All", 7}}};
{"NameRandom", 6},
{"All-Safe", 7},
{"All", 8}}};
bool is_running{false};
@@ -155,6 +157,7 @@ class BLESpamView : public View {
void createSamsungPacket();
void createWindowsPacket();
void createNameSpamPacket();
void createNameRandomPacket();
void createAnyPacket(bool safe);
void createPacket(ATK_TYPE attackType);
void changePacket(bool forced);
@@ -163,6 +166,7 @@ class BLESpamView : public View {
uint64_t get_freq_by_channel_number(uint8_t channel_number);
void randomizeMac();
void randomChn();
char randomNameChar();
void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len);
+10
View File
@@ -80,6 +80,14 @@ set(EXTCPPSRC
#tpmsrx
external/tpmsrx/main.cpp
external/tpmsrx/tpms_app.cpp
#protoview
external/protoview/main.cpp
external/protoview/ui_protoview.cpp
#adsbtx
external/adsbtx/main.cpp
external/adsbtx/ui_adsb_tx.cpp
)
set(EXTAPPLIST
@@ -102,4 +110,6 @@ set(EXTAPPLIST
audio_test
wardrivemap
tpmsrx
protoview
adsbtx
)
+16
View File
@@ -42,6 +42,8 @@ MEMORY
ram_external_app_audio_test(rwx) : org = 0xADC10000, len = 32k
ram_external_app_wardrivemap(rwx) : org = 0xADC20000, len = 32k
ram_external_app_tpmsrx(rwx) : org = 0xADC30000, len = 32k
ram_external_app_protoview(rwx) : org = 0xADC40000, len = 32k
ram_external_app_adsbtx(rwx) : org = 0xADC50000, len = 32k
}
SECTIONS
@@ -160,4 +162,18 @@ SECTIONS
*(*ui*external_app*tpmsrx*);
} > ram_external_app_tpmsrx
.external_app_protoview : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_protoview.application_information));
*(*ui*external_app*protoview*);
} > ram_external_app_protoview
.external_app_adsbtx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_adsbtx.application_information));
*(*ui*external_app*adsbtx*);
} > ram_external_app_adsbtx
}
@@ -103,4 +103,8 @@ void FoxhuntRxView::on_orientation(const OrientationDataMessage* msg) {
geomap.update_my_orientation(msg->angle, true);
}
void FoxhuntRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
} // namespace ui::external_app::foxhunt_rx
@@ -103,6 +103,14 @@ class FoxhuntRxView : public View {
[this](const Message* const p) {
this->on_statistics_update(static_cast<const ChannelStatisticsMessage*>(p)->statistics);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
float my_lat = 200;
float my_lon = 200;
+4
View File
@@ -125,6 +125,10 @@ void NRFRxView::on_data(uint32_t value, bool is_data) {
}
}
void NRFRxView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
NRFRxView::~NRFRxView() {
audio::output::stop();
receiver_model.disable();
+9
View File
@@ -89,6 +89,15 @@ class NRFRxView : public View {
const auto message = static_cast<const AFSKDataMessage*>(p);
this->on_data(message->value, message->is_data);
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} /* namespace ui::external_app::nrf_rx */
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2024 HTotoo
*
* 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.hpp"
#include "ui_protoview.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::protoview {
void initialize_app(ui::NavigationView& nav) {
nav.push<ProtoView>();
}
} // namespace ui::external_app::protoview
extern "C" {
__attribute__((section(".external_app.app_protoview.application_information"), used)) application_information_t _application_information_protoview = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::protoview::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "ProtoView",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0xF8,
0x87,
0x08,
0x84,
0x0F,
0xFC,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0xF3,
0xE0,
0x92,
0xA0,
0x9E,
0xBF,
0x00,
0x00,
0x00,
0x00,
0xFC,
0xF3,
0x04,
0x12,
0x07,
0x1E,
},
/*.icon_color = */ ui::Color::orange().v,
/*.menu_location = */ app_location_t::RX,
/*.m4_app_tag = portapack::spi_flash::image_tag_protoview */ {'P', 'P', 'V', 'W'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+199
View File
@@ -0,0 +1,199 @@
/*
* 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_protoview.hpp"
#include "audio.hpp"
#include "rtc_time.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "file_path.hpp"
using namespace portapack;
using namespace modems;
using namespace ui;
namespace ui::external_app::protoview {
void ProtoView::focus() {
field_frequency.focus();
}
ProtoView::ProtoView(NavigationView& nav)
: nav_{nav} {
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&rssi,
&field_rf_amp,
&field_lna,
&field_vga,
&field_volume,
&field_frequency,
&labels,
&options_zoom,
&button_reset,
&waveform,
&waveform2,
&waveform3,
&waveform4});
field_frequency.set_step(100);
options_zoom.on_change = [this](size_t, int32_t v) {
zoom = v;
draw();
draw2();
};
button_reset.on_select = [this](Button&) {
reset();
};
baseband::set_subghzd_config(0, receiver_model.sampling_rate());
audio::set_rate(audio::Rate::Hz_24000);
audio::output::start();
receiver_model.enable();
}
void ProtoView::reset() {
cnt = 0;
for (uint16_t i = 0; i < MAXSIGNALBUFFER; i++) time_buffer[i] = 0;
needCntReset = false;
draw();
draw2();
}
void ProtoView::on_timer() {
timercnt++;
if ((timercnt % 90) == 0) {
if (datacnt == 0) {
needCntReset = true;
}
datacnt = 0;
}
}
void ProtoView::draw2() {
if (drawcnt < MAXDRAWCNTPERWF) {
waveform.set_length(drawcnt);
waveform2.set_length(0);
waveform3.set_length(0);
waveform4.set_length(0);
} else if (drawcnt < MAXDRAWCNTPERWF * 2) {
waveform.set_length(MAXDRAWCNTPERWF);
waveform2.set_length(drawcnt - MAXDRAWCNTPERWF);
waveform3.set_length(0);
waveform4.set_length(0);
} else if (drawcnt < MAXDRAWCNTPERWF * 3) {
waveform.set_length(MAXDRAWCNTPERWF);
waveform2.set_length(MAXDRAWCNTPERWF);
waveform3.set_length(drawcnt - MAXDRAWCNTPERWF * 2);
waveform4.set_length(0);
} else {
waveform.set_length(MAXDRAWCNTPERWF);
waveform2.set_length(MAXDRAWCNTPERWF);
waveform3.set_length(MAXDRAWCNTPERWF);
waveform4.set_length(drawcnt - MAXDRAWCNTPERWF * 3);
}
waveform.set_dirty();
waveform2.set_dirty();
waveform3.set_dirty();
waveform4.set_dirty();
}
void ProtoView::draw() {
uint32_t remain = 0;
int32_t lmax = 0;
bool lmaxstate = false;
bool state = false;
drawcnt = 0;
for (uint16_t i = 0; i < MAXDRAWCNT; i++) waveform_buffer[i] = 0; // reset
for (uint16_t i = 0; i < MAXSIGNALBUFFER; ++i) {
state = time_buffer[i] >= 0;
int32_t timeabs = state ? time_buffer[i] : -1 * time_buffer[i];
int32_t timesize = timeabs / zoom;
if (timesize == 0) {
remain += timeabs;
if (lmax < timeabs) {
lmax = timeabs;
lmaxstate = state;
}
if (remain / zoom > 0) {
timesize = remain / zoom;
state = lmaxstate;
} else {
continue;
}
}
remain = 0;
lmax = 0;
for (int32_t ii = 0; ii < timesize; ++ii) {
waveform_buffer[drawcnt++] = state;
if (drawcnt >= MAXDRAWCNT) return;
}
}
}
void ProtoView::add_time(int32_t time) {
if (cnt >= MAXSIGNALBUFFER) cnt = 0;
time_buffer[cnt++] = time;
}
void ProtoView::on_data(const ProtoViewDataMessage* message) {
// filter out invalid ones.
uint16_t start = 0;
uint16_t stop = 0;
bool has_valid = false;
for (uint16_t i = 0; i <= message->maxptr; ++i) {
if (message->times[i] >= 30000 || message->times[i] <= -30000) {
if (!has_valid) {
start = i;
}
} else {
has_valid = true;
stop = i;
}
}
if (!has_valid) return; // no valid data arrived
// if (needCntReset) reset(); //todo implement auto reset
datacnt++;
// valid data, redraw
for (uint16_t i = start; i <= stop; i++) {
add_time(message->times[i]);
}
draw();
draw2();
}
void ProtoView::on_freqchg(int64_t freq) {
field_frequency.set_value(freq);
}
ProtoView::~ProtoView() {
audio::output::stop();
receiver_model.disable();
baseband::shutdown();
}
} // namespace ui::external_app::protoview
+173
View File
@@ -0,0 +1,173 @@
/*
* 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_PROTOVIEW_H__
#define __UI_PROTOVIEW_H__
#define MAXSIGNALBUFFER 400
#define MAXDRAWCNT 600
#define MAXDRAWCNTPERWF 150
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "log_file.hpp"
#include "utility.hpp"
using namespace ui;
namespace ui::external_app::protoview {
class ProtoView : public View {
public:
ProtoView(NavigationView& nav);
~ProtoView();
void focus() override;
std::string title() const override { return "ProtoView"; };
private:
int16_t waveform_buffer[MAXDRAWCNT];
int32_t time_buffer[MAXSIGNALBUFFER];
NavigationView& nav_;
RxRadioState radio_state_{};
app_settings::SettingsManager settings_{
"rx_protoview", app_settings::Mode::RX};
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}};
AudioVolumeField field_volume{
{28 * 8, 0 * 16}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
Labels labels{
{{0 * 8, 1 * 16}, "Zoom: ", Theme::getInstance()->fg_light->foreground}};
OptionsField options_zoom{
{7 * 8, 1 * 16},
4,
{{"1", 1},
{"2", 2},
{"5", 5},
{"15", 15},
{"30", 30},
{"50", 50},
{"100", 100},
{"200", 200},
{"500", 500},
{"1000", 1000}}};
Button button_reset{
{screen_width - 12 * 8, 1 * 16, 96, 24},
LanguageHelper::currentMessages[LANG_RESET]};
Waveform waveform{
{0, 5 * 8, 240, 50},
waveform_buffer,
0,
0,
true,
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform2{
{0, 5 * 8 + 55, 240, 50},
&waveform_buffer[MAXDRAWCNTPERWF],
0,
0,
true,
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform3{
{0, 5 * 8 + 110, 240, 50},
&waveform_buffer[MAXDRAWCNTPERWF * 2],
0,
0,
true,
Theme::getInstance()->fg_yellow->foreground};
Waveform waveform4{
{0, 5 * 8 + 165, 240, 50},
&waveform_buffer[MAXDRAWCNTPERWF * 3],
0,
0,
true,
Theme::getInstance()->fg_yellow->foreground};
bool needCntReset = false;
int16_t zoom = 1; // one value in ms
uint16_t cnt = 0; // pointer to next element
uint16_t drawcnt = 0; // pointer to draw next element
uint16_t timercnt = 0; // screen refresh count
uint16_t datacnt = 0; // how many data i got. these are for track if there is no data, so need a cnt reset
void add_time(int32_t time);
void on_timer();
void on_data(const ProtoViewDataMessage* message);
void draw();
void draw2();
void reset();
MessageHandlerRegistration message_handler_packet{
Message::ID::ProtoViewData,
[this](Message* const p) {
const auto message = static_cast<const ProtoViewDataMessage*>(p);
this->on_data(message);
}};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->on_timer();
}};
MessageHandlerRegistration message_handler_freqchg{
Message::ID::FreqChangeCommand,
[this](Message* const p) {
const auto message = static_cast<const FreqChangeCommandMessage*>(p);
this->on_freqchg(message->freq);
}};
void on_freqchg(int64_t freq);
};
} // namespace ui::external_app::protoview
#endif /*__UI_PROTOVIEW_H__*/
+3 -1
View File
@@ -535,6 +535,8 @@ init_status_t init() {
set_cpu_clock_speed();
if (persistent_memory::config_lcd_inverted_mode()) display.set_inverted(true);
if (lcd_fast_setup)
draw_splash_screen_icon(0, ui::bitmap_icon_memory);
@@ -586,7 +588,7 @@ init_status_t init() {
chThdSleepMilliseconds(10);
audio::init(portapack_audio_codec());
battery::BatteryManagement::init();
battery::BatteryManagement::init(persistent_memory::ui_override_batt_calc());
if (lcd_fast_setup)
draw_splash_screen_icon(4, ui::bitmap_icon_speaker);
+16 -16
View File
@@ -68,7 +68,7 @@ ThemeYellow::ThemeYellow() {
.foreground = Color::black(),
};
bg_lightest_small = new Style{
.font = font::fixed_8x16,
.font = font::fixed_5x8,
.background = {255, 255, 204},
.foreground = Color::black(),
};
@@ -105,8 +105,8 @@ ThemeYellow::ThemeYellow() {
};
bg_important_small = new Style{
.font = ui::font::fixed_5x8,
.background = ui::Color::yellow(),
.font = font::fixed_5x8,
.background = Color::yellow(),
.foreground = {31, 31, 0},
};
@@ -201,7 +201,7 @@ ThemeAqua::ThemeAqua() {
.foreground = Color::black(),
};
bg_lightest_small = new Style{
.font = font::fixed_8x16,
.font = font::fixed_5x8,
.background = {204, 255, 255},
.foreground = Color::black(),
};
@@ -238,8 +238,8 @@ ThemeAqua::ThemeAqua() {
};
bg_important_small = new Style{
.font = ui::font::fixed_5x8,
.background = ui::Color::yellow(),
.font = font::fixed_5x8,
.background = Color::yellow(),
.foreground = {0, 31, 31},
};
@@ -334,7 +334,7 @@ ThemeDefault::ThemeDefault() {
.foreground = Color::black(),
};
bg_lightest_small = new Style{
.font = font::fixed_8x16,
.font = font::fixed_5x8,
.background = Color::white(),
.foreground = Color::black(),
};
@@ -371,9 +371,9 @@ ThemeDefault::ThemeDefault() {
};
bg_important_small = new Style{
.font = ui::font::fixed_5x8,
.background = ui::Color::yellow(),
.foreground = ui::Color::black(),
.font = font::fixed_5x8,
.background = Color::yellow(),
.foreground = Color::black(),
};
error_dark = new Style{
@@ -466,7 +466,7 @@ ThemeGreen::ThemeGreen() {
.foreground = Color::black(),
};
bg_lightest_small = new Style{
.font = font::fixed_8x16,
.font = font::fixed_5x8,
.background = {0, 245, 29},
.foreground = Color::black(),
};
@@ -503,8 +503,8 @@ ThemeGreen::ThemeGreen() {
};
bg_important_small = new Style{
.font = ui::font::fixed_5x8,
.background = ui::Color::yellow(),
.font = font::fixed_5x8,
.background = Color::yellow(),
.foreground = {0, 33, 4},
};
@@ -599,7 +599,7 @@ ThemeRed::ThemeRed() {
.foreground = Color::black(),
};
bg_lightest_small = new Style{
.font = font::fixed_8x16,
.font = font::fixed_5x8,
.background = {245, 29, 0},
.foreground = Color::black(),
};
@@ -636,8 +636,8 @@ ThemeRed::ThemeRed() {
};
bg_important_small = new Style{
.font = ui::font::fixed_5x8,
.background = ui::Color::yellow(),
.font = font::fixed_5x8,
.background = Color::yellow(),
.foreground = {33, 4, 0},
};
+3 -3
View File
@@ -35,7 +35,7 @@
#include "ui_about_simple.hpp"
#include "ui_adsb_rx.hpp"
#include "ui_adsb_tx.hpp"
// #include "ui_adsb_tx.hpp" //moved to ext
#include "ui_aprs_rx.hpp"
#include "ui_aprs_tx.hpp"
#include "ui_bht_tx.hpp"
@@ -181,7 +181,7 @@ const NavigationView::AppList NavigationView::appList = {
//{"sstv", "SSTV", RX, Color::dark_grey(), &bitmap_icon_sstv, new ViewFactory<NotImplementedView>()},
//{"tetra", "TETRA", RX, Color::dark_grey(), &bitmap_icon_tetra, new ViewFactory<NotImplementedView>()},
/* TX ********************************************************************/
{"adsbtx", "ADS-B TX", TX, ui::Color::green(), &bitmap_icon_adsb, new ViewFactory<ADSBTxView>()},
//{"adsbtx", "ADS-B TX", TX, ui::Color::green(), &bitmap_icon_adsb, new ViewFactory<ADSBTxView>()},
{"aprstx", "APRS TX", TX, ui::Color::green(), &bitmap_icon_aprs, new ViewFactory<APRSTXView>()},
{"bht", "BHT Xy/EP", TX, ui::Color::green(), &bitmap_icon_bht, new ViewFactory<BHTView>()},
{"bletx", "BLE Tx", TX, ui::Color::green(), &bitmap_icon_btle, new ViewFactory<BLETxView>()},
@@ -1072,7 +1072,7 @@ ModalMessageView::ModalMessageView(
}
void ModalMessageView::paint(Painter& painter) {
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, false);
if (!compact) portapack::display.drawBMP({100, 48}, modal_warning_bmp, true);
// Break lines.
auto lines = split_string(message_, '\n');
+18
View File
@@ -1147,6 +1147,23 @@ static void cmd_asyncmsg(BaseSequentialStream* chp, int argc, char* argv[]) {
chprintf(chp, usage);
}
}
static void cmd_setfreq(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: setfreq freq_in_hz\r\n";
if (argc != 1) {
chprintf(chp, usage);
return;
}
int64_t freq = atol(argv[0]);
if (freq <= 0) {
chprintf(chp, usage);
return;
}
// radio::set_tuning_frequency(freq); // sadly this doesn't update any widget, just change the frequency.
FreqChangeCommandMessage message{freq};
EventDispatcher::send_message(message);
chprintf(chp, "ok\r\n");
}
static const ShellCommand commands[] = {
{"reboot", cmd_reboot},
@@ -1180,6 +1197,7 @@ static const ShellCommand commands[] = {
{"settingsreset", cmd_settingsreset},
{"sendpocsag", cmd_sendpocsag},
{"asyncmsg", cmd_asyncmsg},
{"setfreq", cmd_setfreq},
{NULL, NULL}};
static const ShellConfig shell_cfg1 = {
+17 -6
View File
@@ -328,12 +328,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PADR adsbrx)
### ADS-B TX
set(MODE_CPPSRC
proc_adsbtx.cpp
)
DeclareTargets(PADT adsbtx)
### AFSK TX
@@ -648,6 +642,15 @@ set(MODE_CPPSRC
DeclareTargets(PABP audio_beep)
### ProtoView
set(MODE_CPPSRC
proc_protoview.cpp
)
DeclareTargets(PPVW protoview)
### TPMS
set(MODE_CPPSRC
@@ -656,6 +659,14 @@ set(MODE_CPPSRC
DeclareTargets(PTPM tpms)
### ADS-B TX
set(MODE_CPPSRC
proc_adsbtx.cpp
)
DeclareTargets(PADT adsbtx)
### HackRF "factory" firmware
add_custom_command(
-1
View File
@@ -36,7 +36,6 @@ class FProtoSubGhzDBett : public FProtoSubGhzDBase {
(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);
-3
View File
@@ -53,9 +53,6 @@ class FProtoSubGhzDCame : public FProtoSubGhzDBase {
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;
@@ -43,38 +43,7 @@ class FProtoSubGhzDCameAtomo : public FProtoSubGhzDBase {
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;
-65
View File
@@ -43,9 +43,7 @@ class FProtoSubGhzDCameTwee : public FProtoSubGhzDBase {
} 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;
@@ -79,69 +77,6 @@ class FProtoSubGhzDCameTwee : public FProtoSubGhzDBase {
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
@@ -57,10 +57,7 @@ class FProtoSubGhzDChambCode : public FProtoSubGhzDBase {
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);
}
-6
View File
@@ -62,13 +62,7 @@ class FProtoSubGhzDClemsa : public FProtoSubGhzDBase {
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;
-5
View File
@@ -44,12 +44,7 @@ class FProtoSubGhzDDoitrand : public FProtoSubGhzDBase {
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;
-11
View File
@@ -72,18 +72,7 @@ class FProtoSubGhzDDooya : public FProtoSubGhzDBase {
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;
-1
View File
@@ -43,7 +43,6 @@ class FProtoSubGhzDFaac : public FProtoSubGhzDBase {
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);
+86
View File
@@ -0,0 +1,86 @@
#ifndef __FPROTO_GANGQI_H__
#define __FPROTO_GANGQI_H__
#include "subghzdbase.hpp"
typedef enum {
GangQiDecoderStepReset = 0,
GangQiDecoderStepSaveDuration,
GangQiDecoderStepCheckDuration,
} GangQiDecoderStep;
class FProtoSubGhzDGangqi : public FProtoSubGhzDBase {
public:
FProtoSubGhzDGangqi() {
sensorType = FPS_GANGQI;
te_short = 500;
te_long = 1200;
te_delta = 200;
min_count_bit_for_found = 34;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case GangQiDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 2) < te_delta * 3)) {
// Found GAP
decode_data = 0;
decode_count_bit = 0;
parser_step = GangQiDecoderStepSaveDuration;
}
break;
case GangQiDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = GangQiDecoderStepCheckDuration;
} else {
parser_step = GangQiDecoderStepReset;
}
break;
case GangQiDecoderStepCheckDuration:
if (!level) {
// Bit 0 is short and long timing
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = GangQiDecoderStepSaveDuration;
// Bit 1 is long and short timing
} 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 = GangQiDecoderStepSaveDuration;
} else if (
// End of the key
DURATION_DIFF(duration, te_short * 4) <
te_delta) {
// Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes)
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short * 4) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
}
if ((DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short * 4) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
}
// If got 34 bits key reading is finished
if (decode_count_bit == min_count_bit_for_found) {
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
parser_step = GangQiDecoderStepReset;
} else {
parser_step = GangQiDecoderStepReset;
}
} else {
parser_step = GangQiDecoderStepReset;
}
break;
}
}
};
#endif
-7
View File
@@ -44,14 +44,7 @@ class FProtoSubGhzDGateTx : public FProtoSubGhzDBase {
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;
-17
View File
@@ -49,24 +49,7 @@ class FProtoSubGhzDHoltek : public FProtoSubGhzDBase {
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);
}
}
+2 -8
View File
@@ -44,14 +44,8 @@ class FProtoSubGhzDHoltekHt12x : public FProtoSubGhzDBase {
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);
}
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
@@ -71,10 +71,7 @@ class FProtoSubGhzDHoneywell : public FProtoSubGhzDBase {
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;
@@ -36,7 +36,6 @@ class FProtoSubGhzDHoneywellWdb : public FProtoSubGhzDBase {
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);
-3
View File
@@ -47,10 +47,7 @@ class FProtoSubGhzDHormann : public FProtoSubGhzDBase {
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;
@@ -0,0 +1,129 @@
#ifndef __FPROTO_HORMANNBISECURE_H__
#define __FPROTO_HORMANNBISECURE_H__
#include "subghzdbase.hpp"
typedef enum {
HormannBiSecurDecoderStepReset = 0,
HormannBiSecurDecoderStepFoundPreambleAlternatingShort,
HormannBiSecurDecoderStepFoundPreambleHighVeryLong,
HormannBiSecurDecoderStepFoundPreambleAlternatingLong,
HormannBiSecurDecoderStepFoundData,
} HormannBiSecurDecoderStep;
class FProtoSubGhzDHormannBiSecure : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHormannBiSecure() {
sensorType = FPS_HORMANN;
te_short = 208;
te_long = 416;
te_delta = 104;
min_count_bit_for_found = 176;
}
void subghz_protocol_decoder_hormann_bisecur_reset() {
parser_step = HormannBiSecurDecoderStepReset;
for (uint8_t i = 0; i < 22; ++i) dataa[i] = 0;
data_count_bit = 0;
manchester_saved_state = ManchesterStateStart1;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case HormannBiSecurDecoderStepReset:
if (!level && DURATION_DIFF(duration, duration_short + duration_half_short) < te_delta) {
parser_step = HormannBiSecurDecoderStepFoundPreambleAlternatingShort;
}
break;
case HormannBiSecurDecoderStepFoundPreambleAlternatingShort:
if (DURATION_DIFF(duration, duration_short) < te_delta) {
// stay on the same step, the pattern repeats around 21 times
break;
}
if (level && DURATION_DIFF(duration, duration_long * 4) < te_delta) {
parser_step = HormannBiSecurDecoderStepFoundPreambleHighVeryLong;
break;
}
parser_step = HormannBiSecurDecoderStepReset;
break;
case HormannBiSecurDecoderStepFoundPreambleHighVeryLong:
if (!level && DURATION_DIFF(duration, duration_long) < te_delta) {
sync_cnt = 3;
parser_step = HormannBiSecurDecoderStepFoundPreambleAlternatingLong;
break;
}
parser_step = HormannBiSecurDecoderStepReset;
break;
case HormannBiSecurDecoderStepFoundPreambleAlternatingLong:
if (level == (sync_cnt-- & 1) &&
DURATION_DIFF(duration, duration_long) < te_delta) {
if (!sync_cnt) {
FProtoGeneral::manchester_advance_alt(manchester_saved_state, event, &manchester_saved_state, NULL);
parser_step = HormannBiSecurDecoderStepFoundData;
}
// stay on the same step, or advance to the next if enough transitions are found
break;
}
parser_step = HormannBiSecurDecoderStepReset;
break;
case HormannBiSecurDecoderStepFoundData:
if (DURATION_DIFF(duration, duration_short) < te_delta ||
(
// the last bit can be arbitrary long, but it is parsed as a short
data_count_bit == min_count_bit_for_found - 1 &&
duration > duration_short)) {
event = !level ? ManchesterEventShortHigh : ManchesterEventShortLow;
}
if (DURATION_DIFF(duration, duration_long) < te_delta) {
event = !level ? ManchesterEventLongHigh : ManchesterEventLongLow;
}
if (event == ManchesterEventReset) {
subghz_protocol_decoder_hormann_bisecur_reset();
} else {
bool new_level;
if (manchester_advance_alt(instance->manchester_saved_state, event, &instance->manchester_saved_state, &new_level)) {
subghz_protocol_decoder_hormann_bisecur_add_bit(instance, new_level);
}
}
break;
}
}
void subghz_protocol_decoder_hormann_bisecur_add_bit(bool level) {
if (data_count_bit >= min_count_bit_for_found) {
return;
}
if (level) {
uint8_t byte_index = data_count_bit / 8;
uint8_t bit_index = data_count_bit % 8;
dataa[byte_index] |= 1 << (7 - bit_index);
}
data_count_bit++;
if (data_count_bit >= min_count_bit_for_found) {
if (callback) {
callback(this);
} else {
subghz_protocol_decoder_hormann_bisecur_reset();
}
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
uint8_t dataa[22] = {0};
uint8_t sync_cnt = 0;
};
#endif
-7
View File
@@ -44,14 +44,7 @@ class FProtoSubGhzDIdo : public FProtoSubGhzDBase {
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;
@@ -66,9 +66,7 @@ class FProtoSubGhzDIntertechnoV3 : public FProtoSubGhzDBase {
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;
@@ -120,29 +118,6 @@ class FProtoSubGhzDIntertechnoV3 : public FProtoSubGhzDBase {
}
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
+4 -11
View File
@@ -57,17 +57,8 @@ class FProtoSubGhzDKeeLoq : public FProtoSubGhzDBase {
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);
}
data_count_bit = min_count_bit_for_found;
if (callback) callback(this);
decode_data = 0;
decode_count_bit = 0;
header_count = 0;
@@ -102,6 +93,8 @@ class FProtoSubGhzDKeeLoq : public FProtoSubGhzDBase {
break;
}
}
uint16_t header_count = 0;
};
#endif
@@ -50,7 +50,8 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
DURATION_DIFF(duration, te_short * 2) < te_delta * 2) {
parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
decode_data = 0;
data_2 = 0;
// data_2 = 0;
// data = 0;
decode_count_bit = 0;
header_count = 0;
}
@@ -58,22 +59,17 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
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;
if (decode_count_bit == min_count_bit_for_found) {
// data = data_2;
// data_2 = decode_data; // TODO DATA2
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);
// decode_data = data;
if (callback) callback(this);
}
parser_step = KingGates_stylo_4kDecoderStepReset;
decode_data = 0;
data_2 = 0;
// data_2 = 0;
decode_count_bit = 0;
header_count = 0;
break;
@@ -106,7 +102,7 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
header_count = 0;
}
if (decode_count_bit == 53) {
data_2 = decode_data;
// data_2 = decode_data;
decode_data = 0;
}
} else {
@@ -118,7 +114,9 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
}
protected:
uint64_t data_2 = 0;
// uint64_t data_2 = 0;
// uint64_t data = 0;
uint16_t header_count = 0;
};
#endif
+107
View File
@@ -0,0 +1,107 @@
#ifndef __FPROTO_LEGRAND_H__
#define __FPROTO_LEGRAND_H__
#include "subghzdbase.hpp"
typedef enum {
LegrandDecoderStepReset = 0,
LegrandDecoderStepFirstBit,
LegrandDecoderStepSaveDuration,
LegrandDecoderStepCheckDuration,
} LegrandDecoderStep;
class FProtoSubGhzDLegrand : public FProtoSubGhzDBase {
public:
FProtoSubGhzDLegrand() {
sensorType = FPS_LEGRAND;
te_short = 375;
te_long = 1125;
te_delta = 150;
min_count_bit_for_found = 18;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case LegrandDecoderStepReset:
if (!level && DURATION_DIFF(duration, te_short * 16) < te_delta * 8) {
parser_step = LegrandDecoderStepFirstBit;
decode_data = 0;
decode_count_bit = 0;
te = 0;
}
break;
case LegrandDecoderStepFirstBit:
if (level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
subghz_protocol_blocks_add_bit(0);
te += duration * 4; // long low that is part of sync, then short high
}
if (DURATION_DIFF(duration, te_long) < te_delta * 3) {
subghz_protocol_blocks_add_bit(1);
te += duration / 3 * 4; // short low that is part of sync, then long high
}
if (decode_count_bit > 0) {
// advance to the next step if either short or long is found
parser_step = LegrandDecoderStepSaveDuration;
break;
}
}
parser_step = LegrandDecoderStepReset;
break;
case LegrandDecoderStepSaveDuration:
if (!level) {
te_last = duration;
te += duration;
parser_step = LegrandDecoderStepCheckDuration;
break;
}
parser_step = LegrandDecoderStepReset;
break;
case LegrandDecoderStepCheckDuration:
if (level) {
uint8_t found = 0;
if (DURATION_DIFF(te_last, te_long) < te_delta * 3 && DURATION_DIFF(duration, te_short) < te_delta) {
found = 1;
subghz_protocol_blocks_add_bit(0);
}
if (DURATION_DIFF(te_last, te_short) < te_delta && DURATION_DIFF(duration, te_long) < te_delta * 3) {
found = 1;
subghz_protocol_blocks_add_bit(1);
}
if (found) {
te += duration;
if (decode_count_bit < min_count_bit_for_found) {
parser_step = LegrandDecoderStepSaveDuration;
break;
}
// enough bits for a packet found, save it only if there was a previous packet
// with the same data
te /= decode_count_bit * 4;
data_count_bit = decode_count_bit;
if (callback) {
callback(this);
}
// fallthrough to reset, the next bit is expected to be a sync
// it also takes care of resetting the decoder state
}
}
parser_step = LegrandDecoderStepReset;
break;
}
}
protected:
uint32_t te = 0;
};
#endif
-4
View File
@@ -54,11 +54,7 @@ class FProtoSubGhzDLinear : public FProtoSubGhzDBase {
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;
@@ -49,15 +49,8 @@ class FProtoSubGhzDLinearDelta3 : public FProtoSubGhzDBase {
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);
}
data_count_bit = decode_count_bit;
if (callback) callback(this);
parser_step = LinearD3DecoderStepSaveDuration;
}
break;
+1 -6
View File
@@ -90,13 +90,7 @@ class FProtoSubGhzDMagellan : public FProtoSubGhzDBase {
// 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;
@@ -134,6 +128,7 @@ class FProtoSubGhzDMagellan : public FProtoSubGhzDBase {
}
return crc;
}
uint16_t header_count = 0;
};
#endif
-4
View File
@@ -43,11 +43,7 @@ class FProtoSubGhzDMarantec : public FProtoSubGhzDBase {
} 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;
@@ -0,0 +1,93 @@
#ifndef __FPROTO_MARANTEC24_H__
#define __FPROTO_MARANTEC24_H__
#include "subghzdbase.hpp"
typedef enum {
Marantec24DecoderStepReset = 0,
Marantec24DecoderStepSaveDuration,
Marantec24DecoderStepCheckDuration,
} Marantec24DecoderStep;
class FProtoSubGhzDMarantec24 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDMarantec24() {
sensorType = FPS_MARANTEC24;
te_short = 800;
te_long = 1600;
te_delta = 200;
min_count_bit_for_found = 24;
}
void feed(bool level, uint32_t duration) {
// Key samples
// 101011000000010111001000 = AC05C8
// 101011000000010111000100 = AC05C4
// 101011000000010111001100 = AC05CC
// 101011000000010111000000 = AC05C0
switch (parser_step) {
case Marantec24DecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 9) < te_delta * 4)) {
// Found GAP
decode_data = 0;
decode_count_bit = 0;
parser_step = Marantec24DecoderStepSaveDuration;
}
break;
case Marantec24DecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = Marantec24DecoderStepCheckDuration;
} else {
parser_step = Marantec24DecoderStepReset;
}
break;
case Marantec24DecoderStepCheckDuration:
if (!level) {
// Bit 0 is long and short x2 timing = 1600us HIGH (te_last) and 2400us LOW
if ((DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short * 3) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = Marantec24DecoderStepSaveDuration;
// Bit 1 is short and long x2 timing = 800us HIGH (te_last) and 3200us LOW
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long * 2) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = Marantec24DecoderStepSaveDuration;
} else if (
// End of the key
DURATION_DIFF(duration, te_long * 9) < te_delta * 4) {
// Found next GAP and add bit 0 or 1 (only bit 0 was found on the remotes)
if ((DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_long * 9) < te_delta * 4)) {
subghz_protocol_blocks_add_bit(0);
}
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long * 9) < te_delta * 4)) {
subghz_protocol_blocks_add_bit(1);
}
// If got 24 bits key reading is finished
if (decode_count_bit == min_count_bit_for_found) {
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
parser_step = Marantec24DecoderStepReset;
} else {
parser_step = Marantec24DecoderStepReset;
}
} else {
parser_step = Marantec24DecoderStepReset;
}
break;
}
}
protected:
};
#endif
+1 -4
View File
@@ -61,11 +61,8 @@ class FProtoSubGhzDMastercode : public FProtoSubGhzDBase {
}
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;
+1 -11
View File
@@ -48,18 +48,8 @@ class FProtoSubGhzDMegacode : public FProtoSubGhzDBase {
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;
+1 -1
View File
@@ -67,7 +67,6 @@ class FProtoSubGhzDNeroSketch : public FProtoSubGhzDBase {
// 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);
}
@@ -103,6 +102,7 @@ class FProtoSubGhzDNeroSketch : public FProtoSubGhzDBase {
break;
}
}
uint16_t header_count = 0;
};
#endif
+1 -1
View File
@@ -82,7 +82,6 @@ class FProtoSubGhzDNeroRadio : public FProtoSubGhzDBase {
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);
@@ -110,6 +109,7 @@ class FProtoSubGhzDNeroRadio : public FProtoSubGhzDBase {
break;
}
}
uint16_t header_count = 0;
};
#endif
-4
View File
@@ -47,10 +47,6 @@ class FProtoSubGhzDNiceflo : public FProtoSubGhzDBase {
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);
}
+3 -7
View File
@@ -56,13 +56,9 @@ class FProtoSubGhzDNiceflors : public FProtoSubGhzDBase {
// 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);
decode_data = 0;
}
break;
} else {
@@ -89,8 +85,8 @@ class FProtoSubGhzDNiceflors : public FProtoSubGhzDBase {
parser_step = NiceFlorSDecoderStepReset;
}
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
decode_data = 0;
// data = decode_data;
// decode_data = 0;
}
break;
}
@@ -45,14 +45,7 @@ class FProtoSubGhzDPhoenixV2 : public FProtoSubGhzDBase {
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;
@@ -49,14 +49,8 @@ class FProtoSubGhzDPowerSmart : public FProtoSubGhzDBase {
}
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;
@@ -42,11 +42,7 @@ class FProtoSubGhzDPrinceton : public FProtoSubGhzDBase {
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;
@@ -50,7 +50,6 @@ class FProtoSubGhzDSecPlusV2 : public FProtoSubGhzDBase {
} 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
-1
View File
@@ -42,7 +42,6 @@ class FProtoSubGhzDSmc5326 : public FProtoSubGhzDBase {
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);
}
@@ -0,0 +1,129 @@
#ifndef __FPROTO_SOMIFYKEYTIS_H__
#define __FPROTO_SOMIFYKEYTIS_H__
#include "subghzdbase.hpp"
typedef enum {
SomfyKeytisDecoderStepReset = 0,
SomfyKeytisDecoderStepCheckPreambula,
SomfyKeytisDecoderStepFoundPreambula,
SomfyKeytisDecoderStepStartDecode,
SomfyKeytisDecoderStepDecoderData,
} SomfyKeytisDecoderStep;
class FProtoSubGhzDSomifyKeytis : public FProtoSubGhzDBase {
public:
FProtoSubGhzDSomifyKeytis() {
sensorType = FPS_SOMIFY_KEYTIS;
te_short = 640;
te_long = 1280;
te_delta = 250;
min_count_bit_for_found = 80;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case SomfyKeytisDecoderStepReset:
if ((level) && DURATION_DIFF(duration, te_short * 4) < te_delta * 4) {
parser_step = SomfyKeytisDecoderStepFoundPreambula;
header_count++;
}
break;
case SomfyKeytisDecoderStepFoundPreambula:
if ((!level) && (DURATION_DIFF(duration, te_short * 4) < te_delta * 4)) {
parser_step = SomfyKeytisDecoderStepCheckPreambula;
} else {
header_count = 0;
parser_step = SomfyKeytisDecoderStepReset;
}
break;
case SomfyKeytisDecoderStepCheckPreambula:
if (level) {
if (DURATION_DIFF(duration, te_short * 4) < te_delta * 4) {
parser_step = SomfyKeytisDecoderStepFoundPreambula;
header_count++;
} else if (
(header_count > 1) && (DURATION_DIFF(duration, te_short * 7) < te_delta * 4)) {
parser_step = SomfyKeytisDecoderStepDecoderData;
decode_data = 0;
decode_count_bit = 0;
// press_duration_counter = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
}
}
break;
case SomfyKeytisDecoderStepDecoderData:
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 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
// check crc
uint64_t data_tmp = decode_data ^ (decode_data >> 8);
if (((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_keytis_crc(data_tmp)) {
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
}
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);
parser_step = SomfyKeytisDecoderStepReset;
} else {
parser_step = SomfyKeytisDecoderStepReset;
}
} 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 = SomfyKeytisDecoderStepReset;
}
}
if (event != ManchesterEventReset) {
bool data;
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
if (data_ok) {
if (decode_count_bit < 56) {
decode_data = (decode_data << 1) | data;
} else {
// press_duration_counter = (press_duration_counter << 1) | data;
}
decode_count_bit++;
}
}
break;
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
uint8_t subghz_protocol_somfy_keytis_crc(uint64_t data) {
uint8_t crc = 0;
data &= 0xFFF0FFFFFFFFFF;
for (uint8_t i = 0; i < 56; i += 8) {
crc = crc ^ data >> i ^ (data >> (i + 4));
}
return crc & 0xf;
}
uint16_t header_count = 0;
};
#endif
@@ -0,0 +1,123 @@
#ifndef __FPROTO_SOMIFYTELIS_H__
#define __FPROTO_SOMIFYTELIS_H__
#include "subghzdbase.hpp"
typedef enum {
SomfyTelisDecoderStepReset = 0,
SomfyTelisDecoderStepCheckPreambula,
SomfyTelisDecoderStepFoundPreambula,
SomfyTelisDecoderStepStartDecode,
SomfyTelisDecoderStepDecoderData,
} SomfyTelisDecoderStep;
class FProtoSubGhzDSomifyTelis : public FProtoSubGhzDBase {
public:
FProtoSubGhzDSomifyTelis() {
sensorType = FPS_SOMIFY_TELIS;
te_short = 640;
te_long = 1280;
te_delta = 250;
min_count_bit_for_found = 56;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case SomfyTelisDecoderStepReset:
if ((level) && DURATION_DIFF(duration, te_short * 4) < te_delta * 4) {
parser_step = SomfyTelisDecoderStepFoundPreambula;
header_count++;
}
break;
case SomfyTelisDecoderStepFoundPreambula:
if ((!level) && (DURATION_DIFF(duration, te_short * 4) < te_delta * 4)) {
parser_step = SomfyTelisDecoderStepCheckPreambula;
} else {
header_count = 0;
parser_step = SomfyTelisDecoderStepReset;
}
break;
case SomfyTelisDecoderStepCheckPreambula:
if (level) {
if (DURATION_DIFF(duration, te_short * 4) < te_delta * 4) {
parser_step = SomfyTelisDecoderStepFoundPreambula;
header_count++;
} else if (
(header_count > 1) &&
(DURATION_DIFF(duration, te_short * 7) < te_delta * 4)) {
parser_step = SomfyTelisDecoderStepDecoderData;
decode_data = 0;
decode_count_bit = 0;
header_count = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
}
}
break;
case SomfyTelisDecoderStepDecoderData:
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 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
// check crc
uint64_t data_tmp = decode_data ^ (decode_data >> 8);
if (((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_telis_crc(data_tmp)) {
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
}
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);
parser_step = SomfyTelisDecoderStepReset;
} else {
parser_step = SomfyTelisDecoderStepReset;
}
} 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 = SomfyTelisDecoderStepReset;
}
}
if (event != ManchesterEventReset) {
bool data;
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &data);
if (data_ok) {
decode_data = (decode_data << 1) | data;
decode_count_bit++;
}
}
break;
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
uint8_t subghz_protocol_somfy_telis_crc(uint64_t data) {
uint8_t crc = 0;
data &= 0xFFF0FFFFFFFFFF;
for (uint8_t i = 0; i < 56; i += 8) {
crc = crc ^ data >> i ^ (data >> (i + 4));
}
return crc & 0xf;
}
uint16_t header_count = 0;
};
#endif
+3 -5
View File
@@ -53,11 +53,8 @@ class FProtoSubGhzDStarLine : public FProtoSubGhzDBase {
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);
}
data_count_bit = min_count_bit_for_found;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
@@ -101,6 +98,7 @@ class FProtoSubGhzDStarLine : public FProtoSubGhzDBase {
break;
}
}
uint16_t header_count = 0;
};
#endif
-1
View File
@@ -47,7 +47,6 @@ class FProtoSubGhzDX10 : public FProtoSubGhzDBase {
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);
+3 -6
View File
@@ -25,11 +25,8 @@ class FProtoSubGhzDBase {
// 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;
uint64_t decode_data = 0;
protected:
// Helper functions to keep it as compatible with flipper as we can, so adding new protos will be easy.
@@ -45,11 +42,11 @@ class FProtoSubGhzDBase {
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;
//
};
+13 -5
View File
@@ -47,7 +47,12 @@ So include here the .hpp, and add a new element to the protos vector in the cons
#include "s-smc5326.hpp"
#include "s-star_line.hpp"
#include "s-x10.hpp"
// #include "s-hormannbisecure.hpp" //fm
#include "s-legrand.hpp"
#include "s-somify_keytis.hpp"
#include "s-somify_telis.hpp"
#include "s-gangqi.hpp"
#include "s-marantec24.hpp"
// GENIE FROM PR
#ifndef __FPROTO_PROTOLISTSGZ_H__
@@ -94,11 +99,14 @@ class SubGhzDProtos : public FProtoListGeneral {
protos[FPS_SECPLUSV1] = new FProtoSubGhzDSecPlusV1();
protos[FPS_SECPLUSV2] = new FProtoSubGhzDSecPlusV2();
protos[FPS_SMC5326] = new FProtoSubGhzDSmc5326();
// somify keytis skipped
// somify telis skipped
protos[FPS_SOMIFY_KEYTIS] = new FProtoSubGhzDSomifyKeytis();
protos[FPS_SOMIFY_TELIS] = new FProtoSubGhzDSomifyTelis();
protos[FPS_STARLINE] = new FProtoSubGhzDStarLine();
protos[FPS_X10] = new FProtoSubGhzDX10();
// genie skipped
// protos[FPS_HORMANNBISECURE] = new FProtoSubGhzDHormannBiSecure(); //fm
protos[FPS_LEGRAND] = new FProtoSubGhzDLegrand();
protos[FPS_GANGQI] = new FProtoSubGhzDGangqi();
protos[FPS_MARANTEC24] = new FProtoSubGhzDMarantec24();
for (uint8_t i = 0; i < FPS_COUNT; ++i) {
if (protos[i] != NULL) protos[i]->setCallback(callbackTarget);
@@ -115,7 +123,7 @@ class SubGhzDProtos : public FProtoListGeneral {
};
static void callbackTarget(FProtoSubGhzDBase* instance) {
SubGhzDDataMessage packet_message{instance->sensorType, instance->btn, instance->data_count_bit, instance->serial, instance->data, instance->cnt};
SubGhzDDataMessage packet_message{instance->sensorType, instance->data_count_bit, instance->decode_data};
shared_memory.application_queue.push(packet_message);
}
+6 -5
View File
@@ -11,10 +11,6 @@ Also it must have a switch-case element in the getSubGhzDSensorTypeName() functi
#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,
@@ -35,10 +31,12 @@ enum FPROTO_SUBGHZD_SENSOR : uint8_t {
FPS_HONEYWELL,
FPS_HONEYWELLWDB,
FPS_HORMANN,
// FPS_HORMANNBISECURE,
FPS_IDO,
FPS_INTERTECHNOV3,
FPS_KEELOQ,
FPS_KINGGATESSTYLO4K,
FPS_LEGRAND,
FPS_LINEAR,
FPS_LINEARDELTA3,
FPS_MAGELLAN,
@@ -56,7 +54,10 @@ enum FPROTO_SUBGHZD_SENSOR : uint8_t {
FPS_SMC5326,
FPS_STARLINE,
FPS_X10,
FPS_SOMIFY_KEYTIS,
FPS_SOMIFY_TELIS,
FPS_GANGQI,
FPS_MARANTEC24,
FPS_COUNT
};
@@ -80,9 +80,6 @@ class FProtoWeatherAcurite592TXR : public FProtoWeatherBase {
if ((decode_count_bit ==
min_count_bit_for_found) &&
ws_protocol_acurite_592txr_check_crc()) {
data = decode_data;
data_count_bit = decode_count_bit;
ws_protocol_acurite_592txr_remote_controller();
if (callback) callback(this);
}
decode_data = 0;
@@ -138,18 +135,6 @@ class FProtoWeatherAcurite592TXR : public FProtoWeatherBase {
return false;
}
}
void ws_protocol_acurite_592txr_remote_controller() {
uint8_t channel_[] = {3, 0, 2, 1};
uint8_t channel_raw = ((data >> 54) & 0x03);
channel = channel_[channel_raw];
id = (data >> 40) & 0x3FFF;
battery_low = !((data >> 38) & 1);
humidity = (data >> 24) & 0x7F;
uint16_t temp_raw = ((data >> 9) & 0xF80) | ((data >> 8) & 0x7F);
temp = ((float)(temp_raw)-1000) / 10.0f;
btn = WS_NO_BTN;
}
};
#endif
+135
View File
@@ -0,0 +1,135 @@
#ifndef __FPROTO_Acurite_5in1_H__
#define __FPROTO_Acurite_5in1_H__
#include "weatherbase.hpp"
typedef enum {
Acurite_5n1DecoderStepReset = 0,
Acurite_5n1DecoderStepCheckPreambule,
Acurite_5n1DecoderStepSaveDuration,
Acurite_5n1DecoderStepCheckDuration,
} Acurite_5n1DecoderStep;
class FProtoWeatherAcurite5in1 : public FProtoWeatherBase {
public:
FProtoWeatherAcurite5in1() {
sensorType = FPW_Acurite5in1;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case Acurite_5n1DecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short * 3) < te_delta * 2)) {
parser_step = Acurite_5n1DecoderStepCheckPreambule;
te_last = duration;
header_count = 0;
}
break;
case Acurite_5n1DecoderStepCheckPreambule:
if (level) {
te_last = duration;
} else {
if ((DURATION_DIFF(te_last, te_short * 3) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short * 3) < te_delta * 2)) {
// Found preambule
header_count++;
} else if ((header_count > 2) && (header_count < 5)) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
decode_data = 0;
decode_count_bit = 0;
subghz_protocol_blocks_add_bit(0);
parser_step = Acurite_5n1DecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
decode_data = 0;
decode_count_bit = 0;
subghz_protocol_blocks_add_bit(1);
parser_step = Acurite_5n1DecoderStepSaveDuration;
} else {
parser_step = Acurite_5n1DecoderStepReset;
}
} else {
parser_step = Acurite_5n1DecoderStepReset;
}
}
break;
case Acurite_5n1DecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = Acurite_5n1DecoderStepCheckDuration;
} else {
parser_step = Acurite_5n1DecoderStepReset;
}
break;
case Acurite_5n1DecoderStepCheckDuration:
if (!level) {
if (duration >= ((uint32_t)te_short * 5)) {
if ((decode_count_bit == min_count_bit_for_found) &&
ws_protocol_acurite_5n1_check_crc() &&
ws_protocol_acurite_5n1_check_message_type()) {
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
parser_step = Acurite_5n1DecoderStepReset;
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 = Acurite_5n1DecoderStepSaveDuration;
} 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 = Acurite_5n1DecoderStepSaveDuration;
} else {
parser_step = Acurite_5n1DecoderStepReset;
}
} else {
parser_step = Acurite_5n1DecoderStepReset;
}
break;
}
}
protected:
uint32_t te_short = 200;
uint32_t te_long = 400;
uint32_t te_delta = 90;
uint32_t min_count_bit_for_found = 64;
bool ws_protocol_acurite_5n1_check_message_type() {
if (((decode_data >> 40) & 0x3F) == 0x38) {
return true;
} else {
return false;
}
}
bool ws_protocol_acurite_5n1_check_crc() {
uint8_t msg[] = {
(uint8_t)(decode_data >> 56),
(uint8_t)(decode_data >> 48),
(uint8_t)(decode_data >> 40),
(uint8_t)(decode_data >> 32),
(uint8_t)(decode_data >> 24),
(uint8_t)(decode_data >> 16),
(uint8_t)(decode_data >> 8)};
if ((FProtoGeneral::subghz_protocol_blocks_add_bytes(msg, 7) ==
(uint8_t)(decode_data & 0xFF)) &&
(!FProtoGeneral::subghz_protocol_blocks_parity_bytes(&msg[2], 5))) {
return true;
} else {
return false;
}
}
};
#endif
@@ -48,9 +48,6 @@ class FProtoWeatherAcurite606TX : public FProtoWeatherBase {
if ((decode_count_bit ==
min_count_bit_for_found) &&
ws_protocol_acurite_606tx_check()) {
data = decode_data;
data_count_bit = decode_count_bit;
ws_protocol_acurite_606tx_remote_controller();
if (callback) callback(this);
}
decode_data = 0;
@@ -83,18 +80,6 @@ class FProtoWeatherAcurite606TX : public FProtoWeatherBase {
uint32_t te_delta = 150;
uint32_t min_count_bit_for_found = 32;
void ws_protocol_acurite_606tx_remote_controller() {
id = (data >> 24) & 0xFF;
battery_low = (data >> 23) & 1;
channel = WS_NO_CHANNEL;
if (!((data >> 19) & 1)) {
temp = (float)((data >> 8) & 0x07FF) / 10.0f;
} else {
temp = (float)((~(data >> 8) & 0x07FF) + 1) / -10.0f;
}
btn = WS_NO_BTN;
humidity = WS_NO_HUMIDITY;
}
bool ws_protocol_acurite_606tx_check() {
if (!decode_data) return false;
uint8_t msg[] = {
+6 -32
View File
@@ -39,31 +39,19 @@ class FProtoWeatherAcurite609TX : public FProtoWeatherBase {
case Acurite_609TXCDecoderStepCheckDuration:
if (!level) {
if (DURATION_DIFF(te_last, te_short) <
te_delta) {
if ((DURATION_DIFF(duration, te_short) <
te_delta) ||
(duration > te_long * 3)) {
if (DURATION_DIFF(te_last, te_short) < te_delta) {
if ((DURATION_DIFF(duration, te_short) < te_delta) || (duration > te_long * 3)) {
// Found syncPostfix
parser_step = Acurite_609TXCDecoderStepReset;
if ((decode_count_bit ==
min_count_bit_for_found) &&
ws_protocol_acurite_609txc_check()) {
data = decode_data;
data_count_bit = decode_count_bit;
ws_protocol_acurite_609txc_remote_controller();
if ((decode_count_bit == min_count_bit_for_found) && ws_protocol_acurite_609txc_check()) {
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
} else if (
DURATION_DIFF(duration, te_long) <
te_delta * 2) {
} else if (DURATION_DIFF(duration, te_long) < te_delta * 2) {
subghz_protocol_blocks_add_bit(0);
parser_step = Acurite_609TXCDecoderStepSaveDuration;
} else if (
DURATION_DIFF(duration, te_long * 2) <
te_delta * 4) {
} else if (DURATION_DIFF(duration, te_long * 2) < te_delta * 4) {
subghz_protocol_blocks_add_bit(1);
parser_step = Acurite_609TXCDecoderStepSaveDuration;
} else {
@@ -81,24 +69,10 @@ class FProtoWeatherAcurite609TX : public FProtoWeatherBase {
protected:
uint32_t te_short = 500;
uint32_t te_long = 2000;
uint32_t te_long = 1000;
uint32_t te_delta = 150;
uint32_t min_count_bit_for_found = 32;
void ws_protocol_acurite_609txc_remote_controller() {
id = (data >> 32) & 0xFF;
battery_low = (data >> 31) & 1;
channel = WS_NO_CHANNEL;
// Temperature in Celsius is encoded as a 12 bit integer value
// multiplied by 10 using the 4th - 6th nybbles (bytes 1 & 2)
// negative values are recovered by sign extend from int16_t.
int16_t temp_raw =
(int16_t)(((data >> 12) & 0xf000) | ((data >> 16) << 4));
temp = (temp_raw >> 4) * 0.1f;
humidity = (data >> 8) & 0xff;
btn = WS_NO_BTN;
}
bool ws_protocol_acurite_609txc_check() {
if (!decode_data) return false;
uint8_t crc = (uint8_t)(decode_data >> 32) +
@@ -84,9 +84,6 @@ class FProtoWeatherAcurite986 : public FProtoWeatherBase {
// Found syncPostfix
parser_step = Acurite_986DecoderStepReset;
if ((decode_count_bit == min_count_bit_for_found) && ws_protocol_acurite_986_check()) {
data = decode_data;
data_count_bit = decode_count_bit;
ws_protocol_acurite_986_remote_controller();
if (callback) callback(this);
}
decode_data = 0;
@@ -105,23 +102,6 @@ class FProtoWeatherAcurite986 : public FProtoWeatherBase {
uint32_t te_delta = 50;
uint32_t min_count_bit_for_found = 40;
void ws_protocol_acurite_986_remote_controller() {
int temp;
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 = FProtoGeneral::subghz_protocol_blocks_reverse_key(data >> 32, 8);
if (temp & 0x80) {
temp = -(temp & 0x7F);
}
temp = FProtoGeneral::locale_fahrenheit_to_celsius((float)temp);
btn = WS_NO_BTN;
humidity = WS_NO_HUMIDITY;
}
bool ws_protocol_acurite_986_check() {
if (!decode_data) return false;
uint8_t msg[] = {

Some files were not shown because too many files have changed in this diff Show More