Compare commits

...

43 Commits

Author SHA1 Message Date
jLynx 53bbf74f4d V1.9.0 (#1660)
* Update version.txt

* Update past_version.txt
2023-12-22 07:35:54 +13:00
Mark Thompson 19a66fac20 Enable Sample Rate to be spec'd in .ini file for Weather & SubGhzDecoder apps (#1659) 2023-12-21 19:33:06 +01:00
Totoo 36e1b9a36e Portapack USB screen tx (#1653)
* implemented 2 screen read functions, and haxfixed windows usb bug
* Support up to 64 byte per packet for faster tx
* Added filesize command
2023-12-19 22:25:22 +01:00
Netro 1ca9288bbe initial USB serial thread integrated to BLE RX (#1651) 2023-12-19 22:20:13 +01:00
Mark Thompson 4ca2d1e352 Fix compile error (#1650) 2023-12-18 07:35:00 +01:00
gullradriel 0b11730835 fix wrong direction in tx freq correction (#1649)
Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-12-17 18:47:20 -06:00
Bernd Herzog 6069145b68 Usb serial (#1648)
* enabled usb clock
* added usb stack to application
* fixed pll0usb clock setup
* implemented serial usb handshake
* implemented serial communication
* integrated chibios shell
* implemented device reset
* implemented enter dfu mode
* implemented hackrf mode command
* implemented flash command
* implemented memory manipulation
* implemented button control
* fixed mode change
* improved reset behavior
* implemented directory commands
* implemented file commands
* improved data communication
* refactorings
2023-12-17 17:20:35 +01:00
Totoo 2ccda5aebd Subghz decoder (#1646)
* Initial commit - wip

* Half part of the transition of baseband processor.

* More SGD

* WIP, Weather refactor, UI improv

* Rename

* Added 4msps, and fixes

* Fixes

* princeton working

* Renamed proc_weather, bc now multifunctional

* Proto: bett

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

* Came Atomo, fixes

* Separate weather and sgd, bc of baseband size limit

* Fix display

* Save space

* More protos

* Dooya proto added

* More protos

* add protos

* More protos

* Move weather to ext app

* nw

* Revert "Move weather to ext app"

This reverts commit 8a84aac2f5.

* revert

* Fix merge

* Better naming

* More protos

* More protos

* Add protos

* Fix warning

* Add NeroRadio

* more protos

* more protos

* More protos

* Shrink a bit

* fixes

* More protos

* Nicer code

* Fix naming

* Fix format

* Remove unused

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

* Modify key calculation
2023-12-16 16:37:51 -06:00
Totoo 02810bf527 Move Jammer ext app (with baseband image too) (#1647) 2023-12-15 20:18:21 +01:00
Totoo b58ee761a1 Move more apps + language module (#1643)
* CoasterP to ext (Burger pager)
* LGE to ext app
* Solve compiler literal removal with centralizing common string literals.
2023-12-12 16:55:50 +01:00
Totoo 5b9d898202 Fix oregon2 weather protocol (#1642) 2023-12-12 06:45:56 +01:00
Totoo 5f8e44e7b7 NRF RX to ext app (#1641)
* NFR Rx to ext app
2023-12-11 11:57:04 +01:00
Totoo ec80859d93 AnalogTV to ext (#1638) 2023-12-11 08:55:07 +01:00
Totoo 5e9d92d65c Set sr to 4m (#1636)
* Set sr to 4m

* Rename variable
2023-12-10 23:40:11 +01:00
Mark Thompson c3bb9c0a16 Weather display beautification (#1635) 2023-12-10 17:57:21 +01:00
Netro bd475e6f9c BLE RX - Find From File Initial Commit. (#1627)
* initial work
* Handling capping count only at total count
* Adding logging back in
2023-12-09 18:02:00 +01:00
Mark Thompson 0f1708d8fd Increase SSP timing margin for MAX2839 registers (#1632) 2023-12-09 14:09:20 +01:00
Mark Thompson efa0e270ad Updated Weather & TPMS app icons (#1630) 2023-12-09 09:25:41 +01:00
Totoo ff882024d4 Ble spam improvements (#1626)
* Much faster ble  spam tx
* Better Android handling
2023-12-09 09:24:02 +01:00
Brumi-2021 60d95447fb Improved sensitivity/selectivity Weather App (#1628) 2023-12-08 21:33:48 +01:00
Mark Thompson a474bf8fe3 Add files via upload (#1629) 2023-12-08 19:20:59 +01:00
Totoo 1bf0d2b3e6 BLE spam ext app (#1624)
* BLESpam ext app first release

* Ios partly added

* Fix missing!

* Maybe fix Ios

* Add comment

* Fixes

* Added samsung and windows

* cleaning a bit

* Partial size fix

* Move continuity to blespam namespace.

* Optimize code layout, so not using fw space

* Ui changes
2023-12-06 08:35:23 -05:00
Mark Thompson ea02d98a59 Preserve BLE RX app settings across runs, and changed BLE app settings file names (#1622)
* Add files via upload

* Add files via upload

* Add files via upload
2023-12-04 12:06:07 -05:00
Mark Thompson 9624d7b429 Fix compiler warning (#1620) 2023-12-03 08:19:04 +01:00
Totoo 4f440055cb Fix to_string_hex_array string reserve (#1619) 2023-12-02 16:42:09 -06:00
Netro 7354ba8fd5 Random data selection feature done. (#1617) 2023-12-02 08:05:29 +01:00
Netro b7b4a10485 Refined Tx Random Data (Still WIP) (#1616)
* managing initial cursor
* work on adding marked cursors
2023-11-30 21:57:22 +01:00
Totoo cca0e18f5a Weather improvements (#1615)
* Added Acurite986 protocol
* Added signal age
* Added myself to about screen
2023-11-30 12:36:59 +01:00
Netro 8846926b68 BLE TX: use text viewer to inject rand data WIP (#1613) 2023-11-29 18:54:17 +01:00
Mark Thompson 7ceefb1b81 Weather app tweaks (#1610)
* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload

* Add files via upload
2023-11-29 10:37:01 -06:00
Erwin Ried 861f684d00 Discord icon/invite more clear on top (#1612) 2023-11-29 13:22:51 +01:00
Erwin Ried 17b146b960 Update README.md (#1611) 2023-11-29 11:23:27 +01:00
Totoo 02251eeeb4 Added weather station app with 18 protocol parsers (#1607)
* Added weather station app with 18 protocol parsers
* Fix button and formatting
* Set BW to 1.75m, changed to us in dsp part
2023-11-28 21:11:30 +01:00
Netro c486572d3d BLE TX; Added feature to randomize found substring in data (#1609) 2023-11-28 21:11:07 +01:00
Netro a6ed6e3099 BLE RX: Making auto channel timer independent of packet events. (#1608) 2023-11-28 18:18:15 +01:00
Netro 175e5e2e8c BLE: Minor improvements to fix occasional not start on tx app first time. (#1605) 2023-11-27 21:59:29 +01:00
Mark Thompson 7bc27038be Save Morse app settings (#1602) 2023-11-26 12:17:23 +01:00
Brumi-2021 609bf3219f solve Morse_TX_App latch up in r9 (#1601) 2023-11-25 01:01:52 +01:00
Mark Thompson bdbd616a84 added a frequency field to the ERT app 2023-11-24 09:25:50 +01:00
Mark Thompson 1cbe5cb293 Save TPMS units in App Settings and tune frequency (#1598) 2023-11-21 18:44:10 +01:00
gullradriel df6003c08f fix button label (#1597)
Co-authored-by: GullCode <gullradriel@hotmail.com>
2023-11-20 12:09:47 -05:00
Netro 50fcbf0f5a BLE: added deleting and renaming temp file (#1596) 2023-11-20 08:37:56 +01:00
Netro 5a1676a0b9 BLE List improvements (#1595)
* WIP on add to save list

* Added rewriting existing lines and appending new to same file.

* format

* not looking on new file.

* Added temp list due to some issue of recent being updated during save

I was receiving a system halt and this seems to fix it. Needs more testing though.

* clang format

* Stored entries backward. Fix.

* Update file working, need to replace temp file though.
2023-11-19 20:53:53 -05:00
161 changed files with 13709 additions and 446 deletions
+1 -1
View File
@@ -1 +1 @@
v1.7.4
v1.8.0
+1 -1
View File
@@ -1 +1 @@
v1.8.0
v1.9.0
+6 -4
View File
@@ -1,6 +1,6 @@
# PortaPack Mayhem
[![Build Status](https://travis-ci.com/eried/portapack-mayhem.svg?branch=master)](https://travis-ci.com/eried/portapack-mayhem) [![Nightly Release](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml/badge.svg?branch=next)](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/total)](https://github.com/eried/portapack-mayhem/releases) [![GitHub Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/latest/total)](https://github.com/eried/portapack-mayhem/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://img.shields.io/discord/719669764804444213.svg)](https://discord.gg/tuwVMv3) [![Check bounties!](https://img.shields.io/bountysource/team/portapack-mayhem/activity?color=%2333ccff&label=bountysource%20%28USD%29&style=plastic)](https://www.bountysource.com/teams/portapack-mayhem/issues)
[![Build Status](https://travis-ci.com/eried/portapack-mayhem.svg?branch=master)](https://travis-ci.com/eried/portapack-mayhem) [![Nightly Release](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml/badge.svg?branch=next)](https://github.com/eried/portapack-mayhem/actions/workflows/create_nightly_release.yml) [![CodeScene Code Health](https://codescene.io/projects/8381/status-badges/code-health)](https://codescene.io/projects/8381) [![GitHub All Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/total)](https://github.com/eried/portapack-mayhem/releases) [![GitHub Releases](https://img.shields.io/github/downloads/eried/portapack-mayhem/latest/total)](https://github.com/eried/portapack-mayhem/releases/latest) [![Docker Hub Pulls](https://img.shields.io/docker/pulls/eried/portapack.svg)](https://hub.docker.com/r/eried/portapack) [![Discord Chat](https://dcbadge.vercel.app/api/server/tuwVMv3?style=flat)](https://discord.gg/tuwVMv3)
This is a fork of the [Havoc](https://github.com/furrtek/portapack-havoc/) firmware, which itself was a fork of the [PortaPack](https://github.com/sharebrained/portapack-hackrf) firmware, an add-on for the [HackRF](http://greatscottgadgets.com/hackrf/). A fork is a derivate, in this case one that has extra features and fixes when compared to the older versions.
@@ -22,7 +22,7 @@ This repository expands upon the previous work by many people and aims to consta
:heavy_check_mark: A recommended one is this [PortaPack H2](https://s.click.aliexpress.com/e/_DmU7GQX), that includes everything you need with the plastic case "inspired" on [this](https://github.com/eried/portapack-mayhem/wiki/H2-Enclosure).
:heavy_check_mark: Our friends at OpenSourceSDRLab give away five units every three months in [our discord](https://discord.com/channels/719669764804444213/1165624357129834547) of this [PortaPack H2](https://www.aliexpress.com/item/4000247041639.html?gatewayAdapt=4itemAdapt), you can support them too.
:heavy_check_mark: Our friends at OpenSourceSDRLab give away five units every three months in our discord (check the badge on top) of their [PortaPack H2](https://www.aliexpress.com/item/4000247041639.html?gatewayAdapt=4itemAdapt), you can support them too by ordering.
:heavy_check_mark: Another popular option is the clone of the [PortaPack H1](https://s.click.aliexpress.com/e/_Dkbqs2X).
@@ -50,9 +50,11 @@ To support the people behind the hardware, please buy a genuine [HackRF](https:/
## What if I really want something specific?
If what you need can be relevant in general, you can [request a feature](https://github.com/eried/portapack-mayhem/issues/new?labels=enhancement&template=feature_request.md).
You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/eried/portapack-mayhem/issues/new/choose) or [choosing](https://github.com/eried/portapack-mayhem/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).
<del>You can create a bounty and invite people to your own bounty. This will incentivize coders to work on a new feature, solving a bug or even writting documentation. Start a bounty by [creating](https://github.com/eried/portapack-mayhem/issues/new/choose) or [choosing](https://github.com/eried/portapack-mayhem/issues/) an existing issue. Then, go to [Bountysource](https://www.bountysource.com/) and post a bounty using the link to that specific [issue](https://www.bountysource.com/teams/portapack-mayhem/issues).</del>
Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).
<del>Promote your bounty over our Discord by clicking the chat badge on [top](#portapack-mayhem).</del>
Bountysource has not been reliable lately, so until this changes, please **DO NOT** post a bounty there. Go to our Discord by clicking the chat badge on [top](#portapack-mayhem) and discuss there.
## What if I need help?
First, check the [documentation](https://github.com/eried/portapack-mayhem/wiki). If you find a bug or you think the problem is related to the current repository, please open an [issue](https://github.com/eried/portapack-mayhem/issues/new/choose).
+1
View File
@@ -54,6 +54,7 @@ add_custom_target(
add_custom_target(
program
COMMAND ${PROJECT_SOURCE_DIR}/tools/enter_mode.sh hackrf
COMMAND dfu-util --device 1fc9:000c --download ${HACKRF_FIRMWARE_DFU_IMAGE} || (exit 0) # We need to add it for dfu-utils v.011 , (in v.09 it is not necessary)
COMMAND sleep 3s
COMMAND hackrf_spiflash -i -R -w ${FIRMWARE_FILENAME}
+28 -8
View File
@@ -113,6 +113,17 @@ set(CSRC
${BOARDSRC}
${FATFSSRC}
firmware_info.c
usb_serial_cdc.c
usb_serial_descriptor.c
usb_serial_endpoints.c
usb_serial_io.c
${HACKRF_PATH}/firmware/common/usb.c
${HACKRF_PATH}/firmware/common/usb_queue.c
${HACKRF_PATH}/firmware/hackrf_usb/usb_device.c
${HACKRF_PATH}/firmware/common/usb_request.c
${HACKRF_PATH}/firmware/common/usb_standard_request.c
${CHIBIOS}/os/various/shell.c
${CHIBIOS}/os/various/chprintf.c
)
# C++ sources that can be compiled in ARM or THUMB mode depending on the global
@@ -162,6 +173,7 @@ set(CPPSRC
${COMMON}/ui_painter.cpp
${COMMON}/ui_text.cpp
${COMMON}/ui_widget.cpp
${COMMON}/ui_language.cpp
${COMMON}/utility.cpp
${COMMON}/wm8731.cpp
${COMMON}/performance_counter.cpp
@@ -190,6 +202,10 @@ set(CPPSRC
log_file.cpp
metadata_file.cpp
portapack.cpp
usb_serial_shell.cpp
usb_serial_event.cpp
usb_serial_thread.cpp
usb_serial.cpp
qrcodegen.cpp
radio.cpp
receiver_model.cpp
@@ -243,15 +259,14 @@ set(CPPSRC
apps/acars_app.cpp
apps/ais_app.cpp
apps/analog_audio_app.cpp
apps/analog_tv_app.cpp
# apps/analog_tv_app.cpp
apps/ble_comm_app.cpp
apps/ble_rx_app.cpp
apps/ble_tx_app.cpp
apps/capture_app.cpp
apps/ert_app.cpp
apps/gps_sim_app.cpp
apps/lge_app.cpp
apps/lge_app.cpp
# apps/lge_app.cpp
apps/pocsag_app.cpp
# apps/replay_app.cpp
apps/soundboard_app.cpp
@@ -264,7 +279,7 @@ set(CPPSRC
apps/ui_aprs_tx.cpp
apps/ui_bht_tx.cpp
apps/ui_btle_rx.cpp
apps/ui_coasterp.cpp
# apps/ui_coasterp.cpp
apps/ui_debug.cpp
apps/ui_dfu_menu.cpp
apps/ui_encoders.cpp
@@ -273,15 +288,15 @@ set(CPPSRC
apps/ui_freqman.cpp
apps/ui_fsk_rx.cpp
apps/ui_iq_trim.cpp
apps/ui_jammer.cpp
# apps/ui_jammer.cpp
# apps/ui_keyfob.cpp
apps/ui_lcr.cpp
# apps/ui_lcr.cpp
apps/ui_level.cpp
apps/ui_looking_glass_app.cpp
apps/ui_mictx.cpp
apps/ui_modemsetup.cpp
apps/ui_morse.cpp
apps/ui_nrf_rx.cpp
# apps/ui_nrf_rx.cpp
# apps/ui_nuoptix.cpp
apps/ui_playlist.cpp
apps/ui_pocsag_tx.cpp
@@ -301,19 +316,21 @@ set(CPPSRC
apps/ui_spectrum_painter.cpp
apps/ui_ss_viewer.cpp
apps/ui_sstvtx.cpp
apps/ui_subghzd.cpp
# apps/ui_test.cpp
apps/ui_text_editor.cpp
apps/ui_tone_search.cpp
apps/ui_touch_calibration.cpp
apps/ui_touchtunes.cpp
apps/ui_view_wav.cpp
apps/ui_weatherstation.cpp
apps/ui_whipcalc.cpp
protocols/aprs.cpp
protocols/ax25.cpp
protocols/bht.cpp
protocols/dcs.cpp
protocols/encoders.cpp
protocols/lcr.cpp
# protocols/lcr.cpp
protocols/modems.cpp
protocols/rds.cpp
# ui_handwrite.cpp
@@ -361,6 +378,9 @@ set(INCDIR ${CMAKE_CURRENT_BINARY_DIR} ${COMMON} ${PORTINC} ${KERNINC} ${TESTINC
${HALINC} ${PLATFORMINC} ${BOARDINC}
${FATFSINC}
${CHIBIOS}/os/various
${HACKRF_PATH}/firmware/libopencm3/include
${HACKRF_PATH}/firmware/common
${HACKRF_PATH}/firmware
ui
hw
apps
+271 -80
View File
@@ -22,19 +22,22 @@
*/
#include "ble_rx_app.hpp"
#include "ble_rx_app.hpp"
#include "ui_modemsetup.hpp"
#include "modems.hpp"
#include "audio.hpp"
#include "io_file.hpp"
#include "rtc_time.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
#include "ui_fileman.hpp"
#include "ui_textentry.hpp"
using namespace portapack;
using namespace modems;
namespace fs = std::filesystem;
void BLELogger::log_raw_data(const std::string& data) {
log_file.write_entry(data);
@@ -199,8 +202,6 @@ BleRecentEntryDetailView::BleRecentEntryDetailView(NavigationView& nav, const Bl
}
void BleRecentEntryDetailView::on_save_file(const std::string value, BLETxPacket packetToSave) {
std::filesystem::path packet_save_path{u"BLERX/Packets/Packet_????.TXT"};
ensure_directory(packet_save_path);
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
@@ -403,6 +404,10 @@ void BLERxView::focus() {
options_channel.focus();
}
void BLERxView::file_error() {
nav_.display_modal("Error", "File read error.");
}
BLERxView::BLERxView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_btle_rx);
@@ -415,9 +420,12 @@ BLERxView::BLERxView(NavigationView& nav)
&options_channel,
&field_frequency,
&check_log,
&button_find,
&check_name,
&label_sort,
&options_sort,
&label_found,
&text_found_count,
&button_filter,
&button_save_list,
&button_clear_list,
@@ -428,6 +436,14 @@ BLERxView::BLERxView(NavigationView& nav)
nav_.push<BleRecentEntryDetailView>(entry);
};
usb_serial_thread = std::make_unique<UsbSerialThread>();
ensure_directory(find_packet_path);
ensure_directory(log_packets_path);
ensure_directory(packet_save_path);
filterBuffer = filter;
button_filter.on_select = [this](Button&) {
text_prompt(
nav_,
@@ -438,6 +454,21 @@ BLERxView::BLERxView(NavigationView& nav)
});
};
logger = std::make_unique<BLELogger>();
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
str_log = "";
logging = v;
if (logger && logging)
logger->append(
"BLERX/Logs"
"/BLELOG_" +
to_string_timestamp(rtc_time::now()) + ".TXT");
};
button_save_list.on_select = [this, &nav](const ui::Button&) {
listFileBuffer = "";
text_prompt(
@@ -459,46 +490,47 @@ BLERxView::BLERxView(NavigationView& nav)
field_frequency.set_step(0);
check_log.set_value(logging);
check_log.on_select = [this](Checkbox&, bool v) {
str_log = "";
logging = v;
if (logger && logging)
logger->append(LOG_ROOT_DIR "/BLELOG_" + to_string_timestamp(rtc_time::now()) + ".TXT");
};
check_name.set_value(true);
check_name.set_value(name_enable);
check_name.on_select = [this](Checkbox&, bool v) {
name_enable = v;
setAllMembersToValue(recent, &BleRecentEntry::include_name, v);
recent_entries_view.set_dirty();
};
options_channel.on_change = [this](size_t, int32_t i) {
options_channel.on_change = [this](size_t index, int32_t v) {
channel_index = (uint8_t)index;
// If we selected Auto don't do anything and Auto will handle changing.
if (i == 40) {
if (v == 40) {
auto_channel = true;
return;
} else {
auto_channel = false;
}
field_frequency.set_value(get_freq_by_channel_number(i));
channel_number = i;
field_frequency.set_value(get_freq_by_channel_number(v));
channel_number = v;
baseband::set_btlerx(channel_number);
};
options_sort.on_change = [this](size_t, int32_t index) {
handle_entries_sort(index);
options_sort.on_change = [this](size_t index, int32_t v) {
sort_index = (uint8_t)index;
handle_entries_sort(v);
};
options_channel.set_selected_index(0, true);
options_sort.set_selected_index(0, true);
options_channel.set_selected_index(channel_index, true);
options_sort.set_selected_index(sort_index, true);
logger = std::make_unique<BLELogger>();
button_find.on_select = [this](Button&) {
auto open_view = nav_.push<FileLoadView>(".TXT");
open_view->on_changed = [this](std::filesystem::path new_file_path) {
on_file_changed(new_file_path);
// nav_.set_on_pop([this]() { button_play.focus(); });
};
};
// Auto-configure modem for LCR RX (will be removed later)
baseband::set_btlerx(channel_number);
@@ -506,10 +538,23 @@ BLERxView::BLERxView(NavigationView& nav)
receiver_model.enable();
}
void BLERxView::on_save_file(const std::string value) {
std::filesystem::path packet_save_path{u"BLERX/Lists/List_????.csv"};
std::string BLERxView::build_line_str(BleRecentEntry entry) {
std::string macAddressStr = to_string_mac_address(entry.packetData.macAddress, 6, false) + ",";
std::string timestameStr = entry.timestamp + ",";
std::string nameStr = entry.nameString + ",";
std::string pduStr = pdu_type_to_string(entry.pduType) + ",";
std::string dataStr = "0x" + entry.dataString + ",";
std::string hitsStr = to_string_dec_int(entry.numHits) + ",";
std::string dbStr = to_string_dec_int(entry.dbValue) + ",";
std::string channelStr = to_string_dec_int(entry.channelNumber) + ",";
ensure_directory(packet_save_path);
std::string lineStr = timestameStr + macAddressStr + nameStr + pduStr + dataStr + hitsStr + dbStr + channelStr;
lineStr += pad_string_with_spaces(maxLineLength - lineStr.length());
return lineStr;
}
void BLERxView::on_save_file(const std::string value) {
auto folder = packet_save_path.parent_path();
auto ext = packet_save_path.extension();
auto new_path = folder / value + ext;
@@ -518,72 +563,139 @@ void BLERxView::on_save_file(const std::string value) {
}
bool BLERxView::saveFile(const std::filesystem::path& path) {
File f;
auto error = f.create(path);
if (error)
// Check to see if file was previously saved.
bool file_existed = file_exists(path);
// Attempt to open, if it can't be opened. Create new.
auto src = std::make_unique<File>();
auto error = src->open(path, false, true);
if (error) {
return false;
auto it = recent.begin();
std::string headerStr = "Timestamp, MAC Address, Name, Packet Type, Data, Hits, dB, Channel \n";
f.write(headerStr.c_str(), headerStr.length());
while (it != recent.end()) {
BleRecentEntry entry = (BleRecentEntry)*it;
std::string macAddressStr = to_string_mac_address(entry.packetData.macAddress, 6, false) + ", ";
std::string timestameStr = entry.timestamp + ", ";
std::string nameStr = entry.nameString + ", ";
std::string pduStr = pdu_type_to_string(entry.pduType) + ", ";
std::string dataStr = "0x" + entry.dataString + ", ";
std::string hitsStr = to_string_dec_int(entry.numHits) + ", ";
std::string dbStr = to_string_dec_int(entry.dbValue) + ", ";
std::string channelStr = to_string_dec_int(entry.channelNumber) + "\n";
f.write(timestameStr.c_str(), timestameStr.length());
f.write(macAddressStr.c_str(), macAddressStr.length());
f.write(nameStr.c_str(), nameStr.length());
f.write(pduStr.c_str(), pduStr.length());
f.write(dataStr.c_str(), dataStr.length());
f.write(hitsStr.c_str(), hitsStr.length());
f.write(dbStr.c_str(), dbStr.length());
f.write(channelStr.c_str(), channelStr.length());
it++;
}
for (const auto& entry : recent) {
tempList.emplace_back(entry);
}
if (!file_existed) {
src->write_line(headerStr.c_str());
auto it = tempList.begin();
while (it != tempList.end()) {
BleRecentEntry entry = (BleRecentEntry)*it;
src->write_line(build_line_str(entry).c_str());
it++;
}
} else {
// Check file for macAddressStr before adding.
char currentLine[maxLineLength];
uint64_t startPos = headerStr.length();
uint64_t bytesRead = 0;
uint64_t bytePos = 0;
File::Size currentSize = src->size();
auto dst = std::make_unique<File>();
const std::filesystem::path tempFilePath = path + "~";
auto error = dst->open(tempFilePath, false, true);
if (error) {
return false;
}
dst->write_line(headerStr.c_str());
src->seek(startPos);
// Look for ones found and rewrite.
do {
memset(currentLine, 0, maxLineLength);
bytesRead = readUntil(*src, currentLine, currentSize, '\n');
if (!bytesRead) {
break;
}
bytePos += bytesRead;
std::string lineStr = "";
std::string macAddressStr = "";
BleRecentEntry foundEntry;
char* token;
token = strtok(currentLine, ",");
while (token != NULL) {
auto it = tempList.begin();
while (it != tempList.end()) {
BleRecentEntry& entry = reinterpret_cast<BleRecentEntry&>(*it);
macAddressStr = to_string_mac_address(entry.packetData.macAddress, 6, false);
if (strstr(token, macAddressStr.c_str()) != NULL) {
entry.entryFound = true;
foundEntry = entry;
break;
}
it++;
}
if (foundEntry.entryFound) {
break;
}
token = strtok(NULL, ",");
}
if (foundEntry.entryFound) {
dst->write_line(build_line_str(foundEntry).c_str());
}
} while (bytePos <= currentSize);
// Write the ones not found.
auto it = tempList.begin();
while (it != tempList.end()) {
BleRecentEntry entry = (BleRecentEntry)*it;
if (!entry.entryFound) {
dst->write_line(build_line_str(entry).c_str());
}
it++;
}
// Close files before renaming/deleting.
src.reset();
dst.reset();
// Delete original and overwrite with temp file.
delete_file(path);
rename_file(tempFilePath, path);
}
tempList.clear();
return true;
}
void BLERxView::on_data(BlePacketData* packet) {
if (auto_channel) {
int min = 37;
int max = 39;
int randomChannel = min + std::rand() % (max - min + 1);
field_frequency.set_value(get_freq_by_channel_number(randomChannel));
baseband::set_btlerx(randomChannel);
}
std::string str_console = "";
if (!logging) {
str_log = "";
}
str_console += pdu_type_to_string((ADV_PDU_TYPE)packet->type);
str_console += " Len:";
str_console += to_string_dec_uint(packet->size);
str_console += "\n";
str_console += "Mac:";
str_console += " Mac:";
str_console += to_string_mac_address(packet->macAddress, 6, false);
str_console += "\n";
str_console += "Data:";
str_console += " Data:";
int i;
@@ -591,8 +703,6 @@ void BLERxView::on_data(BlePacketData* packet) {
str_console += to_string_hex(packet->data[i], 2);
}
str_console += "\n";
uint64_t macAddressEncoded = copy_mac_address_to_uint64(packet->macAddress);
// Start of Packet stuffing.
@@ -610,7 +720,29 @@ void BLERxView::on_data(BlePacketData* packet) {
// Log at End of Packet.
if (logger && logging) {
logger->log_raw_data(str_console);
logger->log_raw_data(str_console + "\r\n");
}
usb_serial_thread->serial_str = str_console + "\r\n";
usb_serial_thread->str_ready = true;
str_console = "";
if (!searchList.empty()) {
auto it = searchList.begin();
while (it != searchList.end()) {
std::string searchStr = (std::string)*it;
if (entry.dataString.find(searchStr) != std::string::npos) {
searchList.erase(it);
found_count++;
break;
}
it++;
}
text_found_count.set(to_string_dec_uint(found_count) + "/" + to_string_dec_uint(total_count));
}
}
@@ -625,6 +757,65 @@ void BLERxView::on_filter_change(std::string value) {
filter = value;
}
void BLERxView::on_file_changed(const std::filesystem::path& new_file_path) {
file_path = fs::path(u"/") + new_file_path;
found_count = 0;
total_count = 0;
searchList.clear();
{ // Get the size of the data file.
File data_file;
auto error = data_file.open(file_path, true, false);
if (error) {
file_error();
file_path = "";
return;
}
uint64_t bytesRead = 0;
uint64_t bytePos = 0;
char currentLine[maxLineLength];
do {
memset(currentLine, 0, maxLineLength);
bytesRead = readUntil(data_file, currentLine, maxLineLength, '\n');
// Remove return if found.
if (currentLine[strlen(currentLine)] == '\r') {
currentLine[strlen(currentLine)] = '\0';
}
if (!bytesRead) {
break;
}
searchList.push_back(currentLine);
total_count++;
bytePos += bytesRead;
} while (bytePos <= data_file.size());
}
}
// called each 1/60th of second, so 6 = 100ms
void BLERxView::on_timer() {
if (++timer_count == timer_period) {
timer_count = 0;
if (auto_channel) {
int min = 37;
int max = 39;
int randomChannel = min + std::rand() % (max - min + 1);
field_frequency.set_value(get_freq_by_channel_number(randomChannel));
baseband::set_btlerx(randomChannel);
}
}
}
void BLERxView::handle_entries_sort(uint8_t index) {
switch (index) {
case 0:
+63 -9
View File
@@ -35,6 +35,7 @@
#include "radio_state.hpp"
#include "log_file.hpp"
#include "utility.hpp"
#include "usb_serial_thread.hpp"
#include "recent_entries.hpp"
@@ -85,6 +86,7 @@ struct BleRecentEntry {
uint16_t numHits;
ADV_PDU_TYPE pduType;
uint8_t channelNumber;
bool entryFound;
BleRecentEntry()
: BleRecentEntry{0} {
@@ -101,7 +103,8 @@ struct BleRecentEntry {
include_name{},
numHits{},
pduType{},
channelNumber{} {
channelNumber{},
entryFound{} {
}
Key key() const {
@@ -130,6 +133,7 @@ class BleRecentEntryDetailView : public View {
void on_save_file(const std::string value, BLETxPacket packetToSave);
bool saveFile(const std::filesystem::path& path, BLETxPacket packetToSave);
std::string packetFileBuffer{};
std::filesystem::path packet_save_path{u"BLERX/Lists/????.csv"};
static constexpr uint8_t total_data_lines{5};
@@ -188,10 +192,15 @@ class BLERxView : public View {
std::string title() const override { return "BLE RX"; };
private:
std::string build_line_str(BleRecentEntry entry);
void on_save_file(const std::string value);
bool saveFile(const std::filesystem::path& path);
std::unique_ptr<UsbSerialThread> usb_serial_thread{};
void on_data(BlePacketData* packetData);
void on_filter_change(std::string value);
void on_file_changed(const std::filesystem::path& new_file_path);
void file_error();
void on_timer();
void handle_entries_sort(uint8_t index);
void updateEntry(const BlePacketData* packet, BleRecentEntry& entry, ADV_PDU_TYPE pdu_type);
@@ -201,19 +210,48 @@ class BLERxView : public View {
4000000 /* bandwidth */,
4000000 /* sampling rate */,
ReceiverModel::Mode::WidebandFMAudio};
app_settings::SettingsManager settings_{
"BLE Rx", app_settings::Mode::RX};
uint8_t channel_index{0};
uint8_t sort_index{0};
std::string filter{};
bool logging{false};
bool name_enable{true};
app_settings::SettingsManager settings_{
"rx_ble",
app_settings::Mode::RX,
{
{"channel_index"sv, &channel_index},
{"sort_index"sv, &sort_index},
{"filter"sv, &filter},
{"log"sv, &logging},
{"name"sv, &name_enable},
}};
std::string str_console = "";
uint8_t console_color{0};
uint32_t prev_value{0};
uint8_t channel_number = 37;
bool auto_channel = false;
std::string filterBuffer{};
std::string filter{};
std::string listFileBuffer{};
int16_t timer_count{0};
int16_t timer_period{6}; // 100ms
static constexpr auto header_height = 3 * 16;
std::string filterBuffer{};
std::string listFileBuffer{};
std::string headerStr = "Timestamp, MAC Address, Name, Packet Type, Data, Hits, dB, Channel";
uint16_t maxLineLength = 140;
std::filesystem::path file_path{};
uint64_t found_count = 0;
uint64_t total_count = 0;
std::vector<std::string> searchList{};
std::filesystem::path find_packet_path{u"BLERX/Find/????.TXT"};
std::filesystem::path log_packets_path{u"BLERX/Logs/????.TXT"};
std::filesystem::path packet_save_path{u"BLERX/Lists/????.csv"};
static constexpr auto header_height = 4 * 16;
static constexpr auto switch_button_height = 3 * 16;
OptionsField options_channel{
@@ -271,6 +309,17 @@ class BLERxView : public View {
"Name",
true};
Button button_find{
{0 * 8, 6 * 8, 4 * 8, 16},
"Find"};
Labels label_found{
{{5 * 8, 6 * 8}, "Found:", Color::light_grey()}};
Text text_found_count{
{11 * 8, 3 * 16, 20 * 8, 16},
"0/0"};
Console console{
{0, 4 * 16, 240, 240}};
@@ -287,11 +336,10 @@ class BLERxView : public View {
"Tx"};
std::string str_log{""};
bool logging{false};
std::unique_ptr<BLELogger> logger{};
BleRecentEntries recent{};
BleRecentEntries tempList{};
const RecentEntriesColumns columns{{
{"Mac Address", 17},
@@ -307,6 +355,12 @@ class BLERxView : public View {
const auto message = static_cast<const BLEPacketMessage*>(p);
this->on_data(message->packet);
}};
MessageHandlerRegistration message_handler_frame_sync{
Message::ID::DisplayFrameSync,
[this](const Message* const) {
this->on_timer();
}};
};
} /* namespace ui */
+141 -69
View File
@@ -67,7 +67,7 @@ std::vector<std::string> splitIntoStrings(const char* input) {
while (start < length) {
int remaining = length - start;
int chunkSize = (remaining > 30) ? 30 : remaining;
int chunkSize = (remaining > 29) ? 29 : remaining;
result.push_back(std::string(input + start, chunkSize));
start += chunkSize;
}
@@ -94,33 +94,6 @@ uint32_t stringToUint32(const std::string& str) {
return result;
}
void readUntil(File& file, char* result, std::size_t maxBufferSize, char delimiter) {
std::size_t bytesRead = 0;
while (true) {
char ch;
File::Result<File::Size> readResult = file.read(&ch, 1);
if (readResult.is_ok() && readResult.value() > 0) {
if (ch == delimiter) {
// Found a space character, stop reading
break;
} else if (bytesRead < maxBufferSize) {
// Append the character to the result if there's space
result[bytesRead++] = ch;
} else {
// Buffer is full, break to prevent overflow
break;
}
} else {
break; // End of file or error
}
}
// Null-terminate the result string
result[bytesRead] = '\0';
}
static std::uint64_t get_freq_by_channel_number(uint8_t channel_number) {
uint64_t freq_hz;
@@ -194,16 +167,8 @@ void BLETxView::toggle() {
}
void BLETxView::start() {
int randomChannel = channel_number;
if (auto_channel) {
int min = 37;
int max = 39;
randomChannel = min + std::rand() % (max - min + 1);
field_frequency.set_value(get_freq_by_channel_number(randomChannel));
}
baseband::run_image(portapack::spi_flash::image_tag_btle_tx);
transmitter_model.enable();
// Generate new random Mac Address.
generateRandomMacAddress(randomMac);
@@ -219,27 +184,65 @@ void BLETxView::start() {
return;
}
transmitter_model.enable();
button_play.set_bitmap(&bitmap_stop);
is_running = true;
}
// Send next packet.
progressbar.set_max(packets[current_packet].packet_count);
baseband::set_btletx(randomChannel, random_mac ? randomMac : packets[current_packet].macAddress, packets[current_packet].advertisementData, pduType);
char advertisementData[63] = {0};
strcpy(advertisementData, packets[current_packet].advertisementData);
is_sending = true;
// TODO: Make this a checkbox.
if (!markedBytes.empty()) {
for (size_t i = 0; i < strlen(advertisementData); i++) {
bool found = false;
if ((packet_counter % 10) == 0) {
text_packets_sent.set(to_string_dec_uint(packet_counter));
auto it = std::find(markedBytes.begin(), markedBytes.end(), i);
if (it != markedBytes.end()) {
found = true;
}
if (found) {
uint8_t hexDigit;
switch (marked_data_sequence.selected_index_value()) {
case 0:
hexDigit = marked_counter++;
break;
case 1:
hexDigit = marked_counter--;
break;
case 2: {
uint8_t min = 0x00;
uint8_t max = 0x0F;
hexDigit = min + std::rand() % (max - min + 1);
} break;
default:
hexDigit = 0;
break;
}
advertisementData[i] = uint_to_char(hexDigit, 16);
// Bounding to Hex.
if (marked_counter == 16) {
marked_counter = 0;
} else if (marked_counter == 255) {
marked_counter = 15;
}
}
}
}
packet_counter--;
progressbar.set_value(packets[current_packet].packet_count - packet_counter);
// Setup next packet configuration.
progressbar.set_max(packets[current_packet].packet_count);
baseband::set_btletx(channel_number, random_mac ? randomMac : packets[current_packet].macAddress, advertisementData, pduType);
}
void BLETxView::stop() {
transmitter_model.disable();
baseband::shutdown();
progressbar.set_value(0);
button_play.set_bitmap(&bitmap_play);
check_loop.set_value(false);
@@ -249,47 +252,72 @@ void BLETxView::stop() {
is_running = false;
}
void BLETxView::reset() {
transmitter_model.disable();
baseband::shutdown();
start();
}
// called each 1/60th of second, so 6 = 100ms
void BLETxView::on_timer() {
if (++mscounter == timer_period) {
mscounter = 0;
if (++timer_count == timer_period) {
timer_count = 0;
if (is_active() && !is_sending) {
if (is_active()) {
// Reached end of current packet repeats.
if (packet_counter == 0) {
// Done sending all packets.
if (current_packet == (num_packets - 1)) {
current_packet = 0;
// If looping, restart from beginning.
if (check_loop.value()) {
update_current_packet(packets[current_packet], 0);
start();
update_current_packet(packets[current_packet], current_packet);
reset();
} else {
stop();
}
} else {
current_packet++;
update_current_packet(packets[current_packet], current_packet);
start();
reset();
}
} else {
start();
reset();
}
}
}
if (++auto_channel_counter == auto_channel_period) {
auto_channel_counter = 0;
if (auto_channel) {
int min = 37;
int max = 39;
channel_number = min + std::rand() % (max - min + 1);
field_frequency.set_value(get_freq_by_channel_number(channel_number));
}
}
}
void BLETxView::on_tx_progress(const bool done) {
if (done) {
if (is_active()) {
is_sending = false;
if ((packet_counter % 10) == 0) {
text_packets_sent.set(to_string_dec_uint(packet_counter));
}
packet_counter--;
progressbar.set_value(packets[current_packet].packet_count - packet_counter);
}
}
}
BLETxView::BLETxView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_btle_tx);
add_children({&button_open,
&text_filename,
&progressbar,
@@ -302,6 +330,8 @@ BLETxView::BLETxView(NavigationView& nav)
&options_speed,
&options_channel,
&options_adv_type,
&label_marked_data,
&marked_data_sequence,
&label_packet_index,
&text_packet_index,
&label_packets_sent,
@@ -309,9 +339,10 @@ BLETxView::BLETxView(NavigationView& nav)
&label_mac_address,
&text_mac_address,
&label_data_packet,
&dataEditView,
&button_clear_marked,
&button_save_packet,
&button_switch,
&console});
&button_switch});
field_frequency.set_step(0);
@@ -336,7 +367,7 @@ BLETxView::BLETxView(NavigationView& nav)
options_speed.on_change = [this](size_t, int32_t i) {
timer_period = i;
mscounter = 0;
timer_count = 0;
};
options_adv_type.on_change = [this](size_t, int32_t i) {
@@ -375,6 +406,33 @@ BLETxView::BLETxView(NavigationView& nav)
button_switch.on_select = [&nav](Button&) {
nav.replace<BLERxView>();
};
dataEditView.on_select = [this] {
// Save last selected cursor.
cursor_pos.line = dataEditView.line();
cursor_pos.col = dataEditView.col();
// Reject setting newline at index 29.
if (cursor_pos.col != 29) {
uint16_t dataBytePos = (cursor_pos.line * 29) + cursor_pos.col;
auto it = std::find(markedBytes.begin(), markedBytes.end(), dataBytePos);
if (it != markedBytes.end()) {
markedBytes.erase(it);
} else {
markedBytes.push_back(dataBytePos);
}
dataEditView.cursor_mark_selected();
}
};
button_clear_marked.on_select = [this](Button&) {
marked_counter = 0;
markedBytes.clear();
dataEditView.cursor_clear_marked();
};
}
BLETxView::BLETxView(
@@ -450,8 +508,6 @@ void BLETxView::on_data(uint32_t value, bool is_data) {
if (is_data) {
str_console += (char)(value);
}
console.write(str_console);
}
void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex) {
@@ -465,23 +521,39 @@ void BLETxView::update_current_packet(BLETxPacket packet, uint32_t currentIndex)
text_mac_address.set(formattedMacAddress);
console.clear(true);
for (const std::string& str : strings) {
console.writeln(str);
}
packet_counter = packet.packet_count;
current_packet = currentIndex;
dataFile.create(dataTempFilePath);
for (const std::string& str : strings) {
dataFile.write(str.c_str(), str.size());
dataFile.write("\n", 1);
;
}
dataFile.~File();
auto result = FileWrapper::open(dataTempFilePath);
if (!result)
return;
dataFileWrapper = *std::move(result);
dataEditView.set_font_zoom(true);
dataEditView.set_file(*dataFileWrapper);
dataEditView.redraw(true, true);
}
void BLETxView::set_parent_rect(const Rect new_parent_rect) {
View::set_parent_rect(new_parent_rect);
const Rect content_rect{0, header_height, new_parent_rect.width(), new_parent_rect.height() - header_height - switch_button_height};
console.set_parent_rect(content_rect);
dataEditView.set_parent_rect(content_rect);
}
BLETxView::~BLETxView() {
delete_file(dataTempFilePath);
transmitter_model.disable();
baseband::shutdown();
}
+54 -20
View File
@@ -28,6 +28,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_transmitter.hpp"
#include "ui_text_editor.hpp"
#include "ui_freq_field.hpp"
#include "ui_record_view.hpp"
#include "app_settings.hpp"
@@ -108,6 +109,7 @@ class BLETxView : public View {
void toggle();
void start();
void stop();
void reset();
void handle_replay_thread_done(const uint32_t return_code);
void file_error();
bool saveFile(const std::filesystem::path& path);
@@ -120,6 +122,7 @@ class BLETxView : public View {
void on_file_changed(const std::filesystem::path& new_file_path);
void on_save_file(const std::string value);
void on_tx_progress(const bool done);
void on_random_data_change(std::string value);
void update_current_packet(BLETxPacket packet, uint32_t currentIndex);
NavigationView& nav_;
@@ -129,7 +132,7 @@ class BLETxView : public View {
4'000'000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"ble_tx_app", app_settings::Mode::TX};
"tx_ble", app_settings::Mode::TX};
uint8_t console_color{0};
uint32_t prev_value{0};
@@ -142,9 +145,12 @@ class BLETxView : public View {
char randomMac[13] = "010203040506";
bool is_running = false;
bool is_sending = false;
uint64_t timer_count{0};
int16_t timer_count{0};
int16_t timer_period{1};
int16_t auto_channel_counter = 0;
int16_t auto_channel_period{6};
bool repeatLoop = false;
uint32_t packet_counter{0};
uint32_t num_packets{0};
@@ -152,19 +158,30 @@ class BLETxView : public View {
bool random_mac = false;
bool file_override = false;
typedef struct {
uint16_t line;
uint16_t col;
} CursorPos;
std::unique_ptr<FileWrapper> dataFileWrapper{};
File dataFile{};
std::filesystem::path dataTempFilePath{u"BLETX/dataFileTemp.TXT"};
std::vector<uint16_t> markedBytes{};
CursorPos cursor_pos{};
uint8_t marked_counter = 0;
static constexpr uint8_t mac_address_size_str{12};
static constexpr uint8_t max_packet_size_str{62};
static constexpr uint8_t max_packet_repeat_str{10};
static constexpr uint32_t max_packet_repeat_count{UINT32_MAX};
static constexpr uint32_t max_num_packets{32};
int16_t mscounter = 0;
BLETxPacket packets[max_num_packets];
PKT_TYPE pduType = {PKT_TYPE_DISCOVERY};
static constexpr auto header_height = 9 * 16;
static constexpr auto switch_button_height = 3 * 16;
static constexpr auto header_height = 10 * 16;
static constexpr auto switch_button_height = 6 * 16;
Button button_open{
{0 * 8, 0 * 16, 10 * 8, 2 * 16},
@@ -180,7 +197,7 @@ class BLETxView : public View {
Checkbox check_rand_mac{
{21 * 8, 1 * 16},
6,
"Random",
"?? Mac",
true};
TxFrequencyField field_frequency{
@@ -235,32 +252,49 @@ class BLETxView : public View {
{"SCAN_RSP", PKT_TYPE_SCAN_RSP},
{"CONNECT_REQ", PKT_TYPE_CONNECT_REQ}}};
Labels label_marked_data{
{{0 * 8, 4 * 16}, "Marked Data:", Color::light_grey()}};
OptionsField marked_data_sequence{
{12 * 8, 8 * 8},
8,
{{"Ascend", 0},
{"Descend", 1},
{"Random", 2}}};
Labels label_packet_index{
{{0 * 8, 10 * 8}, "Packet Index:", Color::light_grey()}};
{{0 * 8, 12 * 8}, "Packet Index:", Color::light_grey()}};
Text text_packet_index{
{13 * 8, 5 * 16, 12 * 8, 16},
"-"};
Labels label_packets_sent{
{{0 * 8, 12 * 8}, "Packets Left:", Color::light_grey()}};
Text text_packets_sent{
{13 * 8, 6 * 16, 12 * 8, 16},
"-"};
Labels label_packets_sent{
{{0 * 8, 14 * 8}, "Repeat Count:", Color::light_grey()}};
Text text_packets_sent{
{13 * 8, 7 * 16, 12 * 8, 16},
"-"};
Labels label_mac_address{
{{0 * 8, 14 * 8}, "Mac Address:", Color::light_grey()}};
{{0 * 8, 16 * 8}, "Mac Address:", Color::light_grey()}};
Text text_mac_address{
{12 * 8, 7 * 16, 20 * 8, 16},
{12 * 8, 8 * 16, 20 * 8, 16},
"-"};
Labels label_data_packet{
{{0 * 8, 16 * 8}, "Packet Data:", Color::light_grey()}};
{{0 * 8, 9 * 16}, "Packet Data:", Color::light_grey()}};
Console console{
{0, 8 * 16, 240, 240}};
{0, 9 * 18, 240, 240}};
TextViewer dataEditView{
{0, 9 * 18, 240, 240}};
Button button_clear_marked{
{1 * 8, 14 * 16, 13 * 8, 3 * 8},
"Clear Marked"};
Button button_save_packet{
{1 * 8, 16 * 16, 13 * 8, 2 * 16},
@@ -268,7 +302,7 @@ class BLETxView : public View {
Button button_switch{
{16 * 8, 16 * 16, 13 * 8, 2 * 16},
"Switch to Tx"};
"Switch to Rx"};
std::string str_log{""};
bool logging{true};
+12 -4
View File
@@ -65,9 +65,13 @@ std::string commodity_type(CommodityType value) {
} /* namespace ert */
void ERTLogger::on_packet(const ert::Packet& packet) {
void ERTLogger::on_packet(const ert::Packet& packet, const uint32_t target_frequency) {
const auto formatted = packet.symbols_formatted();
std::string entry = ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors;
// TODO: function doesn't take uint64_t, so when >= 1<<32, weirdness will ensue!
const auto target_frequency_str = to_string_dec_uint(target_frequency, 10);
std::string entry = target_frequency_str + " " + ert::format::type(packet.type()) + " " + formatted.data + "/" + formatted.errors;
log_file.write_entry(packet.received_at(), entry);
}
@@ -99,10 +103,12 @@ void RecentEntriesTable<ERTRecentEntries>::draw(
painter.draw_string(target_rect.location(), style, line);
}
ERTAppView::ERTAppView(NavigationView&) {
ERTAppView::ERTAppView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_ert);
add_children({
&field_frequency,
&field_rf_amp,
&field_lna,
&field_vga,
@@ -110,6 +116,8 @@ ERTAppView::ERTAppView(NavigationView&) {
&recent_entries_view,
});
field_frequency.set_step(1000000);
receiver_model.enable();
logger = std::make_unique<ERTLogger>();
@@ -134,7 +142,7 @@ void ERTAppView::set_parent_rect(const Rect new_parent_rect) {
void ERTAppView::on_packet(const ert::Packet& packet) {
if (logger) {
logger->on_packet(packet);
logger->on_packet(packet, receiver_model.target_frequency());
}
if (packet.crc_ok()) {
+7 -1
View File
@@ -24,6 +24,7 @@
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "ui_rssi.hpp"
#include "ui_channel.hpp"
@@ -95,7 +96,7 @@ class ERTLogger {
return log_file.append(filename);
}
void on_packet(const ert::Packet& packet);
void on_packet(const ert::Packet& packet, const uint32_t target_frequency);
private:
LogFile log_file{};
@@ -126,6 +127,7 @@ class ERTAppView : public View {
ERTRecentEntries recent{};
std::unique_ptr<ERTLogger> logger{};
NavigationView& nav_;
RxRadioState radio_state_{
911600000 /* frequency */,
2500000 /* bandwidth */,
@@ -143,6 +145,10 @@ class ERTAppView : public View {
static constexpr auto header_height = 1 * 16;
RxFrequencyField field_frequency{
{5 * 8, 0 * 16},
nav_};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
+6 -11
View File
@@ -34,9 +34,6 @@ namespace tpms {
namespace format {
static bool use_kpa = true;
static bool use_celsius = true;
std::string type(Reading::Type type) {
return to_string_dec_uint(toUType(type), 2);
}
@@ -46,11 +43,11 @@ std::string id(TransponderID id) {
}
std::string pressure(Pressure pressure) {
return to_string_dec_int(use_kpa ? pressure.kilopascal() : pressure.psi(), 3);
return to_string_dec_int(units_psi ? pressure.psi() : pressure.kilopascal(), 3);
}
std::string temperature(Temperature temperature) {
return to_string_dec_int(use_celsius ? temperature.celsius() : temperature.fahrenheit(), 3);
return to_string_dec_int(units_fahr ? temperature.fahrenheit() : temperature.celsius(), 3);
}
std::string flags(Flags flags) {
@@ -165,18 +162,16 @@ TPMSAppView::TPMSAppView(NavigationView&) {
options_band.set_by_value(receiver_model.target_frequency());
options_pressure.on_change = [this](size_t, int32_t i) {
tpms::format::use_kpa = !i;
tpms::format::units_psi = (bool)i;
update_view();
};
options_pressure.set_selected_index(0, true);
options_pressure.set_selected_index(tpms::format::units_psi, true);
options_temperature.on_change = [this](size_t, int32_t i) {
tpms::format::use_celsius = !i;
tpms::format::units_fahr = (bool)i;
update_view();
};
options_temperature.set_selected_index(0, true);
options_temperature.set_selected_index(tpms::format::units_fahr, true);
logger = std::make_unique<TPMSLogger>();
if (logger) {
+21 -3
View File
@@ -37,6 +37,17 @@
#include "tpms_packet.hpp"
namespace tpms {
namespace format {
static bool units_psi{false};
static bool units_fahr{false};
} /* namespace format */
} /* namespace tpms */
namespace std {
} /* namespace std */
@@ -103,11 +114,17 @@ class TPMSAppView : public View {
private:
RxRadioState radio_state_{
315000000 /* frequency*/,
314950000 /* frequency*/,
1750000 /* bandwidth */,
2457600 /* sampling rate */};
app_settings::SettingsManager settings_{
"rx_tpms", app_settings::Mode::RX};
"rx_tpms",
app_settings::Mode::RX,
{
{"units_psi"sv, &tpms::format::units_psi},
{"units_fahr"sv, &tpms::format::units_fahr},
}};
MessageHandlerRegistration message_handler_packet{
Message::ID::TPMSPacket,
@@ -129,11 +146,12 @@ class TPMSAppView : public View {
{21 * 8, 5, 6 * 8, 4},
};
// "315 MHz" TPMS sensors transmit at either 314.9 or 315 MHz but we should pick up either
OptionsField options_band{
{0 * 8, 0 * 16},
3,
{
{"315", 315000000},
{"315", 314950000},
{"434", 433920000},
}};
@@ -37,7 +37,7 @@ void AboutView::update() {
break;
case 2:
console.writeln("NotherNgineer,zxkmm,u-foka");
console.writeln("Netro");
console.writeln("Netro,HTotoo");
console.writeln("");
break;
+43 -23
View File
@@ -56,13 +56,23 @@ static msg_t ookthread_fn(void* arg) {
v = (symbol < 2) ? 1 : 0; // TX on for dot or dash, off for pause
delay = morse_symbols[symbol];
gpio_og_tx.write(v);
if (hackrf_r9) { // r9 has a common PIN 54 for MODE CONTROL TX / RX, <=r6 has two independent MODE CONTROL pins (TX and RX)
gpio_r9_rx.write(!v); // in hackrf r9 opposite logic "0" means tx , "1" rx = no tx)
} else {
gpio_og_tx.write(v); // in hackrf <=r6, "1" means tx , "0" no tx.
}
arg_c->on_tx_progress(i, false);
chThdSleepMilliseconds(delay * arg_c->time_unit_ms);
}
gpio_og_tx.write(0); // Ensure TX is off
if (hackrf_r9) { // r9 has a common PIN 54 for MODE CONTROL TX / RX, <=r6 has two independent MODE CONTROL pins (TX and RX)
gpio_r9_rx.write(1); // Hackrf_r9 , common pin, rx="1" (we ensure TX is off) / tx="0"
} else {
gpio_og_tx.write(0); // Hackrf <=r6 independent TX / RX pins ,now touching the tx pin ==> Ensure TX is off
}
arg_c->on_tx_progress(0, true);
chThdExit(0);
@@ -121,10 +131,10 @@ bool MorseView::start_tx() {
transmitter_model.set_baseband_bandwidth(1'750'000); // Min TX LPF .already tested in FM morse max tone 9,999k , max dev 150khz
transmitter_model.enable();
if (modulation == CW) {
baseband::set_tones_config(transmitter_model.channel_bandwidth(), 0, symbol_count, false, false);
if (mode_cw) {
ookthread = chThdCreateStatic(ookthread_wa, sizeof(ookthread_wa), NORMALPRIO + 10, ookthread_fn, this);
} else if (modulation == FM) {
baseband::set_tones_config(transmitter_model.channel_bandwidth(), 0, symbol_count, false, false);
}
return true;
@@ -133,8 +143,8 @@ bool MorseView::start_tx() {
void MorseView::update_tx_duration() {
uint32_t duration_ms;
time_unit_ms = 1200 / field_speed.value();
symbol_count = morse_encode(message, time_unit_ms, field_tone.value(), &time_units);
time_unit_ms = 1200 / speed;
symbol_count = morse_encode(message, time_unit_ms, tone, &time_units);
if (symbol_count) {
duration_ms = time_units * time_unit_ms;
@@ -175,7 +185,7 @@ void MorseView::on_loop_progress(const uint32_t progress, const bool done) {
}
void MorseView::set_foxhunt(size_t i) {
message = foxhunt_codes[i];
message = foxhunt_codes[i - 1];
buffer = message.c_str();
text_message.set(message);
update_tx_duration();
@@ -188,7 +198,7 @@ MorseView::MorseView(
add_children({&labels,
&checkbox_foxhunt,
&options_foxhunt,
&field_foxhunt,
&field_speed,
&field_tone,
&options_modulation,
@@ -200,36 +210,46 @@ MorseView::MorseView(
&tx_view});
// Default settings
field_speed.set_value(15); // 15wps
field_tone.set_value(700); // 700Hz FM tone
options_modulation.set_selected_index(0); // CW mode
options_loop.set_selected_index(0); // Off
field_speed.set_value(speed);
field_tone.set_value(tone);
options_modulation.set_by_value(mode_cw);
options_loop.set_by_value(loop);
checkbox_foxhunt.set_value(foxhunt_mode);
field_foxhunt.set_value(foxhunt_code);
checkbox_foxhunt.on_select = [this](Checkbox&, bool value) {
foxhunt_mode = value;
if (foxhunt_mode)
set_foxhunt(options_foxhunt.selected_index_value());
set_foxhunt(foxhunt_code);
};
options_foxhunt.on_change = [this](size_t i, int32_t) {
field_foxhunt.on_change = [this](int32_t value) {
foxhunt_code = value;
if (foxhunt_mode)
set_foxhunt(i);
set_foxhunt(foxhunt_code);
};
options_modulation.on_change = [this](size_t i, int32_t) {
modulation = (modulation_t)i;
};
options_loop.on_change = [this](size_t i, uint32_t n) {
options_modulation.on_change = [this](size_t i, int32_t value) {
(void)i; // avoid unused warning
loop = n;
mode_cw = (bool)value;
};
field_speed.on_change = [this](int32_t) {
options_loop.on_change = [this](size_t i, uint32_t value) {
(void)i; // avoid unused warning
loop = value;
};
field_speed.on_change = [this](int32_t value) {
speed = value;
update_tx_duration();
};
field_tone.on_change = [this](int32_t value) {
tone = value;
};
button_message.on_select = [this, &nav](Button&) {
this->on_set_text(nav);
};
+25 -24
View File
@@ -65,7 +65,6 @@ class MorseView : public View {
private:
NavigationView& nav_;
std::string buffer{"PORTAPACK"};
std::string message{};
uint32_t time_units{0};
@@ -74,14 +73,25 @@ class MorseView : public View {
1750000 /* bandwidth */,
1536000 /* sampling rate */
};
app_settings::SettingsManager settings_{
"tx_morse", app_settings::Mode::TX};
enum modulation_t {
CW = 0,
FM = 1
};
modulation_t modulation{CW};
std::string buffer{"PORTAPACK"};
bool mode_cw{true};
bool foxhunt_mode{false};
uint32_t foxhunt_code{1};
uint32_t speed{15};
uint32_t tone{700};
app_settings::SettingsManager settings_{
"tx_morse",
app_settings::Mode::TX,
{
{"message"sv, &buffer},
{"foxhunt"sv, &foxhunt_mode},
{"foxhunt_code"sv, &foxhunt_code},
{"speed"sv, &speed},
{"tone"sv, &tone},
{"mode_cw"sv, &mode_cw},
{"loop"sv, &loop},
}};
bool start_tx();
void update_tx_duration();
@@ -90,7 +100,6 @@ class MorseView : public View {
Thread* ookthread{nullptr};
Thread* loopthread{nullptr};
bool foxhunt_mode{false};
bool run{false};
Labels labels{
@@ -104,20 +113,12 @@ class MorseView : public View {
{4 * 8, 16},
8,
"Foxhunt:"};
OptionsField options_foxhunt{
NumberField field_foxhunt{
{17 * 8, 16 + 4},
7,
{{"1 (MOE)", 0},
{"2 (MOI)", 1},
{"3 (MOS)", 2},
{"4 (MOH)", 3},
{"5 (MO5)", 4},
{"6 (MON)", 5},
{"7 (MOD)", 6},
{"8 (MOB)", 7},
{"9 (MO6)", 8},
{"X (MO) ", 9},
{"T (S) ", 10}}};
2,
{1, 11},
1,
' '};
NumberField field_speed{
{10 * 8, 6 * 8},
@@ -136,8 +137,8 @@ class MorseView : public View {
OptionsField options_modulation{
{15 * 8, 10 * 8},
2,
{{"CW", 0},
{"FM", 1}}};
{{"CW", true},
{"FM", false}}};
OptionsField options_loop{
{9 * 8, 12 * 8},
+1 -1
View File
@@ -459,7 +459,7 @@ SetFrequencyCorrectionView::SetFrequencyCorrectionView(NavigationView& nav) {
pmem::set_freq_rx_correction_updown(v);
};
opt_tx_correction_mode.set_by_value(pmem::config_freq_rx_correction_updown());
opt_tx_correction_mode.set_by_value(pmem::config_freq_tx_correction_updown());
opt_tx_correction_mode.on_change = [this](size_t, OptionsField::value_t v) {
pmem::set_freq_tx_correction_updown(v);
};
+242
View File
@@ -0,0 +1,242 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui_subghzd.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace ui;
namespace ui {
void SubGhzDRecentEntryDetailView::update_data() {
// set text elements
text_type.set(SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry_.sensorType));
text_id.set("0x" + to_string_hex(entry_.serial));
if (entry_.bits > 0) console.writeln("Bits: " + to_string_dec_uint(entry_.bits));
if (entry_.btn != SD_NO_BTN) console.writeln("Btn: " + to_string_dec_uint(entry_.btn));
if (entry_.cnt != SD_NO_CNT) console.writeln("Cnt: " + to_string_dec_uint(entry_.cnt));
if (entry_.data != 0) console.writeln("Data: " + to_string_hex(entry_.data));
}
SubGhzDRecentEntryDetailView::SubGhzDRecentEntryDetailView(NavigationView& nav, const SubGhzDRecentEntry& entry)
: nav_{nav},
entry_{entry} {
add_children({&button_done,
&text_type,
&text_id,
&console,
&labels});
button_done.on_select = [&nav](const ui::Button&) {
nav.pop();
};
update_data();
}
void SubGhzDRecentEntryDetailView::focus() {
button_done.focus();
}
void SubGhzDView::focus() {
field_frequency.focus();
}
SubGhzDView::SubGhzDView(NavigationView& nav)
: nav_{nav} {
add_children({&rssi,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&button_clear_list,
&recent_entries_view});
baseband::run_image(portapack::spi_flash::image_tag_subghzd);
button_clear_list.on_select = [this](Button&) {
recent.clear();
recent_entries_view.set_dirty();
};
field_frequency.set_step(100000);
const Rect content_rect{0, header_height, screen_width, screen_height - header_height};
recent_entries_view.set_parent_rect(content_rect);
recent_entries_view.on_select = [this](const SubGhzDRecentEntry& entry) {
nav_.push<SubGhzDRecentEntryDetailView>(entry);
};
baseband::set_subghzd_config(0, receiver_model.sampling_rate()); // 0=am
receiver_model.enable();
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
on_tick_second();
};
}
void SubGhzDView::on_tick_second() {
for (auto& entry : recent) {
entry.inc_age(1);
}
recent_entries_view.set_dirty();
}
void SubGhzDView::on_data(const SubGhzDDataMessage* data) {
SubGhzDRecentEntry key{data->sensorType, data->serial, data->bits, data->data, data->btn, data->cnt};
auto matching_recent = find(recent, key.key());
if (matching_recent != std::end(recent)) {
// Found within. Move to front of list, increment counter.
(*matching_recent).reset_age();
recent.push_front(*matching_recent);
recent.erase(matching_recent);
} else {
recent.emplace_front(key);
truncate_entries(recent, 64);
}
recent_entries_view.set_dirty();
}
SubGhzDView::~SubGhzDView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
receiver_model.disable();
baseband::shutdown();
}
const char* SubGhzDView::getSensorTypeName(FPROTO_SUBGHZD_SENSOR type) {
switch (type) {
case FPS_PRINCETON:
return "Princeton";
case FPS_BETT:
return "Bett";
case FPS_CAME:
return "Came";
case FPS_PRASTEL:
return "Prastel";
case FPS_AIRFORCE:
return "Airforce";
case FPS_CAMEATOMO:
return "Came Atomo";
case FPS_CAMETWEE:
return "Came Twee";
case FPS_CHAMBCODE:
return "Chamb Code";
case FPS_CLEMSA:
return "Clemsa";
case FPS_DOITRAND:
return "Doitrand";
case FPS_DOOYA:
return "Dooya";
case FPS_FAAC:
return "Faac";
case FPS_GATETX:
return "Gate TX";
case FPS_HOLTEK:
return "Holtek";
case FPS_HOLTEKHT12X:
return "Holtek HT12X";
case FPS_HONEYWELL:
return "Honeywell";
case FPS_HONEYWELLWDB:
return "Honeywell Wdb";
case FPS_HORMANN:
return "Hormann";
case FPS_IDO:
return "Ido 11x";
case FPS_INTERTECHNOV3:
return "InterTehcno v3";
case FPS_KEELOQ:
return "KeeLoq";
case FPS_KINGGATESSTYLO4K:
return "Kinggate Stylo4K";
case FPS_LINEAR:
return "Linear";
case FPS_LINEARDELTA3:
return "Linear Delta3";
case FPS_MAGELLAN:
return "Magellan";
case FPS_MARANTEC:
return "Marantec";
case FPS_MASTERCODE:
return "Mastercode";
case FPS_MEGACODE:
return "Megacode";
case FPS_NERORADIO:
return "Nero Radio";
case FPS_NERO_SKETCH:
return "Nero Sketch";
case FPS_NICEFLO:
return "Nice Flo";
case FPS_NICEFLORS:
return "Nice Flor S";
case FPS_PHOENIXV2:
return "Phoenix V2";
case FPS_POWERSMART:
return "PowerSmart";
case FPS_SECPLUSV1:
return "SecPlus V1";
case FPS_SECPLUSV2:
return "SecPlus V2";
case FPS_SMC5326:
return "SMC5326";
case FPS_STARLINE:
return "Star Line";
case FPS_X10:
return "X10";
case FPS_Invalid:
default:
return "Unknown";
}
}
std::string SubGhzDView::pad_string_with_spaces(int snakes) {
std::string paddedStr(snakes, ' ');
return paddedStr;
}
template <>
void RecentEntriesTable<ui::SubGhzDRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
std::string line{};
line.reserve(30);
line = SubGhzDView::getSensorTypeName((FPROTO_SUBGHZD_SENSOR)entry.sensorType);
line = line + " " + to_string_hex(entry.serial);
if (line.length() < 19) {
line += SubGhzDView::pad_string_with_spaces(19 - line.length());
} else {
line = truncate(line, 19);
}
std::string ageStr = to_string_dec_uint(entry.age);
std::string bitsStr = to_string_dec_uint(entry.bits);
line += SubGhzDView::pad_string_with_spaces(5 - bitsStr.length()) + bitsStr;
line += SubGhzDView::pad_string_with_spaces(4 - ageStr.length()) + ageStr;
line.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.location(), style, line);
}
} // namespace ui
+171
View File
@@ -0,0 +1,171 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2017 Furrtek
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#ifndef __UI_SUBGHZD_H__
#define __UI_SUBGHZD_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "utility.hpp"
#include "recent_entries.hpp"
#include "../baseband/fprotos/subghztypes.hpp"
using namespace ui;
namespace ui {
struct SubGhzDRecentEntry {
using Key = uint64_t;
static constexpr Key invalid_key = 0x0fffffff;
uint8_t sensorType = FPS_Invalid;
uint8_t btn = SD_NO_BTN;
uint32_t serial = SD_NO_SERIAL;
uint16_t bits = 0;
uint16_t age = 0; // updated on each seconds, show how long the signal was last seen
uint32_t cnt = SD_NO_CNT;
uint64_t data = 0;
SubGhzDRecentEntry() {}
SubGhzDRecentEntry(
uint8_t sensorType,
uint32_t serial,
uint16_t bits = 0,
uint64_t data = 0,
uint8_t btn = SD_NO_BTN,
uint32_t cnt = SD_NO_CNT)
: sensorType{sensorType},
btn{btn},
serial{serial},
bits{bits},
cnt{cnt},
data{data} {
}
Key key() const {
return (data ^ ((static_cast<uint64_t>(serial) << 32) | (static_cast<uint64_t>(sensorType) & 0xFF) << 0));
}
void inc_age(int delta) {
if (UINT16_MAX - delta > age) age += delta;
}
void reset_age() {
age = 0;
}
};
using SubGhzDRecentEntries = RecentEntries<SubGhzDRecentEntry>;
using SubGhzDRecentEntriesView = RecentEntriesView<SubGhzDRecentEntries>;
class SubGhzDView : public View {
public:
SubGhzDView(NavigationView& nav);
~SubGhzDView();
void focus() override;
std::string title() const override { return "SubGhzD"; };
static const char* getSensorTypeName(FPROTO_SUBGHZD_SENSOR type);
static std::string pad_string_with_spaces(int snakes);
private:
void on_tick_second();
void on_data(const SubGhzDDataMessage* data);
NavigationView& nav_;
RxRadioState radio_state_{
433'920'000 /* frequency */,
1'750'000 /* bandwidth */,
4'000'000 /* sampling rate */,
ReceiverModel::Mode::AMAudio};
app_settings::SettingsManager settings_{
"rx_subghzd",
app_settings::Mode::RX,
{}};
SubGhzDRecentEntries recent{};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
SignalToken signal_token_tick_second{};
Button button_clear_list{
{0, 16, 7 * 8, 32},
"Clear"};
static constexpr auto header_height = 3 * 16;
const RecentEntriesColumns columns{{
{"Type", 19},
{"Bits", 4},
{"Age", 3},
}};
SubGhzDRecentEntriesView recent_entries_view{columns, recent};
MessageHandlerRegistration message_handler_packet{
Message::ID::SubGhzDData,
[this](Message* const p) {
const auto message = static_cast<const SubGhzDDataMessage*>(p);
this->on_data(message);
}};
};
class SubGhzDRecentEntryDetailView : public View {
public:
SubGhzDRecentEntryDetailView(NavigationView& nav, const SubGhzDRecentEntry& entry);
void update_data();
void focus() override;
private:
NavigationView& nav_;
SubGhzDRecentEntry entry_{};
Text text_type{{0 * 8, 1 * 16, 15 * 8, 16}, "?"};
Text text_id{{6 * 8, 2 * 16, 10 * 8, 16}, "?"};
Console console{
{0, 4 * 16, 240, screen_height - (4 * 16) - 36}};
Labels labels{
{{0 * 8, 0 * 16}, "Type:", Color::light_grey()},
{{0 * 8, 2 * 16}, "Serial: ", Color::light_grey()},
{{0 * 8, 3 * 16}, "Data:", Color::light_grey()},
};
Button button_done{
{screen_width - 96 - 4, screen_height - 32 - 12, 96, 32},
"Done"};
};
} // namespace ui
#endif /*__UI_SUBGHZD_H__*/
+74 -2
View File
@@ -72,6 +72,11 @@ void TextViewer::paint(Painter& painter) {
paint_state_.redraw_text = false;
}
if (paint_state_.redraw_marked) {
paint_marked(painter);
paint_state_.redraw_marked = false;
}
paint_cursor(painter);
}
@@ -118,8 +123,9 @@ bool TextViewer::on_encoder(EncoderEvent delta) {
return updated;
}
void TextViewer::redraw(bool redraw_text) {
void TextViewer::redraw(bool redraw_text, bool redraw_marked) {
paint_state_.redraw_text = redraw_text;
paint_state_.redraw_marked = redraw_marked;
set_dirty();
}
@@ -140,6 +146,32 @@ void TextViewer::cursor_end() {
redraw();
}
void TextViewer::cursor_set(uint16_t line, uint16_t col) {
cursor_.line = line;
cursor_.col = col;
}
void TextViewer::cursor_mark_selected() {
LineColPair newMarker = std::make_pair(cursor_.line, cursor_.col);
auto it = std::find(lineColPair.begin(), lineColPair.end(), newMarker);
if (it != lineColPair.end()) {
lineColPair.erase(it);
} else {
lineColPair.push_back(newMarker);
}
// Mark pending change.
cursor_.mark_change = false;
redraw();
}
void TextViewer::cursor_clear_marked() {
lineColPair.clear();
redraw(true, true);
}
uint16_t TextViewer::line_length() {
return file_->line_length(cursor_.line);
}
@@ -247,12 +279,52 @@ void TextViewer::paint_cursor(Painter& painter) {
};
if (paint_state_.line != UINT32_MAX) // only XOR old cursor if it still appears on the screen
xor_cursor(paint_state_.line, paint_state_.col);
{
// Only reset previous cursor if we aren't marking.
if (paint_state_.mark_change) {
xor_cursor(paint_state_.line, paint_state_.col);
}
}
xor_cursor(cursor_.line, cursor_.col);
paint_state_.line = cursor_.line;
paint_state_.col = cursor_.col;
paint_state_.mark_change = cursor_.mark_change;
// Reset marking and wait for new change.
cursor_.mark_change = true;
}
void TextViewer::paint_marked(Painter& painter) {
auto xor_cursor = [this, &painter](int32_t line, uint16_t col) {
int cursor_width = char_width + 1;
int x = (col - paint_state_.first_col) * char_width - 1;
if (x < 0) { // cursor is one pixel narrower when in left column
cursor_width--;
x = 0;
}
int y = screen_rect().top() + (line - paint_state_.first_line) * char_height;
// Converting one row at a time to reduce buffer size
auto pbuf8 = cursor_.pixel_buffer8;
auto pbuf = cursor_.pixel_buffer;
for (auto col = 0; col < char_height; col++) {
// Annoyingly, read_pixels uses a 24-bit pixel format vs draw_pixels which uses 16-bit
portapack::display.read_pixels({x, y + col, cursor_width, 1}, pbuf8, cursor_width);
for (auto i = 0; i < cursor_width; i++)
pbuf[i] = Color(pbuf8[i].r, pbuf8[i].g, pbuf8[i].b).v ^ 0xFFFF;
portapack::display.draw_pixels({x, y + col, cursor_width, 1}, pbuf, cursor_width);
}
};
auto it = lineColPair.begin();
while (it != lineColPair.end()) {
LineColPair entry = (LineColPair)*it;
xor_cursor(entry.first, entry.second);
it++;
}
}
void TextViewer::reset_file(FileWrapper* file) {
+12 -1
View File
@@ -59,7 +59,7 @@ class TextViewer : public Widget {
bool on_key(KeyEvent key) override;
bool on_encoder(EncoderEvent delta) override;
void redraw(bool redraw_text = false);
void redraw(bool redraw_text = false, bool redraw_marked = false);
void set_file(FileWrapper& file) { reset_file(&file); }
void clear_file() { reset_file(); }
@@ -71,6 +71,12 @@ class TextViewer : public Widget {
void cursor_home();
void cursor_end();
void cursor_set(uint16_t line, uint16_t col);
void cursor_mark_selected();
void cursor_clear_marked();
typedef std::pair<uint16_t, uint16_t> LineColPair;
std::vector<LineColPair> lineColPair{};
// Gets the length of the current line.
uint16_t line_length();
@@ -97,6 +103,7 @@ class TextViewer : public Widget {
void paint_text(Painter& painter, uint32_t line, uint16_t col);
void paint_cursor(Painter& painter);
void paint_marked(Painter& painter);
void reset_file(FileWrapper* file = nullptr);
@@ -111,6 +118,8 @@ class TextViewer : public Widget {
uint32_t first_line{};
uint16_t first_col{};
bool redraw_text{true};
bool redraw_marked{false};
bool mark_change{true};
} paint_state_{};
struct {
@@ -121,6 +130,8 @@ class TextViewer : public Widget {
// Pixel buffer used for cursor XOR'ing - Max cursor width = Max char width + 1
ColorRGB888 pixel_buffer8[ui::char_width + 1]{};
Color pixel_buffer[ui::char_width + 1]{};
bool mark_change{true};
} cursor_{};
};
@@ -0,0 +1,235 @@
/*
* 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_weatherstation.hpp"
#include "modems.hpp"
#include "audio.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
#include "portapack_persistent_memory.hpp"
using namespace portapack;
using namespace ui;
namespace ui {
void WeatherRecentEntryDetailView::update_data() {
// set text elements
text_type.set(WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)entry_.sensorType));
if (entry_.id != WS_NO_ID)
text_id.set("0x" + to_string_hex(entry_.id));
else
text_id.set("-");
if (entry_.temp != WS_NO_TEMPERATURE)
text_temp.set(weather_units_fahr ? to_string_decimal((entry_.temp * 9 / 5) + 32, 1) + STR_DEGREES_F : to_string_decimal(entry_.temp, 2) + STR_DEGREES_C);
else
text_temp.set("-");
if (entry_.humidity != WS_NO_HUMIDITY)
text_hum.set(to_string_dec_uint(entry_.humidity) + "%");
else
text_hum.set("-");
if (entry_.channel != WS_NO_CHANNEL)
text_ch.set(to_string_dec_uint(entry_.channel));
else
text_ch.set("-");
if (entry_.battery_low != WS_NO_BATT)
text_batt.set(to_string_dec_uint(entry_.battery_low) + " " + ((entry_.battery_low == 0) ? "OK" : "LOW"));
else
text_batt.set("-");
text_age.set(to_string_dec_uint(entry_.age) + " sec");
}
WeatherRecentEntryDetailView::WeatherRecentEntryDetailView(NavigationView& nav, const WeatherRecentEntry& entry)
: nav_{nav},
entry_{entry} {
add_children({&button_done,
&text_type,
&text_id,
&text_temp,
&text_hum,
&text_ch,
&text_batt,
&text_age,
&labels});
button_done.on_select = [&nav](const ui::Button&) {
nav.pop();
};
update_data();
}
void WeatherRecentEntryDetailView::focus() {
button_done.focus();
}
void WeatherView::focus() {
field_frequency.focus();
}
WeatherView::WeatherView(NavigationView& nav)
: nav_{nav} {
add_children({&rssi,
&field_rf_amp,
&field_lna,
&field_vga,
&field_frequency,
&options_temperature,
&button_clear_list,
&recent_entries_view});
baseband::run_image(portapack::spi_flash::image_tag_weather);
button_clear_list.on_select = [this](Button&) {
recent.clear();
recent_entries_view.set_dirty();
};
field_frequency.set_step(100000);
options_temperature.on_change = [this](size_t, int32_t i) {
weather_units_fahr = (bool)i;
recent_entries_view.set_dirty();
};
options_temperature.set_selected_index(weather_units_fahr, false);
const Rect content_rect{0, header_height, screen_width, screen_height - header_height};
recent_entries_view.set_parent_rect(content_rect);
recent_entries_view.on_select = [this](const WeatherRecentEntry& entry) {
nav_.push<WeatherRecentEntryDetailView>(entry);
};
baseband::set_subghzd_config(0, receiver_model.sampling_rate()); // 0=am
receiver_model.enable();
signal_token_tick_second = rtc_time::signal_tick_second += [this]() {
on_tick_second();
};
}
void WeatherView::on_tick_second() {
for (auto& entry : recent) {
entry.inc_age(1);
}
recent_entries_view.set_dirty();
}
void WeatherView::on_data(const WeatherDataMessage* data) {
WeatherRecentEntry key{data->sensorType, data->id, data->temp, data->humidity, data->channel, data->battery_low};
auto matching_recent = find(recent, key.key());
if (matching_recent != std::end(recent)) {
// Found within. Move to front of list, increment counter.
(*matching_recent).reset_age();
recent.push_front(*matching_recent);
recent.erase(matching_recent);
} else {
recent.emplace_front(key);
truncate_entries(recent, 64);
}
recent_entries_view.set_dirty();
}
WeatherView::~WeatherView() {
rtc_time::signal_tick_second -= signal_token_tick_second;
receiver_model.disable();
baseband::shutdown();
}
const char* WeatherView::getWeatherSensorTypeName(FPROTO_WEATHER_SENSOR type) {
switch (type) {
case FPW_NexusTH:
return "NexusTH";
case FPW_Acurite592TXR:
return "Acurite592TXR";
case FPW_Acurite606TX:
return "Acurite606TX";
case FPW_Acurite609TX:
return "Acurite609TX";
case FPW_Ambient:
return "Ambient";
case FPW_AuriolAhfl:
return "AuriolAhfl";
case FPW_AuriolTH:
return "AuriolTH";
case FPW_GTWT02:
return "GT-WT02";
case FPW_GTWT03:
return "GT-WT03";
case FPW_INFACTORY:
return "InFactory";
case FPW_LACROSSETX:
return "LaCrosse TX";
case FPW_LACROSSETX141thbv2:
return "LaCrosse TX141THBv2";
case FPW_OREGON2:
return "Oregon2";
case FPW_OREGON3:
return "Oregon3";
case FPW_OREGONv1:
return "OregonV1";
case FPW_THERMOPROTX4:
return "ThermoPro TX4";
case FPW_TX_8300:
return "TX 8300";
case FPW_WENDOX_W6726:
return "Wendox W6726";
case FPW_Acurite986:
return "Acurite986";
case FPW_Invalid:
default:
return "Unknown";
}
}
std::string WeatherView::pad_string_with_spaces(int snakes) {
std::string paddedStr(snakes, ' ');
return paddedStr;
}
template <>
void RecentEntriesTable<ui::WeatherRecentEntries>::draw(
const Entry& entry,
const Rect& target_rect,
Painter& painter,
const Style& style) {
std::string line{};
line.reserve(30);
line = WeatherView::getWeatherSensorTypeName((FPROTO_WEATHER_SENSOR)entry.sensorType);
if (line.length() < 10) {
line += WeatherView::pad_string_with_spaces(10 - line.length());
} else {
line = truncate(line, 10);
}
std::string temp = (weather_units_fahr ? to_string_decimal((entry.temp * 9 / 5) + 32, 1) : to_string_decimal(entry.temp, 1));
std::string humStr = (entry.humidity != WS_NO_HUMIDITY) ? to_string_dec_uint(entry.humidity) + "%" : "-";
std::string chStr = (entry.channel != WS_NO_CHANNEL) ? to_string_dec_uint(entry.channel) : "-";
std::string ageStr = to_string_dec_uint(entry.age);
line += WeatherView::pad_string_with_spaces(6 - temp.length()) + temp;
line += WeatherView::pad_string_with_spaces(5 - humStr.length()) + humStr;
line += WeatherView::pad_string_with_spaces(4 - chStr.length()) + chStr;
line += WeatherView::pad_string_with_spaces(5 - ageStr.length()) + ageStr;
line.resize(target_rect.width() / 8, ' ');
painter.draw_string(target_rect.location(), style, line);
}
} // namespace ui
@@ -0,0 +1,194 @@
/*
* 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_WEATHER_H__
#define __UI_WEATHER_H__
#include "ui.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
#include "app_settings.hpp"
#include "radio_state.hpp"
#include "utility.hpp"
#include "recent_entries.hpp"
#include "../baseband/fprotos/weathertypes.hpp"
using namespace ui;
namespace ui {
static bool weather_units_fahr{false};
struct WeatherRecentEntry {
using Key = uint64_t;
static constexpr Key invalid_key = 0x0fffffff; // todo calc the invalid all
uint8_t sensorType = FPW_Invalid;
uint32_t id = WS_NO_ID;
float temp = WS_NO_TEMPERATURE;
uint8_t humidity = WS_NO_HUMIDITY;
uint8_t battery_low = WS_NO_BATT;
uint8_t channel = WS_NO_CHANNEL;
uint16_t age = 0; // updated on each seconds, show how long the signal was last seen
WeatherRecentEntry() {}
WeatherRecentEntry(
uint8_t sensorType,
uint32_t id,
float temp,
uint8_t humidity,
uint8_t channel,
uint8_t battery_low = WS_NO_BATT)
: sensorType{sensorType},
id{id},
temp{temp},
humidity{humidity},
battery_low{battery_low},
channel{channel} {
}
Key key() const {
return (((static_cast<uint64_t>(temp * 10) & 0xFFFF) << 48) ^ static_cast<uint64_t>(id) << 24) |
(static_cast<uint64_t>(sensorType) & 0xFF) << 16 |
(static_cast<uint64_t>(humidity) & 0xFF) << 8 |
(static_cast<uint64_t>(battery_low) & 0xF) << 4 |
(static_cast<uint64_t>(channel) & 0xF);
}
void inc_age(int delta) {
if (UINT16_MAX - delta > age) age += delta;
}
void reset_age() {
age = 0;
}
};
using WeatherRecentEntries = RecentEntries<WeatherRecentEntry>;
using WeatherRecentEntriesView = RecentEntriesView<WeatherRecentEntries>;
class WeatherView : public View {
public:
WeatherView(NavigationView& nav);
~WeatherView();
void focus() override;
std::string title() const override { return "Weather"; };
static const char* getWeatherSensorTypeName(FPROTO_WEATHER_SENSOR type);
static std::string pad_string_with_spaces(int snakes);
private:
void on_tick_second();
void on_data(const WeatherDataMessage* data);
NavigationView& nav_;
RxRadioState radio_state_{
433'920'000 /* frequency */,
1'750'000 /* bandwidth */,
2'000'000 /* sampling rate */,
ReceiverModel::Mode::AMAudio};
app_settings::SettingsManager settings_{
"rx_weather",
app_settings::Mode::RX,
{
{"units_fahr"sv, &weather_units_fahr},
}};
WeatherRecentEntries recent{};
OptionsField options_temperature{
{10 * 8, 0 * 16},
2,
{{STR_DEGREES_C, 0},
{STR_DEGREES_F, 1}}};
RFAmpField field_rf_amp{
{13 * 8, 0 * 16}};
LNAGainField field_lna{
{15 * 8, 0 * 16}};
VGAGainField field_vga{
{18 * 8, 0 * 16}};
RSSI rssi{
{21 * 8, 0, 6 * 8, 4}};
RxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
SignalToken signal_token_tick_second{};
Button button_clear_list{
{0, 16, 7 * 8, 32},
"Clear"};
static constexpr auto header_height = 3 * 16;
const RecentEntriesColumns columns{{
{"Type", 10},
{"Temp", 5},
{"Hum", 4},
{"Ch", 3},
{"Age", 4},
}};
WeatherRecentEntriesView recent_entries_view{columns, recent};
MessageHandlerRegistration message_handler_packet{
Message::ID::WeatherData,
[this](Message* const p) {
const auto message = static_cast<const WeatherDataMessage*>(p);
this->on_data(message);
}};
};
class WeatherRecentEntryDetailView : public View {
public:
WeatherRecentEntryDetailView(NavigationView& nav, const WeatherRecentEntry& entry);
void update_data();
void focus() override;
private:
NavigationView& nav_;
WeatherRecentEntry entry_{};
Text text_type{{10 * 8, 1 * 16, 15 * 8, 16}, "?"};
Text text_id{{10 * 8, 2 * 16, 10 * 8, 16}, "?"};
Text text_temp{{10 * 8, 3 * 16, 8 * 8, 16}, "?"};
Text text_hum{{10 * 8, 4 * 16, 6 * 8, 16}, "?"};
Text text_ch{{10 * 8, 5 * 16, 6 * 8, 16}, "?"};
Text text_batt{{10 * 8, 6 * 16, 6 * 8, 16}, "?"};
Text text_age{{10 * 8, 7 * 16, 10 * 8, 16}, "?"};
Labels labels{
{{0 * 8, 0 * 16}, "Weather Station", Color::light_grey()},
{{0 * 8, 1 * 16}, "Type:", Color::light_grey()},
{{0 * 8, 2 * 16}, "Id: ", Color::light_grey()},
{{0 * 8, 3 * 16}, "Temp:", Color::light_grey()},
{{0 * 8, 4 * 16}, "Humidity:", Color::light_grey()},
{{0 * 8, 5 * 16}, "Channel:", Color::light_grey()},
{{0 * 8, 6 * 16}, "Battery:", Color::light_grey()},
{{0 * 8, 7 * 16}, "Age:", Color::light_grey()},
};
Button button_done{
{screen_width - 96 - 4, screen_height - 32 - 12, 96, 32},
"Done"};
};
} // namespace ui
#endif /*__UI_WEATHER_H__*/
+5
View File
@@ -319,6 +319,11 @@ void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bo
send_message(&message);
}
void set_subghzd_config(uint8_t modulation = 0, uint32_t sampling_rate = 0) {
const SubGhzFPRxConfigureMessage message{modulation, sampling_rate};
send_message(&message);
}
static bool baseband_image_running = false;
void run_image(const spi_flash::image_tag_t image_tag) {
+1
View File
@@ -88,6 +88,7 @@ void set_spectrum(const size_t sampling_rate, const size_t trigger);
void set_siggen_tone(const uint32_t tone);
void set_siggen_config(const uint32_t bw, const uint32_t shape, const uint32_t duration);
void set_spectrum_painter_config(const uint16_t width, const uint16_t height, bool update, int32_t bw);
void set_subghzd_config(uint8_t modulation, uint32_t sampling_rate);
void request_beep();
void run_image(const portapack::spi_flash::image_tag_t image_tag);
+52 -14
View File
@@ -1169,20 +1169,20 @@ static constexpr uint8_t bitmap_icon_tpms_data[] = {
0xEC,
0x37,
0x36,
0x6C,
0x1A,
0x58,
0x0B,
0xD0,
0x0B,
0xD0,
0x0B,
0xD0,
0x0B,
0xD0,
0x1A,
0x58,
0x36,
0x6D,
0x3A,
0x59,
0x4B,
0xD5,
0x8B,
0xD3,
0xCB,
0xD1,
0xAB,
0xD2,
0x9A,
0x5C,
0xB6,
0x6C,
0xEC,
0x37,
@@ -5721,6 +5721,44 @@ static constexpr Bitmap bitmap_icon_hide{
{16, 16},
bitmap_icon_hide_data};
static constexpr uint8_t bitmap_icon_thermometer_data[] = {
0xC0,
0x00,
0x20,
0x01,
0x10,
0x02,
0x10,
0x3A,
0x10,
0x02,
0x10,
0x1A,
0x10,
0x02,
0xD0,
0x3A,
0xD0,
0x02,
0xD0,
0x1A,
0xD0,
0x02,
0xE8,
0x05,
0xE8,
0x05,
0xC8,
0x04,
0x10,
0x02,
0xE0,
0x01,
};
static constexpr Bitmap bitmap_icon_thermometer{
{16, 16},
bitmap_icon_thermometer_data};
} /* namespace ui */
#endif /*__BITMAP_HPP__*/
+1
View File
@@ -113,6 +113,7 @@ void EventDispatcher::run() {
while (is_running) {
const auto events = wait();
dispatch(events);
portapack::usb_serial.dispatch();
}
}
+4 -3
View File
@@ -24,6 +24,7 @@
#define __UI_AFSK_RX_H__
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
@@ -92,16 +93,16 @@ class AFSKRxView : public View {
Checkbox check_log{
{0 * 8, 1 * 16},
3,
"LOG",
LanguageHelper::currentMessages[LANG_LOG],
false};
Text text_debug{
{0 * 8, 12 + 2 * 16, screen_width, 16},
"DEBUG"};
LanguageHelper::currentMessages[LANG_DEBUG]};
Button button_modem_setup{
{screen_width - 12 * 8, 1 * 16, 96, 24},
"Modem setup"};
LanguageHelper::currentMessages[LANG_MODEM_SETUP]};
Console console{
{0, 4 * 16, 240, screen_width}};
@@ -37,7 +37,7 @@ using namespace tonekey;
#include "string_format.hpp"
namespace ui {
namespace ui::external_app::analogtv {
/* AnalogTvView *******************************************************/
@@ -192,10 +192,11 @@ void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) {
baseband::shutdown();
portapack::spi_flash::image_tag_t image_tag;
image_tag = portapack::spi_flash::image_tag_am_tv;
// portapack::spi_flash::image_tag_t image_tag; //moved to ext app, disabled
// image_tag = portapack::spi_flash::image_tag_am_tv;
baseband::run_image(image_tag);
// baseband::run_image(image_tag);
baseband::run_prepared_image(portapack::memory::map::m4_code.base()); // moved the baseband too
receiver_model.set_modulation(modulation);
receiver_model.set_sampling_rate(2000000);
@@ -203,4 +204,4 @@ void AnalogTvView::update_modulation(const ReceiverModel::Mode modulation) {
receiver_model.enable();
}
} /* namespace ui */
} // namespace ui::external_app::analogtv
@@ -36,7 +36,7 @@
#include "tone_key.hpp"
namespace ui {
namespace ui::external_app::analogtv {
class AnalogTvView : public View {
public:
@@ -111,6 +111,6 @@ class AnalogTvView : public View {
void update_modulation(const ReceiverModel::Mode modulation);
};
} /* namespace ui */
} // namespace ui::external_app::analogtv
#endif /*__ANALOG_TV_APP_H__*/
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "analog_tv_app.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::analogtv {
void initialize_app(ui::NavigationView& nav) {
nav.push<AnalogTvView>();
}
} // namespace ui::external_app::analogtv
extern "C" {
__attribute__((section(".external_app.app_analogtv.application_information"), used)) application_information_t _application_information_analogtv = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::analogtv::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Analog TV",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0xFE,
0x7F,
0x03,
0xC0,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0xAB,
0xCA,
0x53,
0xD5,
0x03,
0xC0,
0xFF,
0xFF,
0xFB,
0xD7,
0xFE,
0x7F,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::RX,
/*.m4_app_tag = portapack::spi_flash::image_tag_am_tv */ {'P', 'A', 'M', 'T'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_blespam.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::blespam {
void initialize_app(ui::NavigationView& nav) {
nav.push<BLESpamView>();
}
} // namespace ui::external_app::blespam
extern "C" {
__attribute__((section(".external_app.app_blespam.application_information"), used)) application_information_t _application_information_blespam = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::blespam::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "BLESpam",
/*.bitmap_data = */ {
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
0xF8,
0x1F,
0x04,
0x20,
0x02,
0x40,
0xFF,
0xFF,
0xFF,
0xFF,
0xAB,
0xDF,
0xAB,
0xDF,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x00,
0x00,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk_rx */ {'P', 'B', 'T', 'T'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
File diff suppressed because it is too large Load Diff
+253
View File
@@ -0,0 +1,253 @@
/*
* 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.
*/
// Code from https://github.com/Flipper-XFW/Xtreme-Apps/tree/04c3a60093e2c2378e79498b4505aa8072980a42/ble_spam/protocols
// Thanks for the work of the original creators!
// Saying thanks in the main view!
#ifndef __UI_BLESPAM_H__
#define __UI_BLESPAM_H__
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_transmitter.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::blespam {
enum ATK_TYPE {
ATK_ANDROID,
ATK_IOS,
ATK_IOS_CRASH,
ATK_WINDOWS,
ATK_SAMSUNG
};
enum PKT_TYPE {
PKT_TYPE_INVALID_TYPE,
PKT_TYPE_RAW,
PKT_TYPE_DISCOVERY,
PKT_TYPE_IBEACON,
PKT_TYPE_ADV_IND,
PKT_TYPE_ADV_DIRECT_IND,
PKT_TYPE_ADV_NONCONN_IND,
PKT_TYPE_ADV_SCAN_IND,
PKT_TYPE_SCAN_REQ,
PKT_TYPE_SCAN_RSP,
PKT_TYPE_CONNECT_REQ,
PKT_TYPE_LL_DATA,
PKT_TYPE_LL_CONNECTION_UPDATE_REQ,
PKT_TYPE_LL_CHANNEL_MAP_REQ,
PKT_TYPE_LL_TERMINATE_IND,
PKT_TYPE_LL_ENC_REQ,
PKT_TYPE_LL_ENC_RSP,
PKT_TYPE_LL_START_ENC_REQ,
PKT_TYPE_LL_START_ENC_RSP,
PKT_TYPE_LL_UNKNOWN_RSP,
PKT_TYPE_LL_FEATURE_REQ,
PKT_TYPE_LL_FEATURE_RSP,
PKT_TYPE_LL_PAUSE_ENC_REQ,
PKT_TYPE_LL_PAUSE_ENC_RSP,
PKT_TYPE_LL_VERSION_IND,
PKT_TYPE_LL_REJECT_IND,
PKT_TYPE_NUM_PKT_TYPE
};
class BLESpamView : public View {
public:
BLESpamView(NavigationView& nav);
~BLESpamView();
void focus() override;
std::string title() const override {
return "BLESpam";
};
private:
NavigationView& nav_;
TxRadioState radio_state_{
2'402'000'000 /* frequency */,
4'000'000 /* bandwidth */,
4'000'000 /* sampling rate */
};
TxFrequencyField field_frequency{
{0 * 8, 0 * 16},
nav_};
TransmitterView2 tx_view{
{11 * 8, 0 * 16},
/*short_ui*/ true};
app_settings::SettingsManager settings_{
"tx_blespam", app_settings::Mode::TX};
Button button_startstop{
{0, 3 * 16, 96, 24},
LanguageHelper::currentMessages[LANG_START]};
Checkbox chk_randdev{{100, 16}, 10, "Rnd device", true};
Console console{
{0, 70, 240, 220}};
OptionsField options_atkmode{
{0 * 8, 2 * 8},
10,
{{"Android", 0},
{"iOs", 1},
{"iOs crash", 2},
{"Windows", 3},
{"Samsung", 4}}};
bool is_running{false};
uint8_t counter = 0; // for packet change
uint8_t displayCounter = 0; // for packet display
ATK_TYPE attackType = ATK_ANDROID;
bool randomMac{true};
bool randomDev{true};
uint8_t channel_number = 37;
char mac[13] = "010203040507";
char advertisementData[63] = {"03032CFE06162CFED5A59E020AB4\0"};
PKT_TYPE pduType = {PKT_TYPE_DISCOVERY};
void start();
void stop();
void reset();
void createFastPairPacket();
void createIosPacket(bool crash);
void createSamsungPacket();
void createWindowsPacket();
void changePacket(bool forced);
void on_tx_progress(const bool done);
uint64_t get_freq_by_channel_number(uint8_t channel_number);
void randomizeMac();
void randomChn();
void furi_hal_random_fill_buf(uint8_t* buf, uint32_t len);
MessageHandlerRegistration message_handler_tx_progress{
Message::ID::TXProgress,
[this](const Message* const p) {
const auto message = *reinterpret_cast<const TXProgressMessage*>(p);
this->on_tx_progress(message.done);
}};
// continuity
typedef enum {
PayloadModeRandom,
PayloadModeValue,
PayloadModeBruteforce,
} PayloadMode;
typedef enum {
ContinuityTypeAirDrop = 0x05,
ContinuityTypeProximityPair = 0x07,
ContinuityTypeAirplayTarget = 0x09,
ContinuityTypeHandoff = 0x0C,
ContinuityTypeTetheringSource = 0x0E,
ContinuityTypeNearbyAction = 0x0F,
ContinuityTypeNearbyInfo = 0x10,
ContinuityTypeCustomCrash,
ContinuityTypeCOUNT
} ContinuityType;
typedef enum {
ContinuityPpBruteforceModel,
ContinuityPpBruteforceColor,
} ContinuityPpBruteforce;
typedef struct {
uint8_t value;
// const char* name;
} ContinuityColor;
static const uint8_t pp_prefixes_count;
static const uint8_t na_actions_count;
static const uint8_t pp_models_count;
static const ContinuityColor colors_beats_studio_buds_[];
static const ContinuityColor colors_beats_fit_pro[];
static const ContinuityColor colors_beats_studio_pro[];
static const ContinuityColor colors_beats_studio_3[];
static const ContinuityColor colors_beats_x[];
static const ContinuityColor colors_beats_studio_buds[];
static const ContinuityColor colors_beats_solo_pro[];
static const ContinuityColor colors_powerbeats_pro[];
static const ContinuityColor colors_powerbeats_3[];
static const ContinuityColor colors_beats_solo_3[];
static const ContinuityColor colors_beats_flex[];
static const ContinuityColor colors_airpods_max[];
static const ContinuityColor colors_white[];
typedef struct {
uint16_t value;
// const char* name;
const ContinuityColor* colors;
const uint8_t colors_count;
} contiModels;
static const contiModels pp_models[];
typedef struct {
uint8_t value;
} contiU8;
static const contiU8 pp_prefixes[];
static const contiU8 na_actions[];
// fastpair:
static const uint16_t fastpairModels_count;
typedef struct {
uint32_t value;
const char* name; // could be moved too
} fpUi32;
static const fpUi32 fastpairModels[];
// easysetup:
static const uint8_t watch_models_count;
typedef struct {
uint8_t value;
} easyU8;
typedef struct {
uint32_t value;
} easyU32;
typedef enum {
EasysetupTypeBuds = 0x01, // Skip 0 as it means unset
EasysetupTypeWatch,
EasysetupTypeCOUNT,
} EasysetupType;
static const easyU8 watch_models[];
static const uint8_t buds_models_count;
static const easyU32 buds_models[];
};
}; // namespace ui::external_app::blespam
#endif /*__UI_BLESPAM_H__*/
+83
View File
@@ -0,0 +1,83 @@
/*
* 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_coasterp.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::coasterp {
void initialize_app(ui::NavigationView& nav) {
nav.push<CoasterPagerView>();
}
} // namespace ui::external_app::coasterp
extern "C" {
__attribute__((section(".external_app.app_coasterp.application_information"), used)) application_information_t _application_information_coasterp = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::coasterp::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "BurgerPgr",
/*.bitmap_data = */ {
0x00,
0x00,
0xE0,
0x07,
0xF8,
0x1F,
0xFC,
0x3F,
0xFE,
0x7F,
0xFF,
0xFF,
0xFF,
0xFF,
0x00,
0x00,
0x55,
0x55,
0xAA,
0xAA,
0x55,
0x55,
0x00,
0x00,
0xFF,
0xFF,
0xFF,
0xFF,
0xFE,
0x7F,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_fsktx */ {'P', 'F', 'S', 'K'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -30,7 +30,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::coasterp {
void CoasterPagerView::focus() {
sym_data.focus();
@@ -131,4 +131,4 @@ CoasterPagerView::CoasterPagerView(NavigationView& nav) {
};
}
} /* namespace ui */
} /* namespace ui::external_app::coasterp */
@@ -21,6 +21,7 @@
*/
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_transmitter.hpp"
@@ -31,7 +32,7 @@
#include "radio_state.hpp"
#include "portapack.hpp"
namespace ui {
namespace ui::external_app::coasterp {
class CoasterPagerView : public View {
public:
@@ -65,7 +66,7 @@ class CoasterPagerView : public View {
Labels labels{
{{1 * 8, 3 * 8}, "Syscall pager TX beta", Color::light_grey()},
{{1 * 8, 8 * 8}, "Data:", Color::light_grey()}};
{{1 * 8, 8 * 8}, LanguageHelper::currentMessages[LANG_DATADP], Color::light_grey()}};
SymField sym_data{
{7 * 8, 8 * 8},
@@ -75,7 +76,7 @@ class CoasterPagerView : public View {
Checkbox checkbox_scan{
{10 * 8, 14 * 8},
4,
"Scan"};
LanguageHelper::currentMessages[LANG_SCAN]};
/*
ProgressBar progressbar {
@@ -99,4 +100,4 @@ class CoasterPagerView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::coasterp */
+36
View File
@@ -15,6 +15,35 @@ set(EXTCPPSRC
#font_viewer
external/font_viewer/main.cpp
external/font_viewer/ui_font_viewer.cpp
#blespam
external/blespam/main.cpp
external/blespam/ui_blespam.cpp
#analogtv
external/analogtv/main.cpp
external/analogtv/analog_tv_app.cpp
#nrf_rx
external/nrf_rx/main.cpp
external/nrf_rx/ui_nrf_rx.cpp
#coasterp
external/coasterp/main.cpp
external/coasterp/ui_coasterp.cpp
#lge
external/lge/main.cpp
external/lge/lge_app.cpp
#lcr
external/lcr/main.cpp
external/lcr/ui_lcr.cpp
#lcr
external/jammer/main.cpp
external/jammer/ui_jammer.cpp
)
set(EXTAPPLIST
@@ -22,4 +51,11 @@ set(EXTAPPLIST
afsk_rx
calculator
font_viewer
blespam
nrf_rx
analogtv
coasterp
lge
lcr
jammer
)
+54
View File
@@ -21,6 +21,13 @@ MEMORY
ram_external_app_afsk_rx (rwx) : org = 0xEEEA0000, len = 32k
ram_external_app_calculator (rwx) : org = 0xEEEB0000, len = 32k
ram_external_app_font_viewer(rwx) : org = 0xEEEC0000, len = 32k
ram_external_app_blespam(rwx) : org = 0xEEED0000, len = 32k
ram_external_app_analogtv(rwx) : org = 0xEEEE0000, len = 32k
ram_external_app_nrf_rx(rwx) : org = 0xEEEF0000, len = 32k
ram_external_app_coasterp(rwx) : org = 0xEEF00000, len = 32k
ram_external_app_lge(rwx) : org = 0xEEF10000, len = 32k
ram_external_app_lcr(rwx) : org = 0xEEF20000, len = 32k
ram_external_app_jammer(rwx) : org = 0xEEF30000, len = 32k
}
SECTIONS
@@ -48,4 +55,51 @@ SECTIONS
KEEP(*(.external_app.app_font_viewer.application_information));
*(*ui*external_app*font_viewer*);
} > ram_external_app_font_viewer
.external_app_blespam : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_blespam.application_information));
*(*ui*external_app*blespam*);
} > ram_external_app_blespam
.external_app_analogtv : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_analogtv.application_information));
*(*ui*external_app*analogtv*);
} > ram_external_app_analogtv
.external_app_nrf_rx : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_nrf_rx.application_information));
*(*ui*external_app*nrf_rx*);
} > ram_external_app_nrf_rx
.external_app_coasterp : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_coasterp.application_information));
*(*ui*external_app*coasterp*);
} > ram_external_app_coasterp
.external_app_lge : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_lge.application_information));
*(*ui*external_app*lge*);
} > ram_external_app_lge
.external_app_lcr : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_lcr.application_information));
*(*ui*external_app*lcr*);
} > ram_external_app_lcr
.external_app_jammer : ALIGN(4) SUBALIGN(4)
{
KEEP(*(.external_app.app_jammer.application_information));
*(*ui*external_app*jammer*);
} > ram_external_app_jammer
}
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_jammer.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::jammer {
void initialize_app(ui::NavigationView& nav) {
nav.push<JammerView>();
}
} // namespace ui::external_app::jammer
extern "C" {
__attribute__((section(".external_app.app_jammer.application_information"), used)) application_information_t _application_information_jammer = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::jammer::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "Jammer",
/*.bitmap_data = */ {
0xE0,
0x07,
0xF8,
0x1F,
0x1C,
0x38,
0x0E,
0x78,
0x06,
0x7C,
0x03,
0xCE,
0x03,
0xC7,
0x83,
0xC3,
0xC3,
0xC1,
0xE3,
0xC0,
0x73,
0xC0,
0x3E,
0x60,
0x1E,
0x70,
0x1C,
0x38,
0xF8,
0x1F,
0xE0,
0x07,
},
/*.icon_color = */ ui::Color::green().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_jammer */ {'P', 'J', 'A', 'M'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -29,7 +29,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::jammer {
void RangeView::focus() {
check_enabled.focus();
@@ -331,7 +331,8 @@ JammerView::JammerView(
NavigationView& nav)
: nav_{nav} {
Rect view_rect = {0, 3 * 8, 240, 80};
baseband::run_image(portapack::spi_flash::image_tag_jammer);
// baseband::run_image(portapack::spi_flash::image_tag_jammer);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&tab_view,
&view_range_a,
@@ -372,4 +373,4 @@ JammerView::JammerView(
};
}
} /* namespace ui */
} // namespace ui::external_app::jammer
@@ -33,7 +33,7 @@
using namespace jammer;
namespace ui {
namespace ui::external_app::jammer {
class RangeView : public View {
public:
@@ -242,4 +242,4 @@ class JammerView : public View {
}};
};
} /* namespace ui */
} // namespace ui::external_app::jammer
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "ui_lcr.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::lcr {
void initialize_app(ui::NavigationView& nav) {
nav.push<LCRView>();
}
} // namespace ui::external_app::lcr
extern "C" {
__attribute__((section(".external_app.app_lcr.application_information"), used)) application_information_t _application_information_lcr = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::lcr::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "TEDI/LCR",
/*.bitmap_data = */ {
0x0C,
0x00,
0xFF,
0x7F,
0x01,
0x80,
0xC1,
0x9B,
0xFF,
0x7F,
0x0C,
0x00,
0xFF,
0x7F,
0x01,
0x80,
0xC1,
0x9D,
0xFF,
0x7F,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
0x0C,
0x00,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_afsk */ {'P', 'A', 'F', 'T'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -23,7 +23,6 @@
#include "ui_lcr.hpp"
#include "ui_modemsetup.hpp"
#include "lcr.hpp"
#include "baseband_api.hpp"
#include "string_format.hpp"
@@ -31,7 +30,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::lcr {
void LCRView::focus() {
button_set_rgsb.focus();
@@ -42,6 +41,49 @@ LCRView::~LCRView() {
baseband::shutdown();
}
std::string LCRView::generate_message(std::string rgsb, std::vector<std::string> litterals, size_t option_ec) {
const std::string ec_lut[4] = {"A", "J", "N", "S"}; // Eclairage (Auto, Jour, Nuit)
char eom[3] = {3, 0, 0}; // EOM and space for checksum
uint8_t i;
std::string lcr_message{127, 127, 127, 127, 127, 127, 127, 5}; // 5/15 ? Modem sync and SOM
char checksum = 0;
// Pad litterals to 7 chars (not required ?)
for (auto& litteral : litterals)
while (litteral.length() < 7)
litteral += ' ';
// Compose LCR message
lcr_message += rgsb;
lcr_message += "PA ";
i = 1;
for (auto& litteral : litterals) {
lcr_message += "AM=";
lcr_message += to_string_dec_uint(i, 1);
lcr_message += " AF=\"";
lcr_message += litteral;
lcr_message += "\" CL=0 ";
i++;
}
lcr_message += "EC=";
lcr_message += ec_lut[option_ec];
lcr_message += " SAB=0";
// Checksum
i = 7; // Skip modem sync
while (lcr_message[i])
checksum ^= lcr_message[i++];
checksum ^= eom[0]; // EOM char
checksum &= 0x7F; // Trim
eom[1] = checksum;
lcr_message += eom;
return lcr_message;
}
/*
// Recap: frequency @ baudrate
final_str = to_string_short_freq(persistent_memory::tuned_frequency());
@@ -53,7 +95,7 @@ text_recap.set(final_str);*/
void LCRView::update_progress() {
if (tx_mode == IDLE) {
text_status.set("Ready");
text_status.set(LanguageHelper::currentMessages[LANG_READY]);
progress.set_value(0);
} else {
std::string progress_str = to_string_dec_uint(repeat_index) + "/" + to_string_dec_uint(persistent_memory::modem_repeat()) +
@@ -125,7 +167,7 @@ void LCRView::start_tx(const bool scan) {
litterals_list.push_back(litteral[i]);
}
modems::generate_data(lcr::generate_message(rgsb, litterals_list, options_ec.selected_index()), lcr_message_data);
modems::generate_data(generate_message(rgsb, litterals_list, options_ec.selected_index()), lcr_message_data);
/* It is AFSK modulation , measuring original fw 1.7.4 spectrum BW is just around 30khz , NBFM */
transmitter_model.set_baseband_bandwidth(1'750'000); // Min TX LPF 1M75, same spectrum as previous fw 1.7.4
@@ -269,4 +311,4 @@ LCRView::LCRView(NavigationView& nav) {
};
}
} /* namespace ui */
} /* namespace ui::external_app::lcr */
@@ -21,6 +21,7 @@
*/
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_widget.hpp"
#include "ui_textentry.hpp"
#include "ui_transmitter.hpp"
@@ -31,7 +32,7 @@
#include "app_settings.hpp"
#include "radio_state.hpp"
namespace ui {
namespace ui::external_app::lcr {
#define LCR_MAX_AM 5
@@ -101,6 +102,7 @@ class LCRView : public View {
void start_tx(const bool scan);
void on_tx_progress(const uint32_t progress, const bool done);
void on_button_set_am(NavigationView& nav, int16_t button_id);
std::string generate_message(std::string rgsb, std::vector<std::string> litterals, size_t option_ec);
Labels labels{
{{0, 8}, "EC: RGSB:", Color::light_grey()},
@@ -125,11 +127,11 @@ class LCRView : public View {
Checkbox check_scan{
{22 * 8, 4},
4,
"Scan"};
LanguageHelper::currentMessages[LANG_SCAN]};
Button button_modem_setup{
{1 * 8, 4 * 8 + 2, 14 * 8, 24},
"Modem setup"};
LanguageHelper::currentMessages[LANG_MODEM_SETUP]};
OptionsField options_scanlist{
{22 * 8, 4 * 8},
6,
@@ -137,11 +139,11 @@ class LCRView : public View {
Button button_clear{
{22 * 8, 8 * 8, 7 * 8, 19 * 8},
"CLEAR"};
LanguageHelper::currentMessages[LANG_CLEAR]};
Text text_status{
{2 * 8, 27 * 8 + 4, 26 * 8, 16},
"Ready"};
LanguageHelper::currentMessages[LANG_READY]};
ProgressBar progress{
{2 * 8, 29 * 8 + 4, 26 * 8, 16}};
@@ -158,4 +160,4 @@ class LCRView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::lcr */
@@ -36,7 +36,7 @@
using namespace portapack;
namespace ui {
namespace ui::external_app::lge {
void LGEView::focus() {
options_frame.focus();
@@ -354,4 +354,4 @@ LGEView::LGEView(NavigationView& nav) {
};
}
} /* namespace ui */
} /* namespace ui::external_app::lge */
@@ -21,6 +21,7 @@
*/
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_widget.hpp"
#include "ui_navigation.hpp"
#include "ui_transmitter.hpp"
@@ -32,7 +33,7 @@
#include "app_settings.hpp"
#include "radio_state.hpp"
namespace ui {
namespace ui::external_app::lge {
class LGEView : public View {
public:
@@ -105,7 +106,7 @@ class LGEView : public View {
{"Set nickname", 1},
{"Set team", 2},
{"Brdcst nick", 3},
{"Start", 4},
{LanguageHelper::currentMessages[LANG_START], 4},
{"Game over", 5},
{"Set vest", 6}}};
@@ -185,4 +186,4 @@ class LGEView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::lge */
+82
View File
@@ -0,0 +1,82 @@
/*
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2, or (at your option)
* any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; see the file COPYING. If not, write to
* the Free Software Foundation, Inc., 51 Franklin Street,
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include "lge_app.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::lge {
void initialize_app(ui::NavigationView& nav) {
nav.push<LGEView>();
}
} // namespace ui::external_app::lge
extern "C" {
__attribute__((section(".external_app.app_lge.application_information"), used)) application_information_t _application_information_lge = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::lge::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "LGE",
/*.bitmap_data = */ {
0x00,
0x00,
0x80,
0x00,
0xA4,
0x12,
0xA8,
0x0A,
0xD0,
0x05,
0xEC,
0x1B,
0xF0,
0x07,
0xFE,
0xFF,
0xF0,
0x07,
0xEC,
0x1B,
0xD0,
0x05,
0xA8,
0x0A,
0xA4,
0x12,
0x80,
0x00,
0x00,
0x00,
0x00,
0x00,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::TX,
/*.m4_app_tag = portapack::spi_flash::image_tag_fsktx */ {'P', 'F', 'S', 'K'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
+83
View File
@@ -0,0 +1,83 @@
/*
* 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_nrf_rx.hpp"
#include "ui_navigation.hpp"
#include "external_app.hpp"
namespace ui::external_app::nrf_rx {
void initialize_app(ui::NavigationView& nav) {
nav.push<NRFRxView>();
}
} // namespace ui::external_app::nrf_rx
extern "C" {
__attribute__((section(".external_app.app_nrf_rx.application_information"), used)) application_information_t _application_information_nrf_rx = {
/*.memory_location = */ (uint8_t*)0x00000000,
/*.externalAppEntry = */ ui::external_app::nrf_rx::initialize_app,
/*.header_version = */ CURRENT_HEADER_VERSION,
/*.app_version = */ VERSION_MD5,
/*.app_name = */ "NRF",
/*.bitmap_data = */ {
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0x00,
0x01,
0xF8,
0x3F,
0xFC,
0x7F,
0xFC,
0x7F,
0xDC,
0x7F,
0x8C,
0x6B,
0xDC,
0x7F,
0xFC,
0x7F,
0xFC,
0x7F,
0xF8,
0x3F,
},
/*.icon_color = */ ui::Color::yellow().v,
/*.menu_location = */ app_location_t::RX,
/*.m4_app_tag = portapack::spi_flash::image_tag_nrf_rx */ {'P', 'N', 'R', 'R'},
/*.m4_app_offset = */ 0x00000000, // will be filled at compile time
};
}
@@ -34,7 +34,7 @@
using namespace portapack;
using namespace modems;
namespace ui {
namespace ui::external_app::nrf_rx {
void NRFRxView::focus() {
field_frequency.focus();
@@ -42,7 +42,8 @@ void NRFRxView::focus() {
NRFRxView::NRFRxView(NavigationView& nav)
: nav_{nav} {
baseband::run_image(portapack::spi_flash::image_tag_nrf_rx);
// baseband::run_image(portapack::spi_flash::image_tag_nrf_rx);
baseband::run_prepared_image(portapack::memory::map::m4_code.base());
add_children({&rssi,
&channel,
@@ -130,4 +131,4 @@ NRFRxView::~NRFRxView() {
baseband::shutdown();
}
} /* namespace ui */
} // namespace ui::external_app::nrf_rx
@@ -25,6 +25,7 @@
#define __UI_NRF_RX_H__
#include "ui.hpp"
#include "ui_language.hpp"
#include "ui_navigation.hpp"
#include "ui_receiver.hpp"
#include "ui_freq_field.hpp"
@@ -34,7 +35,7 @@
#include "utility.hpp"
namespace ui {
namespace ui::external_app::nrf_rx {
class NRFRxView : public View {
public:
@@ -77,7 +78,7 @@ class NRFRxView : public View {
Button button_modem_setup{
{240 - 12 * 8, 1 * 16, 96, 24},
"Modem setup"};
LanguageHelper::currentMessages[LANG_MODEM_SETUP]};
Console console{
{0, 4 * 16, 240, 240}};
@@ -90,6 +91,6 @@ class NRFRxView : public View {
}};
};
} /* namespace ui */
} /* namespace ui::external_app::nrf_rx */
#endif /*__UI_NRF_RX_H__*/
+1 -1
View File
@@ -285,7 +285,7 @@
* buffers.
*/
#if !defined(SERIAL_BUFFERS_SIZE) || defined(__DOXYGEN__)
#define SERIAL_BUFFERS_SIZE 16
#define SERIAL_BUFFERS_SIZE 64
#endif
/*===========================================================================*/
+23 -4
View File
@@ -122,6 +122,8 @@ static bool touch_update() {
}
static uint8_t switches_raw = 0;
static uint8_t injected_switch = 0;
static uint8_t injected_encoder = 0;
/* The raw data is not packed in a way that makes looping over it easy.
* One option would be an accessor helper (RawSwitch). Another option
@@ -170,8 +172,11 @@ static bool encoder_update(const uint8_t raw) {
static bool encoder_read() {
const auto delta = encoder.update(
encoder_debounce[0].state(),
encoder_debounce[1].state());
encoder_debounce[0].state() | (injected_encoder == 1),
encoder_debounce[1].state() | (injected_encoder == 2));
if (injected_encoder > 0)
injected_encoder = 0;
if (delta != 0) {
encoder_position += delta;
@@ -186,10 +191,10 @@ void timer0_callback(GPTDriver* const) {
if (touch_update()) event_mask |= EVT_MASK_TOUCH;
switches_raw = swizzled_switches();
if (switches_update(switches_raw))
if (switches_update(switches_raw) || (injected_switch > 0))
event_mask |= EVT_MASK_SWITCHES;
if (encoder_update(switches_raw) && encoder_read())
if (encoder_update(switches_raw) || encoder_read())
event_mask |= EVT_MASK_ENCODER;
/* Signal event loop */
@@ -238,6 +243,13 @@ SwitchesState get_switches_state() {
for (size_t i = 0; i < result.size(); i++)
result[i] = switch_debounce[i].state();
if (injected_switch > 0 && injected_switch <= 6) {
result[injected_switch - 1] = 1;
injected_switch = 0xff;
} else if (injected_switch == 0xff) {
injected_switch = 0x00;
}
return result;
}
@@ -277,5 +289,12 @@ uint8_t switches() {
return switches_raw;
}
void inject_switch(uint8_t button) {
if (button <= 6)
injected_switch = button;
else if (button > 6)
injected_encoder = button - 6;
}
} // namespace debug
} // namespace control
+1
View File
@@ -56,6 +56,7 @@ namespace control {
namespace debug {
uint8_t switches();
void inject_switch(uint8_t);
} // namespace debug
} // namespace control
+4 -1
View File
@@ -70,6 +70,7 @@ lcd::ILI9341 display;
I2C i2c0(&I2CD0);
SPI ssp1(&SPID2);
portapack::USBSerial usb_serial;
si5351::Si5351 clock_generator{
i2c0, hackrf::one::si5351_i2c_address};
@@ -368,7 +369,7 @@ static void shutdown_base() {
*
* XTAL_OSC = powered down
*
* PLL0USB = powered down
* PLL0USB = XTAL, 480 MHz
* PLL0AUDIO = GP_CLKIN, Fcco=491.52 MHz, Fout=12.288 MHz
* PLL1 =
* OG: GP_CLKIN * 10 = 200 MHz
@@ -464,6 +465,8 @@ bool init() {
/* Remove /2P divider from PLL1 output to achieve full speed */
cgu::pll1::direct();
usb_serial.initialize();
i2c0.start(i2c_config_fast_clock);
chThdSleepMilliseconds(10);
+2
View File
@@ -31,6 +31,7 @@
#include "si5351.hpp"
#include "lcd_ili9341.hpp"
#include "backlight.hpp"
#include "usb_serial.hpp"
#include "radio.hpp"
#include "clock_manager.hpp"
@@ -46,6 +47,7 @@ extern lcd::ILI9341 display;
extern I2C i2c0;
extern SPI ssp1;
extern portapack::USBSerial usb_serial;
extern si5351::Si5351 clock_generator;
extern ClockManager clock_manager;
-71
View File
@@ -1,71 +0,0 @@
/*
* Copyright (C) 2014 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 "lcr.hpp"
#include "string_format.hpp"
namespace lcr {
std::string generate_message(std::string rgsb, std::vector<std::string> litterals, size_t option_ec) {
const std::string ec_lut[4] = {"A", "J", "N", "S"}; // Eclairage (Auto, Jour, Nuit)
char eom[3] = {3, 0, 0}; // EOM and space for checksum
uint8_t i;
std::string lcr_message{127, 127, 127, 127, 127, 127, 127, 5}; // 5/15 ? Modem sync and SOM
char checksum = 0;
// Pad litterals to 7 chars (not required ?)
for (auto& litteral : litterals)
while (litteral.length() < 7)
litteral += ' ';
// Compose LCR message
lcr_message += rgsb;
lcr_message += "PA ";
i = 1;
for (auto& litteral : litterals) {
lcr_message += "AM=";
lcr_message += to_string_dec_uint(i, 1);
lcr_message += " AF=\"";
lcr_message += litteral;
lcr_message += "\" CL=0 ";
i++;
}
lcr_message += "EC=";
lcr_message += ec_lut[option_ec];
lcr_message += " SAB=0";
// Checksum
i = 7; // Skip modem sync
while (lcr_message[i])
checksum ^= lcr_message[i++];
checksum ^= eom[0]; // EOM char
checksum &= 0x7F; // Trim
eom[1] = checksum;
lcr_message += eom;
return lcr_message;
}
} /* namespace lcr */
+1 -1
View File
@@ -61,7 +61,7 @@ static constexpr SPIConfig ssp_config_max283x = {
.ssport = gpio_max283x_select.port(),
.sspad = gpio_max283x_select.pad(),
.cr0 =
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max283x_spi_f) + 1) | CR0_FRFSPI | CR0_DSS16BIT,
CR0_CLOCKRATE(ssp_scr(ssp1_pclk_f, ssp1_cpsr, max283x_spi_f) + 3) | CR0_FRFSPI | CR0_DSS16BIT,
.cpsr = ssp1_cpsr,
};
+30 -1
View File
@@ -253,7 +253,7 @@ std::string to_string_hex(uint64_t value, int32_t length) {
std::string to_string_hex_array(uint8_t* array, int32_t length) {
std::string str_return;
str_return.reserve(length);
str_return.reserve(length * 2);
for (uint8_t i = 0; i < length; i++)
str_return += to_string_hex(array[i], 2);
@@ -348,6 +348,35 @@ void generateRandomMacAddress(char* macAddress) {
macAddress[12] = '\0'; // Null-terminate the string
}
uint64_t readUntil(File& file, char* result, std::size_t maxBufferSize, char delimiter) {
std::size_t bytesRead = 0;
while (true) {
char ch;
File::Result<File::Size> readResult = file.read(&ch, 1);
if (readResult.is_ok() && readResult.value() > 0) {
if (ch == delimiter) {
// Found a space character, stop reading
break;
} else if (bytesRead < maxBufferSize) {
// Append the character to the result if there's space
result[bytesRead++] = ch;
} else {
// Buffer is full, break to prevent overflow
break;
}
} else {
break; // End of file or error
}
}
// Null-terminate the result string
result[bytesRead] = '\0';
return bytesRead;
}
std::string unit_auto_scale(double n, const uint32_t base_unit, uint32_t precision) {
const uint32_t powers_of_ten[5] = {1, 10, 100, 1000, 10000};
std::string string{""};
+2
View File
@@ -81,6 +81,8 @@ std::string to_string_mac_address(const uint8_t* macAddress, uint8_t length, boo
std::string to_string_formatted_mac_address(const char* macAddress);
void generateRandomMacAddress(char* macAddress);
uint64_t readUntil(File& file, char* result, std::size_t maxBufferSize, char delimiter);
/* Scales 'n' to be a value less than 1000. 'base_unit' is the index of the unit from
* 'unit_prefix' that 'n' is in initially. 3 is the index of the '1s' unit. */
std::string unit_auto_scale(double n, const uint32_t base_unit, uint32_t precision);
+17 -13
View File
@@ -38,7 +38,7 @@
#include "ui_aprs_tx.hpp"
#include "ui_bht_tx.hpp"
#include "ui_btle_rx.hpp"
#include "ui_coasterp.hpp"
// #include "ui_coasterp.hpp" //moved to ext
#include "ui_debug.hpp"
#include "ui_encoders.hpp"
#include "ui_fileman.hpp"
@@ -47,14 +47,14 @@
#include "ui_freqman.hpp"
#include "ui_fsk_rx.hpp"
#include "ui_iq_trim.hpp"
#include "ui_jammer.hpp"
// #include "ui_keyfob.hpp"
#include "ui_lcr.hpp"
// #include "ui_jammer.hpp" //moved to ext
// #include "ui_keyfob.hpp"
// #include "ui_lcr.hpp"
#include "ui_level.hpp"
#include "ui_looking_glass_app.hpp"
#include "ui_mictx.hpp"
#include "ui_morse.hpp"
#include "ui_nrf_rx.hpp"
// #include "ui_nrf_rx.hpp" //moved to ext
// #include "ui_numbers.hpp"
// #include "ui_nuoptix.hpp"
// #include "ui_playdead.hpp"
@@ -79,20 +79,22 @@
#include "ui_tone_search.hpp"
#include "ui_touchtunes.hpp"
#include "ui_view_wav.hpp"
#include "ui_weatherstation.hpp"
#include "ui_subghzd.hpp"
#include "ui_whipcalc.hpp"
#include "ui_external_items_menu_loader.hpp"
// #include "acars_app.hpp"
#include "ais_app.hpp"
#include "analog_audio_app.hpp"
#include "analog_tv_app.hpp"
// #include "analog_tv_app.hpp" //moved to ext
#include "ble_comm_app.hpp"
#include "ble_rx_app.hpp"
#include "ble_tx_app.hpp"
#include "capture_app.hpp"
#include "ert_app.hpp"
#include "gps_sim_app.hpp"
#include "lge_app.hpp"
// #include "lge_app.hpp" //moved to ext
#include "pocsag_app.hpp"
#include "replay_app.hpp"
#include "soundboard_app.hpp"
@@ -551,7 +553,7 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
// {"ACARS", Color::yellow(), &bitmap_icon_adsb, [&nav](){ nav.push<ACARSAppView>(); }},
{"ADS-B", Color::green(), &bitmap_icon_adsb, [&nav]() { nav.push<ADSBRxView>(); }},
{"AIS Boats", Color::green(), &bitmap_icon_ais, [&nav]() { nav.push<AISAppView>(); }},
{"Analog TV", Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push<AnalogTvView>(); }},
//{"Analog TV", Color::yellow(), &bitmap_icon_sstv, [&nav]() { nav.push<AnalogTvView>(); }}, //moved to ext
{"APRS", Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSRXView>(); }},
{"Audio", Color::green(), &bitmap_icon_speaker, [&nav]() { nav.push<AnalogAudioView>(); }},
//{"BTLE", Color::yellow(), &bitmap_icon_btle, [&nav]() { nav.push<BTLERxView>(); }},
@@ -559,12 +561,14 @@ ReceiversMenuView::ReceiversMenuView(NavigationView& nav) {
{"BLE Rx", Color::green(), &bitmap_icon_btle, [&nav]() { nav.push<BLERxView>(); }},
{"ERT Meter", Color::green(), &bitmap_icon_ert, [&nav]() { nav.push<ERTAppView>(); }},
{"Level", Color::green(), &bitmap_icon_options_radio, [&nav]() { nav.push<LevelView>(); }},
{"NRF", Color::yellow(), &bitmap_icon_nrf, [&nav]() { nav.push<NRFRxView>(); }},
//{"NRF", Color::yellow(), &bitmap_icon_nrf, [&nav]() { nav.push<NRFRxView>(); }}, //moved to ext
{"POCSAG", Color::green(), &bitmap_icon_pocsag, [&nav]() { nav.push<POCSAGAppView>(); }},
{"Radiosnde", Color::green(), &bitmap_icon_sonde, [&nav]() { nav.push<SondeView>(); }},
{"Recon", Color::green(), &bitmap_icon_scanner, [&nav]() { nav.push<ReconView>(); }},
{"Search", Color::yellow(), &bitmap_icon_search, [&nav]() { nav.push<SearchView>(); }},
{"TPMS Cars", Color::green(), &bitmap_icon_tpms, [&nav]() { nav.push<TPMSAppView>(); }},
{"Weather", Color::green(), &bitmap_icon_thermometer, [&nav]() { nav.push<WeatherView>(); }},
{"SubGhzD", Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push<SubGhzDView>(); }},
// {"FSK RX", Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push<FskxRxMainView>(); }},
// {"DMR", Color::dark_grey(), &bitmap_icon_dmr, [&nav](){ nav.push<NotImplementedView>(); }},
// {"SIGFOX", Color::dark_grey(), &bitmap_icon_fox, [&nav](){ nav.push<NotImplementedView>(); }},
@@ -589,11 +593,11 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
{"APRS TX", ui::Color::green(), &bitmap_icon_aprs, [&nav]() { nav.push<APRSTXView>(); }},
{"BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav]() { nav.push<BHTView>(); }},
{"BLE Tx", ui::Color::green(), &bitmap_icon_btle, [&nav]() { nav.push<BLETxView>(); }},
{"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push<CoasterPagerView>(); }},
// {"BurgerPgr", ui::Color::yellow(), &bitmap_icon_burger, [&nav]() { nav.push<CoasterPagerView>(); }}, //moved to ext
{"GPS Sim", ui::Color::green(), &bitmap_icon_gps_sim, [&nav]() { nav.push<GpsSimAppView>(); }},
{"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push<JammerView>(); }},
//{"Jammer", ui::Color::green(), &bitmap_icon_jammer, [&nav]() { nav.push<JammerView>(); }}, //moved to ext
// { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push<KeyfobView>(); }},
{"LGE", ui::Color::yellow(), &bitmap_icon_lge, [&nav]() { nav.push<LGEView>(); }},
// {"LGE", ui::Color::yellow(), &bitmap_icon_lge, [&nav]() { nav.push<LGEView>(); }}, //moved to ext
{"Morse", ui::Color::green(), &bitmap_icon_morse, [&nav]() { nav.push<MorseView>(); }},
// { "Nuoptix DTMF", ui::Color::green(), &bitmap_icon_nuoptix, [&nav](){ nav.push<NuoptixView>(); }},
{"OOK", ui::Color::yellow(), &bitmap_icon_remote, [&nav]() { nav.push<EncodersView>(); }},
@@ -602,7 +606,7 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) {
{"Soundbrd", ui::Color::green(), &bitmap_icon_soundboard, [&nav]() { nav.push<SoundBoardView>(); }},
{"S.Painter", ui::Color::orange(), &bitmap_icon_paint, [&nav]() { nav.push<SpectrumPainterView>(); }},
{"SSTV", ui::Color::green(), &bitmap_icon_sstv, [&nav]() { nav.push<SSTVTXView>(); }},
{"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }},
// {"TEDI/LCR", ui::Color::yellow(), &bitmap_icon_lcr, [&nav]() { nav.push<LCRView>(); }}, //moved to ext
{"TouchTune", ui::Color::green(), &bitmap_icon_touchtunes, [&nav]() { nav.push<TouchTunesView>(); }},
});
+97
View File
@@ -0,0 +1,97 @@
extern "C" {
#include "usb_serial_io.h"
#include "usb_serial_cdc.h"
}
#include "usb_serial_shell.hpp"
#include "usb_serial.hpp"
#include "portapack.hpp"
#include <libopencm3/cm3/common.h>
#include <libopencm3/lpc43xx/usb.h>
namespace portapack {
void USBSerial::initialize() {
enable_xtal();
disable_pll0();
setup_pll0();
enable_pll0();
setup_usb_clock();
setup_usb_serial_controller();
init_serial_usb_driver(&SUSBD1);
shellInit();
}
void USBSerial::dispatch() {
if (!connected)
return;
if (shell_created == false) {
shell_created = true;
create_shell();
}
bulk_out_receive();
}
void USBSerial::on_channel_opened() {
connected = true;
}
void USBSerial::on_channel_closed() {
connected = false;
}
void USBSerial::enable_xtal() {
LPC_CGU->XTAL_OSC_CTRL.ENABLE = 0;
LPC_CGU->XTAL_OSC_CTRL.HF = 0;
}
void USBSerial::disable_pll0() {
LPC_CGU->PLL0USB_CTRL.PD = 1;
LPC_CGU->PLL0USB_CTRL.AUTOBLOCK = 1;
LPC_CGU->PLL0USB_CTRL.BYPASS = 0;
LPC_CGU->PLL0USB_CTRL.DIRECTI = 0;
LPC_CGU->PLL0USB_CTRL.DIRECTO = 0;
LPC_CGU->PLL0USB_CTRL.CLKEN = 0;
LPC_CGU->PLL0USB_CTRL.FRM = 0;
LPC_CGU->PLL0USB_CTRL.CLK_SEL = 0x06; // 12MHz internal XTAL
}
void USBSerial::setup_pll0() {
/* use XTAL_OSC as clock source for PLL0USB */
while (LPC_CGU->PLL0USB_STAT.LOCK) {
}
// /* configure PLL0USB to produce 480 MHz clock from 12 MHz XTAL_OSC */
// /* Values from User Manual v1.4 Table 94, for 12MHz oscillator. */
LPC_CGU->PLL0USB_MDIV = 0x06167FFA;
LPC_CGU->PLL0USB_NP_DIV = 0x00302062;
LPC_CGU->PLL0USB_CTRL.PD = 1;
LPC_CGU->PLL0USB_CTRL.DIRECTI = 1;
LPC_CGU->PLL0USB_CTRL.DIRECTO = 1;
LPC_CGU->PLL0USB_CTRL.CLKEN = 1;
}
void USBSerial::enable_pll0() {
// /* power on PLL0USB and wait until stable */
LPC_CGU->PLL0USB_CTRL.PD = 0;
while (!LPC_CGU->PLL0USB_STAT.LOCK) {
}
}
void USBSerial::setup_usb_clock() {
/* use PLL0USB as clock source for USB0 */
LPC_CGU->BASE_USB0_CLK.AUTOBLOCK = 1;
LPC_CGU->BASE_USB0_CLK.CLK_SEL = 0x07;
LPC_CGU->BASE_USB0_CLK.PD = 0;
}
} // namespace portapack
+48
View File
@@ -0,0 +1,48 @@
/*
* 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.
*/
#pragma once
#include "ch.h"
#include "hal.h"
namespace portapack {
class USBSerial {
public:
void initialize();
void dispatch();
void on_channel_opened();
void on_channel_closed();
private:
void enable_xtal();
void disable_pll0();
void setup_pll0();
void enable_pll0();
void setup_usb_clock();
bool connected{false};
bool shell_created{false};
};
} // namespace portapack
+143
View File
@@ -0,0 +1,143 @@
/*
* 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 "usb_serial_cdc.h"
#include "usb_serial_endpoints.h"
#include "usb_serial_event.hpp"
extern void usb0_isr(void);
CH_IRQ_HANDLER(USB0_IRQHandler) {
CH_IRQ_PROLOGUE();
usb0_isr();
CH_IRQ_EPILOGUE();
}
uint32_t __ldrex(volatile uint32_t* addr) {
__disable_irq();
return *addr;
}
uint32_t __strex(uint32_t val, volatile uint32_t* addr) {
(void)val;
(void)addr;
*addr = val;
__enable_irq();
return 0;
}
void nvic_enable_irq(uint8_t irqn) {
NVIC_ISER(irqn / 32) = (1 << (irqn % 32));
}
void usb_configuration_changed(usb_device_t* const device) {
(void)device;
usb_endpoint_init(&usb_endpoint_int_in);
usb_endpoint_init(&usb_endpoint_bulk_in);
usb_endpoint_init(&usb_endpoint_bulk_out);
}
void setup_usb_serial_controller(void) {
usb_set_configuration_changed_cb(usb_configuration_changed);
usb_peripheral_reset();
usb_device_init(0, &usb_device);
usb_queue_init(&usb_endpoint_control_out_queue);
usb_queue_init(&usb_endpoint_control_in_queue);
usb_queue_init(&usb_endpoint_int_in_queue);
usb_queue_init(&usb_endpoint_bulk_out_queue);
usb_queue_init(&usb_endpoint_bulk_in_queue);
usb_endpoint_init(&usb_endpoint_control_out);
usb_endpoint_init(&usb_endpoint_control_in);
usb_run(&usb_device);
}
const usb_request_handlers_t usb_request_handlers = {
.standard = usb_standard_request,
.class = usb_class_request,
.vendor = 0,
.reserved = 0};
usb_request_status_t usb_class_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) {
usb_request_status_t status = USB_REQUEST_STATUS_STALL;
volatile uint8_t request = endpoint->setup.request;
if (request == 0x21) // GET LINE CODING REQUEST
return usb_get_line_coding_request(endpoint, stage);
if (request == 0x22) // SET CONTROL LINE STATE REQUEST
return usb_set_control_line_state_request(endpoint, stage);
if (request == 0x20) // SET LINE CODING REQUEST
return usb_set_line_coding_request(endpoint, stage);
return USB_REQUEST_STATUS_OK;
return status;
}
usb_request_status_t usb_get_line_coding_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
usb_transfer_schedule_block(
endpoint->in,
&endpoint->buffer,
0,
NULL,
NULL);
} else if (stage == USB_TRANSFER_STAGE_DATA) {
usb_transfer_schedule_ack(endpoint->out);
}
return USB_REQUEST_STATUS_OK;
}
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);
}
return USB_REQUEST_STATUS_OK;
}
usb_request_status_t usb_set_line_coding_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage) {
if (stage == USB_TRANSFER_STAGE_SETUP) {
usb_transfer_schedule_block(
endpoint->out,
&endpoint->buffer,
32,
NULL,
NULL);
} else if (stage == USB_TRANSFER_STAGE_DATA) {
usb_transfer_schedule_ack(endpoint->in);
}
return USB_REQUEST_STATUS_OK;
}
+46
View File
@@ -0,0 +1,46 @@
/*
* 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.
*/
#pragma once
#include "ch.h"
#include "hal.h"
#ifndef __cplusplus
#pragma GCC diagnostic push
// external code, so ignore warnings
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include <common/usb.h>
#include <common/usb_request.h>
#include <common/usb_standard_request.h>
#include <hackrf_usb/usb_device.h>
#include <hackrf_usb/usb_endpoint.h>
#pragma GCC diagnostic pop
usb_request_status_t usb_class_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage);
usb_request_status_t usb_get_line_coding_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage);
usb_request_status_t usb_set_control_line_state_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage);
usb_request_status_t usb_set_line_coding_request(usb_endpoint_t* const endpoint, const usb_transfer_stage_t stage);
#endif
void setup_usb_serial_controller(void);
@@ -0,0 +1,339 @@
/*
* 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 "usb_serial_descriptor.h"
#include "usb_serial_endpoints.h"
#define USB_VENDOR_ID (0x1D50)
#define USB_PRODUCT_ID (0x6018)
#define USB_API_VERSION (0x0100)
#define USB_WORD(x) (x & 0xFF), ((x >> 8) & 0xFF)
#define USB_MAX_PACKET0 (64)
#define USB_MAX_PACKET_BULK_FS (64)
#define USB_MAX_PACKET_BULK_HS (64)
#define USB_STRING_LANGID (0x0409)
uint8_t usb_descriptor_device[] = {
18, // bLength
USB_DESCRIPTOR_TYPE_DEVICE, // bDescriptorType
USB_WORD(0x0200), // bcdUSB
0xef, // bDeviceClass
0x02, // bDeviceSubClass
0x01, // bDeviceProtocol
USB_MAX_PACKET0, // bMaxPacketSize0
USB_WORD(USB_VENDOR_ID), // idVendor
USB_WORD(USB_PRODUCT_ID), // idProduct
USB_WORD(USB_API_VERSION), // bcdDevice
0x01, // iManufacturer
0x02, // iProduct
0x03, // iSerialNumber
0x01 // bNumConfigurations
};
uint8_t usb_descriptor_device_qualifier[] = {
10, // bLength
USB_DESCRIPTOR_TYPE_DEVICE_QUALIFIER, // bDescriptorType
USB_WORD(0x0200), // bcdUSB
0xef, // bDeviceClass
0x02, // bDeviceSubClass
0x01, // bDeviceProtocol
USB_MAX_PACKET0, // bMaxPacketSize0
0x01, // bNumOtherSpeedConfigurations
0x00 // bReserved
};
uint8_t usb_descriptor_configuration_full_speed[] = {
9, // bLength
USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType
USB_WORD((16 + 8 + 9 + 5 + 5 + 4 + 5 + 9 + 7 + 7)), // wTotalLength
0x02, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0x80, // bmAttributes: USB-powered
250, // bMaxPower: 500mA
8, // bLength
0xb, // bDescriptorType
0,
2,
0x02,
0x02,
0x00,
4,
9, // bLength
USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x02, // bInterfaceClass: vendor-specific
0x02, // bInterfaceSubClass
0x00, // bInterfaceProtocol: vendor-specific
0x04, // iInterface
5,
0x24, // ACM
0x00,
USB_WORD(0x0110),
5,
0x24,
0x01,
0x00,
0x01,
4,
0x24,
0x02,
0x02,
5,
0x24,
0x06,
0x00,
0x01,
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_INT_IN_EP_ADDR, // bEndpointAddress
0x03, // bmAttributes: BULK
USB_WORD(16), // wMaxPacketSize
0xFF, // bInterval: no NAK
9, // bLength
USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x0a, // bInterfaceClass: vendor-specific
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol: vendor-specific
0x00, // iInterface
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_BULK_OUT_EP_ADDR, // bEndpointAddress
0x02, // bmAttributes: BULK
USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize
0x01, // bInterval: no NAK
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_BULK_IN_EP_ADDR, // bEndpointAddress
0x02, // bmAttributes: BULK
USB_WORD(USB_MAX_PACKET_BULK_FS), // wMaxPacketSize
0x01, // bInterval: NAK
0, // TERMINATOR
};
uint8_t usb_descriptor_configuration_high_speed[] = {
9, // bLength
USB_DESCRIPTOR_TYPE_CONFIGURATION, // bDescriptorType
USB_WORD((16 + 8 + 9 + 5 + 5 + 4 + 5 + 9 + 7 + 7)), // wTotalLength
0x02, // bNumInterfaces
0x01, // bConfigurationValue
0x00, // iConfiguration
0x80, // bmAttributes: USB-powered
250, // bMaxPower: 500mA
8, // bLength
0xb, // bDescriptorType
0,
2,
0x02,
0x02,
0x00,
4,
9, // bLength
USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType
0x00, // bInterfaceNumber
0x00, // bAlternateSetting
0x01, // bNumEndpoints
0x02, // bInterfaceClass: vendor-specific
0x02, // bInterfaceSubClass
0x00, // bInterfaceProtocol: vendor-specific
0x04, // iInterface
5,
0x24,
0x00,
USB_WORD(0x0110),
5,
0x24,
0x01,
0x00,
0x01,
4,
0x24,
0x02,
0x02,
5,
0x24,
0x06,
0x00,
0x01,
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_INT_IN_EP_ADDR, // bEndpointAddress
0x03, // bmAttributes: BULK
USB_WORD(16), // wMaxPacketSize
0xFF, // bInterval: no NAK
9, // bLength
USB_DESCRIPTOR_TYPE_INTERFACE, // bDescriptorType
0x01, // bInterfaceNumber
0x00, // bAlternateSetting
0x02, // bNumEndpoints
0x0a, // bInterfaceClass: vendor-specific
0x00, // bInterfaceSubClass
0x00, // bInterfaceProtocol: vendor-specific
0x00, // iInterface
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_BULK_OUT_EP_ADDR, // bEndpointAddress
0x02, // bmAttributes: BULK
USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize
0x01, // bInterval: no NAK
7, // bLength
USB_DESCRIPTOR_TYPE_ENDPOINT, // bDescriptorType
USB_BULK_IN_EP_ADDR, // bEndpointAddress
0x02, // bmAttributes: BULK
USB_WORD(USB_MAX_PACKET_BULK_HS), // wMaxPacketSize
0x01, // bInterval: NAK
0, // TERMINATOR
};
uint8_t usb_descriptor_string_languages[] = {
0x04, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
USB_WORD(USB_STRING_LANGID), // wLANGID
};
// clang-format off
uint8_t usb_descriptor_string_manufacturer[] = {
40, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'G', 0x00,
'r', 0x00,
'e', 0x00,
'a', 0x00,
't', 0x00,
' ', 0x00,
'S', 0x00,
'c', 0x00,
'o', 0x00,
't', 0x00,
't', 0x00,
' ', 0x00,
'G', 0x00,
'a', 0x00,
'd', 0x00,
'g', 0x00,
'e', 0x00,
't', 0x00,
's', 0x00,
};
uint8_t usb_descriptor_string_product[] = {
43, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'P', 0x00,
'o', 0x00,
'r', 0x00,
't', 0x00,
'a', 0x00,
'P', 0x00,
'a', 0x00,
'c', 0x00,
'k', 0x00,
' ', 0x00,
'M', 0x00,
'a', 0x00,
'y', 0x00,
'h', 0x00,
'e', 0x00,
'm', 0x00,
};
uint8_t usb_descriptor_string_config_description[] = {
24, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'T', 0x00,
'r', 0x00,
'a', 0x00,
'n', 0x00,
's', 0x00,
'c', 0x00,
'e', 0x00,
'i', 0x00,
'v', 0x00,
'e', 0x00,
'r', 0x00,
};
uint8_t usb_descriptor_string_serial_number[USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN];
uint8_t* usb_descriptor_strings[] = {
usb_descriptor_string_languages,
usb_descriptor_string_manufacturer,
usb_descriptor_string_product,
usb_descriptor_string_config_description,
usb_descriptor_string_serial_number,
0, // TERMINATOR
};
uint8_t wcid_string_descriptor[] = {
18, // bLength
USB_DESCRIPTOR_TYPE_STRING, // bDescriptorType
'M', 0x00,
'S', 0x00,
'F', 0x00,
'T', 0x00,
'1', 0x00,
'0', 0x00,
'0', 0x00,
USB_WCID_VENDOR_REQ, // vendor request code for further descriptor
0x00
};
uint8_t wcid_feature_descriptor[] = {
0x28, 0x00, 0x00, 0x00, // bLength
USB_WORD(0x0100), // WCID version
USB_WORD(0x0004), // WICD descriptor index
0x01, // bNumSections
0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Reserved
0x00, // bInterfaceNumber
0x01, // Reserved
'W', 'I', 'N', 'U', 'S', 'B', 0x00,0x00, // Compatible ID, padded with zeros
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, // Sub-compatible ID
0x00,0x00,0x00,0x00,0x00,0x00 // Reserved
};
@@ -0,0 +1,44 @@
/*
* 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.
*/
#pragma once
#include "ch.h"
#include "hal.h"
extern uint8_t usb_descriptor_device[];
extern uint8_t usb_descriptor_device_qualifier[];
extern uint8_t usb_descriptor_configuration_full_speed[];
extern uint8_t usb_descriptor_configuration_high_speed[];
extern uint8_t usb_descriptor_string_languages[];
extern uint8_t usb_descriptor_string_manufacturer[];
extern uint8_t usb_descriptor_string_product[];
#define USB_DESCRIPTOR_STRING_SERIAL_LEN 32
#define USB_DESCRIPTOR_STRING_SERIAL_BUF_LEN \
(USB_DESCRIPTOR_STRING_SERIAL_LEN * 2 + 2) /* UTF-16LE */
extern uint8_t usb_descriptor_string_serial_number[];
extern uint8_t* usb_descriptor_strings[];
#define USB_WCID_VENDOR_REQ 0x19
extern uint8_t wcid_string_descriptor[];
extern uint8_t wcid_feature_descriptor[];
@@ -0,0 +1,81 @@
/*
* 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 "usb_serial_endpoints.h"
#pragma GCC diagnostic push
// external code, so ignore warnings
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include <common/usb.h>
#include <common/usb_request.h>
#include <common/usb_standard_request.h>
#include <hackrf_usb/usb_device.h>
#include <hackrf_usb/usb_endpoint.h>
#pragma GCC diagnostic pop
usb_endpoint_t usb_endpoint_control_out = {
.address = USB_CONTROL_OUT_EP_ADDR,
.device = &usb_device,
.in = &usb_endpoint_control_in,
.out = &usb_endpoint_control_out,
.setup_complete = usb_setup_complete,
.transfer_complete = usb_control_out_complete,
};
USB_DEFINE_QUEUE(usb_endpoint_control_out, 4);
usb_endpoint_t usb_endpoint_control_in = {
.address = USB_CONTROL_IN_EP_ADDR,
.device = &usb_device,
.in = &usb_endpoint_control_in,
.out = &usb_endpoint_control_out,
.setup_complete = 0,
.transfer_complete = usb_control_in_complete,
};
static USB_DEFINE_QUEUE(usb_endpoint_control_in, 4);
usb_endpoint_t usb_endpoint_int_in = {
.address = USB_INT_IN_EP_ADDR,
.device = &usb_device,
.in = &usb_endpoint_int_in,
.out = 0,
.setup_complete = 0,
.transfer_complete = usb_queue_transfer_complete};
static USB_DEFINE_QUEUE(usb_endpoint_int_in, 1);
usb_endpoint_t usb_endpoint_bulk_in = {
.address = USB_BULK_IN_EP_ADDR,
.device = &usb_device,
.in = &usb_endpoint_bulk_in,
.out = 0,
.setup_complete = 0,
.transfer_complete = usb_queue_transfer_complete};
static USB_DEFINE_QUEUE(usb_endpoint_bulk_in, 1);
usb_endpoint_t usb_endpoint_bulk_out = {
.address = USB_BULK_OUT_EP_ADDR,
.device = &usb_device,
.in = 0,
.out = &usb_endpoint_bulk_out,
.setup_complete = 0,
.transfer_complete = usb_queue_transfer_complete};
static USB_DEFINE_QUEUE(usb_endpoint_bulk_out, 1);
@@ -0,0 +1,51 @@
/*
* 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.
*/
#pragma once
#include "ch.h"
#include "hal.h"
#include <usb_type.h>
#include <usb_queue.h>
#define USB_CONTROL_IN_EP_ADDR (0x80)
#define USB_CONTROL_OUT_EP_ADDR (0x00)
#define USB_INT_IN_EP_ADDR (0x82)
#define USB_BULK_OUT_EP_ADDR (0x01)
#define USB_BULK_IN_EP_ADDR (0x81)
extern usb_endpoint_t usb_endpoint_control_out;
extern USB_DECLARE_QUEUE(usb_endpoint_control_out);
extern usb_endpoint_t usb_endpoint_control_in;
extern USB_DECLARE_QUEUE(usb_endpoint_control_in);
extern usb_endpoint_t usb_endpoint_int_in;
extern USB_DECLARE_QUEUE(usb_endpoint_int_in);
extern usb_endpoint_t usb_endpoint_bulk_in;
extern USB_DECLARE_QUEUE(usb_endpoint_bulk_in);
extern usb_endpoint_t usb_endpoint_bulk_out;
extern USB_DECLARE_QUEUE(usb_endpoint_bulk_out);
@@ -1,6 +1,5 @@
/*
* Copyright (C) 2014 Jared Boone, ShareBrained Technology, Inc.
* Copyright (C) 2016 Furrtek
* Copyright (C) 2023 Bernd Herzog
*
* This file is part of PortaPack.
*
@@ -20,18 +19,16 @@
* Boston, MA 02110-1301, USA.
*/
#include "ui.hpp"
#include <cstring>
#include <string>
#include <vector>
#include "usb_serial_event.hpp"
#ifndef __LCR_H__
#define __LCR_H__
#include "portapack.hpp"
namespace lcr {
extern "C" {
void on_channel_opened() {
portapack::usb_serial.on_channel_opened();
}
std::string generate_message(std::string rgsb, std::vector<std::string> litterals, size_t option_ec);
} /* namespace lcr */
#endif /*__LCR_H__*/
void on_channel_closed() {
portapack::usb_serial.on_channel_closed();
}
}
+34
View File
@@ -0,0 +1,34 @@
/*
* 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.
*/
#pragma once
#include "ch.h"
#include "hal.h"
#ifdef __cplusplus
extern "C" {
#endif
void on_channel_opened(void);
void on_channel_closed(void);
#ifdef __cplusplus
}
#endif
+144
View File
@@ -0,0 +1,144 @@
/*
* 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 "usb_serial_io.h"
#include "usb_serial_endpoints.h"
#pragma GCC diagnostic push
// external code, so ignore warnings
#pragma GCC diagnostic ignored "-Wstrict-prototypes"
#include <common/usb.h>
#include <common/usb_request.h>
#include <common/usb_standard_request.h>
#include <hackrf_usb/usb_device.h>
#include <hackrf_usb/usb_endpoint.h>
#pragma GCC diagnostic pop
#include <usb_request.h>
#include <string.h>
SerialUSBDriver SUSBD1;
void bulk_out_receive(void) {
int ret;
do {
ret = usb_transfer_schedule(
&usb_endpoint_bulk_out,
&usb_endpoint_bulk_out.buffer[0],
32,
serial_bulk_transfer_complete,
NULL);
} while (ret != -1);
}
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred) {
(void)user_data;
chSysLockFromIsr();
for (unsigned int i = 0; i < bytes_transferred; i++) {
msg_t ret;
do {
ret = chIQPutI(&SUSBD1.iqueue, usb_endpoint_bulk_out.buffer[i]);
if (ret == Q_FULL)
chThdSleepMilliseconds(1);
} while (ret == Q_FULL);
}
chSysUnlockFromIsr();
}
static void onotify(GenericQueue* qp) {
SerialUSBDriver* sdp = chQGetLink(qp);
uint8_t buff[64];
int n = chOQGetFullI(&sdp->oqueue);
if (n > 64) n = 64; // don't overflow
if (n > 0) {
for (int i = 0; i < n; i++) {
buff[i] = chOQGetI(&sdp->oqueue);
}
int ret;
chSysUnlock();
do {
ret = usb_transfer_schedule(
&usb_endpoint_bulk_in,
&buff[0],
n,
NULL,
NULL);
if (ret == -1)
chThdSleepMilliseconds(1);
} while (ret == -1);
chSysLock();
}
}
static size_t write(void* ip, const uint8_t* bp, size_t n) {
return chOQWriteTimeout(&((SerialUSBDriver*)ip)->oqueue, bp,
n, TIME_INFINITE);
}
static size_t read(void* ip, uint8_t* bp, size_t n) {
return chIQReadTimeout(&((SerialUSBDriver*)ip)->iqueue, bp,
n, TIME_INFINITE);
}
static msg_t put(void* ip, uint8_t b) {
return chOQPutTimeout(&((SerialUSBDriver*)ip)->oqueue, b, TIME_INFINITE);
}
static msg_t get(void* ip) {
return chIQGetTimeout(&((SerialUSBDriver*)ip)->iqueue, TIME_INFINITE);
}
static msg_t putt(void* ip, uint8_t b, systime_t timeout) {
return chOQPutTimeout(&((SerialUSBDriver*)ip)->oqueue, b, timeout);
}
static msg_t gett(void* ip, systime_t timeout) {
return chIQGetTimeout(&((SerialUSBDriver*)ip)->iqueue, timeout);
}
static size_t writet(void* ip, const uint8_t* bp, size_t n, systime_t time) {
return chOQWriteTimeout(&((SerialUSBDriver*)ip)->oqueue, bp, n, time);
}
static size_t readt(void* ip, uint8_t* bp, size_t n, systime_t time) {
return chIQReadTimeout(&((SerialUSBDriver*)ip)->iqueue, bp, n, time);
}
static const struct SerialUSBDriverVMT vmt = {
write, read, put, get,
putt, gett, writet, readt};
void init_serial_usb_driver(SerialUSBDriver* sdp) {
sdp->vmt = &vmt;
chIQInit(&sdp->iqueue, sdp->ib, SERIAL_BUFFERS_SIZE, NULL, sdp);
chOQInit(&sdp->oqueue, sdp->ob, SERIAL_BUFFERS_SIZE, onotify, sdp);
}
+46
View File
@@ -0,0 +1,46 @@
/*
* 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.
*/
#pragma once
#include "ch.h"
#include "hal.h"
struct SerialUSBDriverVMT {
_base_asynchronous_channel_methods
};
struct SerialUSBDriver {
/** @brief Virtual Methods Table.*/
const struct SerialUSBDriverVMT* vmt;
InputQueue iqueue; /* Output queue.*/
OutputQueue oqueue; /* Input circular buffer.*/
uint8_t ib[SERIAL_BUFFERS_SIZE]; /* Output circular buffer.*/
uint8_t ob[SERIAL_BUFFERS_SIZE];
};
typedef struct SerialUSBDriver SerialUSBDriver;
extern SerialUSBDriver SUSBD1;
void init_serial_usb_driver(SerialUSBDriver* sdp);
void bulk_out_receive(void);
void serial_bulk_transfer_complete(void* user_data, unsigned int bytes_transferred);
+510
View File
@@ -0,0 +1,510 @@
/*
* 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 "usb_serial_shell.hpp"
#include "event_m0.hpp"
#include "baseband_api.hpp"
#include "core_control.hpp"
#include "bitmap.hpp"
#include "png_writer.hpp"
#include "irq_controls.hpp"
#include "usb_serial_io.h"
#include "ff.h"
#include "chprintf.h"
#include "chqueues.h"
#include <string>
#include <codecvt>
#include <cstring>
#include <locale>
#define SHELL_WA_SIZE THD_WA_SIZE(1024 * 3)
#define palOutputPad(port, pad) (LPC_GPIO->DIR[(port)] |= 1 << (pad))
// queue handler from ch
static msg_t qwait(GenericQueue* qp, systime_t time) {
if (TIME_IMMEDIATE == time)
return Q_TIMEOUT;
currp->p_u.wtobjp = qp;
queue_insert(currp, &qp->q_waiting);
return chSchGoSleepTimeoutS(THD_STATE_WTQUEUE, time);
}
// This function fills the output buffer, and sends all data in 1 packet
static size_t fillOBuffer(OutputQueue* oqp, const uint8_t* bp, size_t n) {
qnotify_t nfy = oqp->q_notify;
size_t w = 0;
chDbgCheck(n > 0, "chOQWriteTimeout");
chSysLock();
while (TRUE) {
while (chOQIsFullI(oqp)) {
if (qwait((GenericQueue*)oqp, TIME_INFINITE) != Q_OK) {
chSysUnlock();
return w;
}
}
while (!chOQIsFullI(oqp) && n > 0) {
oqp->q_counter--;
*oqp->q_wrptr++ = *bp++;
if (oqp->q_wrptr >= oqp->q_top)
oqp->q_wrptr = oqp->q_buffer;
w++;
--n;
}
if (nfy) nfy(oqp);
chSysUnlock(); /* Gives a preemption chance in a controlled point.*/
if (n == 0) return w;
chSysLock();
}
}
static void cmd_reboot(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)chp;
(void)argc;
(void)argv;
m4_request_shutdown();
chThdSleepMilliseconds(50);
LPC_RGU->RESET_CTRL[0] = (1 << 0);
}
static void cmd_dfu(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)chp;
(void)argc;
(void)argv;
m4_request_shutdown();
chThdSleepMilliseconds(50);
LPC_SCU->SFSP2_8 = (LPC_SCU->SFSP2_8 & ~(7)) | 4;
palOutputPad(5, 7);
palSetPad(5, 7);
LPC_RGU->RESET_CTRL[0] = (1 << 0);
}
static void cmd_hackrf(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)chp;
(void)argc;
(void)argv;
m4_request_shutdown();
chThdSleepMilliseconds(50);
EventDispatcher::request_stop();
}
static void cmd_sd_over_usb(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)chp;
(void)argc;
(void)argv;
ui::Painter painter;
painter.fill_rectangle(
{0, 0, portapack::display.width(), portapack::display.height()},
ui::Color::black());
painter.draw_bitmap(
{portapack::display.width() / 2 - 8, portapack::display.height() / 2 - 8},
ui::bitmap_icon_hackrf,
ui::Color::yellow(),
ui::Color::black());
sdcDisconnect(&SDCD1);
sdcStop(&SDCD1);
m4_request_shutdown();
chThdSleepMilliseconds(50);
portapack::shutdown(true);
m4_init(portapack::spi_flash::image_tag_usb_sd, portapack::memory::map::m4_code, false);
m0_halt();
}
std::filesystem::path path_from_string8(char* path) {
std::wstring_convert<std::codecvt_utf8_utf16<char16_t>, char16_t> conv;
return conv.from_bytes(path);
}
static void cmd_flash(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "Usage: flash /FIRMWARE/portapack-h1_h2-mayhem.bin\r\n");
return;
}
auto path = path_from_string8(argv[0]);
size_t filename_length = strlen(argv[0]);
if (!std::filesystem::file_exists(path)) {
chprintf(chp, "file not found.\r\n");
return;
}
std::memcpy(&shared_memory.bb_data.data[0], path.c_str(), (filename_length + 1) * 2);
m4_request_shutdown();
chThdSleepMilliseconds(50);
m4_init(portapack::spi_flash::image_tag_flash_utility, portapack::memory::map::m4_code, false);
m0_halt();
}
static void cmd_screenshot(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
ensure_directory("SCREENSHOTS");
auto path = next_filename_matching_pattern(u"SCREENSHOTS/SCR_????.PNG");
if (path.empty())
return;
PNGWriter png;
auto error = png.create(path);
if (error)
return;
for (int i = 0; i < ui::screen_height; i++) {
std::array<ui::ColorRGB888, ui::screen_width> row;
portapack::display.read_pixels({0, i, ui::screen_width, 1}, row);
png.write_scanline(row);
}
chprintf(chp, "generated %s\r\n", path.string().c_str());
}
// gives full color.
static void cmd_screenframe(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
for (int i = 0; i < ui::screen_height; i++) {
std::array<ui::ColorRGB888, ui::screen_width> row;
portapack::display.read_pixels({0, i, ui::screen_width, 1}, row);
for (int px = 0; px < ui::screen_width; px += 5) {
char buffer[5 * 3 * 2];
sprintf(buffer, "%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X%02X", row[px].r, row[px].g, row[px].b, row[px + 1].r, row[px + 1].g, row[px + 1].b, row[px + 2].r, row[px + 2].g, row[px + 2].b, row[px + 3].r, row[px + 3].g, row[px + 3].b, row[px + 4].r, row[px + 4].g, row[px + 4].b);
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)buffer, 5 * 3 * 2);
}
chprintf(chp, "\r\n");
}
chprintf(chp, "ok\r\n");
}
static char getChrFromRgb(uint8_t r, uint8_t g, uint8_t b) {
uint8_t chR = r >> 6; // 3bit
uint8_t chG = g >> 6; // 3bit
uint8_t chB = b >> 6; // 3bit
uint8_t res = chR << 4 | chG << 2 | chB;
res += 32;
return res;
}
// sends only 1 byte (printable only) per pixel, so around 96 colors
static void cmd_screenframeshort(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argc;
(void)argv;
for (int y = 0; y < ui::screen_height; y++) {
std::array<ui::ColorRGB888, ui::screen_width> row;
portapack::display.read_pixels({0, y, ui::screen_width, 1}, row);
for (int px = 0; px < ui::screen_width; px += 60) {
char buffer[60];
for (int i = 0; i < 60; ++i) {
buffer[i] = getChrFromRgb(row[px + i].r, row[px + i].g, row[px + i].b);
}
fillOBuffer(&((SerialUSBDriver*)chp)->oqueue, (const uint8_t*)buffer, 60);
}
chprintf(chp, "\r\n");
}
chprintf(chp, "ok\r\n");
}
static void cmd_write_memory(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 2) {
chprintf(chp, "usage: write_memory <address> <value (1 or 4 bytes)>\r\n");
chprintf(chp, "example: write_memory 0x40004008 0x00000002\r\n");
return;
}
int value_length = strlen(argv[1]);
if (value_length != 10 && value_length != 4) {
chprintf(chp, "usage: write_memory <address> <value (1 or 4 bytes)>\r\n");
chprintf(chp, "example: write_memory 0x40004008 0x00000002\r\n");
return;
}
uint32_t address = (uint32_t)strtol(argv[0], NULL, 16);
uint32_t value = (uint32_t)strtol(argv[1], NULL, 16);
if (value_length == 10) {
uint32_t* data_pointer = (uint32_t*)address;
*data_pointer = value;
} else {
uint8_t* data_pointer = (uint8_t*)address;
*data_pointer = (uint8_t)value;
}
chprintf(chp, "ok\r\n");
}
static void cmd_read_memory(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "usage: read_memory 0x40004008\r\n");
return;
}
int address = (int)strtol(argv[0], NULL, 16);
uint32_t* data_pointer = (uint32_t*)address;
chprintf(chp, "%x\r\n", *data_pointer);
}
static void cmd_button(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "usage: button 1\r\n");
return;
}
int button = (int)strtol(argv[0], NULL, 10);
if (button < 1 || button > 8) {
chprintf(chp, "usage: button <number 1 to 8>\r\n");
return;
}
control::debug::inject_switch(button);
chprintf(chp, "ok\r\n");
}
static void cmd_sd_list_dir(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "usage: ls /\r\n");
return;
}
auto path = path_from_string8(argv[0]);
for (const auto& entry : std::filesystem::directory_iterator(path, "*")) {
if (std::filesystem::is_directory(entry.status())) {
chprintf(chp, "%s/\r\n", entry.path().string().c_str());
} else if (std::filesystem::is_regular_file(entry.status())) {
chprintf(chp, "%s\r\n", entry.path().string().c_str());
} else {
chprintf(chp, "%s *\r\n", entry.path().string().c_str());
}
}
}
static void cmd_sd_delete(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "usage: rm <path>\r\n");
return;
}
auto path = path_from_string8(argv[0]);
if (!std::filesystem::file_exists(path)) {
chprintf(chp, "file not found.\r\n");
return;
}
delete_file(path);
chprintf(chp, "ok\r\n");
}
File* shell_file = nullptr;
static void cmd_sd_filesize(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "usage: filesize <path>\r\n");
return;
}
auto path = path_from_string8(argv[0]);
FILINFO res;
auto stat = f_stat(path.tchar(), &res);
if (stat == FR_OK) {
chprintf(chp, "%lu\r\n", res.fsize);
chprintf(chp, "ok\r\n");
} else {
chprintf(chp, "error\r\n");
}
}
static void cmd_sd_open(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "usage: open <path>\r\n");
return;
}
if (shell_file != nullptr) {
chprintf(chp, "file already open\r\n");
return;
}
auto path = path_from_string8(argv[0]);
shell_file = new File();
shell_file->open(path, false, true);
chprintf(chp, "ok\r\n");
}
static void cmd_sd_seek(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "usage: seek <offset>\r\n");
return;
}
if (shell_file == nullptr) {
chprintf(chp, "no open file\r\n");
return;
}
int address = (int)strtol(argv[0], NULL, 10);
shell_file->seek(address);
chprintf(chp, "ok\r\n");
}
static void cmd_sd_close(BaseSequentialStream* chp, int argc, char* argv[]) {
(void)argv;
if (argc != 0) {
chprintf(chp, "usage: close\r\n");
return;
}
if (shell_file == nullptr) {
chprintf(chp, "no open file\r\n");
return;
}
delete shell_file;
shell_file = nullptr;
chprintf(chp, "ok\r\n");
}
static void cmd_sd_read(BaseSequentialStream* chp, int argc, char* argv[]) {
if (argc != 1) {
chprintf(chp, "usage: read <number of bytes>\r\n");
return;
}
if (shell_file == nullptr) {
chprintf(chp, "no open file\r\n");
return;
}
int size = (int)strtol(argv[0], NULL, 10);
uint8_t buffer[16];
do {
File::Size bytes_to_read = size > 16 ? 16 : size;
auto bytes_read = shell_file->read(buffer, bytes_to_read);
if (bytes_read.is_error()) {
chprintf(chp, "error %d\r\n", bytes_read.error());
return;
}
for (size_t i = 0; i < bytes_read.value(); i++)
chprintf(chp, "%02X", buffer[i]);
chprintf(chp, "\r\n");
if (bytes_to_read != bytes_read.value())
return;
size -= bytes_to_read;
} while (size > 0);
chprintf(chp, "ok\r\n");
}
static void cmd_sd_write(BaseSequentialStream* chp, int argc, char* argv[]) {
const char* usage = "usage: write 0123456789ABCDEF\r\n";
if (argc != 1) {
chprintf(chp, usage);
return;
}
if (shell_file == nullptr) {
chprintf(chp, "no open file\r\n");
return;
}
size_t data_string_len = strlen(argv[0]);
if (data_string_len % 2 != 0) {
chprintf(chp, usage);
return;
}
for (size_t i = 0; i < data_string_len; i++) {
char c = argv[0][i];
if ((c < '0' || c > '9') && (c < 'A' || c > 'F')) {
chprintf(chp, usage);
return;
}
}
char buffer[3] = {0, 0, 0};
for (size_t i = 0; i < data_string_len / 2; i++) {
buffer[0] = argv[0][i * 2];
buffer[1] = argv[0][i * 2 + 1];
uint8_t value = (uint8_t)strtol(buffer, NULL, 16);
shell_file->write(&value, 1);
}
chprintf(chp, "ok\r\n");
}
static const ShellCommand commands[] = {
{"reboot", cmd_reboot},
{"dfu", cmd_dfu},
{"hackrf", cmd_hackrf},
{"sd_over_usb", cmd_sd_over_usb},
{"flash", cmd_flash},
{"screenshot", cmd_screenshot},
{"screenframe", cmd_screenframe},
{"screenframeshort", cmd_screenframeshort},
{"write_memory", cmd_write_memory},
{"read_memory", cmd_read_memory},
{"button", cmd_button},
{"ls", cmd_sd_list_dir},
{"rm", cmd_sd_delete},
{"open", cmd_sd_open},
{"seek", cmd_sd_seek},
{"close", cmd_sd_close},
{"read", cmd_sd_read},
{"write", cmd_sd_write},
{"filesize", cmd_sd_filesize},
{NULL, NULL}};
static const ShellConfig shell_cfg1 = {
(BaseSequentialStream*)&SUSBD1,
commands};
void create_shell() {
shellCreate(&shell_cfg1, SHELL_WA_SIZE, NORMALPRIO);
}
+29
View File
@@ -0,0 +1,29 @@
/*
* 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.
*/
#pragma once
#include "ch.h"
#include "hal.h"
#include "shell.h"
void create_shell(void);
@@ -0,0 +1,62 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* 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 "usb_serial_thread.hpp"
#include "buffer_exchange.hpp"
// UsbSerialThread //////////////////////////////////////////////////////////
UsbSerialThread::UsbSerialThread() {
create_thread();
}
UsbSerialThread::~UsbSerialThread() {
stop();
}
void UsbSerialThread::create_thread() {
thread = chThdCreateFromHeap(NULL, 1024, NORMALPRIO + 10, UsbSerialThread::static_fn, this);
}
void UsbSerialThread::stop() {
if (thread) {
chThdTerminate(thread);
chThdWait(thread);
thread = nullptr;
}
}
msg_t UsbSerialThread::static_fn(void* arg) {
auto obj = static_cast<UsbSerialThread*>(arg);
obj->run();
return 0;
}
void UsbSerialThread::run() {
while (!chThdShouldTerminate()) {
if (str_ready) {
str_ready = false;
chprintf((BaseSequentialStream*)&SUSBD1, serial_str.c_str());
}
chThdSleepMilliseconds(50);
}
}
@@ -0,0 +1,60 @@
/*
* Copyright (C) 2016 Jared Boone, ShareBrained Technology, Inc.
*
* 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 __USB_SERIAL_THREAD_H__
#define __USB_SERIAL_THREAD_H__
#include "ch.h"
#include "event_m0.hpp"
#include "io.hpp"
#include "optional.hpp"
#include "chprintf.h"
#include "usb_serial_io.h"
#include <cstdint>
#include <cstddef>
#include <utility>
class UsbSerialThread {
public:
UsbSerialThread();
~UsbSerialThread();
void stop();
UsbSerialThread(const UsbSerialThread&) = delete;
UsbSerialThread(UsbSerialThread&&) = delete;
UsbSerialThread& operator=(const UsbSerialThread&) = delete;
UsbSerialThread& operator=(UsbSerialThread&&) = delete;
bool str_ready = false;
std::string serial_str{};
private:
Thread* thread{nullptr};
static msg_t static_fn(void* arg);
void run();
void create_thread();
};
#endif /*__CAPTURE_THREAD_H__*/
+37 -18
View File
@@ -347,12 +347,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PAPR aprsrx)
### NRF RX
set(MODE_CPPSRC
proc_nrfrx.cpp
)
DeclareTargets(PNRR nrfrx)
### BTLE RX
@@ -382,12 +376,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PAMA am_audio)
### AM TV
set(MODE_CPPSRC
proc_am_tv.cpp
)
DeclareTargets(PAMT am_tv)
### Audio transmit
@@ -431,12 +419,6 @@ set(MODE_CPPSRC
)
DeclareTargets(PFSR fskrx)
### Jammer
set(MODE_CPPSRC
proc_jammer.cpp
)
DeclareTargets(PJAM jammer)
### Microphone transmit
@@ -550,6 +532,21 @@ set(MODE_CPPSRC
)
DeclareTargets(PWFM wfm_audio)
### SubGhz Decoders
set(MODE_CPPSRC
proc_subghzd.cpp
)
DeclareTargets(PSGD subghzd)
### Weather Stations
set(MODE_CPPSRC
proc_weather.cpp
)
DeclareTargets(PWTH weather)
### Flash Utility
include(${CHIBIOS_PORTAPACK}/os/various/fatfs_bindings/fatfs.cmake)
@@ -618,6 +615,28 @@ set(MODE_CPPSRC
)
DeclareTargets(PAFR afskrx)
### NRF RX
set(MODE_CPPSRC
proc_nrfrx.cpp
)
DeclareTargets(PNRR nrfrx)
### Jammer
set(MODE_CPPSRC
proc_jammer.cpp
)
DeclareTargets(PJAM jammer)
### AM TV
set(MODE_CPPSRC
proc_am_tv.cpp
)
DeclareTargets(PAMT am_tv)
### Test
set(MODE_CPPSRC
+200
View File
@@ -0,0 +1,200 @@
#ifndef __FPROTO_GENERAL_H__
#define __FPROTO_GENERAL_H__
// useful methods for both weather and subghzd
#include <stdint.h>
#include <stddef.h>
#define bit_read(value, bit) (((value) >> (bit)) & 0x01)
#define bit_set(value, bit) \
({ \
__typeof__(value) _one = (1); \
(value) |= (_one << (bit)); \
})
#define bit_clear(value, bit) \
({ \
__typeof__(value) _one = (1); \
(value) &= ~(_one << (bit)); \
})
#define bit_write(value, bit, bitvalue) (bitvalue ? bit_set(value, bit) : bit_clear(value, bit))
#define DURATION_DIFF(x, y) (((x) < (y)) ? ((y) - (x)) : ((x) - (y)))
typedef enum {
ManchesterStateStart1 = 0,
ManchesterStateMid1 = 1,
ManchesterStateMid0 = 2,
ManchesterStateStart0 = 3
} ManchesterState;
typedef enum {
ManchesterEventShortLow = 0,
ManchesterEventShortHigh = 2,
ManchesterEventLongLow = 4,
ManchesterEventLongHigh = 6,
ManchesterEventReset = 8
} ManchesterEvent;
class FProtoGeneral {
public:
static bool manchester_advance(
ManchesterState state,
ManchesterEvent event,
ManchesterState* next_state,
bool* data) {
bool result = false;
ManchesterState new_state;
if (event == ManchesterEventReset) {
new_state = ManchesterStateMid1;
} else {
new_state = (ManchesterState)(transitions[state] >> event & 0x3);
if (new_state == state) {
new_state = ManchesterStateMid1;
} else {
if (new_state == ManchesterStateMid0) {
if (data) *data = false;
result = true;
} else if (new_state == ManchesterStateMid1) {
if (data) *data = true;
result = true;
}
}
}
*next_state = new_state;
return result;
}
static uint8_t subghz_protocol_blocks_get_parity(uint64_t key, uint8_t bit_count) {
uint8_t parity = 0;
for (uint8_t i = 0; i < bit_count; i++) {
parity += bit_read(key, i);
}
return parity & 0x01;
}
static uint8_t subghz_protocol_blocks_add_bytes(uint8_t const message[], size_t size) {
uint32_t result = 0;
for (size_t i = 0; i < size; ++i) {
result += message[i];
}
return (uint8_t)result;
}
static uint8_t subghz_protocol_blocks_parity8(uint8_t byte) {
byte ^= byte >> 4;
byte &= 0xf;
return (0x6996 >> byte) & 1;
}
static uint8_t subghz_protocol_blocks_parity_bytes(uint8_t const message[], size_t size) {
uint8_t result = 0;
for (size_t i = 0; i < size; ++i) {
result ^= subghz_protocol_blocks_parity8(message[i]);
}
return result;
}
static uint8_t subghz_protocol_blocks_lfsr_digest8(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key) {
uint8_t sum = 0;
for (size_t byte = 0; byte < size; ++byte) {
uint8_t data = message[byte];
for (int i = 7; i >= 0; --i) {
// XOR key into sum if data bit is set
if ((data >> i) & 1) sum ^= key;
// roll the key right (actually the LSB is dropped here)
// and apply the gen (needs to include the dropped LSB as MSB)
if (key & 1)
key = (key >> 1) ^ gen;
else
key = (key >> 1);
}
}
return sum;
}
static float locale_fahrenheit_to_celsius(float temp_f) {
return (temp_f - 32.f) / 1.8f;
}
static uint8_t subghz_protocol_blocks_crc4(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = init << 4; // LSBs are unused
uint8_t poly = polynomial << 4;
uint8_t bit;
while (size--) {
remainder ^= *message++;
for (bit = 0; bit < 8; bit++) {
if (remainder & 0x80) {
remainder = (remainder << 1) ^ poly;
} else {
remainder = (remainder << 1);
}
}
}
return remainder >> 4 & 0x0f; // discard the LSBs
}
static uint8_t subghz_protocol_blocks_lfsr_digest8_reflect(
uint8_t const message[],
size_t size,
uint8_t gen,
uint8_t key) {
uint8_t sum = 0;
// Process message from last byte to first byte (reflected)
for (int byte = size - 1; byte >= 0; --byte) {
uint8_t data = message[byte];
// Process individual bits of each byte (reflected)
for (uint8_t i = 0; i < 8; ++i) {
// XOR key into sum if data bit is set
if ((data >> i) & 1) {
sum ^= key;
}
// roll the key left (actually the LSB is dropped here)
// and apply the gen (needs to include the dropped lsb as MSB)
if (key & 0x80)
key = (key << 1) ^ gen;
else
key = (key << 1);
}
}
return sum;
}
static uint64_t subghz_protocol_blocks_reverse_key(uint64_t key, uint8_t bit_count) {
uint64_t reverse_key = 0;
for (uint8_t i = 0; i < bit_count; i++) {
reverse_key = reverse_key << 1 | bit_read(key, i);
}
return reverse_key;
}
static uint8_t subghz_protocol_blocks_crc8(
uint8_t const message[],
size_t size,
uint8_t polynomial,
uint8_t init) {
uint8_t remainder = init;
for (size_t byte = 0; byte < size; ++byte) {
remainder ^= message[byte];
for (uint8_t bit = 0; bit < 8; ++bit) {
if (remainder & 0x80) {
remainder = (remainder << 1) ^ polynomial;
} else {
remainder = (remainder << 1);
}
}
}
return remainder;
}
private:
static inline const uint8_t transitions[] = {0b00000001, 0b10010001, 0b10011011, 0b11111011};
};
#endif
@@ -0,0 +1,16 @@
#ifndef __FPROTO_PROTOLISTGENERAL_H__
#define __FPROTO_PROTOLISTGENERAL_H__
#include <stdint.h>
class FProtoListGeneral {
public:
FProtoListGeneral() {}
virtual ~FProtoListGeneral() {}
virtual void feed(bool level, uint32_t duration) = 0;
void setModulation(uint8_t modulation) { modulation_ = modulation; }
protected:
uint8_t modulation_ = 0;
};
#endif
+83
View File
@@ -0,0 +1,83 @@
#ifndef __FPROTO_BETT_H__
#define __FPROTO_BETT_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
BETTDecoderStepReset = 0,
BETTDecoderStepSaveDuration,
BETTDecoderStepCheckDuration,
} BETTDecoderStep;
class FProtoSubGhzDBett : public FProtoSubGhzDBase {
public:
FProtoSubGhzDBett() {
sensorType = FPS_BETT;
te_short = 340;
te_long = 2000;
te_delta = 150;
min_count_bit_for_found = 18;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case BETTDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 44) <
(te_delta * 15))) {
decode_data = 0;
decode_count_bit = 0;
parser_step = BETTDecoderStepCheckDuration;
}
break;
case BETTDecoderStepSaveDuration:
if (!level) {
if (DURATION_DIFF(duration, te_short * 44) <
(te_delta * 15)) {
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// dip decoder needed
if (callback) callback(this);
} else {
parser_step = BETTDecoderStepReset;
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
if ((DURATION_DIFF(duration, te_short) <
te_delta) ||
(DURATION_DIFF(duration, te_long) <
te_delta * 3)) {
parser_step = BETTDecoderStepCheckDuration;
} else {
parser_step = BETTDecoderStepReset;
}
}
}
break;
case BETTDecoderStepCheckDuration:
if (level) {
if (DURATION_DIFF(duration, te_long) <
te_delta * 3) {
subghz_protocol_blocks_add_bit(1);
parser_step = BETTDecoderStepSaveDuration;
} else if (
DURATION_DIFF(duration, te_short) <
te_delta) {
subghz_protocol_blocks_add_bit(0);
parser_step = BETTDecoderStepSaveDuration;
} else {
parser_step = BETTDecoderStepReset;
}
} else {
parser_step = BETTDecoderStepReset;
}
break;
}
}
};
#endif
+95
View File
@@ -0,0 +1,95 @@
#ifndef __FPROTO_CAME_H__
#define __FPROTO_CAME_H__
#include "subghzdbase.hpp"
#define CAME_12_COUNT_BIT 12
#define CAME_24_COUNT_BIT 24
#define PRASTEL_COUNT_BIT 25
#define AIRFORCE_COUNT_BIT 18
typedef enum : uint8_t {
CameDecoderStepReset = 0,
CameDecoderStepFoundStartBit,
CameDecoderStepSaveDuration,
CameDecoderStepCheckDuration,
} CameDecoderStep;
class FProtoSubGhzDCame : public FProtoSubGhzDBase {
public:
FProtoSubGhzDCame() {
sensorType = FPS_CAME;
te_short = 320;
te_long = 640;
te_delta = 150;
min_count_bit_for_found = 12;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case CameDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 56) < te_delta * 47)) {
// Found header CAME
parser_step = CameDecoderStepFoundStartBit;
}
break;
case CameDecoderStepFoundStartBit:
if (!level) {
break;
} else if (
DURATION_DIFF(duration, te_short) < te_delta) {
// Found start bit CAME
parser_step = CameDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = CameDecoderStepReset;
}
break;
case CameDecoderStepSaveDuration:
if (!level) { // save interval
if (duration >= (te_short * 4)) {
parser_step = CameDecoderStepFoundStartBit;
if ((decode_count_bit == min_count_bit_for_found) || (decode_count_bit == AIRFORCE_COUNT_BIT) ||
(decode_count_bit == PRASTEL_COUNT_BIT) || (decode_count_bit == CAME_24_COUNT_BIT)) {
serial = SD_NO_SERIAL;
btn = SD_NO_BTN;
data = decode_data;
data_count_bit = decode_count_bit;
// if flippa hacky, i hacky
sensorType = FPS_CAME;
if (decode_count_bit == PRASTEL_COUNT_BIT) sensorType = FPS_PRASTEL;
if (decode_count_bit == AIRFORCE_COUNT_BIT) sensorType = FPS_AIRFORCE;
if (callback) callback(this);
}
break;
}
te_last = duration;
parser_step = CameDecoderStepCheckDuration;
} else {
parser_step = CameDecoderStepReset;
}
break;
case CameDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = CameDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = CameDecoderStepSaveDuration;
} else
parser_step = CameDecoderStepReset;
} else {
parser_step = CameDecoderStepReset;
}
break;
}
}
};
#endif
+133
View File
@@ -0,0 +1,133 @@
#ifndef __FPROTO_CAMEATOMO_H__
#define __FPROTO_CAMEATOMO_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
CameAtomoDecoderStepReset = 0,
CameAtomoDecoderStepDecoderData,
} CameAtomoDecoderStep;
class FProtoSubGhzDCameAtomo : public FProtoSubGhzDBase {
public:
FProtoSubGhzDCameAtomo() {
sensorType = FPS_CAMEATOMO;
te_short = 600;
te_long = 1200;
te_delta = 250;
min_count_bit_for_found = 62;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case CameAtomoDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 60) < te_delta * 40)) {
// Found header CAME
parser_step = CameAtomoDecoderStepDecoderData;
decode_data = 0;
decode_count_bit = 1;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
}
break;
case CameAtomoDecoderStepDecoderData:
if (!level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortLow;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongLow;
} else if (
duration >= ((uint32_t)te_long * 2 + te_delta)) {
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
data ^= 0xFFFFFFFFFFFFFFFF;
data <<= 4;
uint8_t pack[8] = {};
pack[0] = (data >> 56);
pack[1] = ((data >> 48) & 0xFF);
pack[2] = ((data >> 40) & 0xFF);
pack[3] = ((data >> 32) & 0xFF);
pack[4] = ((data >> 24) & 0xFF);
pack[5] = ((data >> 16) & 0xFF);
pack[6] = ((data >> 8) & 0xFF);
pack[7] = (data & 0xFF);
atomo_decrypt(pack);
cnt = (uint16_t)pack[1] << 8 | pack[2];
serial = (uint32_t)(pack[3]) << 24 | pack[4] << 16 | pack[5] << 8 | pack[6];
uint8_t btn_decode = (pack[7] >> 4);
if (btn_decode == 0x0) {
btn = 0x1;
} else if (btn_decode == 0x2) {
btn = 0x2;
} else if (btn_decode == 0x4) {
btn = 0x3;
} else if (btn_decode == 0x6) {
btn = 0x4;
}
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 1;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventReset, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
} else {
parser_step = CameAtomoDecoderStepReset;
}
} else {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortHigh;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongHigh;
} else {
parser_step = CameAtomoDecoderStepReset;
}
}
if (event != ManchesterEventReset) {
bool bit;
bool data_ok = FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit);
if (data_ok) {
decode_data = (decode_data << 1) | !bit;
decode_count_bit++;
}
}
break;
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
void atomo_decrypt(uint8_t* buff) {
buff[0] = (buff[0] ^ 5) & 0x7F;
uint8_t tmpB = (-buff[0]) & 0x7F;
uint8_t bitCnt = 8;
while (bitCnt < 59) {
if ((tmpB & 0x18) && (((tmpB / 8) & 3) != 3)) {
tmpB = ((tmpB << 1) & 0xFF) | 1;
} else {
tmpB = (tmpB << 1) & 0xFF;
}
if (tmpB & 0x80) {
buff[bitCnt / 8] ^= (0x80 >> (bitCnt & 7));
}
bitCnt++;
}
}
};
#endif
+147
View File
@@ -0,0 +1,147 @@
#ifndef __FPROTO_CAMETWEE_H__
#define __FPROTO_CAMETWEE_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
CameTweeDecoderStepReset = 0,
CameTweeDecoderStepDecoderData,
} CameTweeDecoderStep;
class FProtoSubGhzDCameTwee : public FProtoSubGhzDBase {
public:
FProtoSubGhzDCameTwee() {
sensorType = FPS_CAMETWEE;
te_short = 500;
te_long = 1000;
te_delta = 250;
min_count_bit_for_found = 54;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
switch (parser_step) {
case CameTweeDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 51) < te_delta * 20)) {
// Found header CAME
parser_step = CameTweeDecoderStepDecoderData;
decode_data = 0;
decode_count_bit = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongLow, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
}
break;
case CameTweeDecoderStepDecoderData:
if (!level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortLow;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongLow;
} else if (
duration >= ((uint32_t)te_long * 2 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
subghz_protocol_came_twee_remote_controller();
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongLow, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventLongHigh, &manchester_saved_state, NULL);
FProtoGeneral::manchester_advance(manchester_saved_state, ManchesterEventShortLow, &manchester_saved_state, NULL);
} else {
parser_step = CameTweeDecoderStepReset;
}
} else {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortHigh;
} else if (
DURATION_DIFF(duration, te_long) < te_delta) {
event = ManchesterEventLongHigh;
} else {
parser_step = CameTweeDecoderStepReset;
}
}
if (event != ManchesterEventReset) {
bool bit;
if (FProtoGeneral::manchester_advance(manchester_saved_state, event, &manchester_saved_state, &bit)) {
decode_data = (decode_data << 1) | !bit;
decode_count_bit++;
}
}
break;
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
void subghz_protocol_came_twee_remote_controller() {
/* Came Twee 54 bit, rolling code 15 parcels with
* a decreasing counter from 0xE to 0x0
* with originally coded dip switches on the console 10 bit code
*
* 0x003FFF72E04A6FEE
* 0x003FFF72D17B5EDD
* 0x003FFF72C2684DCC
* 0x003FFF72B3193CBB
* 0x003FFF72A40E2BAA
* 0x003FFF72953F1A99
* 0x003FFF72862C0988
* 0x003FFF7277DDF877
* 0x003FFF7268C2E766
* 0x003FFF7259F3D655
* 0x003FFF724AE0C544
* 0x003FFF723B91B433
* 0x003FFF722C86A322
* 0x003FFF721DB79211
* 0x003FFF720EA48100
*
* decryption
* the last 32 bits, do XOR by the desired number, divide the result by 4,
* convert the first 16 bits of the resulting 32-bit number to bin and do
* bit-by-bit mirroring, adding up to 10 bits
*
* Example
* Step 1. 0x003FFF721DB79211 => 0x1DB79211
* Step 4. 0x1DB79211 xor 0x1D1D1D11 => 0x00AA8F00
* Step 4. 0x00AA8F00 / 4 => 0x002AA3C0
* Step 5. 0x002AA3C0 => 0x002A
* Step 6. 0x002A bin => b101010
* Step 7. b101010 => b0101010000
* Step 8. b0101010000 => (Dip) Off ON Off ON Off ON Off Off Off Off
*/
uint8_t cnt_parcel = (uint8_t)(data & 0xF);
serial = (uint32_t)(data & 0x0FFFFFFFF);
data = (data ^ came_twee_magic_numbers_xor[cnt_parcel]);
data /= 4;
btn = (data >> 4) & 0x0F;
data >>= 16;
data = (uint16_t)FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 16);
cnt = data >> 6;
}
inline static const uint32_t came_twee_magic_numbers_xor[15] = {
0x0E0E0E00,
0x1D1D1D11,
0x2C2C2C22,
0x3B3B3B33,
0x4A4A4A44,
0x59595955,
0x68686866,
0x77777777,
0x86868688,
0x95959599,
0xA4A4A4AA,
0xB3B3B3BB,
0xC2C2C2CC,
0xD1D1D1DD,
0xE0E0E0EE,
};
};
#endif
+151
View File
@@ -0,0 +1,151 @@
#ifndef __FPROTO_CHAMBCODE_H__
#define __FPROTO_CHAMBCODE_H__
#include "subghzdbase.hpp"
#define CHAMBERLAIN_CODE_BIT_STOP 0b0001
#define CHAMBERLAIN_CODE_BIT_1 0b0011
#define CHAMBERLAIN_CODE_BIT_0 0b0111
#define CHAMBERLAIN_7_CODE_MASK 0xF000000FF0F
#define CHAMBERLAIN_8_CODE_MASK 0xF00000F00F
#define CHAMBERLAIN_9_CODE_MASK 0xF000000000F
#define CHAMBERLAIN_7_CODE_MASK_CHECK 0x10000001101
#define CHAMBERLAIN_8_CODE_MASK_CHECK 0x1000001001
#define CHAMBERLAIN_9_CODE_MASK_CHECK 0x10000000001
typedef enum : uint8_t {
Chamb_CodeDecoderStepReset = 0,
Chamb_CodeDecoderStepFoundStartBit,
Chamb_CodeDecoderStepSaveDuration,
Chamb_CodeDecoderStepCheckDuration,
} Chamb_CodeDecoderStep;
class FProtoSubGhzDChambCode : public FProtoSubGhzDBase {
public:
FProtoSubGhzDChambCode() {
sensorType = FPS_CHAMBCODE;
te_short = 1000;
te_long = 3000;
te_delta = 200;
min_count_bit_for_found = 10;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case Chamb_CodeDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 39) < te_delta * 20)) {
// Found header Chamb_Code
parser_step = Chamb_CodeDecoderStepFoundStartBit;
}
break;
case Chamb_CodeDecoderStepFoundStartBit:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
// Found start bit Chamb_Code
decode_data = 0;
decode_count_bit = 0;
decode_data = decode_data << 4 | CHAMBERLAIN_CODE_BIT_STOP;
decode_count_bit++;
parser_step = Chamb_CodeDecoderStepSaveDuration;
} else {
parser_step = Chamb_CodeDecoderStepReset;
}
break;
case Chamb_CodeDecoderStepSaveDuration:
if (!level) { // save interval
if (duration > te_short * 5) {
if (decode_count_bit >= min_count_bit_for_found) {
serial = SD_NO_SERIAL;
btn = SD_NO_BTN;
if (subghz_protocol_decoder_chamb_code_check_mask_and_parse()) {
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
}
parser_step = Chamb_CodeDecoderStepReset;
} else {
te_last = duration;
parser_step = Chamb_CodeDecoderStepCheckDuration;
}
} else {
parser_step = Chamb_CodeDecoderStepReset;
}
break;
case Chamb_CodeDecoderStepCheckDuration:
if (level) { // Found stop bit Chamb_Code
if ((DURATION_DIFF(te_last, te_short * 3) <
te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
decode_data = decode_data << 4 | CHAMBERLAIN_CODE_BIT_STOP;
decode_count_bit++;
parser_step = Chamb_CodeDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short * 2) < te_delta) &&
(DURATION_DIFF(duration, te_short * 2) < te_delta)) {
decode_data = decode_data << 4 | CHAMBERLAIN_CODE_BIT_1;
decode_count_bit++;
parser_step = Chamb_CodeDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short * 3) < te_delta)) {
decode_data = decode_data << 4 | CHAMBERLAIN_CODE_BIT_0;
decode_count_bit++;
parser_step = Chamb_CodeDecoderStepSaveDuration;
} else {
parser_step = Chamb_CodeDecoderStepReset;
}
} else {
parser_step = Chamb_CodeDecoderStepReset;
}
break;
}
}
protected:
bool subghz_protocol_decoder_chamb_code_check_mask_and_parse() {
if (decode_count_bit > min_count_bit_for_found + 1)
return false;
if ((decode_data & CHAMBERLAIN_7_CODE_MASK) == CHAMBERLAIN_7_CODE_MASK_CHECK) {
decode_count_bit = 7;
decode_data &= ~CHAMBERLAIN_7_CODE_MASK;
decode_data = (decode_data >> 12) | ((decode_data >> 4) & 0xF);
} else if (
(decode_data & CHAMBERLAIN_8_CODE_MASK) == CHAMBERLAIN_8_CODE_MASK_CHECK) {
decode_count_bit = 8;
decode_data &= ~CHAMBERLAIN_8_CODE_MASK;
decode_data = decode_data >> 4 | CHAMBERLAIN_CODE_BIT_0 << 8; // DIP 6 no use
} else if (
(decode_data & CHAMBERLAIN_9_CODE_MASK) == CHAMBERLAIN_9_CODE_MASK_CHECK) {
decode_count_bit = 9;
decode_data &= ~CHAMBERLAIN_9_CODE_MASK;
decode_data >>= 4;
} else {
return false;
}
return subghz_protocol_chamb_code_to_bit(&decode_data, decode_count_bit);
}
bool subghz_protocol_chamb_code_to_bit(uint64_t* data, uint8_t size) {
uint64_t data_tmp = data[0];
uint64_t data_res = 0;
for (uint8_t i = 0; i < size; i++) {
if ((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_0) {
bit_write(data_res, i, 0);
} else if ((data_tmp & 0xFll) == CHAMBERLAIN_CODE_BIT_1) {
bit_write(data_res, i, 1);
} else {
return false;
}
data_tmp >>= 4;
}
data[0] = data_res;
return true;
}
};
#endif
+89
View File
@@ -0,0 +1,89 @@
#ifndef __FPROTO_CLEMSA_H__
#define __FPROTO_CLEMSA_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
ClemsaDecoderStepReset = 0,
ClemsaDecoderStepSaveDuration,
ClemsaDecoderStepCheckDuration,
} ClemsaDecoderStep;
class FProtoSubGhzDClemsa : public FProtoSubGhzDBase {
public:
FProtoSubGhzDClemsa() {
sensorType = FPS_CLEMSA;
te_short = 385;
te_long = 2695;
te_delta = 150;
min_count_bit_for_found = 18;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case ClemsaDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 51) < te_delta * 25)) {
parser_step = ClemsaDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
}
break;
case ClemsaDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = ClemsaDecoderStepCheckDuration;
} else {
parser_step = ClemsaDecoderStepReset;
}
break;
case ClemsaDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = ClemsaDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = ClemsaDecoderStepSaveDuration;
} else if (
DURATION_DIFF(duration, te_short * 51) < te_delta * 25) {
if ((DURATION_DIFF(te_last, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
} else if ((DURATION_DIFF(te_last, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(1);
} else {
parser_step = ClemsaDecoderStepReset;
}
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
serial = (data >> 2) & 0xFFFF;
btn = (data & 0x03);
if (callback) callback(this);
}
parser_step = ClemsaDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = ClemsaDecoderStepReset;
}
} else {
parser_step = ClemsaDecoderStepReset;
}
break;
}
}
};
#endif
+86
View File
@@ -0,0 +1,86 @@
#ifndef __FPROTO_DOITRAND_H__
#define __FPROTO_DOITRAND_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
DoitrandDecoderStepReset = 0,
DoitrandDecoderStepFoundStartBit,
DoitrandDecoderStepSaveDuration,
DoitrandDecoderStepCheckDuration,
} DoitrandDecoderStep;
class FProtoSubGhzDDoitrand : public FProtoSubGhzDBase {
public:
FProtoSubGhzDDoitrand() {
sensorType = FPS_DOITRAND;
te_short = 400;
te_long = 1100;
te_delta = 150;
min_count_bit_for_found = 37;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case DoitrandDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 62) < te_delta * 30)) {
// Found Preambula
parser_step = DoitrandDecoderStepFoundStartBit;
}
break;
case DoitrandDecoderStepFoundStartBit:
if (level && ((DURATION_DIFF(duration, (te_short * 2)) < te_delta * 3))) {
// Found start bit
parser_step = DoitrandDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = DoitrandDecoderStepReset;
}
break;
case DoitrandDecoderStepSaveDuration:
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
parser_step = DoitrandDecoderStepFoundStartBit;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
cnt = (data >> 24) | ((data >> 15) & 0x1);
btn = ((data >> 18) & 0x3);
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = DoitrandDecoderStepCheckDuration;
}
}
break;
case DoitrandDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = DoitrandDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = DoitrandDecoderStepSaveDuration;
} else {
parser_step = DoitrandDecoderStepReset;
}
} else {
parser_step = DoitrandDecoderStepReset;
}
break;
}
}
};
#endif
+111
View File
@@ -0,0 +1,111 @@
#ifndef __FPROTO_DOOYA_H__
#define __FPROTO_DOOYA_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
DooyaDecoderStepReset = 0,
DooyaDecoderStepFoundStartBit,
DooyaDecoderStepSaveDuration,
DooyaDecoderStepCheckDuration,
} DooyaDecoderStep;
class FProtoSubGhzDDooya : public FProtoSubGhzDBase {
public:
FProtoSubGhzDDooya() {
sensorType = FPS_DOOYA;
te_short = 366;
te_long = 733;
te_delta = 120;
min_count_bit_for_found = 40;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case DooyaDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_long * 12) < te_delta * 20)) {
parser_step = DooyaDecoderStepFoundStartBit;
}
break;
case DooyaDecoderStepFoundStartBit:
if (!level) {
if (DURATION_DIFF(duration, te_long * 2) < te_delta * 3) {
parser_step = DooyaDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = DooyaDecoderStepReset;
}
} else if (
DURATION_DIFF(duration, te_short * 13) < te_delta * 5) {
break;
} else {
parser_step = DooyaDecoderStepReset;
}
break;
case DooyaDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = DooyaDecoderStepCheckDuration;
} else {
parser_step = DooyaDecoderStepReset;
}
break;
case DooyaDecoderStepCheckDuration:
if (!level) {
if (duration >= (te_long * 4)) {
// add last bit
if (DURATION_DIFF(te_last, te_short) < te_delta) {
subghz_protocol_blocks_add_bit(0);
} else if (
DURATION_DIFF(te_last, te_long) <
te_delta * 2) {
subghz_protocol_blocks_add_bit(1);
} else {
parser_step = DooyaDecoderStepReset;
break;
}
parser_step = DooyaDecoderStepFoundStartBit;
if (decode_count_bit ==
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller:
serial = (data >> 16);
if ((data >> 12) & 0x0F) {
cnt = (data >> 8) & 0x0F;
} else {
cnt = 0xFF;
}
btn = data & 0xFF;
if (callback) callback(this);
}
break;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
subghz_protocol_blocks_add_bit(0);
parser_step = DooyaDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = DooyaDecoderStepSaveDuration;
} else {
parser_step = DooyaDecoderStepReset;
}
} else {
parser_step = DooyaDecoderStepReset;
}
break;
}
}
};
#endif
+85
View File
@@ -0,0 +1,85 @@
#ifndef __FPROTO_FAAC_H__
#define __FPROTO_FAAC_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
FaacSLHDecoderStepReset = 0,
FaacSLHDecoderStepFoundPreambula,
FaacSLHDecoderStepSaveDuration,
FaacSLHDecoderStepCheckDuration,
} FaacSLHDecoderStep;
class FProtoSubGhzDFaac : public FProtoSubGhzDBase {
public:
FProtoSubGhzDFaac() {
sensorType = FPS_FAAC;
te_short = 255;
te_long = 595;
te_delta = 100;
min_count_bit_for_found = 64;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case FaacSLHDecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_long * 2) < te_delta * 3)) {
parser_step = FaacSLHDecoderStepFoundPreambula;
}
break;
case FaacSLHDecoderStepFoundPreambula:
if ((!level) && (DURATION_DIFF(duration, te_long * 2) < te_delta * 3)) {
// Found Preambula
parser_step = FaacSLHDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = FaacSLHDecoderStepReset;
}
break;
case FaacSLHDecoderStepSaveDuration:
if (level) {
if (duration >= ((uint32_t)te_short * 3 + te_delta)) {
parser_step = FaacSLHDecoderStepFoundPreambula;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// remark controller skipped
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = FaacSLHDecoderStepCheckDuration;
}
} else {
parser_step = FaacSLHDecoderStepReset;
}
break;
case FaacSLHDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = FaacSLHDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = FaacSLHDecoderStepSaveDuration;
} else {
parser_step = FaacSLHDecoderStepReset;
}
} else {
parser_step = FaacSLHDecoderStepReset;
}
break;
}
}
};
#endif
+88
View File
@@ -0,0 +1,88 @@
#ifndef __FPROTO_GATETX_H__
#define __FPROTO_GATETX_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
GateTXDecoderStepReset = 0,
GateTXDecoderStepFoundStartBit,
GateTXDecoderStepSaveDuration,
GateTXDecoderStepCheckDuration,
} GateTXDecoderStep;
class FProtoSubGhzDGateTx : public FProtoSubGhzDBase {
public:
FProtoSubGhzDGateTx() {
sensorType = FPS_GATETX;
te_short = 350;
te_long = 700;
te_delta = 100;
min_count_bit_for_found = 24;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case GateTXDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 47) < te_delta * 47)) {
// Found Preambula
parser_step = GateTXDecoderStepFoundStartBit;
}
break;
case GateTXDecoderStepFoundStartBit:
if (level && ((DURATION_DIFF(duration, te_long) < te_delta * 3))) {
// Found start bit
parser_step = GateTXDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = GateTXDecoderStepReset;
}
break;
case GateTXDecoderStepSaveDuration:
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
parser_step = GateTXDecoderStepFoundStartBit;
if (decode_count_bit == min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
uint32_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
serial = (code_found_reverse & 0xFF) << 12 | ((code_found_reverse >> 8) & 0xFF) << 4 | ((code_found_reverse >> 20) & 0x0F);
btn = ((code_found_reverse >> 16) & 0x0F);
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = GateTXDecoderStepCheckDuration;
}
}
break;
case GateTXDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = GateTXDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = GateTXDecoderStepSaveDuration;
} else {
parser_step = GateTXDecoderStepReset;
}
} else {
parser_step = GateTXDecoderStepReset;
}
break;
}
}
};
#endif
+107
View File
@@ -0,0 +1,107 @@
#ifndef __FPROTO_HOLTEK_H__
#define __FPROTO_HOLTEK_H__
#include "subghzdbase.hpp"
#define HOLTEK_HEADER_MASK 0xF000000000
#define HOLTEK_HEADER 0x5000000000
typedef enum : uint8_t {
HoltekDecoderStepReset = 0,
HoltekDecoderStepFoundStartBit,
HoltekDecoderStepSaveDuration,
HoltekDecoderStepCheckDuration,
} HoltekDecoderStep;
class FProtoSubGhzDHoltek : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHoltek() {
sensorType = FPS_HOLTEK;
te_short = 430;
te_long = 870;
te_delta = 100;
min_count_bit_for_found = 40;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case HoltekDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 36) < te_delta * 36)) {
// Found Preambula
parser_step = HoltekDecoderStepFoundStartBit;
}
break;
case HoltekDecoderStepFoundStartBit:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
// Found StartBit
parser_step = HoltekDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = HoltekDecoderStepReset;
}
break;
case HoltekDecoderStepSaveDuration:
// save duration
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
if (decode_count_bit ==
min_count_bit_for_found) {
if ((decode_data & HOLTEK_HEADER_MASK) == HOLTEK_HEADER) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
serial = FProtoGeneral::subghz_protocol_blocks_reverse_key((data >> 16) & 0xFFFFF, 20);
uint16_t btn = data & 0xFFFF;
if ((btn & 0xf) != 0xA) {
btn = 0x1 << 4 | (btn & 0xF);
} else if (((btn >> 4) & 0xF) != 0xA) {
btn = 0x2 << 4 | ((btn >> 4) & 0xF);
} else if (((btn >> 8) & 0xF) != 0xA) {
btn = 0x3 << 4 | ((btn >> 8) & 0xF);
} else if (((btn >> 12) & 0xF) != 0xA) {
btn = 0x4 << 4 | ((btn >> 12) & 0xF);
} else {
btn = 0;
}
if (callback) callback(this);
}
}
decode_data = 0;
decode_count_bit = 0;
parser_step = HoltekDecoderStepFoundStartBit;
break;
} else {
te_last = duration;
parser_step = HoltekDecoderStepCheckDuration;
}
} else {
parser_step = HoltekDecoderStepReset;
}
break;
case HoltekDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
subghz_protocol_blocks_add_bit(0);
parser_step = HoltekDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = HoltekDecoderStepSaveDuration;
} else {
parser_step = HoltekDecoderStepReset;
}
} else {
parser_step = HoltekDecoderStepReset;
}
break;
}
}
};
#endif
@@ -0,0 +1,90 @@
#ifndef __FPROTO_HOLTEKTH12X_H__
#define __FPROTO_HOLTEKTH12X_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
Holtek_HT12XDecoderStepReset = 0,
Holtek_HT12XDecoderStepFoundStartBit,
Holtek_HT12XDecoderStepSaveDuration,
Holtek_HT12XDecoderStepCheckDuration,
} Holtek_HT12XDecoderStep;
class FProtoSubGhzDHoltekHt12x : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHoltekHt12x() {
sensorType = FPS_HOLTEKHT12X;
te_short = 320;
te_long = 640;
te_delta = 200;
min_count_bit_for_found = 12;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case Holtek_HT12XDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 36) < te_delta * 36)) {
// Found Preambula
parser_step = Holtek_HT12XDecoderStepFoundStartBit;
}
break;
case Holtek_HT12XDecoderStepFoundStartBit:
if ((level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
// Found StartBit
parser_step = Holtek_HT12XDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = Holtek_HT12XDecoderStepReset;
}
break;
case Holtek_HT12XDecoderStepSaveDuration:
// save duration
if (!level) {
if (duration >= ((uint32_t)te_short * 10 + te_delta)) {
if (decode_count_bit == min_count_bit_for_found) {
if (data != decode_data) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
btn = data & 0x0F;
cnt = (data >> 4) & 0xFF;
if (callback) callback(this);
}
}
decode_data = 0;
decode_count_bit = 0;
parser_step = Holtek_HT12XDecoderStepFoundStartBit;
break;
} else {
te_last = duration;
parser_step = Holtek_HT12XDecoderStepCheckDuration;
}
} else {
parser_step = Holtek_HT12XDecoderStepReset;
}
break;
case Holtek_HT12XDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = Holtek_HT12XDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
subghz_protocol_blocks_add_bit(0);
parser_step = Holtek_HT12XDecoderStepSaveDuration;
} else {
parser_step = Holtek_HT12XDecoderStepReset;
}
} else {
parser_step = Holtek_HT12XDecoderStepReset;
}
break;
}
}
};
#endif
+108
View File
@@ -0,0 +1,108 @@
#ifndef __FPROTO_HONEYWELL_H__
#define __FPROTO_HONEYWELL_H__
#include "subghzdbase.hpp"
class FProtoSubGhzDHoneywell : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHoneywell() {
sensorType = FPS_HONEYWELL;
te_short = 280;
te_long = 143;
te_delta = 51;
min_count_bit_for_found = 62;
}
void feed(bool level, uint32_t duration) {
ManchesterEvent event = ManchesterEventReset;
if (!level) {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortLow;
} else if (
DURATION_DIFF(duration, te_long) < te_delta * 2) {
event = ManchesterEventLongLow;
}
} else {
if (DURATION_DIFF(duration, te_short) < te_delta) {
event = ManchesterEventShortHigh;
} else if (
DURATION_DIFF(duration, te_long) < te_delta * 2) {
event = ManchesterEventLongHigh;
}
}
if (event != ManchesterEventReset) {
bool bit;
bool data_ok = FProtoGeneral::manchester_advance(
manchester_saved_state, event, &manchester_saved_state, &bit);
if (data_ok) {
subghz_protocol_decoder_honeywell_addbit(bit);
}
} else {
decode_data = 0;
decode_count_bit = 0;
}
}
protected:
ManchesterState manchester_saved_state = ManchesterStateMid1;
void subghz_protocol_decoder_honeywell_addbit(bool bit) {
decode_data = (decode_data << 1) | bit;
decode_count_bit++;
uint16_t preamble = (decode_data >> 48) & 0xFFFF;
// can be multiple, since flipper can't read it well..
if (preamble == 0b0011111111111110 || preamble == 0b0111111111111110 ||
preamble == 0b1111111111111110) {
uint8_t datatocrc[4];
datatocrc[0] = (decode_data >> 40) & 0xFFFF;
datatocrc[1] = (decode_data >> 32) & 0xFFFF;
datatocrc[2] = (decode_data >> 24) & 0xFFFF;
datatocrc[3] = (decode_data >> 16) & 0xFFFF;
uint8_t channel = (decode_data >> 44) & 0xF;
uint16_t crc_calc = 0;
if (channel == 0x2 || channel == 0x4 || channel == 0xA) {
// 2GIG brand
crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8050, 0);
} else { // channel == 0x8
crc_calc = subghz_protocol_honeywell_crc16(datatocrc, 4, 0x8005, 0);
}
uint16_t crc = decode_data & 0xFFFF;
if (crc == crc_calc) {
// the data is good. process it.
data = decode_data;
data_count_bit = decode_count_bit; // maybe set it to 64, and hack the first 2 bits to 1! will see if replay needs it
serial = (decode_data >> 24) & 0xFFFFF;
btn = (decode_data >> 16) & 0xFF; // not exactly button, but can contain btn data too.
if (callback) callback(this);
decode_data = 0;
decode_count_bit = 0;
} else {
return;
}
}
}
uint16_t subghz_protocol_honeywell_crc16(
uint8_t const message[],
unsigned nBytes,
uint16_t polynomial,
uint16_t init) {
uint16_t remainder = init;
unsigned byte, bit;
for (byte = 0; byte < nBytes; ++byte) {
remainder ^= message[byte] << 8;
for (bit = 0; bit < 8; ++bit) {
if (remainder & 0x8000) {
remainder = (remainder << 1) ^ polynomial;
} else {
remainder = (remainder << 1);
}
}
}
return remainder;
}
};
#endif
@@ -0,0 +1,74 @@
#ifndef __FPROTO_HONEYWELLWDB_H__
#define __FPROTO_HONEYWELLWDB_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
Honeywell_WDBDecoderStepReset = 0,
Honeywell_WDBDecoderStepFoundStartBit,
Honeywell_WDBDecoderStepSaveDuration,
Honeywell_WDBDecoderStepCheckDuration,
} Honeywell_WDBDecoderStep;
class FProtoSubGhzDHoneywellWdb : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHoneywellWdb() {
sensorType = FPS_HONEYWELLWDB;
te_short = 160;
te_long = 320;
te_delta = 61;
min_count_bit_for_found = 48;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case Honeywell_WDBDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 3) < te_delta)) {
// Found header Honeywell_WDB
decode_count_bit = 0;
decode_data = 0;
parser_step = Honeywell_WDBDecoderStepSaveDuration;
}
break;
case Honeywell_WDBDecoderStepSaveDuration:
if (level) { // save interval
if (DURATION_DIFF(duration, te_short * 3) < te_delta) {
if ((decode_count_bit == min_count_bit_for_found) &&
((decode_data & 0x01) == FProtoGeneral::subghz_protocol_blocks_get_parity(decode_data >> 1, min_count_bit_for_found - 1))) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller has too much, should be done on ui side
if (callback) callback(this);
}
parser_step = Honeywell_WDBDecoderStepReset;
break;
}
te_last = duration;
parser_step = Honeywell_WDBDecoderStepCheckDuration;
} else {
parser_step = Honeywell_WDBDecoderStepReset;
}
break;
case Honeywell_WDBDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = Honeywell_WDBDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = Honeywell_WDBDecoderStepSaveDuration;
} else
parser_step = Honeywell_WDBDecoderStepReset;
} else {
parser_step = Honeywell_WDBDecoderStepReset;
}
break;
}
}
};
#endif
+85
View File
@@ -0,0 +1,85 @@
#ifndef __FPROTO_HORMANN_H__
#define __FPROTO_HORMANN_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
HormannDecoderStepReset = 0,
HormannDecoderStepFoundStartHeader,
HormannDecoderStepFoundHeader,
HormannDecoderStepFoundStartBit,
HormannDecoderStepSaveDuration,
HormannDecoderStepCheckDuration,
} HormannDecoderStep;
#define HORMANN_HSM_PATTERN 0xFF000000003
class FProtoSubGhzDHormann : public FProtoSubGhzDBase {
public:
FProtoSubGhzDHormann() {
sensorType = FPS_HORMANN;
te_short = 500;
te_long = 1000;
te_delta = 200;
min_count_bit_for_found = 44;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case HormannDecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short * 24) < te_delta * 24)) {
parser_step = HormannDecoderStepFoundStartBit;
}
break;
case HormannDecoderStepFoundStartBit:
if ((!level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = HormannDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = HormannDecoderStepReset;
}
break;
case HormannDecoderStepSaveDuration:
if (level) { // save interval
if (duration >= (te_short * 5) && (decode_data & HORMANN_HSM_PATTERN) == HORMANN_HSM_PATTERN) {
parser_step = HormannDecoderStepFoundStartBit;
if (decode_count_bit >=
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
btn = (data >> 4) & 0xF;
if (callback) callback(this);
}
break;
}
te_last = duration;
parser_step = HormannDecoderStepCheckDuration;
} else {
parser_step = HormannDecoderStepReset;
}
break;
case HormannDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = HormannDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = HormannDecoderStepSaveDuration;
} else
parser_step = HormannDecoderStepReset;
} else {
parser_step = HormannDecoderStepReset;
}
break;
}
}
};
#endif
+91
View File
@@ -0,0 +1,91 @@
#ifndef __FPROTO_IDO_H__
#define __FPROTO_IDO_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
IDoDecoderStepReset = 0,
IDoDecoderStepFoundPreambula,
IDoDecoderStepSaveDuration,
IDoDecoderStepCheckDuration,
} IDoDecoderStep;
class FProtoSubGhzDIdo : public FProtoSubGhzDBase {
public:
FProtoSubGhzDIdo() {
sensorType = FPS_IDO;
te_short = 450;
te_long = 1450;
te_delta = 150;
min_count_bit_for_found = 48;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case IDoDecoderStepReset:
if ((level) && (DURATION_DIFF(duration, te_short * 10) < te_delta * 5)) {
parser_step = IDoDecoderStepFoundPreambula;
}
break;
case IDoDecoderStepFoundPreambula:
if ((!level) && (DURATION_DIFF(duration, te_short * 10) < te_delta * 5)) {
// Found Preambula
parser_step = IDoDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = IDoDecoderStepReset;
}
break;
case IDoDecoderStepSaveDuration:
if (level) {
if (duration >= ((uint32_t)te_short * 5 + te_delta)) {
parser_step = IDoDecoderStepFoundPreambula;
if (decode_count_bit >=
min_count_bit_for_found) {
data = decode_data;
data_count_bit = decode_count_bit;
// controller
uint64_t code_found_reverse = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
uint32_t code_fix = code_found_reverse & 0xFFFFFF;
serial = code_fix & 0xFFFFF;
btn = (code_fix >> 20) & 0x0F;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
break;
} else {
te_last = duration;
parser_step = IDoDecoderStepCheckDuration;
}
} else {
parser_step = IDoDecoderStepReset;
}
break;
case IDoDecoderStepCheckDuration:
if (!level) {
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 3)) {
subghz_protocol_blocks_add_bit(0);
parser_step = IDoDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta * 3) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = IDoDecoderStepSaveDuration;
} else {
parser_step = IDoDecoderStepReset;
}
} else {
parser_step = IDoDecoderStepReset;
}
break;
}
}
};
#endif
@@ -0,0 +1,148 @@
#ifndef __FPROTO_INTERTECHNOV3_H__
#define __FPROTO_INTERTECHNOV3_H__
#include "subghzdbase.hpp"
#define INTERTECHNO_V3_DIMMING_COUNT_BIT 36
typedef enum : uint8_t {
IntertechnoV3DecoderStepReset = 0,
IntertechnoV3DecoderStepStartSync,
IntertechnoV3DecoderStepFoundSync,
IntertechnoV3DecoderStepStartDuration,
IntertechnoV3DecoderStepSaveDuration,
IntertechnoV3DecoderStepCheckDuration,
IntertechnoV3DecoderStepEndDuration,
} IntertechnoV3DecoderStep;
class FProtoSubGhzDIntertechnoV3 : public FProtoSubGhzDBase {
public:
FProtoSubGhzDIntertechnoV3() {
sensorType = FPS_INTERTECHNOV3;
te_short = 275;
te_long = 1375;
te_delta = 150;
min_count_bit_for_found = 32;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case IntertechnoV3DecoderStepReset:
if ((!level) &&
(DURATION_DIFF(duration, te_short * 37) < te_delta * 15)) {
parser_step = IntertechnoV3DecoderStepStartSync;
}
break;
case IntertechnoV3DecoderStepStartSync:
if (level && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = IntertechnoV3DecoderStepFoundSync;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepFoundSync:
if (!level && (DURATION_DIFF(duration, te_short * 10) < te_delta * 3)) {
parser_step = IntertechnoV3DecoderStepStartDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepStartDuration:
if (level && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = IntertechnoV3DecoderStepSaveDuration;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepSaveDuration:
if (!level) { // save interval
if (duration >= (te_short * 11)) {
parser_step = IntertechnoV3DecoderStepStartSync;
if ((decode_count_bit == min_count_bit_for_found) ||
(decode_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT)) {
data = decode_data;
data_count_bit = decode_count_bit;
remote_controller();
if (callback) callback(this);
}
break;
}
te_last = duration;
parser_step = IntertechnoV3DecoderStepCheckDuration;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepCheckDuration:
if (level) {
// Add 0 bit
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = IntertechnoV3DecoderStepEndDuration;
} else if (
// Add 1 bit
(DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = IntertechnoV3DecoderStepEndDuration;
} else if (
// Add dimm_state
(DURATION_DIFF(te_last, te_short) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta) &&
(decode_count_bit == 27)) {
subghz_protocol_blocks_add_bit(0);
parser_step = IntertechnoV3DecoderStepEndDuration;
} else
parser_step = IntertechnoV3DecoderStepReset;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
case IntertechnoV3DecoderStepEndDuration:
if (!level && ((DURATION_DIFF(duration, te_short) < te_delta) ||
(DURATION_DIFF(duration, te_long) < te_delta * 2))) {
parser_step = IntertechnoV3DecoderStepStartDuration;
} else {
parser_step = IntertechnoV3DecoderStepReset;
}
break;
}
}
protected:
void remote_controller() {
if (data_count_bit == min_count_bit_for_found) {
serial = (data >> 6) & 0x3FFFFFF;
if ((data >> 5) & 0x1) {
cnt = 1 << 5;
} else {
cnt = (~data & 0xF);
}
btn = (data >> 4) & 0x1;
} else if (data_count_bit == INTERTECHNO_V3_DIMMING_COUNT_BIT) {
serial = (data >> 10) & 0x3FFFFFF;
if ((data >> 9) & 0x1) {
cnt = 1 << 5;
} else {
cnt = (~(data >> 4) & 0xF);
}
btn = data & 0xF;
} else {
serial = 0;
cnt = 0;
btn = 0;
}
}
};
#endif
+107
View File
@@ -0,0 +1,107 @@
#ifndef __FPROTO_KEELOQ_H__
#define __FPROTO_KEELOQ_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
KeeloqDecoderStepReset = 0,
KeeloqDecoderStepCheckPreambula,
KeeloqDecoderStepSaveDuration,
KeeloqDecoderStepCheckDuration,
} KeeloqDecoderStep;
class FProtoSubGhzDKeeLoq : public FProtoSubGhzDBase {
public:
FProtoSubGhzDKeeLoq() {
sensorType = FPS_KEELOQ;
te_short = 400;
te_long = 800;
te_delta = 140;
min_count_bit_for_found = 64;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case KeeloqDecoderStepReset:
if ((level) && DURATION_DIFF(duration, te_short) < te_delta) {
parser_step = KeeloqDecoderStepCheckPreambula;
header_count++;
}
break;
case KeeloqDecoderStepCheckPreambula:
if ((!level) && (DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = KeeloqDecoderStepReset;
break;
}
if ((header_count > 2) && (DURATION_DIFF(duration, te_short * 10) < te_delta * 10)) {
// Found header
parser_step = KeeloqDecoderStepSaveDuration;
decode_data = 0;
decode_count_bit = 0;
} else {
parser_step = KeeloqDecoderStepReset;
header_count = 0;
}
break;
case KeeloqDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = KeeloqDecoderStepCheckDuration;
}
break;
case KeeloqDecoderStepCheckDuration:
if (!level) {
if (duration >= ((uint32_t)te_short * 2 + te_delta)) {
// Found end TX
parser_step = KeeloqDecoderStepReset;
if ((decode_count_bit >= min_count_bit_for_found) &&
(decode_count_bit <= min_count_bit_for_found + 2)) {
if (data != decode_data) {
data = decode_data;
data_count_bit = min_count_bit_for_found;
// controller
uint64_t key = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, data_count_bit);
uint32_t key_fix = key >> 32;
// uint32_t key_hop = key & 0x00000000ffffffff; //unused
serial = key_fix & 0x0FFFFFFF;
btn = key_fix >> 28;
if (callback) callback(this);
}
decode_data = 0;
decode_count_bit = 0;
header_count = 0;
}
break;
} else if (
(DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
if (decode_count_bit < min_count_bit_for_found) {
subghz_protocol_blocks_add_bit(1);
} else {
decode_count_bit++;
}
parser_step = KeeloqDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta * 2) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
if (decode_count_bit < min_count_bit_for_found) {
subghz_protocol_blocks_add_bit(0);
} else {
decode_count_bit++;
}
parser_step = KeeloqDecoderStepSaveDuration;
} else {
parser_step = KeeloqDecoderStepReset;
header_count = 0;
}
} else {
parser_step = KeeloqDecoderStepReset;
header_count = 0;
}
break;
}
}
};
#endif
@@ -0,0 +1,124 @@
#ifndef __FPROTO_KINGGATES_STYLO_4K_H__
#define __FPROTO_KINGGATES_STYLO_4K_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
KingGates_stylo_4kDecoderStepReset = 0,
KingGates_stylo_4kDecoderStepCheckPreambula,
KingGates_stylo_4kDecoderStepCheckStartBit,
KingGates_stylo_4kDecoderStepSaveDuration,
KingGates_stylo_4kDecoderStepCheckDuration,
} KingGates_stylo_4kDecoderStep;
class FProtoSubGhzDKinggatesStylo4K : public FProtoSubGhzDBase {
public:
FProtoSubGhzDKinggatesStylo4K() {
sensorType = FPS_KINGGATESSTYLO4K;
te_short = 400;
te_long = 1100;
te_delta = 140;
min_count_bit_for_found = 89;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case KingGates_stylo_4kDecoderStepReset:
if ((level) && DURATION_DIFF(duration, te_short) < te_delta) {
parser_step = KingGates_stylo_4kDecoderStepCheckPreambula;
header_count++;
}
break;
case KingGates_stylo_4kDecoderStepCheckPreambula:
if ((!level) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
parser_step = KingGates_stylo_4kDecoderStepReset;
break;
}
if ((header_count > 2) &&
(DURATION_DIFF(duration, te_long * 2) < te_delta * 2)) {
// Found header
parser_step = KingGates_stylo_4kDecoderStepCheckStartBit;
} else {
parser_step = KingGates_stylo_4kDecoderStepReset;
header_count = 0;
}
break;
case KingGates_stylo_4kDecoderStepCheckStartBit:
if ((level) &&
DURATION_DIFF(duration, te_short * 2) < te_delta * 2) {
parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
decode_data = 0;
data_2 = 0;
decode_count_bit = 0;
header_count = 0;
}
break;
case KingGates_stylo_4kDecoderStepSaveDuration:
if (!level) {
if (duration >= ((uint32_t)te_long * 3)) {
if (decode_count_bit ==
min_count_bit_for_found) {
data = data_2;
data_2 = decode_data;
data_count_bit = decode_count_bit;
// controller
uint64_t fix = FProtoGeneral::subghz_protocol_blocks_reverse_key(data, 53);
btn = (fix >> 17) & 0x0F;
serial = ((fix >> 5) & 0xFFFF0000) | (fix & 0xFFFF);
if (callback) callback(this);
}
parser_step = KingGates_stylo_4kDecoderStepReset;
decode_data = 0;
data_2 = 0;
decode_count_bit = 0;
header_count = 0;
break;
} else {
te_last = duration;
parser_step = KingGates_stylo_4kDecoderStepCheckDuration;
}
} else {
parser_step = KingGates_stylo_4kDecoderStepReset;
header_count = 0;
}
break;
case KingGates_stylo_4kDecoderStepCheckDuration:
if (level) {
if ((DURATION_DIFF(
te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta * 2)) {
subghz_protocol_blocks_add_bit(1);
parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(
te_last, te_long) <
te_delta * 2) &&
(DURATION_DIFF(duration, te_short) <
te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = KingGates_stylo_4kDecoderStepSaveDuration;
} else {
parser_step = KingGates_stylo_4kDecoderStepReset;
header_count = 0;
}
if (decode_count_bit == 53) {
data_2 = decode_data;
decode_data = 0;
}
} else {
parser_step = KingGates_stylo_4kDecoderStepReset;
header_count = 0;
}
break;
}
}
protected:
uint64_t data_2 = 0;
};
#endif
+88
View File
@@ -0,0 +1,88 @@
#ifndef __FPROTO_LINEAR_H__
#define __FPROTO_LINEAR_H__
#include "subghzdbase.hpp"
typedef enum : uint8_t {
LinearDecoderStepReset = 0,
LinearDecoderStepSaveDuration,
LinearDecoderStepCheckDuration,
} LinearDecoderStep;
class FProtoSubGhzDLinear : public FProtoSubGhzDBase {
public:
FProtoSubGhzDLinear() {
sensorType = FPS_LINEAR;
te_short = 500;
te_long = 1500;
te_delta = 150;
min_count_bit_for_found = 10;
}
void feed(bool level, uint32_t duration) {
switch (parser_step) {
case LinearDecoderStepReset:
if ((!level) && (DURATION_DIFF(duration, te_short * 42) < te_delta * 20)) {
// Found header Linear
decode_data = 0;
decode_count_bit = 0;
parser_step = LinearDecoderStepSaveDuration;
}
break;
case LinearDecoderStepSaveDuration:
if (level) {
te_last = duration;
parser_step = LinearDecoderStepCheckDuration;
} else {
parser_step = LinearDecoderStepReset;
}
break;
case LinearDecoderStepCheckDuration:
if (!level) { // save interval
if (duration >= (te_short * 5)) {
parser_step = LinearDecoderStepReset;
// checking that the duration matches the guardtime
if ((DURATION_DIFF(duration, te_short * 42) > te_delta * 20)) {
break;
}
if (DURATION_DIFF(te_last, te_short) < te_delta) {
subghz_protocol_blocks_add_bit(0);
} else if (
DURATION_DIFF(te_last, te_long) <
te_delta) {
subghz_protocol_blocks_add_bit(1);
}
if (decode_count_bit == min_count_bit_for_found) {
serial = SD_NO_SERIAL;
btn = SD_NO_BTN;
data = decode_data;
data_count_bit = decode_count_bit;
if (callback) callback(this);
}
break;
}
if ((DURATION_DIFF(te_last, te_short) < te_delta) &&
(DURATION_DIFF(duration, te_long) < te_delta)) {
subghz_protocol_blocks_add_bit(0);
parser_step = LinearDecoderStepSaveDuration;
} else if (
(DURATION_DIFF(te_last, te_long) < te_delta) &&
(DURATION_DIFF(duration, te_short) < te_delta)) {
subghz_protocol_blocks_add_bit(1);
parser_step = LinearDecoderStepSaveDuration;
} else {
parser_step = LinearDecoderStepReset;
}
} else {
parser_step = LinearDecoderStepReset;
}
break;
}
}
};
#endif

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