diff --git a/firmware/application/CMakeLists.txt b/firmware/application/CMakeLists.txt index 3697ab4ff..dd8b04417 100644 --- a/firmware/application/CMakeLists.txt +++ b/firmware/application/CMakeLists.txt @@ -181,6 +181,7 @@ set(CPPSRC ui_geomap.cpp # ui_handwrite.cpp ui_jammer.cpp + ui_keyfob.cpp ui_lcr.cpp ui_menu.cpp ui_mictx.cpp diff --git a/firmware/application/bitmap.hpp b/firmware/application/bitmap.hpp index 44966047d..a02c755d2 100644 --- a/firmware/application/bitmap.hpp +++ b/firmware/application/bitmap.hpp @@ -809,6 +809,28 @@ static constexpr Bitmap bitmap_icon_rds { { 16, 16 }, bitmap_icon_rds_data }; +static constexpr uint8_t bitmap_icon_keyfob_data[] = { + 0x30, 0x00, + 0x30, 0x00, + 0x30, 0x00, + 0x30, 0x00, + 0x30, 0x00, + 0x30, 0x00, + 0xFC, 0x00, + 0xCE, 0x01, + 0x86, 0x01, + 0xFE, 0x01, + 0x86, 0x31, + 0x86, 0x49, + 0xCE, 0x87, + 0xFC, 0x84, + 0xFC, 0x4B, + 0x78, 0x30, +}; +static constexpr Bitmap bitmap_icon_keyfob { + { 16, 16 }, bitmap_icon_keyfob_data +}; + static constexpr uint8_t bitmap_icon_nordic_data[] = { 0x00, 0x00, 0x18, 0x18, diff --git a/firmware/application/protocols/encoders.cpp b/firmware/application/protocols/encoders.cpp index 409a91a1d..ad025f1d7 100644 --- a/firmware/application/protocols/encoders.cpp +++ b/firmware/application/protocols/encoders.cpp @@ -54,5 +54,34 @@ size_t make_bitstream(std::string& fragments) { return bitstream_length; } + +void bitstream_append(size_t& bitstream_length, uint32_t bit_count, uint32_t bits) { + uint8_t * bitstream = shared_memory.bb_data.data; + uint32_t bit_mask = 1 << (bit_count - 1); + uint32_t bit_index; + uint8_t byte = 0; + + if (bitstream_length & 7) + byte = bitstream[bitstream_length >> 3]; + + bit_index = 7 - (bitstream_length & 7); + + for (size_t i = 0; i < bit_count; i++) { + if (bits & bit_mask) + byte |= (1 << bit_index); + + if (!bit_index) { + bitstream[bitstream_length >> 3] = byte; + byte = 0; + } + + bit_index = (bit_index - 1) & 7; + bits <<= 1; + + bitstream_length++; + } + + bitstream[bitstream_length >> 3] = byte; +} } /* namespace encoders */ diff --git a/firmware/application/protocols/encoders.hpp b/firmware/application/protocols/encoders.hpp index 4b9d55397..b30b5deb8 100644 --- a/firmware/application/protocols/encoders.hpp +++ b/firmware/application/protocols/encoders.hpp @@ -35,6 +35,7 @@ namespace encoders { #define ENCODER_UM3750 8 size_t make_bitstream(std::string& fragments); + void bitstream_append(size_t& bitstream_length, uint32_t bit_count, uint32_t bits); struct encoder_def_t { std::string name; // Encoder chip ref/name diff --git a/firmware/application/ui_about.cpp b/firmware/application/ui_about.cpp index e59962b9a..5b9182b53 100644 --- a/firmware/application/ui_about.cpp +++ b/firmware/application/ui_about.cpp @@ -97,9 +97,8 @@ void AboutView::update() { if (timer < 0) { timer = 240; loop = true; - } else { + } else timer += 16; - } render_line = 0; credits_index++; @@ -111,7 +110,7 @@ void AboutView::update() { const auto glyph = style().font.glyph(c); const size_t start = (glyph.size().width() / 8) * render_line; - for(Dim c=0; c> 3)] & (1U << (c & 0x7)); pixel_row[start_pos + i + c] = pixel ? Color::white() : Color::black(); } diff --git a/firmware/application/ui_about.hpp b/firmware/application/ui_about.hpp index de48d578a..dd304b816 100644 --- a/firmware/application/ui_about.hpp +++ b/firmware/application/ui_about.hpp @@ -72,28 +72,31 @@ private: int32_t delay; } credits_t; - const credits_t credits[22] = { - { 60, "PortaPack|HAVOC", 0 }, - { 7 * 8, "Git hash " GIT_REVISION, 16 }, - { 11 * 8, "Gurus J. Boone", 0 }, - { 18 * 8, "M. Ossmann", 16 }, - { 11 * 8, "HAVOC Furrtek", 16 }, - { 7 * 8, "POCSAG rx T. Sailer", 0 }, - { 18 * 8, "E. Oenal", 16 }, - { 0 * 8, "Radiosonde infos F4GMU", 0 }, - { 18 * 8, "RS1729", 16 }, - { 4 * 8, "RDS waveform C. Jacquet", 16 }, - { 7 * 8, "Xy. infos cLx", 16 }, - { 2 * 8, "OOK scan trick Samy Kamkar", 16 }, - { 7 * 8, "World map NASA", 16 }, - { 0 * 8, "TouchTunes infos Notpike", 24 }, - { 12 * 8, "Thanks", 16 }, - { 1 * 8, "Rainer Matla Keld Norman", 0 }, - { 1 * 8, " Giorgio C. DC1RDB", 0 }, - { 1 * 8, " Sigmounte Waax", 0 }, - { 1 * 8, " Windyoona Channels", 0 }, - { 1 * 8, " F4GEV", 24 }, - { 12 * 8, "MMXVII", -1 } + const credits_t credits[24] = { + // 012345678901234567890123456789 + { 60, "PortaPack|HAVOC", 0 }, + { 7 * 8, "Git hash " GIT_REVISION, 16 }, + { 11 * 8, "Gurus J. Boone", 0 }, + { 18 * 8, "M. Ossmann", 16 }, + { 11 * 8, "HAVOC Furrtek", 16 }, + { 7 * 8, "POCSAG rx T. Sailer", 0 }, + { 18 * 8, "E. Oenal", 16 }, + { 0 * 8, "Radiosonde infos F4GMU", 0 }, + { 18 * 8, "RS1729", 16 }, + { 4 * 8, "RDS waveform C. Jacquet", 16 }, + { 7 * 8, "Xy. infos cLx", 16 }, + { 2 * 8, "OOK scan trick Samy Kamkar", 16 }, + { 7 * 8, "World map NASA", 16 }, + { 0 * 8, "TouchTunes infos Notpike", 16 }, + { 4 * 8, "Subaru infos Tom", 0 }, + { 18 * 8, "Wimmenhove", 24 }, + { 12 * 8, "Thanks", 16 }, + { 1 * 8, "Rainer Matla Keld Norman", 0 }, + { 1 * 8, " Giorgio C. DC1RDB", 0 }, + { 1 * 8, " Sigmounte Waax", 0 }, + { 1 * 8, " Windyoona Channels", 0 }, + { 1 * 8, " F4GEV", 24 }, + { 12 * 8, "MMXVII", -1 } }; CreditsWidget credits_display { diff --git a/firmware/application/ui_keyfob.cpp b/firmware/application/ui_keyfob.cpp new file mode 100644 index 000000000..f6e6fdb29 --- /dev/null +++ b/firmware/application/ui_keyfob.cpp @@ -0,0 +1,250 @@ +/* + * Copyright (C) 2015 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_keyfob.hpp" + +#include "baseband_api.hpp" +#include "string_format.hpp" + +using namespace portapack; + +namespace ui { + +uint8_t KeyfobView::subaru_get_checksum() { + uint8_t checksum = 0; + + for (size_t i = 0; i < 9; i++) { + checksum ^= (frame[i] & 0x0F); // 00 11 22 33 44 55 66 77 88 9- + checksum ^= ((frame[i] >> 4) & 0x0F); + } + + checksum ^= ((frame[9] >> 4) & 0x0F); + checksum++; + checksum &= 0x0F; + + return checksum; +} + +bool KeyfobView::subaru_is_valid() { + if (frame[0] != 0x55) + return false; + + if (subaru_get_checksum() != (frame[9] & 0x0F)) + return false; + + return true; +} + +uint16_t KeyfobView::subaru_get_code() { + // 77777777 88888888 9999 + return (frame[7] << 12) | (frame[8] << 4) | (frame[9] >> 4); +} + +void KeyfobView::subaru_set_code(const uint16_t code) { + frame[7] = (code >> 12) & 0xFF; + frame[8] = (code >> 4) & 0xFF; + frame[9] &= 0x0F; + frame[9] |= (code & 0x0F) << 4; +} + +int32_t KeyfobView::subaru_get_command() { + uint32_t command_a = frame[5] & 0x0F; + uint32_t command_b = frame[6] & 0x0F; + + if (command_a != command_b) + return -1; + + return command_a; +} + +void KeyfobView::subaru_set_command(const uint32_t command) { + frame[5] &= 0xF0; + frame[5] |= command; + frame[6] &= 0xF0; + frame[6] |= command; +} + +void KeyfobView::generate_payload(size_t& bitstream_length) { + for (size_t i = 0; i < (10 * 8); i++) { + if (frame[i >> 3] & (1 << (7 - (i & 7)))) + bitstream_append(bitstream_length, 2, 0b10); + else + bitstream_append(bitstream_length, 2, 0b01); + } +} + +size_t KeyfobView::generate_frame() { + size_t bitstream_length = 0; + uint64_t payload; + + // Symfield word to frame + payload = field_payload_a.value_hex_u64(); + for (size_t i = 0; i < 5; i++) { + frame[4 - i] = payload & 0xFF; + payload >>= 8; + } + payload = field_payload_b.value_hex_u64(); + for (size_t i = 0; i < 5; i++) { + frame[9 - i] = payload & 0xFF; + payload >>= 8; + } + + // Recompute checksum + frame[9] = (frame[9] & 0xF0) | subaru_get_checksum(); + update_symfields(); + + // Preamble: 128x 01 + for (size_t i = 0; i < 128; i++) + bitstream_append(bitstream_length, 2, 0b01); + + // Space: 4x 0 + bitstream_append(bitstream_length, 4, 0b0000); + + // Payload + generate_payload(bitstream_length); + + // Space: 8x 0 + bitstream_append(bitstream_length, 8, 0b00000000); + + // Payload again + generate_payload(bitstream_length); + + return bitstream_length; +} + +void KeyfobView::focus() { + options_make.focus(); +} + +KeyfobView::~KeyfobView() { + transmitter_model.disable(); + baseband::shutdown(); +} + +void KeyfobView::update_progress(const uint32_t progress) { + text_status.set("Repeat #" + to_string_dec_uint(progress)); +} + +void KeyfobView::on_tx_progress(const uint32_t progress, const bool done) { + if (!done) { + // Repeating... + update_progress(progress + 1); + progressbar.set_value(progress); + } else { + transmitter_model.disable(); + text_status.set("Done"); + progressbar.set_value(0); + tx_view.set_transmitting(false); + } +} + +void KeyfobView::on_make_change(size_t index) { + (void)index; +} + +// DEBUG +void KeyfobView::update_symfields() { + for (size_t i = 0; i < 5; i++) { + field_payload_a.set_sym(i << 1, frame[i] >> 4); + field_payload_a.set_sym((i << 1) + 1, frame[i] & 0x0F); + } + for (size_t i = 0; i < 5; i++) { + field_payload_b.set_sym(i << 1, frame[5 + i] >> 4); + field_payload_b.set_sym((i << 1) + 1, frame[5 + i] & 0x0F); + } +} + +void KeyfobView::on_command_change(uint32_t value) { + subaru_set_command(value); + update_symfields(); +} + +void KeyfobView::start_tx() { + progressbar.set_max(repeats - 1); + + update_progress(1); + + size_t bitstream_length = generate_frame(); + + transmitter_model.set_sampling_rate(OOK_SAMPLERATE); + transmitter_model.set_rf_amp(true); + transmitter_model.set_baseband_bandwidth(1750000); + transmitter_model.enable(); + + baseband::set_ook_data( + bitstream_length, + subaru_samples_per_bit, + repeats, + 200 // Pause symbols + ); +} + +KeyfobView::KeyfobView( + NavigationView& nav +) : nav_ { nav } +{ + baseband::run_image(portapack::spi_flash::image_tag_ook); + + add_children({ + &labels, + &options_make, + &options_command, + &field_payload_a, + &field_payload_b, + &text_status, + &progressbar, + &tx_view + }); + + frame[0] = 0x55; + update_symfields(); + + options_make.on_change = [this](size_t index, int32_t) { + on_make_change(index); + }; + + options_command.on_change = [this](size_t, int32_t value) { + on_command_change(value); + }; + + options_make.set_selected_index(0); + + transmitter_model.set_tuning_frequency(433920000); // Fixed 433.92MHz + + tx_view.on_edit_frequency = [this, &nav]() { + auto new_view = nav.push(transmitter_model.tuning_frequency()); + new_view->on_changed = [this](rf::Frequency f) { + transmitter_model.set_tuning_frequency(f); + }; + }; + + tx_view.on_start = [this]() { + start_tx(); + tx_view.set_transmitting(true); + }; + + tx_view.on_stop = [this]() { + tx_view.set_transmitting(false); + }; +} + +} /* namespace ui */ diff --git a/firmware/application/ui_keyfob.hpp b/firmware/application/ui_keyfob.hpp new file mode 100644 index 000000000..7f28d554b --- /dev/null +++ b/firmware/application/ui_keyfob.hpp @@ -0,0 +1,131 @@ +/* + * Copyright (C) 2015 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.hpp" +#include "ui_transmitter.hpp" +#include "transmitter_model.hpp" +#include "encoders.hpp" + +using namespace encoders; + +namespace ui { + +class KeyfobView : public View { +public: + KeyfobView(NavigationView& nav); + ~KeyfobView(); + + void focus() override; + + std::string title() const override { return "Keyfob (BETA)"; }; + +private: + NavigationView& nav_; + + // 1013210ns / bit + static constexpr uint32_t subaru_samples_per_bit = (OOK_SAMPLERATE * 0.00101321); + static constexpr uint32_t repeats = 4; + + uint8_t frame[10] { }; + + void generate_payload(size_t& bitstream_length); + size_t generate_frame(); + void start_tx(); + void on_tx_progress(const uint32_t progress, const bool done); + void update_progress(const uint32_t progress); + void on_make_change(size_t index); + void on_command_change(uint32_t value); + + // DEBUG + void update_symfields(); + + uint8_t subaru_get_checksum(); + bool subaru_is_valid(); + uint16_t subaru_get_code(); + void subaru_set_code(const uint16_t code); + int32_t subaru_get_command(); + void subaru_set_command(const uint32_t command); + + Labels labels { + { { 5 * 8, 1 * 16 }, "Make:", Color::light_grey() }, + { { 2 * 8, 2 * 16 }, "Command:", Color::light_grey() }, + { { 2 * 8, 4 * 16 }, "Payload: #####", Color::light_grey() }, + { { 2 * 8, 7 * 16 }, "Checksum is fixed just", Color::light_grey() }, + { { 2 * 8, 8 * 16 }, "before transmission.", Color::light_grey() }, + }; + + OptionsField options_make { + { 10 * 8, 1 * 16 }, + 8, + { + { "Subaru", 0 } + } + }; + + OptionsField options_command { + { 10 * 8, 2 * 16 }, + 6, + { + { "Lock", 1 }, + { "Unlock", 2 }, + { "Trunk", 11 }, + { "Panic", 10 } + } + }; + + SymField field_payload_a { + { 2 * 8, 5 * 16 }, + 10, + SymField::SYMFIELD_HEX + }; + SymField field_payload_b { + { 13 * 8, 5 * 16 }, + 10, + SymField::SYMFIELD_HEX + }; + + Text text_status { + { 2 * 8, 13 * 16, 128, 16 }, + "Ready" + }; + + ProgressBar progressbar { + { 2 * 8, 13 * 16 + 20, 208, 16 } + }; + + TransmitterView tx_view { + 16 * 16, + 0, + 15, + true + }; + + MessageHandlerRegistration message_handler_tx_progress { + Message::ID::TXProgress, + [this](const Message* const p) { + const auto message = *reinterpret_cast(p); + this->on_tx_progress(message.progress, message.done); + } + }; +}; + +} /* namespace ui */ diff --git a/firmware/application/ui_navigation.cpp b/firmware/application/ui_navigation.cpp index 7f12dc3b8..d07ef1f8f 100644 --- a/firmware/application/ui_navigation.cpp +++ b/firmware/application/ui_navigation.cpp @@ -31,18 +31,18 @@ #include "portapack_persistent_memory.hpp" #include "ui_about.hpp" -#include "ui_adsb_tx.hpp" #include "ui_adsb_rx.hpp" -#include "ui_aprs_tx.hpp" +#include "ui_adsb_tx.hpp" #include "ui_afsk_rx.hpp" +#include "ui_aprs_tx.hpp" #include "ui_bht_tx.hpp" #include "ui_coasterp.hpp" -#include "ui_siggen.hpp" #include "ui_debug.hpp" #include "ui_encoders.hpp" #include "ui_fileman.hpp" #include "ui_freqman.hpp" #include "ui_jammer.hpp" +#include "ui_keyfob.hpp" #include "ui_lcr.hpp" #include "ui_mictx.hpp" #include "ui_morse.hpp" @@ -50,11 +50,12 @@ #include "ui_nuoptix.hpp" #include "ui_playdead.hpp" #include "ui_pocsag_tx.hpp" -#include "ui_sonde.hpp" #include "ui_rds.hpp" -#include "ui_sd_wipe.hpp" #include "ui_scanner.hpp" +#include "ui_sd_wipe.hpp" #include "ui_setup.hpp" +#include "ui_siggen.hpp" +#include "ui_sonde.hpp" #include "ui_soundboard.hpp" #include "ui_sstvtx.hpp" #include "ui_touchtunes.hpp" @@ -312,11 +313,12 @@ TransmittersMenuView::TransmittersMenuView(NavigationView& nav) { { "APRS", ui::Color::grey(), &bitmap_icon_aprs, [&nav](){ nav.push(); } }, { "BHT Xy/EP", ui::Color::green(), &bitmap_icon_bht, [&nav](){ nav.push(); } }, { "Jammer", ui::Color::yellow(), &bitmap_icon_jammer, [&nav](){ nav.push(); } }, + { "Key fob", ui::Color::orange(), &bitmap_icon_keyfob, [&nav](){ nav.push(); } }, { "Microphone", ui::Color::green(), &bitmap_icon_microphone, [&nav](){ nav.push(); } }, { "Morse code", ui::Color::green(), &bitmap_icon_morse, [&nav](){ nav.push(); } }, { "NTTWorks burger pager", ui::Color::yellow(), &bitmap_icon_burger, [&nav](){ nav.push(); } }, { "Nuoptix DTMF timecode", ui::Color::green(), &bitmap_icon_nuoptix, [&nav](){ nav.push(); } }, - { "OOK remote encoders", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, + { "Generic OOK remotes", ui::Color::yellow(), &bitmap_icon_remote, [&nav](){ nav.push(); } }, { "POCSAG", ui::Color::green(), &bitmap_icon_pocsag, [&nav](){ nav.push(); } }, { "RDS", ui::Color::green(), &bitmap_icon_rds, [&nav](){ nav.push(); } }, { "Signal generator", ui::Color::green(), &bitmap_icon_cwgen, [&nav](){ nav.push(); } }, diff --git a/firmware/common/ui_widget.cpp b/firmware/common/ui_widget.cpp index c5e370880..f8674b4a1 100644 --- a/firmware/common/ui_widget.cpp +++ b/firmware/common/ui_widget.cpp @@ -1328,7 +1328,7 @@ uint64_t SymField::value_hex_u64() { if (type_ != SYMFIELD_DEF) { for (c = 0; c < length_; c++) - v += values_[c] << (4 * (length_ - 1 - c)); + v += (uint64_t)(values_[c]) << (4 * (length_ - 1 - c)); return v; } else return 0; diff --git a/firmware/graphics/icon_keyfob.png b/firmware/graphics/icon_keyfob.png new file mode 100644 index 000000000..9cb6826e3 Binary files /dev/null and b/firmware/graphics/icon_keyfob.png differ diff --git a/firmware/portapack-h1-havoc.bin b/firmware/portapack-h1-havoc.bin index 0edf721b6..252ccfbab 100644 Binary files a/firmware/portapack-h1-havoc.bin and b/firmware/portapack-h1-havoc.bin differ