mirror of
https://github.com/portapack-mayhem/mayhem-firmware.git
synced 2026-09-13 18:19:33 +00:00
Compare commits
16 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| df3981876d | |||
| 05146638fc | |||
| 765e3be55b | |||
| 9211975868 | |||
| 96a60e82bc | |||
| 5d9428e568 | |||
| 2fdd531fe7 | |||
| f13756d4c7 | |||
| ae6724b359 | |||
| e11c28cf30 | |||
| b93fbad207 | |||
| 2bedb5f09f | |||
| 19eb6b44d5 | |||
| 048359fb0e | |||
| ae75540d35 | |||
| 9ef58b7763 |
@@ -54,7 +54,7 @@ jobs:
|
||||
run: docker run -e VERSION_STRING=${{ steps.version_date.outputs.date }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Small SD Card ZIP - No World Map
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
|
||||
@@ -66,7 +66,7 @@ jobs:
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version_date.outputs.date }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -38,7 +38,7 @@ jobs:
|
||||
run: docker run -e VERSION_STRING=${{ steps.version.outputs.version }} -i -v ${{ github.workspace }}:/havoc portapack-dev
|
||||
- name: Create Small SD Card ZIP - No World Map
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard-no-map.zip . && cd ..
|
||||
- name: Download world map
|
||||
run: |
|
||||
wget https://github.com/portapack-mayhem/mayhem-firmware/releases/download/world_map/world_map.zip
|
||||
@@ -50,7 +50,7 @@ jobs:
|
||||
zip -j firmware.zip build/firmware/portapack-h1_h2-mayhem.bin && cd flashing && zip -r ../firmware.zip *
|
||||
- name: Create SD Card ZIP
|
||||
run: |
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
mkdir -p sdcard/FIRMWARE && cp build/firmware/portapack-h1_h2-mayhem.bin sdcard/FIRMWARE/portapack-mayhem_${{ steps.version.outputs.version }}.bin && mkdir -p sdcard/APPS && cp build/firmware/application/*.ppma sdcard/APPS && cp build/firmware/standalone/*/*.ppmp sdcard/APPS && cd sdcard && zip -r ../sdcard.zip . && cd ..
|
||||
- name: Create changelog
|
||||
env:
|
||||
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
@@ -16,9 +16,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="What is the HackRF One Portapack H2+?" src="https://img.youtube.com/vi/alrFbY5vxt4/maxresdefault.jpg" width="512">](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="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="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="HackRF 101 : Everything You Need to Know to Get Started!" src="https://img.youtube.com/vi/xGR_PMD9LeU/maxresdefault.jpg" width="254">](https://grabify.link/C0J6ZR)
|
||||
|
||||
# Frequently Asked Questions
|
||||
|
||||
|
||||
@@ -177,6 +177,7 @@ set(CPPSRC
|
||||
${COMMON}/utility.cpp
|
||||
${COMMON}/wm8731.cpp
|
||||
${COMMON}/ads1110.cpp
|
||||
${COMMON}/max17055.cpp
|
||||
${COMMON}/battery.cpp
|
||||
${COMMON}/performance_counter.cpp
|
||||
${COMMON}/bmpfile.cpp
|
||||
@@ -281,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
|
||||
@@ -508,6 +509,7 @@ include_directories(. ${INCDIR})
|
||||
link_directories(${LLIBDIR})
|
||||
target_link_libraries(${PROJECT_NAME}.elf ${LIBS} "-L${CMAKE_CURRENT_LIST_DIR}/external")
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,--print-memory-usage)
|
||||
|
||||
add_custom_command(
|
||||
OUTPUT ${PROJECT_NAME}.bin
|
||||
|
||||
@@ -24,14 +24,13 @@
|
||||
|
||||
#include "event_m0.hpp"
|
||||
#include "portapack.hpp"
|
||||
|
||||
#include "battery.hpp"
|
||||
#include <cstring>
|
||||
|
||||
using namespace portapack;
|
||||
|
||||
namespace ui {
|
||||
|
||||
bool BattinfoView::needRun = true;
|
||||
void BattinfoView::focus() {
|
||||
button_exit.focus();
|
||||
}
|
||||
@@ -53,24 +52,25 @@ void BattinfoView::update_result() {
|
||||
return;
|
||||
}
|
||||
bool uichg = false;
|
||||
battery::BatteryManagement::getBatteryInfo(percent, voltage, current, isCharging);
|
||||
uint8_t valid_mask = 0;
|
||||
battery::BatteryManagement::getBatteryInfo(valid_mask, percent, voltage, current);
|
||||
// update text fields
|
||||
if (percent <= 100)
|
||||
if (percent <= 100 && (valid_mask & battery::BatteryManagement::BATT_VALID_VOLTAGE) == battery::BatteryManagement::BATT_VALID_VOLTAGE)
|
||||
text_percent.set(to_string_dec_uint(percent) + " %");
|
||||
else
|
||||
text_percent.set("UNKNOWN");
|
||||
if (voltage > 1) {
|
||||
if (voltage > 1 && (valid_mask & battery::BatteryManagement::BATT_VALID_VOLTAGE) == battery::BatteryManagement::BATT_VALID_VOLTAGE) {
|
||||
text_voltage.set(to_string_decimal(voltage / 1000.0, 3) + " V");
|
||||
} else {
|
||||
text_voltage.set("UNKNOWN");
|
||||
}
|
||||
if (current != 0) {
|
||||
if ((valid_mask & battery::BatteryManagement::BATT_VALID_CURRENT) == battery::BatteryManagement::BATT_VALID_CURRENT) {
|
||||
if (labels_opt.hidden()) uichg = true;
|
||||
labels_opt.hidden(false);
|
||||
text_current.hidden(false);
|
||||
text_charge.hidden(false);
|
||||
text_current.set(to_string_dec_int(current) + " mA");
|
||||
text_charge.set(isCharging ? "charge" : "discharge");
|
||||
text_charge.set(current >= 0 ? "Charging" : "Discharging");
|
||||
labels_opt.hidden(false);
|
||||
} else {
|
||||
if (!labels_opt.hidden()) uichg = true;
|
||||
@@ -80,7 +80,7 @@ void BattinfoView::update_result() {
|
||||
}
|
||||
if (uichg) set_dirty();
|
||||
// to update status bar too, send message in behalf of batt manager
|
||||
BatteryStateMessage msg{percent, isCharging, voltage};
|
||||
BatteryStateMessage msg{valid_mask, percent, current >= 0, voltage};
|
||||
EventDispatcher::send_message(msg);
|
||||
}
|
||||
|
||||
@@ -99,13 +99,12 @@ BattinfoView::BattinfoView(NavigationView& nav)
|
||||
};
|
||||
|
||||
update_result();
|
||||
needRun = true;
|
||||
thread = chThdCreateFromHeap(NULL, 512, NORMALPRIO + 10, BattinfoView::static_fn, this);
|
||||
if (thread == nullptr) thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, BattinfoView::static_fn, this);
|
||||
}
|
||||
|
||||
msg_t BattinfoView::static_fn(void* arg) {
|
||||
auto obj = static_cast<BattinfoView*>(arg);
|
||||
while (needRun) {
|
||||
while (!chThdShouldTerminate()) {
|
||||
chThdSleepMilliseconds(16);
|
||||
obj->on_timer();
|
||||
}
|
||||
@@ -113,7 +112,6 @@ msg_t BattinfoView::static_fn(void* arg) {
|
||||
}
|
||||
|
||||
BattinfoView::~BattinfoView() {
|
||||
needRun = false;
|
||||
if (thread) {
|
||||
chThdTerminate(thread);
|
||||
chThdWait(thread);
|
||||
|
||||
@@ -50,7 +50,6 @@ class BattinfoView : public View {
|
||||
uint8_t percent = 0;
|
||||
uint16_t voltage = 0;
|
||||
int32_t current = 0;
|
||||
bool isCharging = false;
|
||||
|
||||
Labels labels{
|
||||
{{2 * 8, 1 * 16}, "Percent:", Theme::getInstance()->fg_light->foreground},
|
||||
@@ -77,7 +76,6 @@ class BattinfoView : public View {
|
||||
{72, 17 * 16, 96, 32},
|
||||
"Back"};
|
||||
static msg_t static_fn(void* arg);
|
||||
static bool needRun;
|
||||
Thread* thread{nullptr};
|
||||
};
|
||||
|
||||
|
||||
@@ -225,6 +225,8 @@ uint32_t RegistersWidget::reg_read(const uint32_t register_number) {
|
||||
return radio::debug::second_if::register_read(register_number);
|
||||
case CT_SI5351:
|
||||
return portapack::clock_generator.read_register(register_number);
|
||||
case CT_BATTERY:
|
||||
return battery::BatteryManagement::read_register(register_number);
|
||||
case CT_AUDIO:
|
||||
return audio::debug::reg_read(register_number);
|
||||
}
|
||||
@@ -246,6 +248,9 @@ void RegistersWidget::reg_write(const uint32_t register_number, const uint32_t v
|
||||
case CT_SI5351:
|
||||
portapack::clock_generator.write_register(register_number, value);
|
||||
break;
|
||||
case CT_BATTERY:
|
||||
battery::BatteryManagement::write_register(register_number, value);
|
||||
break;
|
||||
case CT_AUDIO:
|
||||
audio::debug::reg_write(register_number, value);
|
||||
break;
|
||||
@@ -459,6 +464,10 @@ void DebugPeripheralsMenuView::on_populate() {
|
||||
{si5351x, Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this, si5351x]() { nav_.push<RegistersView>(si5351x, RegistersWidgetConfig{CT_SI5351, 188, 96, 8}); }},
|
||||
{audio::debug::codec_name(), Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>(audio::debug::codec_name(), RegistersWidgetConfig{CT_AUDIO, audio::debug::reg_count(), audio::debug::reg_count(), audio::debug::reg_bits()}); }},
|
||||
});
|
||||
if (battery::BatteryManagement::detectedModule() == battery::BatteryManagement::BatteryModules::BATT_MAX17055) {
|
||||
add_item(
|
||||
{"MAX17055", Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals_details, [this]() { nav_.push<RegistersView>("MAX17055", RegistersWidgetConfig{CT_BATTERY, 256, 16, 16}); }});
|
||||
}
|
||||
set_max_rows(2); // allow wider buttons
|
||||
}
|
||||
|
||||
@@ -496,10 +505,10 @@ void DebugMenuView::on_populate() {
|
||||
{"Peripherals", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_peripherals, [this]() { nav_.push<DebugPeripheralsMenuView>(); }},
|
||||
{"Pers. Memory", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_memory, [this]() { nav_.push<DebugPmemView>(); }},
|
||||
//{ "Radio State", ui::Theme::getInstance()->bg_darkest->foreground, nullptr, [this](){ nav_.push<NotImplementedView>(); } },
|
||||
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
|
||||
{"SD Card", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_sdcard, [this]() { nav_.push<SDCardDebugView>(); }},
|
||||
{"Temperature", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_temperature, [this]() { nav_.push<TemperatureView>(); }},
|
||||
{"Touch Test", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_notepad, [this]() { nav_.push<DebugScreenTest>(); }},
|
||||
{"Reboot", ui::Theme::getInstance()->fg_darkcyan->foreground, &bitmap_icon_setup, [this]() { nav_.push<DebugReboot>(); }},
|
||||
});
|
||||
|
||||
for (auto const& gridItem : ExternalItemsMenuLoader::load_external_items(app_location_t::DEBUG, nav_)) {
|
||||
|
||||
@@ -139,6 +139,7 @@ typedef enum {
|
||||
CT_MAX283X,
|
||||
CT_SI5351,
|
||||
CT_AUDIO,
|
||||
CT_BATTERY,
|
||||
} chip_type_t;
|
||||
|
||||
struct RegistersWidgetConfig {
|
||||
|
||||
@@ -285,7 +285,7 @@ void MicTXView::set_rxbw_defaults(bool use_app_settings) { // Initially in that
|
||||
field_rxbw.set_by_value(0);
|
||||
} else if ((mic_mod_index == MIC_MOD_USB) | (mic_mod_index == MIC_MOD_LSB)) {
|
||||
field_bw.set_value(3); // In SSB by default let's limit TX_BW to 3kHz.
|
||||
field_bw.set_range(2, 3); // User range to modify that TX_BW. (TODO pending to investigate 4khz).
|
||||
field_bw.set_range(2, 3); // User TXBW GUI range to modify that SSB TX_BW to limit SSB radiated spectrum.
|
||||
field_bw.set_step(1);
|
||||
}
|
||||
// field_bw is hidden in other modulation cases
|
||||
|
||||
@@ -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,13 @@ 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_Invalid:
|
||||
default:
|
||||
return "Unknown";
|
||||
@@ -224,7 +234,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 +249,469 @@ 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;
|
||||
}
|
||||
}
|
||||
} // namespace ui
|
||||
@@ -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;
|
||||
@@ -149,6 +145,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 +166,8 @@ class SubGhzDRecentEntryDetailView : public View {
|
||||
Button button_done{
|
||||
{screen_width - 96 - 4, screen_height - 32 - 12, 96, 32},
|
||||
"Done"};
|
||||
|
||||
void parseProtocol();
|
||||
};
|
||||
|
||||
} // namespace ui
|
||||
|
||||
@@ -203,7 +203,12 @@ 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_Invalid:
|
||||
default:
|
||||
return "Unknown";
|
||||
|
||||
+82
@@ -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
|
||||
};
|
||||
}
|
||||
Vendored
+4
-3
@@ -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 */
|
||||
Vendored
+2
-2
@@ -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
|
||||
@@ -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
@@ -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
@@ -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
@@ -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
|
||||
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,195 @@
|
||||
/*
|
||||
* 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();
|
||||
}
|
||||
|
||||
ProtoView::~ProtoView() {
|
||||
audio::output::stop();
|
||||
receiver_model.disable();
|
||||
baseband::shutdown();
|
||||
}
|
||||
|
||||
} // namespace ui::external_app::protoview
|
||||
@@ -0,0 +1,164 @@
|
||||
/*
|
||||
* 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();
|
||||
}};
|
||||
};
|
||||
|
||||
} // namespace ui::external_app::protoview
|
||||
|
||||
#endif /*__UI_PROTOVIEW_H__*/
|
||||
@@ -34,6 +34,7 @@
|
||||
#include "usb_serial.hpp"
|
||||
|
||||
#include "ads1110.hpp"
|
||||
#include "max17055.hpp"
|
||||
|
||||
#include "radio.hpp"
|
||||
#include "clock_manager.hpp"
|
||||
|
||||
@@ -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>()},
|
||||
@@ -385,11 +385,16 @@ void SystemStatusView::on_battery_details() {
|
||||
}
|
||||
|
||||
void SystemStatusView::on_battery_data(const BatteryStateMessage* msg) {
|
||||
if (!batt_was_inited) {
|
||||
batt_was_inited = true;
|
||||
refresh();
|
||||
return;
|
||||
}
|
||||
if (!pmem::ui_hide_numeric_battery()) {
|
||||
battery_text.set_battery(msg->percent, msg->on_charger);
|
||||
battery_text.set_battery(msg->valid_mask, msg->percent, msg->on_charger);
|
||||
}
|
||||
if (!pmem::ui_hide_battery_icon()) {
|
||||
battery_icon.set_battery(msg->percent, msg->on_charger);
|
||||
battery_icon.set_battery(msg->valid_mask, msg->percent, msg->on_charger);
|
||||
};
|
||||
}
|
||||
|
||||
@@ -410,14 +415,15 @@ void SystemStatusView::refresh() {
|
||||
|
||||
if (!pmem::ui_hide_fake_brightness()) status_icons.add(&button_fake_brightness);
|
||||
if (battery::BatteryManagement::isDetected()) {
|
||||
batt_was_inited = true;
|
||||
uint8_t percent = battery::BatteryManagement::getPercent();
|
||||
if (!pmem::ui_hide_battery_icon()) {
|
||||
status_icons.add(&battery_icon);
|
||||
battery_text.set_battery(percent, false); // got an on select, that may pop up the details of the battery.
|
||||
battery_text.set_battery(percent <= 100 ? 1 : 0, percent, false); // got an on select, that may pop up the details of the battery.
|
||||
};
|
||||
if (!pmem::ui_hide_numeric_battery()) {
|
||||
status_icons.add(&battery_text);
|
||||
battery_text.set_battery(percent, false);
|
||||
battery_text.set_battery(percent <= 100 ? 1 : 0, percent, false);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -194,7 +194,8 @@ class SystemStatusView : public View {
|
||||
|
||||
private:
|
||||
static constexpr auto default_title = "";
|
||||
bool batt_info_up = false; // to prevent show multiple batt info dialog
|
||||
bool batt_was_inited = false; // if the battery was off on tart, but later turned on.
|
||||
bool batt_info_up = false; // to prevent show multiple batt info dialog
|
||||
NavigationView& nav_;
|
||||
|
||||
Rectangle backdrop{
|
||||
|
||||
@@ -49,6 +49,7 @@ void USBSerial::on_channel_opened() {
|
||||
}
|
||||
|
||||
void USBSerial::on_channel_closed() {
|
||||
reset_transfer_queues();
|
||||
connected = false;
|
||||
}
|
||||
|
||||
|
||||
@@ -42,6 +42,10 @@ CH_IRQ_HANDLER(USB0_IRQHandler) {
|
||||
chSysUnlockFromIsr();
|
||||
}
|
||||
|
||||
if (status & USB0_USBSTS_D_SLI) {
|
||||
on_channel_closed();
|
||||
}
|
||||
|
||||
CH_IRQ_EPILOGUE();
|
||||
}
|
||||
|
||||
@@ -131,11 +135,7 @@ usb_request_status_t usb_get_line_coding_request(usb_endpoint_t* const endpoint,
|
||||
}
|
||||
usb_request_status_t usb_set_control_line_state_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) {
|
||||
if (stage == USB_TRANSFER_STAGE_SETUP) {
|
||||
// if (endpoint->setup.value == 3) {
|
||||
on_channel_opened();
|
||||
//} else {
|
||||
// on_channel_closed();
|
||||
//}
|
||||
|
||||
usb_transfer_schedule_ack(endpoint->in);
|
||||
}
|
||||
|
||||
@@ -54,6 +54,14 @@ void init_host_to_device() {
|
||||
thread_usb_event = chThdSelf();
|
||||
}
|
||||
|
||||
void reset_transfer_queues() {
|
||||
while (usb_bulk_buffer_queue.empty() == false)
|
||||
usb_bulk_buffer_queue.pop();
|
||||
|
||||
while (usb_bulk_buffer_spare.empty() == false)
|
||||
usb_bulk_buffer_spare.pop();
|
||||
}
|
||||
|
||||
void schedule_host_to_device_transfer() {
|
||||
if (usb_bulk_buffer_queue.size() >= 8)
|
||||
return;
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#define USB_BULK_BUFFER_SIZE 64
|
||||
|
||||
void init_host_to_device();
|
||||
void reset_transfer_queues();
|
||||
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred);
|
||||
void schedule_host_to_device_transfer();
|
||||
void complete_host_to_device_transfer();
|
||||
|
||||
@@ -278,7 +278,9 @@ macro(DeclareTargets chunk_tag name)
|
||||
include_directories(. ${INCDIR} ${MODE_INCDIR})
|
||||
link_directories(${LLIBDIR})
|
||||
target_link_libraries(${PROJECT_NAME}.elf ${LIBS})
|
||||
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,-Map=${PROJECT_NAME}.map)
|
||||
target_link_libraries(${PROJECT_NAME}.elf -Wl,--print-memory-usage)
|
||||
|
||||
if(add_to_firmware)
|
||||
|
||||
@@ -326,12 +328,6 @@ set(MODE_CPPSRC
|
||||
)
|
||||
DeclareTargets(PADR adsbrx)
|
||||
|
||||
### ADS-B TX
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_adsbtx.cpp
|
||||
)
|
||||
DeclareTargets(PADT adsbtx)
|
||||
|
||||
### AFSK TX
|
||||
|
||||
@@ -646,6 +642,15 @@ set(MODE_CPPSRC
|
||||
DeclareTargets(PABP audio_beep)
|
||||
|
||||
|
||||
### ProtoView
|
||||
|
||||
set(MODE_CPPSRC
|
||||
proc_protoview.cpp
|
||||
)
|
||||
DeclareTargets(PPVW protoview)
|
||||
|
||||
|
||||
|
||||
### TPMS
|
||||
|
||||
set(MODE_CPPSRC
|
||||
@@ -654,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(
|
||||
|
||||
@@ -33,11 +33,11 @@ HilbertTransform::HilbertTransform() {
|
||||
}
|
||||
|
||||
void HilbertTransform::execute(float in, float& out_i, float& out_q) {
|
||||
// Synthesized Hilbert Transform, it is implemented based on 1/2 band LPF and later freq shift fs/4, achieving a H.T_BW of transmitted = fs/2 ;
|
||||
// Half_band LPF means a LP filter with f_cut_off = fs/4; Half band = Half max band = 1/2 * fs_max = 1/2 x f_Nyquist = 1/2 * fs/2 = fs/4
|
||||
float a = 0, b = 0;
|
||||
|
||||
float in_filtered = sos_input.execute(in) * 1.0f; // Anti-aliasing LPF at fs/4 mic audio filter front-end.
|
||||
// it is exactly matching with the usefull mic BW_cut_off =fs/4 of the following Hilbert Transf. implementation
|
||||
// Ex. to TX SSB 3khz BW (3KHZ =fs/4) ,we need to run Hiblert_Trf fs = 12khz . And our anti-aliasing filter is also fcut =fs/4 when we use a half_band_LPF .
|
||||
|
||||
switch (n) {
|
||||
case 0:
|
||||
|
||||
@@ -84,18 +84,13 @@ SSB::SSB()
|
||||
void SSB::set_fs_div_factor(float new_bw_ssb) {
|
||||
switch ((int)new_bw_ssb / 1000) {
|
||||
case 2:
|
||||
fs_div_factor = 192; // TXBW_ssb = 2khz = fs/4 ; fs of Hilbert Transform => 8khz fs Hilbert = 1536.000/8000= 192
|
||||
fs_div_factor = 192; // TXBW_ssb = 2khz = BW_cut_off LPF = fs/4 ; BW_HT fs Hilbert Transform (4khz=fs/2) ==> (8k=fs) Hilbert_fs = 1.536.000/8000= 192
|
||||
break;
|
||||
case 3:
|
||||
fs_div_factor = 128; // TXBW_ssb = 3khz = fs/4 ; fs of Hilbert Transform => 12khz fs Hilbert = 1536.000/12000= 128
|
||||
fs_div_factor = 128; // TXBW_ssb = 3khz = BW_cut_off LPF = fs/4 ; BW_HT fs Hilbert Transform (6khz=fs/2) ==> (12k=fs) Hilbert_fs = 1.536.000/12000= 128
|
||||
break;
|
||||
/* TODO pending to investigate 4khz, now is making aliasing folding with audio sweep .
|
||||
case 4:
|
||||
fs_div_factor = 96; // TXBW_ssb = 4khz = fs/4 ; fs of Hilbert Transform => 16khz fs Hilbert = 1536.000/16000= 96
|
||||
break;
|
||||
*/
|
||||
default:
|
||||
fs_div_factor = 128; // TXBW_ssb = 3khz = fs/4 ; fs of Hilbert Transform => 12khz fs Hilbert = 1536.000/12000= 128
|
||||
fs_div_factor = 128; // TXBW_ssb = 3khz = BW_cut_off LPF = fs/4 ; BW_HT fs Hilbert Transform (6khz=fs/2) ==> (12k=fs) Hilbert_fs = 1.536.000/12000= 128
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,8 +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
|
||||
|
||||
@@ -45,36 +45,6 @@ class FProtoSubGhzDCameAtomo : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -45,7 +45,6 @@ class FProtoSubGhzDCameTwee : public FProtoSubGhzDBase {
|
||||
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 +78,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,8 +57,6 @@ 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;
|
||||
|
||||
@@ -64,11 +64,6 @@ class FProtoSubGhzDClemsa : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -46,10 +46,6 @@ class FProtoSubGhzDDoitrand : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -74,16 +74,6 @@ class FProtoSubGhzDDooya : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -46,12 +46,6 @@ class FProtoSubGhzDGateTx : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -51,22 +51,6 @@ class FProtoSubGhzDHoltek : public FProtoSubGhzDBase {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,9 +47,6 @@ class FProtoSubGhzDHoltekHt12x : public FProtoSubGhzDBase {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,8 +73,6 @@ class FProtoSubGhzDHoneywell : public FProtoSubGhzDBase {
|
||||
// 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;
|
||||
|
||||
@@ -49,8 +49,6 @@ class FProtoSubGhzDHormann : public FProtoSubGhzDBase {
|
||||
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,130 @@
|
||||
|
||||
#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;
|
||||
data = 0;
|
||||
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
|
||||
@@ -46,12 +46,6 @@ class FProtoSubGhzDIdo : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -68,7 +68,6 @@ class FProtoSubGhzDIntertechnoV3 : public FProtoSubGhzDBase {
|
||||
(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 +119,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
|
||||
|
||||
@@ -60,12 +60,6 @@ class FProtoSubGhzDKeeLoq : public FProtoSubGhzDBase {
|
||||
if (data != decode_data) {
|
||||
data = decode_data;
|
||||
data_count_bit = min_count_bit_for_found;
|
||||
// controller
|
||||
uint64_t key = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
|
||||
uint32_t key_fix = key >> 32;
|
||||
// uint32_t key_hop = key & 0x00000000ffffffff; //unused
|
||||
serial = key_fix & 0x0FFFFFFF;
|
||||
btn = key_fix >> 28;
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
|
||||
@@ -61,13 +61,8 @@ class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
|
||||
if (decode_count_bit ==
|
||||
min_count_bit_for_found) {
|
||||
data = data_2;
|
||||
data_2 = decode_data;
|
||||
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);
|
||||
if (callback) callback(this);
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,113 @@
|
||||
|
||||
#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
|
||||
if (data && (data != decode_data)) {
|
||||
te /= decode_count_bit * 4;
|
||||
|
||||
data = decode_data;
|
||||
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
|
||||
@@ -54,11 +54,8 @@ 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;
|
||||
|
||||
@@ -50,12 +50,8 @@ class FProtoSubGhzDLinearDelta3 : public FProtoSubGhzDBase {
|
||||
}
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
if ((data == decode_data) && data) {
|
||||
serial = SD_NO_SERIAL;
|
||||
btn = SD_NO_BTN;
|
||||
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
parser_step = LinearD3DecoderStepSaveDuration;
|
||||
|
||||
@@ -92,11 +92,6 @@ class FProtoSubGhzDMagellan : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -45,9 +45,6 @@ class FProtoSubGhzDMarantec : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -63,9 +63,7 @@ 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;
|
||||
|
||||
@@ -50,16 +50,7 @@ class FProtoSubGhzDMegacode : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -47,9 +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);
|
||||
|
||||
@@ -58,10 +58,7 @@ class FProtoSubGhzDNiceflors : public FProtoSubGhzDBase {
|
||||
if ((decode_count_bit == min_count_bit_for_found) || (decode_count_bit == NICE_ONE_COUNT_BIT)) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
// controller-
|
||||
cnt = SD_NO_CNT;
|
||||
serial = SD_NO_SERIAL;
|
||||
btn = SD_NO_BTN;
|
||||
|
||||
if (callback) callback(this);
|
||||
}
|
||||
break;
|
||||
|
||||
@@ -47,12 +47,6 @@ class FProtoSubGhzDPhoenixV2 : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -52,11 +52,6 @@ class FProtoSubGhzDPowerSmart : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -44,9 +44,6 @@ class FProtoSubGhzDPrinceton : public FProtoSubGhzDBase {
|
||||
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;
|
||||
|
||||
@@ -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 = data ^ (data >> 8);
|
||||
if (((data_tmp >> 40) & 0xF) == subghz_protocol_somfy_keytis_crc(data_tmp)) {
|
||||
data = decode_data;
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
#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 = decode_data;
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -25,10 +25,7 @@ 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;
|
||||
|
||||
protected:
|
||||
|
||||
@@ -47,6 +47,10 @@ 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"
|
||||
|
||||
// GENIE FROM PR
|
||||
|
||||
@@ -94,11 +98,12 @@ 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();
|
||||
|
||||
for (uint8_t i = 0; i < FPS_COUNT; ++i) {
|
||||
if (protos[i] != NULL) protos[i]->setCallback(callbackTarget);
|
||||
@@ -115,7 +120,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->data};
|
||||
shared_memory.application_queue.push(packet_message);
|
||||
}
|
||||
|
||||
|
||||
@@ -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,8 @@ enum FPROTO_SUBGHZD_SENSOR : uint8_t {
|
||||
FPS_SMC5326,
|
||||
FPS_STARLINE,
|
||||
FPS_X10,
|
||||
|
||||
FPS_SOMIFY_KEYTIS,
|
||||
FPS_SOMIFY_TELIS,
|
||||
FPS_COUNT
|
||||
};
|
||||
|
||||
|
||||
@@ -148,7 +148,6 @@ class FProtoWeatherAcurite592TXR : public FProtoWeatherBase {
|
||||
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;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
|
||||
#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()) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
ws_protocol_acurite_5n1_remote_controller();
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
void ws_protocol_acurite_5n1_remote_controller() {
|
||||
uint8_t channell[] = {3, 0, 2, 1};
|
||||
uint8_t channel_raw = ((data >> 62) & 0x03);
|
||||
channel = channell[channel_raw];
|
||||
id = (data >> 48) & 0x3FFF;
|
||||
battery_low = !((data >> 46) & 1);
|
||||
humidity = (data >> 8) & 0x7F;
|
||||
uint16_t temp_raw = ((data >> (24 - 7)) & 0x780) | ((data >> 16) & 0x7F);
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)(temp_raw)-400) / 10.0f);
|
||||
};
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -92,7 +92,6 @@ class FProtoWeatherAcurite606TX : public FProtoWeatherBase {
|
||||
} else {
|
||||
temp = (float)((~(data >> 8) & 0x07FF) + 1) / -10.0f;
|
||||
}
|
||||
btn = WS_NO_BTN;
|
||||
humidity = WS_NO_HUMIDITY;
|
||||
}
|
||||
bool ws_protocol_acurite_606tx_check() {
|
||||
|
||||
@@ -39,16 +39,11 @@ 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()) {
|
||||
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();
|
||||
@@ -56,14 +51,10 @@ class FProtoWeatherAcurite609TX : public FProtoWeatherBase {
|
||||
}
|
||||
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,7 +72,7 @@ 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;
|
||||
|
||||
@@ -97,7 +88,6 @@ class FProtoWeatherAcurite609TX : public FProtoWeatherBase {
|
||||
(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;
|
||||
|
||||
@@ -118,7 +118,6 @@ class FProtoWeatherAcurite986 : public FProtoWeatherBase {
|
||||
temp = -(temp & 0x7F);
|
||||
}
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius((float)temp);
|
||||
btn = WS_NO_BTN;
|
||||
humidity = WS_NO_HUMIDITY;
|
||||
}
|
||||
|
||||
|
||||
@@ -62,7 +62,7 @@ class FProtoWeatherAmbient : public FProtoWeatherBase {
|
||||
uint32_t te_long = 1000;
|
||||
uint32_t te_delta = 120;
|
||||
uint32_t min_count_bit_for_found = 48;
|
||||
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
bool ws_protocol_ambient_weather_check_crc() {
|
||||
uint8_t msg[] = {
|
||||
static_cast<uint8_t>(decode_data >> 40),
|
||||
@@ -81,7 +81,6 @@ class FProtoWeatherAmbient : public FProtoWeatherBase {
|
||||
channel = ((data >> 28) & 0x07) + 1;
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data >> 16) & 0x0FFF) - 400.0f) / 10.0f);
|
||||
humidity = (data >> 8) & 0xFF;
|
||||
btn = WS_NO_BTN;
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -91,7 +91,7 @@ class FProtoWeatherAuriolAhfl : public FProtoWeatherBase {
|
||||
void ws_protocol_auriol_ahfl_remote_controller() {
|
||||
id = data >> 34;
|
||||
battery_low = (data >> 33) & 1;
|
||||
btn = (data >> 32) & 1;
|
||||
// btn = (data >> 32) & 1;
|
||||
channel = ((data >> 30) & 0x3) + 1;
|
||||
if (!((data >> 29) & 1)) {
|
||||
temp = (float)((data >> 18) & 0x07FF) / 10.0f;
|
||||
|
||||
@@ -91,7 +91,6 @@ class FProtoWeatherAuriolTh : public FProtoWeatherBase {
|
||||
id = (data >> 31) & 0xFF;
|
||||
battery_low = ((data >> 30) & 1);
|
||||
channel = ((data >> 25) & 0x03) + 1;
|
||||
btn = WS_NO_BTN;
|
||||
if (!((data >> 23) & 1)) {
|
||||
temp = (float)((data >> 13) & 0x07FF) / 10.0f;
|
||||
} else {
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
|
||||
#ifndef __FPROTO_EmosE601x_H__
|
||||
#define __FPROTO_EmosE601x_H__
|
||||
|
||||
#include "weatherbase.hpp"
|
||||
|
||||
#define EmosE601xMAGIC_HEADER 0xaaa583
|
||||
|
||||
typedef enum {
|
||||
EmosE601xDecoderStepReset = 0,
|
||||
EmosE601xDecoderStepCheckPreamble,
|
||||
EmosE601xDecoderStepSaveDuration,
|
||||
EmosE601xDecoderStepCheckDuration,
|
||||
} EmosE601xDecoderStep;
|
||||
|
||||
class FProtoWeatherEmosE601x : public FProtoWeatherBase {
|
||||
public:
|
||||
FProtoWeatherEmosE601x() {
|
||||
sensorType = FPW_EmosE601x;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
switch (parser_step) {
|
||||
case EmosE601xDecoderStepReset:
|
||||
if ((level) && (DURATION_DIFF(duration, te_short * 7) <
|
||||
te_delta * 2)) {
|
||||
parser_step = EmosE601xDecoderStepCheckPreamble;
|
||||
te_last = duration;
|
||||
}
|
||||
break;
|
||||
|
||||
case EmosE601xDecoderStepCheckPreamble:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
} else {
|
||||
if ((DURATION_DIFF(te_last, te_short * 7) <
|
||||
te_delta * 2) &&
|
||||
(DURATION_DIFF(duration, te_short) <
|
||||
te_delta)) {
|
||||
// Found preamble
|
||||
parser_step = EmosE601xDecoderStepSaveDuration;
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
} else {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case EmosE601xDecoderStepSaveDuration:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
parser_step = EmosE601xDecoderStepCheckDuration;
|
||||
} else {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case EmosE601xDecoderStepCheckDuration:
|
||||
if (!level) {
|
||||
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long) < te_delta)) {
|
||||
subghz_protocol_blocks_add_to_128_bit(0, &upper_decode_data);
|
||||
parser_step = EmosE601xDecoderStepSaveDuration;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_long) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_short) < te_delta)) {
|
||||
subghz_protocol_blocks_add_to_128_bit(1, &upper_decode_data);
|
||||
parser_step = EmosE601xDecoderStepSaveDuration;
|
||||
} else {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
|
||||
/* Bail out if the header doesn't match */
|
||||
if (decode_count_bit == min_count_bit_for_found) {
|
||||
if (decode_data != EmosE601xMAGIC_HEADER) {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (decode_count_bit == 120) {
|
||||
data_count_bit = decode_count_bit;
|
||||
if (ws_protocol_emose601x_check()) {
|
||||
ws_protocol_emose601x_extract_data();
|
||||
if (callback) callback(this);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
parser_step = EmosE601xDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t te_short = 260;
|
||||
uint32_t te_long = 800;
|
||||
uint32_t te_delta = 100;
|
||||
uint32_t min_count_bit_for_found = 24;
|
||||
|
||||
uint64_t upper_decode_data = 0;
|
||||
void subghz_protocol_blocks_add_to_128_bit(uint8_t bit, uint64_t* head_64_bit) {
|
||||
if (++decode_count_bit > 64) {
|
||||
(*head_64_bit) = ((*head_64_bit) << 1) | (decode_data >> 63);
|
||||
}
|
||||
decode_data = decode_data << 1 | bit;
|
||||
}
|
||||
bool ws_protocol_emose601x_check() {
|
||||
uint8_t msg[] = {
|
||||
static_cast<uint8_t>(upper_decode_data >> 48),
|
||||
static_cast<uint8_t>(upper_decode_data >> 40),
|
||||
static_cast<uint8_t>(upper_decode_data >> 32),
|
||||
static_cast<uint8_t>(upper_decode_data >> 24),
|
||||
static_cast<uint8_t>(upper_decode_data >> 16),
|
||||
static_cast<uint8_t>(upper_decode_data >> 8),
|
||||
static_cast<uint8_t>(upper_decode_data),
|
||||
static_cast<uint8_t>(decode_data >> 56),
|
||||
static_cast<uint8_t>(decode_data >> 48),
|
||||
static_cast<uint8_t>(decode_data >> 40),
|
||||
static_cast<uint8_t>(decode_data >> 32),
|
||||
static_cast<uint8_t>(decode_data >> 24),
|
||||
static_cast<uint8_t>(decode_data >> 16)};
|
||||
|
||||
uint8_t sum = FProtoGeneral::subghz_protocol_blocks_add_bytes(msg, 13);
|
||||
return (sum == ((decode_data >> 8) & 0xff));
|
||||
}
|
||||
|
||||
void ws_protocol_emose601x_extract_data() {
|
||||
id = (upper_decode_data >> 24) & 0xff;
|
||||
battery_low = (decode_data >> 10) & 1;
|
||||
int16_t temp = (decode_data >> 40) & 0xfff;
|
||||
/* Handle signed data */
|
||||
if (temp & 0x800) {
|
||||
temp |= 0xf000;
|
||||
}
|
||||
temp = (float)temp / 10.0;
|
||||
humidity = (decode_data >> 32) & 0xff;
|
||||
channel = (decode_data >> 52) & 0x03;
|
||||
data = (decode_data >> 16);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -91,7 +91,7 @@ class FProtoWeatherGTWT02 : public FProtoWeatherBase {
|
||||
void ws_protocol_gt_wt_02_remote_controller() {
|
||||
id = (data >> 29) & 0xFF;
|
||||
battery_low = (data >> 28) & 1;
|
||||
btn = (data >> 27) & 1;
|
||||
// btn = (data >> 27) & 1;
|
||||
channel = ((data >> 25) & 0x3) + 1;
|
||||
|
||||
if (!((data >> 24) & 1)) {
|
||||
|
||||
@@ -124,7 +124,7 @@ class FProtoWeatherGTWT03 : public FProtoWeatherBase {
|
||||
}
|
||||
|
||||
battery_low = (data >> 24) & 1;
|
||||
btn = (data >> 23) & 1;
|
||||
// (data >> 23) & 1;
|
||||
channel = ((data >> 21) & 0x03) + 1;
|
||||
|
||||
if (!((data >> 20) & 1)) {
|
||||
|
||||
@@ -125,7 +125,6 @@ class FProtoWeatherInfactory : public FProtoWeatherBase {
|
||||
void ws_protocol_infactory_remote_controller() {
|
||||
id = data >> 32;
|
||||
battery_low = (data >> 26) & 1;
|
||||
btn = WS_NO_BTN;
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)((data >> 12) & 0x0FFF) - 900.0f) / 10.0f);
|
||||
humidity = (((data >> 8) & 0x0F) * 10) + ((data >> 4) & 0x0F); // BCD, 'A0'=100%rH
|
||||
channel = data & 0x03;
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
|
||||
#ifndef __FPROTO_Kedsum_H__
|
||||
#define __FPROTO_Kedsum_H__
|
||||
|
||||
#include "weatherbase.hpp"
|
||||
|
||||
typedef enum {
|
||||
KedsumTHDecoderStepReset = 0,
|
||||
KedsumTHDecoderStepCheckPreambule,
|
||||
KedsumTHDecoderStepSaveDuration,
|
||||
KedsumTHDecoderStepCheckDuration,
|
||||
} KedsumTHDecoderStep;
|
||||
|
||||
class FProtoWeatherKedsum : public FProtoWeatherBase {
|
||||
public:
|
||||
FProtoWeatherKedsum() {
|
||||
sensorType = FPW_KEDSUM;
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
switch (parser_step) {
|
||||
case KedsumTHDecoderStepReset:
|
||||
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
|
||||
parser_step = KedsumTHDecoderStepCheckPreambule;
|
||||
te_last = duration;
|
||||
header_count = 0;
|
||||
}
|
||||
break;
|
||||
|
||||
case KedsumTHDecoderStepCheckPreambule:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
} else {
|
||||
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long * 4) < te_delta * 4)) {
|
||||
// Found preambule
|
||||
header_count++;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(duration < (te_long * 2 + te_delta * 2))) {
|
||||
// Found syncPrefix
|
||||
if (header_count > 0) {
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long * 2) < te_delta * 4)) {
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case KedsumTHDecoderStepSaveDuration:
|
||||
if (level) {
|
||||
te_last = duration;
|
||||
parser_step = KedsumTHDecoderStepCheckDuration;
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
|
||||
case KedsumTHDecoderStepCheckDuration:
|
||||
if (!level) {
|
||||
if (DURATION_DIFF(duration, te_long * 4) < te_delta * 4) {
|
||||
// Found syncPostfix
|
||||
if ((decode_count_bit ==
|
||||
min_count_bit_for_found) &&
|
||||
ws_protocol_kedsum_th_check_crc()) {
|
||||
data = decode_data;
|
||||
data_count_bit = decode_count_bit;
|
||||
ws_protocol_kedsum_th_remote_controller();
|
||||
if (callback) callback(this);
|
||||
}
|
||||
decode_data = 0;
|
||||
decode_count_bit = 0;
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
break;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_short) < te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
|
||||
subghz_protocol_blocks_add_bit(0);
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
} else if (
|
||||
(DURATION_DIFF(te_last, te_short) <
|
||||
te_delta) &&
|
||||
(DURATION_DIFF(duration, te_long * 2) <
|
||||
te_delta * 4)) {
|
||||
subghz_protocol_blocks_add_bit(1);
|
||||
parser_step = KedsumTHDecoderStepSaveDuration;
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
} else {
|
||||
parser_step = KedsumTHDecoderStepReset;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
uint32_t te_short = 500;
|
||||
uint32_t te_long = 2000;
|
||||
uint32_t te_delta = 150;
|
||||
uint32_t min_count_bit_for_found = 42;
|
||||
|
||||
bool ws_protocol_kedsum_th_check_crc() {
|
||||
uint8_t msg[] = {
|
||||
static_cast<uint8_t>(decode_data >> 32),
|
||||
static_cast<uint8_t>(decode_data >> 24),
|
||||
static_cast<uint8_t>(decode_data >> 16),
|
||||
static_cast<uint8_t>(decode_data >> 8),
|
||||
static_cast<uint8_t>(decode_data)};
|
||||
|
||||
uint8_t crc = FProtoGeneral::subghz_protocol_blocks_crc4(msg, 4, 0x03, 0); // CRC-4 poly 0x3 init 0x0 xor last 4 bits
|
||||
crc ^= msg[4] >> 4; // last nibble is only XORed
|
||||
return (crc == (msg[4] & 0x0F));
|
||||
}
|
||||
|
||||
void ws_protocol_kedsum_th_remote_controller() {
|
||||
id = data >> 32;
|
||||
if ((data >> 30) & 0x3) {
|
||||
battery_low = 0;
|
||||
} else {
|
||||
battery_low = 1;
|
||||
}
|
||||
channel = ((data >> 28) & 0x3) + 1;
|
||||
uint16_t temp_raw = ((data >> 16) & 0x0f) << 8 |
|
||||
((data >> 20) & 0x0f) << 4 | ((data >> 24) & 0x0f);
|
||||
temp = FProtoGeneral::locale_fahrenheit_to_celsius(((float)temp_raw - 900.0f) / 10.0f);
|
||||
humidity = ((data >> 8) & 0x0f) << 4 | ((data >> 12) & 0x0f);
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -137,7 +137,6 @@ class FProtoWeatherLaCrosseTx : public FProtoWeatherBase {
|
||||
// furi_crash("WS: WSProtocolLaCrosse_TX incorrect msg_type.");
|
||||
}
|
||||
|
||||
btn = WS_NO_BTN;
|
||||
battery_low = WS_NO_BATT;
|
||||
channel = WS_NO_CHANNEL;
|
||||
}
|
||||
|
||||
@@ -113,7 +113,7 @@ class FProtoWeatherLaCrosseTx141thbv2 : public FProtoWeatherBase {
|
||||
}
|
||||
id = data >> 32;
|
||||
battery_low = (data >> 31) & 1;
|
||||
btn = (data >> 30) & 1;
|
||||
// btn = (data >> 30) & 1;
|
||||
channel = ((data >> 28) & 0x03) + 1;
|
||||
temp = ((float)((data >> 16) & 0x0FFF) - 500.0f) / 10.0f;
|
||||
humidity = (data >> 8) & 0xFF;
|
||||
|
||||
@@ -104,7 +104,6 @@ class FProtoWeatherNexusTH : public FProtoWeatherBase {
|
||||
id = (data >> 28) & 0xFF;
|
||||
battery_low = !((data >> 27) & 1);
|
||||
channel = ((data >> 24) & 0x03) + 1;
|
||||
btn = WS_NO_BTN;
|
||||
if (!((data >> 23) & 1)) {
|
||||
temp = (float)((data >> 12) & 0x07FF) / 10.0f;
|
||||
} else {
|
||||
|
||||
@@ -160,7 +160,7 @@ class FProtoWeatherOregon2 : public FProtoWeatherBase {
|
||||
bool prev_bit = 0;
|
||||
uint8_t var_bits{0};
|
||||
uint32_t var_data{0};
|
||||
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
void ws_protocol_decoder_oregon2_reset() {
|
||||
parser_step = Oregon2DecoderStepReset;
|
||||
decode_data = 0UL;
|
||||
|
||||
@@ -117,7 +117,7 @@ class FProtoWeatherOregon3 : public FProtoWeatherBase {
|
||||
bool prev_bit = false;
|
||||
uint8_t var_bits{0};
|
||||
uint64_t var_data{0};
|
||||
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
ManchesterEvent level_and_duration_to_event(bool level, uint32_t duration) {
|
||||
bool is_long = false;
|
||||
|
||||
|
||||
@@ -137,7 +137,7 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase {
|
||||
uint32_t min_count_bit_for_found = 32;
|
||||
|
||||
uint8_t first_bit{0};
|
||||
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
bool ws_protocol_oregon_v1_check() {
|
||||
if (!decode_data) return false;
|
||||
uint64_t data = FProtoGeneral::subghz_protocol_blocks_reverse_key(decode_data, 32);
|
||||
@@ -159,7 +159,6 @@ class FProtoWeatherOregonV1 : public FProtoWeatherBase {
|
||||
temp = -temp_raw;
|
||||
}
|
||||
battery_low = !((data2 >> 23) & 1ULL);
|
||||
btn = WS_NO_BTN;
|
||||
humidity = WS_NO_HUMIDITY;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -96,7 +96,7 @@ class FProtoWeatherThermoProTx4 : public FProtoWeatherBase {
|
||||
void ws_protocol_thermopro_tx4_remote_controller() {
|
||||
id = (data >> 25) & 0xFF;
|
||||
battery_low = (data >> 24) & 1;
|
||||
btn = (data >> 23) & 1;
|
||||
// btn = (data >> 23) & 1;
|
||||
channel = ((data >> 21) & 0x03) + 1;
|
||||
if (!((data >> 20) & 1)) {
|
||||
temp = (float)((data >> 9) & 0x07FF) / 10.0f;
|
||||
|
||||
@@ -120,7 +120,6 @@ class FProtoWeatherTX8300 : public FProtoWeatherBase {
|
||||
}
|
||||
void ws_protocol_tx_8300_remote_controller() {
|
||||
humidity = (((data >> 28) & 0x0F) * 10) + ((data >> 24) & 0x0F);
|
||||
btn = WS_NO_BTN;
|
||||
if (!((data >> 22) & 0x03))
|
||||
battery_low = 0;
|
||||
else
|
||||
|
||||
@@ -147,8 +147,6 @@ class FProtoWeatherWendoxW6726 : public FProtoWeatherBase {
|
||||
} else if (temp > 70.0f) {
|
||||
temp = 70.0f;
|
||||
}
|
||||
|
||||
btn = WS_NO_BTN;
|
||||
humidity = WS_NO_HUMIDITY;
|
||||
}
|
||||
};
|
||||
|
||||
@@ -24,12 +24,11 @@ class FProtoWeatherBase {
|
||||
void setCallback(SubGhzProtocolDecoderBaseRxCallback cb) { callback = cb; } // this is called when there is a hit.
|
||||
|
||||
uint8_t getSensorType() { return sensorType; }
|
||||
uint32_t getSensorId() { return id; }
|
||||
float getTemp() { return temp; }
|
||||
uint8_t getHumidity() { return humidity; }
|
||||
uint8_t getBattLow() { return battery_low; }
|
||||
uint8_t getChannel() { return channel; }
|
||||
uint8_t getButton() { return btn; }
|
||||
uint32_t getSensorId() { return id; }
|
||||
float getTemp() { return temp; }
|
||||
|
||||
protected:
|
||||
// Helper functions to keep it as compatible with flipper as we can, so adding new protos will be easy.
|
||||
@@ -38,25 +37,27 @@ class FProtoWeatherBase {
|
||||
decode_count_bit++;
|
||||
}
|
||||
|
||||
// General weather data holder
|
||||
// needs to be in this chaotic order, to save flash!
|
||||
// General weather data holder
|
||||
uint8_t sensorType = FPW_Invalid;
|
||||
uint32_t id = WS_NO_ID;
|
||||
float temp = WS_NO_TEMPERATURE;
|
||||
uint8_t humidity = WS_NO_HUMIDITY;
|
||||
uint8_t battery_low = WS_NO_BATT;
|
||||
uint8_t channel = WS_NO_CHANNEL;
|
||||
uint8_t btn = WS_NO_BTN;
|
||||
|
||||
// inner logic stuff, also for flipper compatibility.
|
||||
SubGhzProtocolDecoderBaseRxCallback callback = NULL;
|
||||
uint16_t header_count = 0;
|
||||
// inner logic stuff
|
||||
uint8_t parser_step = 0;
|
||||
// inner logic stuff, also for flipper compatibility.
|
||||
uint16_t header_count = 0;
|
||||
// General weather data holder
|
||||
float temp = WS_NO_TEMPERATURE;
|
||||
uint32_t id = WS_NO_ID;
|
||||
// inner logic stuff,
|
||||
|
||||
uint32_t te_last = 0;
|
||||
uint64_t data = 0;
|
||||
uint32_t data_count_bit = 0;
|
||||
uint64_t decode_data = 0;
|
||||
uint32_t decode_count_bit = 0;
|
||||
ManchesterState manchester_saved_state = ManchesterStateMid1;
|
||||
uint64_t decode_data = 0;
|
||||
uint64_t data = 0;
|
||||
SubGhzProtocolDecoderBaseRxCallback callback = NULL;
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -25,6 +25,10 @@ So include here the .hpp, and add a new element to the protos vector in the cons
|
||||
#include "w-tx8300.hpp"
|
||||
#include "w-wendox-w6726.hpp"
|
||||
#include "w-acurite986.hpp"
|
||||
#include "w-kedsum.hpp"
|
||||
#include "w-acurite5in1.hpp"
|
||||
#include "w-emose601x.hpp"
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
#include "portapack_shared_memory.hpp"
|
||||
@@ -34,49 +38,63 @@ So include here the .hpp, and add a new element to the protos vector in the cons
|
||||
|
||||
class WeatherProtos : public FProtoListGeneral {
|
||||
public:
|
||||
WeatherProtos(const WeatherProtos&) { WeatherProtos(); }; // won't use, but makes compiler happy
|
||||
WeatherProtos& operator=(const WeatherProtos&) { return *this; } // won't use, but makes compiler happy
|
||||
WeatherProtos() {
|
||||
// add protos
|
||||
protos.push_back(std::make_unique<FProtoWeatherNexusTH>()); // 1
|
||||
protos.push_back(std::make_unique<FProtoWeatherAcurite592TXR>()); // 2
|
||||
protos.push_back(std::make_unique<FProtoWeatherAcurite606TX>()); // 3
|
||||
protos.push_back(std::make_unique<FProtoWeatherAcurite609TX>()); // 4
|
||||
protos.push_back(std::make_unique<FProtoWeatherAmbient>()); // 5
|
||||
protos.push_back(std::make_unique<FProtoWeatherAuriolAhfl>()); // 6
|
||||
protos.push_back(std::make_unique<FProtoWeatherAuriolTh>()); // 7
|
||||
protos.push_back(std::make_unique<FProtoWeatherGTWT02>()); // 8
|
||||
protos.push_back(std::make_unique<FProtoWeatherGTWT03>()); // 9
|
||||
protos.push_back(std::make_unique<FProtoWeatherInfactory>()); // 10
|
||||
protos.push_back(std::make_unique<FProtoWeatherLaCrosseTx>()); // 11
|
||||
protos.push_back(std::make_unique<FProtoWeatherLaCrosseTx141thbv2>()); // 12
|
||||
protos.push_back(std::make_unique<FProtoWeatherOregon2>()); // 13
|
||||
protos.push_back(std::make_unique<FProtoWeatherOregon3>()); // 14
|
||||
protos.push_back(std::make_unique<FProtoWeatherOregonV1>()); // 15
|
||||
protos.push_back(std::make_unique<FProtoWeatherThermoProTx4>()); // 16
|
||||
protos.push_back(std::make_unique<FProtoWeatherTX8300>()); // 17
|
||||
protos.push_back(std::make_unique<FProtoWeatherWendoxW6726>()); // 18
|
||||
protos.push_back(std::make_unique<FProtoWeatherAcurite986>()); // 19
|
||||
protos[FPW_NexusTH] = new FProtoWeatherNexusTH();
|
||||
protos[FPW_Acurite592TXR] = new FProtoWeatherAcurite592TXR();
|
||||
protos[FPW_Acurite606TX] = new FProtoWeatherAcurite606TX();
|
||||
protos[FPW_Acurite609TX] = new FProtoWeatherAcurite609TX();
|
||||
protos[FPW_Ambient] = new FProtoWeatherAmbient();
|
||||
protos[FPW_AuriolAhfl] = new FProtoWeatherAuriolAhfl();
|
||||
protos[FPW_AuriolTH] = new FProtoWeatherAuriolTh();
|
||||
protos[FPW_GTWT02] = new FProtoWeatherGTWT02();
|
||||
protos[FPW_GTWT03] = new FProtoWeatherGTWT03();
|
||||
protos[FPW_INFACTORY] = new FProtoWeatherInfactory();
|
||||
protos[FPW_LACROSSETX] = new FProtoWeatherLaCrosseTx();
|
||||
protos[FPW_LACROSSETX141thbv2] = new FProtoWeatherLaCrosseTx141thbv2();
|
||||
protos[FPW_OREGON2] = new FProtoWeatherOregon2();
|
||||
protos[FPW_OREGON3] = new FProtoWeatherOregon3();
|
||||
protos[FPW_OREGONv1] = new FProtoWeatherOregonV1();
|
||||
protos[FPW_THERMOPROTX4] = new FProtoWeatherThermoProTx4();
|
||||
protos[FPW_TX_8300] = new FProtoWeatherTX8300();
|
||||
protos[FPW_WENDOX_W6726] = new FProtoWeatherWendoxW6726();
|
||||
protos[FPW_Acurite986] = new FProtoWeatherAcurite986();
|
||||
protos[FPW_KEDSUM] = new FProtoWeatherKedsum();
|
||||
protos[FPW_Acurite5in1] = new FProtoWeatherAcurite5in1();
|
||||
protos[FPW_EmosE601x] = new FProtoWeatherEmosE601x();
|
||||
|
||||
// set callback for them
|
||||
for (const auto& obj : protos) {
|
||||
obj->setCallback(callbackTarget);
|
||||
for (uint8_t i = 0; i < FPW_COUNT; ++i) {
|
||||
protos[i]->setCallback(callbackTarget);
|
||||
}
|
||||
}
|
||||
|
||||
~WeatherProtos() { // not needed for current operation logic, but a bit more elegant :)
|
||||
for (uint8_t i = 0; i < FPW_COUNT; ++i) {
|
||||
if (protos[i] != NULL) {
|
||||
free(protos[i]);
|
||||
protos[i] = NULL;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
static void callbackTarget(FProtoWeatherBase* instance) {
|
||||
WeatherDataMessage packet_message{instance->getSensorType(), instance->getSensorId(),
|
||||
instance->getTemp(), instance->getHumidity(), instance->getBattLow(),
|
||||
instance->getChannel(), instance->getButton()};
|
||||
instance->getChannel()};
|
||||
shared_memory.application_queue.push(packet_message);
|
||||
}
|
||||
|
||||
void feed(bool level, uint32_t duration) {
|
||||
for (const auto& obj : protos) {
|
||||
obj->feed(level, duration);
|
||||
for (uint8_t i = 0; i < FPW_COUNT; ++i) {
|
||||
if (protos[i] != NULL) protos[i]->feed(level, duration);
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
std::vector<std::unique_ptr<FProtoWeatherBase>> protos{};
|
||||
FProtoWeatherBase* protos[FPW_COUNT] = {NULL};
|
||||
};
|
||||
|
||||
#endif
|
||||
|
||||
@@ -11,7 +11,7 @@ Also it must have a switch-case element in the getWeatherSensorTypeName() functi
|
||||
#define WS_NO_BATT 0xFF
|
||||
#define WS_NO_HUMIDITY 0xFF
|
||||
#define WS_NO_CHANNEL 0xFF
|
||||
#define WS_NO_BTN 0xFF
|
||||
// #define WS_NO_BTN 0xFF
|
||||
#define WS_NO_TEMPERATURE -273.0f
|
||||
|
||||
enum FPROTO_WEATHER_SENSOR {
|
||||
@@ -34,7 +34,11 @@ enum FPROTO_WEATHER_SENSOR {
|
||||
FPW_THERMOPROTX4 = 16,
|
||||
FPW_TX_8300 = 17,
|
||||
FPW_WENDOX_W6726 = 18,
|
||||
FPW_Acurite986 = 19
|
||||
FPW_Acurite986 = 19,
|
||||
FPW_KEDSUM = 20,
|
||||
FPW_Acurite5in1 = 21,
|
||||
FPW_EmosE601x = 22,
|
||||
FPW_COUNT // this must be the last
|
||||
};
|
||||
|
||||
#endif
|
||||
@@ -144,7 +144,7 @@ void MicTXProcessor::on_message(const Message* const msg) {
|
||||
modulator->set_mode(dsp::modulate::Mode::DSB);
|
||||
}
|
||||
|
||||
modulator->set_over(baseband_fs / 24000); // Keep no change.
|
||||
modulator->set_over(baseband_fs / 24000); // "over" is calculated based on relationship fs_transceiver / fs_audio_mic_capture, to be used in dsp_modulate.cpp
|
||||
|
||||
am_enabled = config_message.am_enabled;
|
||||
usb_enabled = config_message.usb_enabled;
|
||||
|
||||
@@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
|
||||
#include "proc_protoview.hpp"
|
||||
#include "portapack_shared_memory.hpp"
|
||||
#include "event_m4.hpp"
|
||||
#include "audio_dma.hpp"
|
||||
|
||||
void ProtoViewProcessor::execute(const buffer_c8_t& buffer) {
|
||||
if (!configured) return;
|
||||
|
||||
// SR = 4Mhz , and we are decimating by /8 in total , decim1_out clock 4Mhz /8= 500khz samples/sec.
|
||||
// buffer has 2048 complex i8 I,Q signed samples
|
||||
// decim0 out: 2048/4 = 512 complex i16 I,Q signed samples
|
||||
// decim1 out: 512/2 = 256 complex i16 I,Q signed samples
|
||||
// Regarding Filters, we are re-using existing FIR filters, @4Mhz, FIR decim1 ilter, BW =+-220Khz (at -3dB's). BW = 440kHZ.
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // Input:2048 complex/4 (decim factor) = 512_output complex (1024 I/Q samples)
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // Input:512 complex/2 (decim factor) = 256_output complex ( 512 I/Q samples)
|
||||
feed_channel_stats(decim_1_out);
|
||||
|
||||
for (size_t i = 0; i < decim_1_out.count; i++) {
|
||||
int16_t re = decim_1_out.p[i].real();
|
||||
int16_t im = decim_1_out.p[i].imag();
|
||||
uint32_t mag = ((uint32_t)re * (uint32_t)re) + ((uint32_t)im * (uint32_t)im);
|
||||
|
||||
mag = (mag >> 12); // Decim samples are calculated with saturated gain . (we could also reduce that sat. param at configure time)
|
||||
|
||||
bool meashl = (mag > threshold);
|
||||
tm += mag;
|
||||
if (meashl == currentHiLow && currentDuration < 30'000'000) // allow pass 'end' signal
|
||||
{
|
||||
currentDuration += nsPerDecSamp;
|
||||
} else { // called on change, so send the last duration and dir.
|
||||
message.times[message.timeptr++] = currentHiLow ? (int32_t)(currentDuration / 1000) : -1 * (int32_t)(currentDuration / 1000);
|
||||
if (message.timeptr > message.maxptr) {
|
||||
shared_memory.application_queue.push(message);
|
||||
message.timeptr = 0;
|
||||
}
|
||||
currentDuration = nsPerDecSamp;
|
||||
currentHiLow = meashl;
|
||||
}
|
||||
}
|
||||
|
||||
cnt += decim_1_out.count; // TODO , check if it is necessary that xdecim factor.
|
||||
if (cnt > 90'000) {
|
||||
threshold = (tm / cnt) / 2;
|
||||
cnt = 0;
|
||||
tm = 0;
|
||||
if (threshold < 50) threshold = 50;
|
||||
if (threshold > 1700) threshold = 1700;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoViewProcessor::on_message(const Message* const message) {
|
||||
switch (message->id) {
|
||||
case Message::ID::SubGhzFPRxConfigure:
|
||||
configure(*reinterpret_cast<const SubGhzFPRxConfigureMessage*>(message));
|
||||
break;
|
||||
|
||||
case Message::ID::AudioBeep:
|
||||
on_beep_message(*reinterpret_cast<const AudioBeepMessage*>(message));
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void ProtoViewProcessor::configure(const SubGhzFPRxConfigureMessage& message) {
|
||||
baseband_fs = message.sampling_rate;
|
||||
baseband_thread.set_sampling_rate(baseband_fs);
|
||||
nsPerDecSamp = 1'000'000'000 / baseband_fs * 8; // Scaled it due to less array buffer sampes due to /8 decimation. 250 nseg (4Mhz) * 8
|
||||
|
||||
// constexpr size_t decim_0_output_fs = baseband_fs / decim_0.decimation_factor; //unused
|
||||
// constexpr size_t decim_1_output_fs = decim_0_output_fs / decim_1.decimation_factor; //unused
|
||||
|
||||
decim_0.configure(taps_200k_wfm_decim_0.taps);
|
||||
decim_1.configure(taps_200k_wfm_decim_1.taps);
|
||||
|
||||
configured = true;
|
||||
}
|
||||
|
||||
void ProtoViewProcessor::on_beep_message(const AudioBeepMessage& message) {
|
||||
audio::dma::beep_start(message.freq, message.sample_rate, message.duration_ms);
|
||||
}
|
||||
|
||||
int main() {
|
||||
audio::dma::init_audio_out();
|
||||
EventDispatcher event_dispatcher{std::make_unique<ProtoViewProcessor>()};
|
||||
event_dispatcher.run();
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
/*
|
||||
* Copyright (C) 2015 Jared Boone, ShareBrained Technology, Inc.
|
||||
* Copyright (C) 2016 Furrtek
|
||||
*
|
||||
* This file is part of PortaPack.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2, or (at your option)
|
||||
* any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; see the file COPYING. If not, write to
|
||||
* the Free Software Foundation, Inc., 51 Franklin Street,
|
||||
* Boston, MA 02110-1301, USA.
|
||||
*/
|
||||
/*
|
||||
Creator: @htotoo
|
||||
*/
|
||||
|
||||
#ifndef __PROC_PROTOVIEW_H__
|
||||
#define __PROC_PROTOVIEW_H__
|
||||
|
||||
#include "baseband_processor.hpp"
|
||||
#include "baseband_thread.hpp"
|
||||
#include "rssi_thread.hpp"
|
||||
#include "message.hpp"
|
||||
#include "dsp_decimate.hpp"
|
||||
|
||||
class ProtoViewProcessor : public BasebandProcessor {
|
||||
public:
|
||||
void execute(const buffer_c8_t& buffer) override;
|
||||
void on_message(const Message* const message) override;
|
||||
|
||||
private:
|
||||
size_t baseband_fs = 0; // will be set later by configure message.
|
||||
uint32_t nsPerDecSamp = 0;
|
||||
|
||||
/* Array Buffer aux. used in decim0 and decim1 IQ c16 signed data ; (decim0 defines the max length of the array) */
|
||||
std::array<complex16_t, 512> dst{}; // decim0 /4 , 2048/4 = 512 complex I,Q
|
||||
const buffer_c16_t dst_buffer{
|
||||
dst.data(),
|
||||
dst.size()};
|
||||
|
||||
/* Decimates */
|
||||
dsp::decimate::FIRC8xR16x24FS4Decim4 decim_0{};
|
||||
dsp::decimate::FIRC16xR16x16Decim2 decim_1{};
|
||||
|
||||
uint32_t currentDuration = 0;
|
||||
uint32_t threshold = 0x0630; // will overwrite after the first iteration
|
||||
|
||||
bool currentHiLow = false;
|
||||
bool configured{false};
|
||||
|
||||
// for threshold
|
||||
uint32_t cnt = 0;
|
||||
uint32_t tm = 0;
|
||||
|
||||
void configure(const SubGhzFPRxConfigureMessage& message);
|
||||
void on_beep_message(const AudioBeepMessage& message);
|
||||
|
||||
ProtoViewDataMessage message = {};
|
||||
|
||||
/* NB: Threads should be the last members in the class definition. */
|
||||
BasebandThread baseband_thread{baseband_fs, this, baseband::Direction::Receive};
|
||||
RSSIThread rssi_thread{};
|
||||
};
|
||||
|
||||
#endif /*__PROC_PROTOVIEW_H__*/
|
||||
@@ -35,6 +35,7 @@ void SubGhzDProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // Input:2048 complex/4 (decim factor) = 512_output complex (1024 I/Q samples)
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // Input:512 complex/2 (decim factor) = 256_output complex ( 512 I/Q samples)
|
||||
feed_channel_stats(decim_1_out);
|
||||
|
||||
for (size_t i = 0; i < decim_1_out.count; i++) {
|
||||
int16_t re = decim_1_out.p[i].real();
|
||||
|
||||
@@ -36,6 +36,7 @@ void WeatherProcessor::execute(const buffer_c8_t& buffer) {
|
||||
|
||||
const auto decim_0_out = decim_0.execute(buffer, dst_buffer); // Input:2048 complex/4 (decim factor) = 512_output complex (1024 I/Q samples)
|
||||
const auto decim_1_out = decim_1.execute(decim_0_out, dst_buffer); // Input:512 complex/2 (decim factor) = 256_output complex ( 512 I/Q samples)
|
||||
feed_channel_stats(decim_1_out);
|
||||
|
||||
for (size_t i = 0; i < decim_1_out.count; i++) {
|
||||
int16_t re = decim_1_out.p[i].real();
|
||||
|
||||
@@ -58,8 +58,6 @@ class WeatherProcessor : public BasebandProcessor {
|
||||
bool currentHiLow = false;
|
||||
bool configured{false};
|
||||
|
||||
uint8_t modulation = 255; // 0 AM, 1 FM 255 = Not set
|
||||
uint8_t protoMode = 255; // 0 weather, 1 subghzd, 255 = Not set
|
||||
// for threshold
|
||||
uint32_t cnt = 0;
|
||||
uint32_t tm = 0;
|
||||
|
||||
@@ -22,13 +22,23 @@
|
||||
|
||||
#include "ch.h"
|
||||
#include "hal.h"
|
||||
#include <libopencm3/lpc43xx/usb.h>
|
||||
|
||||
extern "C" {
|
||||
void start_usb(void);
|
||||
void irq_usb(void);
|
||||
void usb_transfer(void);
|
||||
|
||||
extern volatile bool scsi_running;
|
||||
|
||||
CH_IRQ_HANDLER(Vector60) {
|
||||
const uint32_t status = USB0_USBSTS_D & USB0_USBINTR_D;
|
||||
if (status & USB0_USBSTS_D_SLI) {
|
||||
// USB reset received.
|
||||
if (scsi_running) {
|
||||
LPC_RGU->RESET_CTRL[0] = (1 << 0);
|
||||
}
|
||||
}
|
||||
irq_usb();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,7 +23,7 @@
|
||||
#include "sd_over_usb.h"
|
||||
#include "scsi.h"
|
||||
|
||||
bool scsi_running = false;
|
||||
volatile bool scsi_running = false;
|
||||
|
||||
usb_request_status_t report_max_lun(
|
||||
usb_endpoint_t* const endpoint,
|
||||
|
||||
@@ -118,7 +118,7 @@ uint16_t ADS1110::readVoltage() {
|
||||
return (uint16_t)voltage;
|
||||
}
|
||||
|
||||
void ADS1110::getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage) {
|
||||
void ADS1110::getBatteryInfo(uint8_t& valid_mask, uint8_t& batteryPercentage, uint16_t& voltage) {
|
||||
voltage = readVoltage();
|
||||
|
||||
// Calculate the remaining battery percentage
|
||||
@@ -127,6 +127,7 @@ void ADS1110::getBatteryInfo(uint8_t& batteryPercentage, uint16_t& voltage) {
|
||||
// Limit the values to the valid range
|
||||
batteryPercentage = (batteryPercentage > 100) ? 100 : batteryPercentage;
|
||||
// ToDo: if its > 4, then 100%, if < 3 then 0%
|
||||
valid_mask = 1; // BATT_VALID_VOLTAGE
|
||||
}
|
||||
|
||||
} /* namespace ads1110 */
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user